Ignore:
Files:
2 added
2 deleted
14 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/application/canvasmain.py

    r11415 r11446  
    537537                    checked=True, 
    538538                    shortcut=QKeySequence(Qt.ControlModifier | 
    539                                           Qt.ShiftModifier | Qt.Key_D), 
     539                                          (Qt.ShiftModifier | Qt.Key_D)), 
    540540                    triggered=self.set_tool_dock_expanded) 
    541541 
  • Orange/OrangeCanvas/canvas/__init__.py

    r11369 r11442  
    44====== 
    55 
    6 The :mod:`canvas` package contains classes for visualizing the 
    7 contents of a :class:`.scheme.Scheme`, based on the Qt's Graphics view 
    8 framework. 
     6The :mod:`.canvas` package contains classes for visualizing the 
     7contents of a :class:`~.scheme.Scheme`, utilizing the Qt's `Graphics View 
     8Framework`_. 
     9 
     10.. _`Graphics View Framework`: http://qt-project.org/doc/qt-4.8/graphicsview.html 
    911 
    1012""" 
  • Orange/OrangeCanvas/canvas/items/annotationitem.py

    r11401 r11444  
    6262        return unicode(self.__placeholderText) 
    6363 
    64     placeholderText_ = Property(unicode, placeholderText, setPlaceholderText) 
     64    placeholderText_ = Property(unicode, placeholderText, setPlaceholderText, 
     65                                doc="Placeholder text") 
    6566 
    6667    def paint(self, painter, option, widget=None): 
     
    7374                     self.textInteractionFlags() & Qt.TextEditable): 
    7475            brect = self.boundingRect() 
     76            painter.setFont(self.font()) 
    7577            metrics = painter.fontMetrics() 
    7678            text = metrics.elidedText(self.__placeholderText, Qt.ElideRight, 
  • Orange/OrangeCanvas/canvas/items/linkitem.py

    r11369 r11442  
    11""" 
     2========= 
    23Link Item 
     4========= 
    35 
    46""" 
     
    1618 
    1719class LinkCurveItem(QGraphicsPathItem): 
    18     """Link curve item. The main component of `LinkItem`. 
     20    """ 
     21    Link curve item. The main component of a :class:`LinkItem`. 
    1922    """ 
    2023    def __init__(self, parent): 
    2124        QGraphicsPathItem.__init__(self, parent) 
    22         assert(isinstance(parent, LinkItem)) 
     25        if not isinstance(parent, LinkItem): 
     26            raise TypeError("'LinkItem' expected") 
     27 
    2328        self.setAcceptedMouseButtons(Qt.NoButton) 
    2429        self.__canvasLink = parent 
     
    3944 
    4045    def linkItem(self): 
    41         """Return the :class:`LinkItem` instance this curve belongs to. 
    42  
     46        """ 
     47        Return the :class:`LinkItem` instance this curve belongs to. 
    4348        """ 
    4449        return self.__canvasLink 
     
    8388 
    8489class LinkAnchorIndicator(QGraphicsEllipseItem): 
    85     """A visual indicator of the link anchor point at both ends 
    86     of the `LinkItem`. 
     90    """ 
     91    A visual indicator of the link anchor point at both ends 
     92    of the :class:`LinkItem`. 
    8793 
    8894    """ 
     
    108114class LinkItem(QGraphicsObject): 
    109115    """ 
    110     A Link in the canvas. 
    111     """ 
    112  
     116    A Link item in the canvas that connects two :class:`.NodeItem`\s in the 
     117    canvas. 
     118 
     119    The link curve connects two `Anchor` items (see :func:`setSourceItem` 
     120    and :func:`setSinkItem`). Once the anchors are set the curve 
     121    automatically adjusts its end points whenever the anchors move. 
     122 
     123    An optional source/sink text item can be displayed above the curve's 
     124    central point (:func:`setSourceName`, :func:`setSinkName`) 
     125 
     126    """ 
     127 
     128    #: Z value of the item 
    113129    Z_VALUE = 0 
    114     """Z value of the item""" 
    115130 
    116131    def __init__(self, *args): 
     
    146161    def setSourceItem(self, item, anchor=None): 
    147162        """ 
    148         Set the source `item` (:class:`NodeItem`). Use `anchor` 
    149         (:class:`AnchorPoint`) as the curve start point (if ``None`` a new 
    150         output anchor will be created). 
     163        Set the source `item` (:class:`.NodeItem`). Use `anchor` 
     164        (:class:`.AnchorPoint`) as the curve start point (if ``None`` a new 
     165        output anchor will be created using ``item.newOutputAnchor()``). 
    151166 
    152167        Setting item to ``None`` and a valid anchor is a valid operation 
     
    196211    def setSinkItem(self, item, anchor=None): 
    197212        """ 
    198         Set the sink `item` (:class:`NodeItem`). Use `anchor` 
    199         (:class:`AnchorPoint`) as the curve end point (if ``None`` a new 
    200         input anchor will be created). 
     213        Set the sink `item` (:class:`.NodeItem`). Use `anchor` 
     214        (:class:`.AnchorPoint`) as the curve end point (if ``None`` a new 
     215        input anchor will be created using ``item.newInputAnchor()``). 
    201216 
    202217        Setting item to ``None`` and a valid anchor is a valid operation 
     
    246261    def setFont(self, font): 
    247262        """ 
    248         Set the font for the channel names text. 
     263        Set the font for the channel names text item. 
    249264        """ 
    250265        if font != self.font(): 
     
    398413    def setEnabled(self, enabled): 
    399414        """ 
     415        Reimplemented from :class:`QGraphicsObject` 
     416 
    400417        Set link enabled state. When disabled the link is rendered with a 
    401418        dashed line. 
     
    425442    def setDynamic(self, dynamic): 
    426443        """ 
    427         Mark the link as dynamic (e.i. it responds to the 
    428         ``setDynamicEnabled``). 
     444        Mark the link as dynamic (i.e. it responds to 
     445        :func:`setDynamicEnabled`). 
    429446 
    430447        """ 
  • Orange/OrangeCanvas/canvas/items/nodeitem.py

    r11411 r11442  
    11""" 
    2 NodeItem 
     2========= 
     3Node Item 
     4========= 
    35 
    46""" 
     
    5254    Create and return a default palette for a node. 
    5355    """ 
    54     return create_palette(QColor(NAMED_COLORS["light-orange"]), 
    55                           QColor(NAMED_COLORS["orange"])) 
     56    return create_palette(QColor(NAMED_COLORS["light-yellow"]), 
     57                          QColor(NAMED_COLORS["yellow"])) 
    5658 
    5759 
     
    272274    """ 
    273275 
    274     # Signal emitted when the item's scene position changes. 
     276    #: Signal emitted when the item's scene position changes. 
    275277    scenePositionChanged = Signal(QPointF) 
    276278 
     279    #: Signal emitted when the item's `anchorDirection` changes. 
    277280    anchorDirectionChanged = Signal(QPointF) 
    278281 
     
    692695    """ 
    693696 
    694     # Scene position of the node has changed. 
     697    #: Signal emitted when the scene position of the node has changed. 
    695698    positionChanged = Signal() 
    696699 
    697     # Geometry of the channel anchors changed 
     700    #: Signal emitted when the geometry of the channel anchors changes. 
    698701    anchorGeometryChanged = Signal() 
    699702 
    700     # The item has been activated (by a mouse double click or a keyboard). 
     703    #: Signal emitted when the item has been activated (by a mouse double 
     704    #: click or a keyboard) 
    701705    activated = Signal() 
    702706 
    703     # The item is under the mouse. 
     707    #: The item is under the mouse. 
    704708    hovered = Signal() 
    705709 
     
    852856    def setIcon(self, icon): 
    853857        """ 
    854         Set the node item's icon. 
     858        Set the node item's icon (:class:`QIcon`). 
    855859        """ 
    856860        if isinstance(icon, QIcon): 
     
    993997    def newInputAnchor(self): 
    994998        """ 
    995         Create and return a new input anchor point. 
     999        Create and return a new input :class:`AnchorPoint`. 
    9961000        """ 
    9971001        if not (self.widget_description and self.widget_description.inputs): 
     
    10191023    def newOutputAnchor(self): 
    10201024        """ 
    1021         Create a new output anchor indicator. 
     1025        Create and return a new output :class:`AnchorPoint`. 
    10221026        """ 
    10231027        if not (self.widget_description and self.widget_description.outputs): 
     
    10451049    def inputAnchors(self): 
    10461050        """ 
    1047         Return a list of input anchor points. 
     1051        Return a list of all input anchor points. 
    10481052        """ 
    10491053        return self.inputAnchorItem.anchorPoints() 
     
    10511055    def outputAnchors(self): 
    10521056        """ 
    1053         Return a list of output anchor points. 
     1057        Return a list of all output anchor points. 
    10541058        """ 
    10551059        return self.outputAnchorItem.anchorPoints() 
  • Orange/OrangeCanvas/canvas/scene.py

    r11411 r11442  
    3636class CanvasScene(QGraphicsScene): 
    3737    """ 
    38     A Graphics Scene for displaying and editing an :class:`Scheme`. 
     38    A Graphics Scene for displaying an :class:`~.scheme.Scheme` instance. 
    3939    """ 
    4040 
    41     #: An node item has been added to the scene. 
     41    #: Signal emitted when a :class:`NodeItem` has been added to the scene. 
    4242    node_item_added = Signal(items.NodeItem) 
    4343 
    44     #: An node item has been removed from the scene 
     44    #: Signal emitted when a :class:`NodeItem` has been removed from the 
     45    #: scene. 
    4546    node_item_removed = Signal(items.LinkItem) 
    4647 
    47     #: A new link item has been added to the scene 
     48    #: Signal emitted when a new :class:`LinkItem` has been added to the 
     49    #: scene. 
    4850    link_item_added = Signal(items.LinkItem) 
    4951 
    50     #: Link item has been removed 
     52    #: Signal emitted when a :class:`LinkItem` has been removed. 
    5153    link_item_removed = Signal(items.LinkItem) 
    5254 
    53     #: Annotation item has been added 
     55    #: Signal emitted when a :class:`Annotation` item has been added. 
    5456    annotation_added = Signal(items.annotationitem.Annotation) 
    5557 
    56     #: Annotation item has been removed 
     58    #: Signal emitted when a :class:`Annotation` item has been removed. 
    5759    annotation_removed = Signal(items.annotationitem.Annotation) 
    5860 
    59     #: The position of a node has changed 
     61    #: Signal emitted when the position of a :class:`NodeItem` has changed. 
    6062    node_item_position_changed = Signal(items.NodeItem, QPointF) 
    6163 
    62     #: An node item has been double clicked 
     64    #: Signal emitted when an :class:`NodeItem` has been double clicked. 
    6365    node_item_double_clicked = Signal(items.NodeItem) 
    6466 
     
    122124 
    123125    def clear_scene(self): 
     126        """ 
     127        Clear (reset) the scene. 
     128        """ 
    124129        self.scheme = None 
    125130        self.__node_items = [] 
     
    138143 
    139144    def set_scheme(self, scheme): 
    140         """Set the scheme to display and edit. Populates the scene 
    141         with nodes and links already in the scheme. 
     145        """ 
     146        Set the scheme to display. Populates the scene with nodes and links 
     147        already in the scheme. Any further change to the scheme will be 
     148        reflected in the scene. 
     149 
     150        Parameters 
     151        ---------- 
     152        scheme : :class:`~.scheme.Scheme` 
    142153 
    143154        """ 
     
    194205 
    195206    def set_registry(self, registry): 
    196         """Set the widget registry. 
    197         """ 
     207        """ 
     208        Set the widget registry. 
     209        """ 
     210        # TODO: Remove/Deprecate. Is used only to get the category/background 
     211        # color. That should be part of the SchemeNode/WidgetDescription. 
    198212        log.info("Setting registry '%s on '%s'." % (registry, self)) 
    199213        self.registry = registry 
    200214 
    201215    def set_anchor_layout(self, layout): 
     216        """ 
     217        Set an :class:`~.layout.AnchorLayout` 
     218        """ 
    202219        if self.__anchor_layout != layout: 
    203220            if self.__anchor_layout: 
     
    208225 
    209226    def anchor_layout(self): 
     227        """ 
     228        Return the anchor layout instance. 
     229        """ 
    210230        return self.__anchor_layout 
    211231 
    212232    def set_channel_names_visible(self, visible): 
     233        """ 
     234        Set the channel names visibility. 
     235        """ 
    213236        self.__channel_names_visible = visible 
    214237        for link in self.__link_items: 
     
    216239 
    217240    def channel_names_visible(self): 
     241        """ 
     242        Return the channel names visibility state. 
     243        """ 
    218244        return self.__channel_names_visible 
    219245 
    220246    def set_node_animation_enabled(self, enabled): 
     247        """ 
     248        Set node animation enabled state. 
     249        """ 
    221250        if self.__node_animation_enabled != enabled: 
    222251            self.__node_animation_enabled = enabled 
     
    226255 
    227256    def add_node_item(self, item): 
    228         """Add a :class:`NodeItem` instance to the scene. 
     257        """ 
     258        Add a :class:`.NodeItem` instance to the scene. 
    229259        """ 
    230260        if item in self.__node_items: 
     
    261291 
    262292    def add_node(self, node): 
    263         """Add and return a default constructed `NodeItem` for a 
    264         `SchemeNode` instance. If the node is already in the scene 
    265         do nothing and just return its item. 
     293        """ 
     294        Add and return a default constructed :class:`.NodeItem` for a 
     295        :class:`SchemeNode` instance `node`. If the `node` is already in 
     296        the scene do nothing and just return its item. 
    266297 
    267298        """ 
     
    290321 
    291322    def new_node_item(self, widget_desc, category_desc=None): 
    292         """Construct an new `NodeItem` from a `WidgetDescription`. 
     323        """ 
     324        Construct an new :class:`.NodeItem` from a `WidgetDescription`. 
    293325        Optionally also set `CategoryDescription`. 
    294326 
     
    313345 
    314346    def remove_node_item(self, item): 
    315         """Remove `item` (:class:`NodeItem`) from the scene. 
     347        """ 
     348        Remove `item` (:class:`.NodeItem`) from the scene. 
    316349        """ 
    317350        self.activated_mapper.removePyMappings(item) 
     
    327360 
    328361    def remove_node(self, node): 
    329         """Remove the `NodeItem` instance that was previously constructed for 
    330         a `SchemeNode` node using the `add_node` method. 
     362        """ 
     363        Remove the :class:`.NodeItem` instance that was previously 
     364        constructed for a :class:`SchemeNode` `node` using the `add_node` 
     365        method. 
    331366 
    332367        """ 
     
    341376 
    342377    def node_items(self): 
    343         """Return all :class:`NodeItem` instances in the scene. 
     378        """ 
     379        Return all :class:`.NodeItem` instances in the scene. 
    344380        """ 
    345381        return list(self.__node_items) 
    346382 
    347383    def add_link_item(self, item): 
    348         """Add a link (:class:`LinkItem`)to the scene. 
     384        """ 
     385        Add a link (:class:`.LinkItem`) to the scene. 
    349386        """ 
    350387        if item.scene() is not self: 
     
    364401 
    365402    def add_link(self, scheme_link): 
    366         """Create and add a `LinkItem` instance for a `SchemeLink` 
    367         instance. If the link is already in the scene do nothing 
    368         and just return its `LinkItem`. 
     403        """ 
     404        Create and add a :class:`.LinkItem` instance for a 
     405        :class:`SchemeLink` instance. If the link is already in the scene 
     406        do nothing and just return its :class:`.LinkItem`. 
    369407 
    370408        """ 
     
    392430    def new_link_item(self, source_item, source_channel, 
    393431                      sink_item, sink_channel): 
    394         """Construct and return a new `LinkItem` 
     432        """ 
     433        Construct and return a new :class:`.LinkItem` 
    395434        """ 
    396435        item = items.LinkItem() 
     
    420459 
    421460    def remove_link_item(self, item): 
    422         """Remove a link (:class:`LinkItem`) from the scene. 
     461        """ 
     462        Remove a link (:class:`.LinkItem`) from the scene. 
    423463        """ 
    424464        # Invalidate the anchor layout. 
     
    443483 
    444484    def remove_link(self, scheme_link): 
    445         """ Remove a `LinkItem` instance that was previously constructed for 
    446         a `SchemeLink` node using the `add_link` method. 
     485        """ 
     486        Remove a :class:`.LinkItem` instance that was previously constructed 
     487        for a :class:`SchemeLink` instance `link` using the `add_link` method. 
    447488 
    448489        """ 
     
    452493 
    453494    def link_items(self): 
    454         """Return all :class:`LinkItems` in the scene. 
    455  
     495        """ 
     496        Return all :class:`.LinkItem`\s in the scene. 
    456497        """ 
    457498        return list(self.__link_items) 
    458499 
    459500    def add_annotation_item(self, annotation): 
    460         """Add an `Annotation` item to the scene. 
    461  
     501        """ 
     502        Add an :class:`.Annotation` item to the scene. 
    462503        """ 
    463504        self.__annotation_items.append(annotation) 
     
    467508 
    468509    def add_annotation(self, scheme_annot): 
    469         """Create a new item for :class:`SchemeAnnotation` and add it 
     510        """ 
     511        Create a new item for :class:`SchemeAnnotation` and add it 
    470512        to the scene. If the `scheme_annot` is already in the scene do 
    471513        nothing and just return its item. 
     
    504546 
    505547    def remove_annotation_item(self, annotation): 
    506         """Remove an `Annotation` item from the scene. 
     548        """ 
     549        Remove an :class:`.Annotation` instance from the scene. 
    507550 
    508551        """ 
     
    512555 
    513556    def remove_annotation(self, scheme_annotation): 
     557        """ 
     558        Remove an :class:`.Annotation` instance that was previously added 
     559        using :func:`add_anotation`. 
     560 
     561        """ 
    514562        item = self.__item_for_annotation.pop(scheme_annotation) 
    515563 
     
    526574 
    527575    def annotation_items(self): 
    528         """Return all `Annotation` items in the scene. 
    529  
     576        """ 
     577        Return all :class:`.Annotation` items in the scene. 
    530578        """ 
    531579        return self.__annotation_items 
     
    540588 
    541589    def commit_scheme_node(self, node): 
    542         """Commit the `node` into the scheme. 
     590        """ 
     591        Commit the `node` into the scheme. 
    543592        """ 
    544593        if not self.editable: 
     
    553602            self.scheme.add_node(node) 
    554603        except Exception: 
    555             log.error("An unexpected error occurred while commiting node '%s'", 
     604            log.error("An error occurred while committing node '%s'", 
    556605                      node, exc_info=True) 
    557606            # Cleanup (remove the node item) 
     
    563612 
    564613    def commit_scheme_link(self, link): 
    565         """Commit a scheme link. 
     614        """ 
     615        Commit a scheme link. 
    566616        """ 
    567617        if not self.editable: 
     
    576626 
    577627    def node_for_item(self, item): 
    578         """Return the `SchemeNode` for the `item`. 
     628        """ 
     629        Return the `SchemeNode` for the `item`. 
    579630        """ 
    580631        rev = dict([(v, k) for k, v in self.__item_for_node.items()]) 
     
    582633 
    583634    def item_for_node(self, node): 
    584         """Return the :class:`NodeItem` instance for a :class:`SchemeNode`. 
     635        """ 
     636        Return the :class:`NodeItem` instance for a :class:`SchemeNode`. 
    585637        """ 
    586638        return self.__item_for_node[node] 
    587639 
    588640    def link_for_item(self, item): 
    589         """Return the `SchemeLink for `item` (:class:`LinkItem`). 
     641        """ 
     642        Return the `SchemeLink for `item` (:class:`LinkItem`). 
    590643        """ 
    591644        rev = dict([(v, k) for k, v in self.__item_for_link.items()]) 
     
    593646 
    594647    def item_for_link(self, link): 
    595         """Return the :class:`LinkItem` for a :class:`SchemeLink` 
     648        """ 
     649        Return the :class:`LinkItem` for a :class:`SchemeLink` 
    596650        """ 
    597651        return self.__item_for_link[link] 
    598652 
    599653    def selected_node_items(self): 
    600         """Return the selected :class:`NodeItem`'s. 
     654        """ 
     655        Return the selected :class:`NodeItem`'s. 
    601656        """ 
    602657        return [item for item in self.__node_items if item.isSelected()] 
    603658 
    604659    def selected_annotation_items(self): 
    605         """Return the selected :class:`Annotation`'s 
     660        """ 
     661        Return the selected :class:`Annotation`'s 
    606662        """ 
    607663        return [item for item in self.__annotation_items if item.isSelected()] 
    608664 
    609665    def node_links(self, node_item): 
    610         """Return all links from the `node_item` (:class:`NodeItem`). 
     666        """ 
     667        Return all links from the `node_item` (:class:`NodeItem`). 
    611668        """ 
    612669        return self.node_output_links(node_item) + \ 
     
    614671 
    615672    def node_output_links(self, node_item): 
    616         """Return a list of all output links from `node_item`. 
     673        """ 
     674        Return a list of all output links from `node_item`. 
    617675        """ 
    618676        return [link for link in self.__link_items 
     
    620678 
    621679    def node_input_links(self, node_item): 
    622         """Return a list of all input links for `node_item`. 
     680        """ 
     681        Return a list of all input links for `node_item`. 
    623682        """ 
    624683        return [link for link in self.__link_items 
     
    626685 
    627686    def neighbor_nodes(self, node_item): 
    628         """Return a list of `node_item`'s (class:`NodeItem`) neighbor nodes. 
     687        """ 
     688        Return a list of `node_item`'s (class:`NodeItem`) neighbor nodes. 
    629689        """ 
    630690        neighbors = map(attrgetter("sourceItem"), 
     
    811871 
    812872def grab_svg(scene): 
    813     """Return a SVG rendering of the scene contents. 
     873    """ 
     874    Return a SVG rendering of the scene contents. 
     875 
     876    Parameters 
     877    ---------- 
     878    scene : :class:`CanvasScene` 
     879 
    814880    """ 
    815881    from PyQt4.QtSvg import QSvgGenerator 
  • Orange/OrangeCanvas/document/interactions.py

    r11401 r11450  
    11""" 
    2 User interaction handlers for CanvasScene. 
     2========================= 
     3User Interaction Handlers 
     4========================= 
     5 
     6User interaction handlers for a :class:`~.SchemeEditWidget`. 
     7 
     8User interactions encapsulate the logic of user interactions with the 
     9scheme document. 
     10 
     11All interactions are subclasses of :class:`UserInteraction`. 
     12 
    313 
    414""" 
     15 
    516import logging 
    617 
    718from PyQt4.QtGui import ( 
    8     QApplication, QGraphicsRectItem, QPen, QBrush, QColor, QFontMetrics 
     19    QApplication, QGraphicsRectItem, QPen, QBrush, QColor, QFontMetrics, 
     20    QUndoCommand 
    921) 
    1022 
     
    2133from ..gui.quickhelp import QuickHelpTipEvent 
    2234from . import commands 
     35from .editlinksdialog import EditLinksDialog 
    2336 
    2437log = logging.getLogger(__name__) 
     
    2639 
    2740class UserInteraction(QObject): 
    28     # cancel reason flags 
    29     NoReason = 0  # No specified reason 
    30     UserCancelReason = 1  # User canceled the operation (e.g. pressing ESC) 
    31     InteractionOverrideReason = 3  # Another interaction was set 
    32     ErrorReason = 4  # An internal error occurred 
     41    """ 
     42    Base class for user interaction handlers. 
     43 
     44    Parameters 
     45    ---------- 
     46    document : :class:`~.SchemeEditWidget` 
     47        An scheme editor instance with which the user is interacting. 
     48    parent : :class:`QObject`, optional 
     49        A parent QObject 
     50    deleteOnEnd : bool, optional 
     51        Should the UserInteraction be deleted when it finishes (``True`` 
     52        by default). 
     53 
     54    """ 
     55    # Cancel reason flags 
     56 
     57    #: No specified reason 
     58    NoReason = 0 
     59    #: User canceled the operation (e.g. pressing ESC) 
     60    UserCancelReason = 1 
     61    #: Another interaction was set 
     62    InteractionOverrideReason = 3 
     63    #: An internal error occurred 
     64    ErrorReason = 4 
     65    #: Other (unspecified) reason 
    3366    OtherReason = 5 
    3467 
    35     # Emitted when the interaction is set on the scene. 
     68    #: Emitted when the interaction is set on the scene. 
    3669    started = Signal() 
    3770 
    38     # Emitted when the interaction finishes successfully. 
     71    #: Emitted when the interaction finishes successfully. 
    3972    finished = Signal() 
    4073 
    41     # Emitted when the interaction ends (canceled or finished) 
     74    #: Emitted when the interaction ends (canceled or finished) 
    4275    ended = Signal() 
    4376 
    44     # Emitted when the interaction is canceled. 
     77    #: Emitted when the interaction is canceled. 
    4578    canceled = Signal([], [int]) 
    4679 
     
    5992 
    6093    def start(self): 
    61         """Start the interaction. This is called by the scene when 
     94        """ 
     95        Start the interaction. This is called by the :class:`CanvasScene` when 
    6296        the interaction is installed. 
    6397 
    64         Must be called from subclass implementations. 
     98        .. note:: Must be called from subclass implementations. 
    6599 
    66100        """ 
     
    68102 
    69103    def end(self): 
    70         """Finish the interaction. Restore any leftover state in 
    71         this method. 
    72  
    73         .. note:: This gets called from the default `cancel` implementation. 
     104        """ 
     105        Finish the interaction. Restore any leftover state in this method. 
     106 
     107        .. note:: This gets called from the default :func:`cancel` 
     108                  implementation. 
    74109 
    75110        """ 
     
    91126 
    92127    def cancel(self, reason=OtherReason): 
    93         """Cancel the interaction for `reason`. 
     128        """ 
     129        Cancel the interaction with `reason`. 
    94130        """ 
    95131 
     
    100136 
    101137    def isFinished(self): 
    102         """Has the interaction finished. 
     138        """ 
     139        Is the interaction finished. 
    103140        """ 
    104141        return self.__finished 
    105142 
    106143    def isCanceled(self): 
    107         """Was the interaction canceled. 
     144        """ 
     145        Was the interaction canceled. 
    108146        """ 
    109147        return self.__canceled 
    110148 
    111149    def cancelReason(self): 
    112         """Return the reason the interaction was canceled. 
     150        """ 
     151        Return the reason the interaction was canceled. 
    113152        """ 
    114153        return self.__cancelReason 
    115154 
    116155    def mousePressEvent(self, event): 
     156        """ 
     157        Handle a `QGraphicsScene.mousePressEvent`. 
     158        """ 
    117159        return False 
    118160 
    119161    def mouseMoveEvent(self, event): 
     162        """ 
     163        Handle a `GraphicsScene.mouseMoveEvent`. 
     164        """ 
    120165        return False 
    121166 
    122167    def mouseReleaseEvent(self, event): 
     168        """ 
     169        Handle a `QGraphicsScene.mouseReleaseEvent`. 
     170        """ 
    123171        return False 
    124172 
    125173    def mouseDoubleClickEvent(self, event): 
     174        """ 
     175        Handle a `QGraphicsScene.mouseDoubleClickEvent`. 
     176        """ 
    126177        return False 
    127178 
    128179    def keyPressEvent(self, event): 
     180        """ 
     181        Handle a `QGraphicsScene.keyPressEvent` 
     182        """ 
    129183        if self.cancelOnEsc and event.key() == Qt.Key_Escape: 
    130184            self.cancel(self.UserCancelReason) 
     
    132186 
    133187    def keyReleaseEvent(self, event): 
     188        """ 
     189        Handle a `QGraphicsScene.keyPressEvent` 
     190        """ 
    134191        return False 
    135192 
     
    139196 
    140197 
     198class UserCanceledError(ValueError): 
     199    pass 
     200 
     201 
    141202def reversed_arguments(func): 
    142     """Return a function with reversed argument order. 
     203    """ 
     204    Return a function with reversed argument order. 
    143205    """ 
    144206    def wrapped(*args): 
     
    148210 
    149211class NewLinkAction(UserInteraction): 
    150     """User drags a new link from an existing node anchor item to create 
     212    """ 
     213    User drags a new link from an existing `NodeAnchorItem` to create 
    151214    a connection between two existing nodes or to a new node if the release 
    152215    is over an empty area, in which case a quick menu for new node selection 
     
    165228        self.direction = None 
    166229 
     230        # An `NodeItem` currently under the mouse as a possible 
     231        # link drop target. 
    167232        self.current_target_item = None 
     233        # A temporary `LinkItem` used while dragging. 
    168234        self.tmp_link_item = None 
     235        # An temporary `AnchorPoint` inserted into `current_target_item` 
    169236        self.tmp_anchor_point = None 
     237        # An `AnchorPoint` following the mouse cursor 
    170238        self.cursor_anchor_point = None 
     239        # An QUndoCommand 
     240        self.macro = None 
    171241 
    172242    def remove_tmp_anchor(self): 
    173         """Remove a temp anchor point from the current target item. 
     243        """ 
     244        Remove a temporary anchor point from the current target item. 
    174245        """ 
    175246        if self.direction == self.FROM_SOURCE: 
     
    180251 
    181252    def create_tmp_anchor(self, item): 
    182         """Create a new tmp anchor at the item (`NodeItem`). 
     253        """ 
     254        Create a new tmp anchor at the `item` (:class:`NodeItem`). 
    183255        """ 
    184256        assert(self.tmp_anchor_point is None) 
     
    189261 
    190262    def can_connect(self, target_item): 
    191         """Is the connection between `self.from_item` (item where the drag 
    192         started) and `target_item`. 
     263        """ 
     264        Is the connection between `self.from_item` (item where the drag 
     265        started) and `target_item` possible. 
    193266 
    194267        """ 
     
    202275 
    203276    def set_link_target_anchor(self, anchor): 
    204         """Set the temp line target anchor 
     277        """ 
     278        Set the temp line target anchor. 
    205279        """ 
    206280        if self.direction == self.FROM_SOURCE: 
     
    210284 
    211285    def target_node_item_at(self, pos): 
    212         """Return a suitable NodeItem on which a link can be dropped. 
    213         """ 
    214         # Test for a suitable NodeAnchorItem or NodeItem at pos. 
     286        """ 
     287        Return a suitable :class:`NodeItem` at position `pos` on which 
     288        a link can be dropped. 
     289 
     290        """ 
     291        # Test for a suitable `NodeAnchorItem` or `NodeItem` at pos. 
    215292        if self.direction == self.FROM_SOURCE: 
    216293            anchor_type = items.SinkAnchorItem 
     
    317394            node = None 
    318395            stack = self.document.undoStack() 
    319             stack.beginMacro("Add link") 
     396 
     397            self.macro = QUndoCommand(self.tr("Add link")) 
    320398 
    321399            if item: 
    322                 # If the release was over a widget item 
    323                 # then connect them 
     400                # If the release was over a node item then connect them 
    324401                node = self.scene.node_for_item(item) 
    325402            else: 
     
    334411 
    335412                if node is not None: 
    336                     self.document.addNode(node) 
     413                    commands.AddNodeCommand(self.scheme, node, 
     414                                            parent=self.macro) 
    337415 
    338416            if node is not None: 
    339                 self.connect_existing(node) 
    340             else: 
    341                 self.end() 
    342  
    343             stack.endMacro() 
     417                if self.direction == self.FROM_SOURCE: 
     418                    source_node = self.scene.node_for_item(self.source_item) 
     419                    sink_node = node 
     420                else: 
     421                    source_node = node 
     422                    sink_node = self.scene.node_for_item(self.sink_item) 
     423                self.connect_nodes(source_node, sink_node) 
     424 
     425                if not self.isCanceled() or not self.isFinished() and \ 
     426                        self.macro is not None: 
     427                    # Push (commit) the add link/node action on the stack. 
     428                    stack.push(self.macro) 
     429 
     430            self.end() 
     431 
    344432        else: 
    345433            self.end() 
     
    347435 
    348436    def create_new(self, event): 
    349         """Create and return a new node with a QuickWidgetMenu. 
     437        """ 
     438        Create and return a new node with a `QuickMenu`. 
    350439        """ 
    351440        pos = event.screenPos() 
     
    390479            return node 
    391480 
    392     def connect_existing(self, node): 
    393         """Connect anchor_item to `node`. 
    394         """ 
    395         if self.direction == self.FROM_SOURCE: 
    396             source_item = self.source_item 
    397             source_node = self.scene.node_for_item(source_item) 
    398             sink_node = node 
    399         else: 
    400             source_node = node 
    401             sink_item = self.sink_item 
    402             sink_node = self.scene.node_for_item(sink_item) 
    403  
     481    def connect_nodes(self, source_node, sink_node): 
     482        """ 
     483        Connect `source_node` to `sink_node`. If there are more then one 
     484        equally weighted and non conflicting links possible present a 
     485        detailed dialog for link editing. 
     486 
     487        """ 
    404488        try: 
    405489            possible = self.scheme.propose_links(source_node, sink_node) 
     
    412496 
    413497            source, sink, w = possible[0] 
     498 
     499            # just a list of signal tuples for now, will be converted 
     500            # to SchemeLinks later 
    414501            links_to_add = [(source, sink)] 
    415  
     502            links_to_remove = [] 
    416503            show_link_dialog = False 
    417504 
     
    429516                    show_link_dialog = True 
    430517 
    431                 if show_link_dialog: 
    432                     existing = self.scheme.find_links(source_node=source_node, 
    433                                                       sink_node=sink_node) 
    434  
    435                     if existing: 
    436                         # EditLinksDialog will populate the view with 
    437                         # existing links 
    438                         initial_links = None 
    439                     else: 
    440                         initial_links = [(source, sink)] 
    441  
    442                     links_action = EditNodeLinksAction( 
    443                                     self.document, source_node, sink_node) 
    444                     try: 
    445                         links_action.edit_links(initial_links) 
    446                     except Exception: 
    447                         log.error("'EditNodeLinksAction' failed", 
    448                                   exc_info=True) 
    449                         raise 
    450                     # EditNodeLinksAction already commits the links on accepted 
    451                     links_to_add = [] 
    452  
    453             for source, sink in links_to_add: 
    454                 if sink.single: 
    455                     # Remove an existing link to the sink channel if present. 
    456                     existing_link = self.scheme.find_links( 
    457                         sink_node=sink_node, sink_channel=sink 
     518            if show_link_dialog: 
     519                existing = self.scheme.find_links(source_node=source_node, 
     520                                                  sink_node=sink_node) 
     521 
     522                if existing: 
     523                    # edit_links will populate the view with existing links 
     524                    initial_links = None 
     525                else: 
     526                    initial_links = [(source, sink)] 
     527 
     528                try: 
     529                    rstatus, links_to_add, links_to_remove = self.edit_links( 
     530                        source_node, sink_node, initial_links 
    458531                    ) 
    459  
    460                     if existing_link: 
    461                         self.document.removeLink(existing_link[0]) 
    462  
    463                 # Check if the new link is a duplicate of an existing link 
     532                except Exception: 
     533                    log.error("Failed to edit the links", 
     534                              exc_info=True) 
     535                    raise 
     536                if rstatus == EditLinksDialog.Rejected: 
     537                    raise UserCanceledError 
     538            else: 
     539                # links_to_add now needs to be a list of actual SchemeLinks 
     540                links_to_add = [scheme.SchemeLink( 
     541                                    source_node, source_channel, 
     542                                    sink_node, sink_channel) 
     543                                for source_channel, sink_channel 
     544                                in links_to_add] 
     545 
     546                links_to_add, links_to_remove = \ 
     547                    add_links_plan(self.scheme, links_to_add) 
     548 
     549            # Remove temp items before creating any new links 
     550            self.cleanup() 
     551 
     552            for link in links_to_remove: 
     553                commands.RemoveLinkCommand(self.scheme, link, 
     554                                           parent=self.macro) 
     555 
     556            for link in links_to_add: 
     557                # Check if the new requested link is a duplicate of an 
     558                # existing link 
    464559                duplicate = self.scheme.find_links( 
    465                     source_node, source, sink_node, sink 
     560                    link.source_node, link.source_channel, 
     561                    link.sink_node, link.sink_channel 
    466562                ) 
    467563 
    468                 if duplicate: 
    469                     # Do nothing. 
    470                     continue 
    471  
    472                 # Remove temp items before creating a new link 
    473                 self.cleanup() 
    474  
    475                 link = scheme.SchemeLink(source_node, source, sink_node, sink) 
    476                 self.document.addLink(link) 
     564                if not duplicate: 
     565                    commands.AddLinkCommand(self.scheme, link, 
     566                                            parent=self.macro) 
    477567 
    478568        except scheme.IncompatibleChannelTypeError: 
     
    485575            log.info("Cannot connect: no possible links.") 
    486576            self.cancel() 
     577        except UserCanceledError: 
     578            log.info("User canceled a new link action.") 
     579            self.cancel(UserInteraction.UserCancelReason) 
    487580        except Exception: 
    488581            log.error("An error occurred during the creation of a new link.", 
     
    490583            self.cancel() 
    491584 
    492         if not self.isFinished(): 
    493             self.end() 
     585    def edit_links(self, source_node, sink_node, initial_links=None): 
     586        """ 
     587        Show and execute the `EditLinksDialog`. 
     588        Optional `initial_links` list can provide a list of initial 
     589        `(source, sink)` channel tuples to show in the view, otherwise 
     590        the dialog is populated with existing links in the scheme (passing 
     591        an empty list will disable all initial links). 
     592 
     593        """ 
     594        status, links_to_add, links_to_remove = \ 
     595            edit_links( 
     596                self.scheme, source_node, sink_node, initial_links, 
     597                parent=self.document 
     598            ) 
     599 
     600        if status == EditLinksDialog.Accepted: 
     601            links_to_add = [scheme.SchemeLink( 
     602                                source_node, source_channel, 
     603                                sink_node, sink_channel) 
     604                            for source_channel, sink_channel in links_to_add] 
     605 
     606            links_to_remove = [self.scheme.find_links( 
     607                                   source_node, source_channel, 
     608                                   sink_node, sink_channel) 
     609                               for source_channel, sink_channel 
     610                               in links_to_remove] 
     611 
     612            links_to_remove = reduce(list.__add__, links_to_remove, []) 
     613            conflicting = filter(None, 
     614                                 [conflicting_single_link(self.scheme, link) 
     615                                  for link in links_to_add]) 
     616            for link in conflicting: 
     617                if link not in links_to_remove: 
     618                    links_to_remove.append(link) 
     619 
     620            return status, links_to_add, links_to_remove 
     621        else: 
     622            return status, [], [] 
    494623 
    495624    def end(self): 
    496625        self.cleanup() 
     626        # Remove the help tip set in mousePressEvent 
     627        self.macro = None 
    497628        helpevent = QuickHelpTipEvent("", "") 
    498629        QCoreApplication.postEvent(self.document, helpevent) 
     
    504635 
    505636    def cleanup(self): 
    506         """Cleanup all temp items in the scene that are left. 
     637        """ 
     638        Cleanup all temporary items in the scene that are left. 
    507639        """ 
    508640        if self.tmp_link_item: 
     
    524656 
    525657 
     658def edit_links(scheme, source_node, sink_node, initial_links=None, 
     659               parent=None): 
     660    """ 
     661    Show and execute the `EditLinksDialog`. 
     662    Optional `initial_links` list can provide a list of initial 
     663    `(source, sink)` channel tuples to show in the view, otherwise 
     664    the dialog is populated with existing links in the scheme (passing 
     665    an empty list will disable all initial links). 
     666 
     667    """ 
     668    log.info("Constructing a Link Editor dialog.") 
     669 
     670    dlg = EditLinksDialog(parent) 
     671 
     672    # all SchemeLinks between the two nodes. 
     673    links = scheme.find_links(source_node=source_node, sink_node=sink_node) 
     674    existing_links = [(link.source_channel, link.sink_channel) 
     675                      for link in links] 
     676 
     677    if initial_links is None: 
     678        initial_links = list(existing_links) 
     679 
     680    dlg.setNodes(source_node, sink_node) 
     681    dlg.setLinks(initial_links) 
     682 
     683    log.info("Executing a Link Editor Dialog.") 
     684    rval = dlg.exec_() 
     685 
     686    if rval == EditLinksDialog.Accepted: 
     687        edited_links = dlg.links() 
     688 
     689        # Differences 
     690        links_to_add = set(edited_links) - set(existing_links) 
     691        links_to_remove = set(existing_links) - set(edited_links) 
     692        return rval, list(links_to_add), list(links_to_remove) 
     693    else: 
     694        return rval, [], [] 
     695 
     696 
     697def add_links_plan(scheme, links, force_replace=False): 
     698    """ 
     699    Return a plan for adding a list of links to the scheme. 
     700    """ 
     701    links_to_add = list(links) 
     702    links_to_remove = [conflicting_single_link(scheme, link) 
     703                       for link in links] 
     704    links_to_remove = filter(None, links_to_remove) 
     705 
     706    if not force_replace: 
     707        links_to_add, links_to_remove = remove_duplicates(links_to_add, 
     708                                                          links_to_remove) 
     709    return links_to_add, links_to_remove 
     710 
     711 
     712def conflicting_single_link(scheme, link): 
     713    """ 
     714    Find and return an existing link in `scheme` connected to the same 
     715    input channel as `link` if the channel has the 'single' flag. 
     716    If no such channel exists (or sink channel is not 'single') 
     717    return `None`. 
     718 
     719    """ 
     720 
     721    if link.sink_channel.single: 
     722        existing = scheme.find_links( 
     723            sink_node=link.sink_node, 
     724            sink_channel=link.sink_channel 
     725        ) 
     726 
     727        if existing: 
     728            assert len(existing) == 1 
     729            return existing[0] 
     730    return None 
     731 
     732 
     733def remove_duplicates(links_to_add, links_to_remove): 
     734    def link_key(link): 
     735        return (link.source_node, link.source_channel, 
     736                link.sink_node, link.sink_channel) 
     737 
     738    add_keys = map(link_key, links_to_add) 
     739    remove_keys = map(link_key, links_to_remove) 
     740    duplicate_keys = set(add_keys).intersection(remove_keys) 
     741 
     742    def not_duplicate(link): 
     743        return link_key(link) not in duplicate_keys 
     744 
     745    links_to_add = filter(not_duplicate, links_to_add) 
     746    links_to_remove = filter(not_duplicate, links_to_remove) 
     747    return links_to_add, links_to_remove 
     748 
     749 
    526750class NewNodeAction(UserInteraction): 
    527     """Present the user with a quick menu for node selection and 
     751    """ 
     752    Present the user with a quick menu for node selection and 
    528753    create the selected node. 
    529754 
     
    536761 
    537762    def create_new(self, pos): 
    538         """Create a new widget with a QuickWidgetMenu at `pos` 
    539         (in screen coordinates). 
     763        """ 
     764        Create a new widget with a `QuickMenu` at `pos` (in screen 
     765        coordinates). 
    540766 
    541767        """ 
     
    558784 
    559785class RectangleSelectionAction(UserInteraction): 
    560     """Select items in the scene using a Rectangle selection 
     786    """ 
     787    Select items in the scene using a Rectangle selection 
    561788    """ 
    562789    def __init__(self, document, *args, **kwargs): 
    563790        UserInteraction.__init__(self, document, *args, **kwargs) 
     791        # The initial selection at drag start 
    564792        self.initial_selection = None 
     793        # Selection when last updated in a mouseMoveEvent 
    565794        self.last_selection = None 
     795        # A selection rect (`QRectF`) 
    566796        self.selection_rect = None 
     797        # Keyboard modifiers 
    567798        self.modifiers = 0 
    568799 
     
    603834    def mouseMoveEvent(self, event): 
    604835        if not self.rect_item.scene(): 
     836            # Add the rect item to the scene when the mouse moves. 
    605837            self.scene.addItem(self.rect_item) 
    606838        self.update_selection(event) 
     
    618850 
    619851    def update_selection(self, event): 
     852        """ 
     853        Update the selection rectangle from a QGraphicsSceneMouseEvent 
     854        `event` instance. 
     855 
     856        """ 
    620857        if self.initial_selection is None: 
    621858            self.initial_selection = set(self.scene.selectedItems()) 
     
    625862        self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) 
    626863 
     864        # Make sure the rect_item does not cause the scene rect to grow. 
    627865        rect = self._bound_selection_rect(self.selection_rect.normalized()) 
    628866 
    629         # Need that constant otherwise the sceneRect will still grow 
     867        # Need that 0.5 constant otherwise the sceneRect will still 
     868        # grow (anti-aliasing correction by QGraphicsScene?) 
    630869        pw = self.rect_item.pen().width() + 0.5 
    631870 
     
    662901 
    663902    def viewport_rect(self): 
    664         """Return the bounding rect of the document's viewport on the 
    665         scene. 
    666  
     903        """ 
     904        Return the bounding rect of the document's viewport on the scene. 
    667905        """ 
    668906        view = self.document.view() 
     
    672910 
    673911    def _bound_selection_rect(self, rect): 
    674         """Bound the selection `rect` to a sensible size. 
     912        """ 
     913        Bound the selection `rect` to a sensible size. 
    675914        """ 
    676915        srect = self.scene.sceneRect() 
     
    681920 
    682921class EditNodeLinksAction(UserInteraction): 
     922    """ 
     923    Edit multiple links between two :class:`SchemeNode` instances using 
     924    a :class:`EditLinksDialog` 
     925 
     926    Parameters 
     927    ---------- 
     928    document : :class:`SchemeEditWidget` 
     929        The editor widget. 
     930    source_node : :class:`SchemeNode` 
     931        The source (link start) node for the link editor. 
     932    sink_node : :class:`SchemeNode` 
     933        The sink (link end) node for the link editor. 
     934 
     935    """ 
    683936    def __init__(self, document, source_node, sink_node, *args, **kwargs): 
    684937        UserInteraction.__init__(self, document, *args, **kwargs) 
     
    689942        """ 
    690943        Show and execute the `EditLinksDialog`. 
    691         Optional `initial_links` list can provide the initial 
     944        Optional `initial_links` list can provide a list of initial 
    692945        `(source, sink)` channel tuples to show in the view, otherwise 
    693         the dialog is populated with existing links in the scheme 
    694         (pass an empty list to disable all initial links). 
    695  
    696         """ 
    697         from ..canvas.editlinksdialog import EditLinksDialog 
    698  
     946        the dialog is populated with existing links in the scheme (passing 
     947        an empty list will disable all initial links). 
     948 
     949        """ 
    699950        log.info("Constructing a Link Editor dialog.") 
    700951 
    701         parent = self.scene.views()[0] 
    702         dlg = EditLinksDialog(parent) 
     952        dlg = EditLinksDialog(self.document) 
    703953 
    704954        links = self.scheme.find_links(source_node=self.source_node, 
     
    725975            stack.beginMacro("Edit Links") 
    726976 
    727             # First remove links into a single sink channel, 
     977            # First remove links into a 'Single' sink channel, 
    728978            # but only the ones that do not have self.source_node as 
    729979            # a source (they will be removed later from links_to_remove) 
     
    7601010 
    7611011def point_to_tuple(point): 
    762     return point.x(), point.y() 
     1012    """ 
     1013    Convert a QPointF into a (x, y) tuple. 
     1014    """ 
     1015    return (point.x(), point.y()) 
    7631016 
    7641017 
    7651018class NewArrowAnnotation(UserInteraction): 
    766     """Create a new arrow annotation. 
     1019    """ 
     1020    Create a new arrow annotation handler. 
    7671021    """ 
    7681022    def __init__(self, document, *args, **kwargs): 
     
    7891043 
    7901044    def setColor(self, color): 
     1045        """ 
     1046        Set the color for the new arrow. 
     1047        """ 
    7911048        self.color = color 
    7921049 
     
    8521109 
    8531110def rect_to_tuple(rect): 
     1111    """ 
     1112    Convert a QRectF into a (x, y, width, height) tuple. 
     1113    """ 
    8541114    return rect.x(), rect.y(), rect.width(), rect.height() 
    8551115 
    8561116 
    8571117class NewTextAnnotation(UserInteraction): 
     1118    """ 
     1119    A New Text Annotation interaction handler 
     1120    """ 
    8581121    def __init__(self, document, *args, **kwargs): 
    8591122        UserInteraction.__init__(self, document, *args, **kwargs) 
     
    8841147 
    8851148    def createNewAnnotation(self, rect): 
    886         """Create a new TextAnnotation at with `rect` as the geometry. 
     1149        """ 
     1150        Create a new TextAnnotation at with `rect` as the geometry. 
    8871151        """ 
    8881152        annot = scheme.SchemeTextAnnotation(rect_to_tuple(rect)) 
     
    9521216 
    9531217    def defaultTextGeometry(self, point): 
    954         """Return the default text geometry. Used in case the user 
    955         single clicked in the scene. 
     1218        """ 
     1219        Return the default text geometry. Used in case the user single 
     1220        clicked in the scene. 
    9561221 
    9571222        """ 
     
    9831248 
    9841249class ResizeTextAnnotation(UserInteraction): 
     1250    """ 
     1251    Resize a Text Annotation interaction handler. 
     1252    """ 
    9851253    def __init__(self, document, *args, **kwargs): 
    9861254        UserInteraction.__init__(self, document, *args, **kwargs) 
     
    10221290 
    10231291    def commit(self): 
    1024         """Commit the current item geometry state to the document. 
     1292        """ 
     1293        Commit the current item geometry state to the document. 
    10251294        """ 
    10261295        rect = self.item.geometry() 
     
    10681337 
    10691338class ResizeArrowAnnotation(UserInteraction): 
     1339    """ 
     1340    Resize an Arrow Annotation interaction handler. 
     1341    """ 
    10701342    def __init__(self, document, *args, **kwargs): 
    10711343        UserInteraction.__init__(self, document, *args, **kwargs) 
  • Orange/OrangeCanvas/document/schemeedit.py

    r11425 r11451  
    449449            self.__quickMenuTriggers = triggers 
    450450 
    451     def quickMenuTriggres(self): 
     451    def quickMenuTriggers(self): 
    452452        """ 
    453453        Return quick menu trigger flags. 
  • Orange/OrangeCanvas/gui/lineedit.py

    r11366 r11452  
    107107        slot = _ActionSlot(position, action, button, False) 
    108108        self.__actions[position - 1] = slot 
     109 
     110        if not self.testAttribute(Qt.WA_Resized): 
     111            # Need some sensible height to do the layout. 
     112            self.adjustSize() 
     113 
    109114        self.__layoutActions() 
    110115 
  • Orange/OrangeCanvas/gui/toolbox.py

    r11417 r11441  
    6262    def setNativeStyling(self, state): 
    6363        """ 
    64         Render tab buttons as native :class:`QToolButtons`. 
    65         If set to `False` (default) the button is pained using a 
    66         custom routine. 
     64        Render tab buttons as native (or css styled) :class:`QToolButtons`. 
     65        If set to `False` (default) the button is pained using a custom 
     66        paint routine. 
    6767 
    6868        """ 
     
    231231            self.__tabActionGroup.setExclusive(exclusive) 
    232232            checked = self.__tabActionGroup.checkedAction() 
     233            if checked is None: 
     234                # The action group can be out of sync with the actions state 
     235                # when switching between exclusive states. 
     236                actions_checked = [page.action for page in self.__pages 
     237                                   if page.action.isChecked()] 
     238                if actions_checked: 
     239                    checked = actions_checked[0] 
     240 
    233241            # Trigger/toggle remaining open pages 
    234242            if exclusive and checked is not None: 
  • Orange/OrangeCanvas/registry/__init__.py

    r11418 r11440  
    3838     "yellow": "#F0EC4F", 
    3939     } 
     40 
     41 
     42# default color when the category does not provide it 
     43DEFAULT_COLOR = "light-yellow" 
    4044 
    4145 
  • Orange/OrangeCanvas/registry/qt.py

    r11393 r11440  
    2121from ..resources import icon_loader 
    2222 
    23 from . import cache, NAMED_COLORS 
     23from . import cache, NAMED_COLORS, DEFAULT_COLOR 
    2424 
    2525 
     
    219219            background = desc.background 
    220220        else: 
    221             background = "light-yellow" 
     221            background = DEFAULT_COLOR 
    222222 
    223223        background = NAMED_COLORS.get(background, background) 
     
    254254        elif category.background: 
    255255            background = category.background 
     256        else: 
     257            background = DEFAULT_COLOR 
    256258 
    257259        if background is not None: 
  • docs/canvas/canvas.items.nodeitem.rst

    r11369 r11442  
    1010.. autoclass:: NodeItem 
    1111   :members: 
    12    :exclude-members: from_node, from_node_meta, setupGraphics,  setProgressMessage 
     12   :exclude-members: 
     13      from_node, 
     14      from_node_meta, 
     15      setupGraphics, 
     16      setProgressMessage, 
     17      positionChanged, 
     18      anchorGeometryChanged, 
     19      activated, 
     20      hovered 
    1321   :member-order: bysource 
    1422   :show-inheritance: 
     
    1624   .. autoattribute:: positionChanged() 
    1725 
    18       Signal emitted when the scene position of the node has changes. 
    19  
    2026   .. autoattribute:: anchorGeometryChanged() 
    21  
    22       Signal emitted when the geometry of the channel anchors changes. 
    2327 
    2428   .. autoattribute:: activated() 
    2529 
    26       Signal emitted when the item has been activated (by a mouse double 
    27       click or a keyboard) 
     30 
     31.. autoclass:: AnchorPoint 
     32   :members: 
     33   :exclude-members: 
     34      scenePositionChanged, 
     35      anchorDirectionChanged 
     36   :member-order: bysource 
     37   :show-inheritance: 
     38 
     39   .. autoattribute:: scenePositionChanged(QPointF) 
     40 
     41   .. autoattribute:: anchorDirectionChanged(QPointF) 
  • docs/canvas/canvas.scene.rst

    r11369 r11442  
    4646 
    4747   .. autoattribute:: link_item_hovered(LinkItem) 
     48 
     49 
     50.. autofunction:: grab_svg 
Note: See TracChangeset for help on using the changeset viewer.