Ignore:
Timestamp:
04/08/13 10:01:22 (13 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Refactored link edit interactions code into smaller functions.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/document/interactions.py

    r11447 r11449  
    407407 
    408408            if node is not None: 
    409                 self.connect_existing(node) 
     409                if self.direction == self.FROM_SOURCE: 
     410                    source_node = self.scene.node_for_item(self.source_item) 
     411                    sink_node = node 
     412                else: 
     413                    source_node = node 
     414                    sink_node = self.scene.node_for_item(self.sink_item) 
     415                self.connect_nodes(source_node, sink_node) 
    410416            else: 
    411417                self.end() 
     
    461467            return node 
    462468 
    463     def connect_existing(self, node): 
    464         """ 
    465         Connect anchor_item to `node`. 
    466         """ 
    467         if self.direction == self.FROM_SOURCE: 
    468             source_item = self.source_item 
    469             source_node = self.scene.node_for_item(source_item) 
    470             sink_node = node 
    471         else: 
    472             source_node = node 
    473             sink_item = self.sink_item 
    474             sink_node = self.scene.node_for_item(sink_item) 
    475  
     469    def connect_nodes(self, source_node, sink_node): 
     470        """ 
     471        Connect `source_node` to `sink_node`. If there are more then one 
     472        equally weighted and non conflicting links possible present a 
     473        detailed dialog for link editing. 
     474 
     475        """ 
    476476        try: 
    477477            possible = self.scheme.propose_links(source_node, sink_node) 
     
    484484 
    485485            source, sink, w = possible[0] 
     486 
     487            # just a list of signal tuples for now, will be converted 
     488            # to SchemeLinks later 
    486489            links_to_add = [(source, sink)] 
    487  
     490            links_to_remove = [] 
    488491            show_link_dialog = False 
    489492 
     
    501504                    show_link_dialog = True 
    502505 
    503                 if show_link_dialog: 
    504                     existing = self.scheme.find_links(source_node=source_node, 
    505                                                       sink_node=sink_node) 
    506  
    507                     if existing: 
    508                         # EditLinksDialog will populate the view with 
    509                         # existing links 
    510                         initial_links = None 
    511                     else: 
    512                         initial_links = [(source, sink)] 
    513  
    514                     links_action = EditNodeLinksAction( 
    515                                     self.document, source_node, sink_node) 
    516                     try: 
    517                         links_action.edit_links(initial_links) 
    518                     except Exception: 
    519                         log.error("'EditNodeLinksAction' failed", 
    520                                   exc_info=True) 
    521                         raise 
    522                     # EditNodeLinksAction already commits the links on accepted 
    523                     links_to_add = [] 
    524  
    525             for source, sink in links_to_add: 
    526                 if sink.single: 
    527                     # Remove an existing link to the sink channel if present. 
    528                     existing_link = self.scheme.find_links( 
    529                         sink_node=sink_node, sink_channel=sink 
     506            if show_link_dialog: 
     507                existing = self.scheme.find_links(source_node=source_node, 
     508                                                  sink_node=sink_node) 
     509 
     510                if existing: 
     511                    # edit_links will populate the view with existing links 
     512                    initial_links = None 
     513                else: 
     514                    initial_links = [(source, sink)] 
     515 
     516                try: 
     517                    _, links_to_add, links_to_remove = self.edit_links( 
     518                        source_node, sink_node, initial_links 
    530519                    ) 
    531  
    532                     if existing_link: 
    533                         self.document.removeLink(existing_link[0]) 
    534  
    535                 # Check if the new link is a duplicate of an existing link 
     520                except Exception: 
     521                    log.error("Failed to edit the links", 
     522                              exc_info=True) 
     523                    raise 
     524            else: 
     525                # links_to_add now needs to be a list of actual SchemeLinks 
     526                links_to_add = [scheme.SchemeLink( 
     527                                    source_node, source_channel, 
     528                                    sink_node, sink_channel) 
     529                                for source_channel, sink_channel 
     530                                in links_to_add] 
     531 
     532                links_to_add, links_to_remove = \ 
     533                    add_links_plan(self.scheme, links_to_add) 
     534 
     535            # Remove temp items before creating any new links 
     536            self.cleanup() 
     537 
     538            for link in links_to_remove: 
     539                self.document.removeLink(link) 
     540 
     541            for link in links_to_add: 
     542                # Check if the new requested link is a duplicate of an 
     543                # existing link 
    536544                duplicate = self.scheme.find_links( 
    537                     source_node, source, sink_node, sink 
     545                    link.source_node, link.source_channel, 
     546                    link.sink_node, link.sink_channel 
    538547                ) 
    539548 
    540                 if duplicate: 
    541                     # Do nothing. 
    542                     continue 
    543  
    544                 # Remove temp items before creating a new link 
    545                 self.cleanup() 
    546  
    547                 link = scheme.SchemeLink(source_node, source, sink_node, sink) 
    548                 self.document.addLink(link) 
     549                if not duplicate: 
     550                    self.document.addLink(link) 
    549551 
    550552        except scheme.IncompatibleChannelTypeError: 
     
    565567            self.end() 
    566568 
     569    def edit_links(self, source_node, sink_node, initial_links=None): 
     570        """ 
     571        Show and execute the `EditLinksDialog`. 
     572        Optional `initial_links` list can provide a list of initial 
     573        `(source, sink)` channel tuples to show in the view, otherwise 
     574        the dialog is populated with existing links in the scheme (passing 
     575        an empty list will disable all initial links). 
     576 
     577        """ 
     578        status, links_to_add, links_to_remove = \ 
     579            edit_links( 
     580                self.scheme, source_node, sink_node, initial_links, 
     581                parent=self.document 
     582            ) 
     583 
     584        if status == EditLinksDialog.Accepted: 
     585            links_to_add = [scheme.SchemeLink( 
     586                                source_node, source_channel, 
     587                                sink_node, sink_channel) 
     588                            for source_channel, sink_channel in links_to_add] 
     589 
     590            links_to_remove = [self.scheme.find_links( 
     591                                   source_node, source_channel, 
     592                                   sink_node, sink_channel) 
     593                               for source_channel, sink_channel 
     594                               in links_to_remove] 
     595 
     596            links_to_remove = reduce(list.__add__, links_to_remove, []) 
     597            conflicting = filter(None, 
     598                                 [conflicting_single_link(self.scheme, link) 
     599                                  for link in links_to_add]) 
     600            for link in conflicting: 
     601                if link not in links_to_remove: 
     602                    links_to_remove.append(link) 
     603 
     604            return status, links_to_add, links_to_remove 
     605        else: 
     606            return status, [], [] 
     607 
    567608    def end(self): 
    568609        self.cleanup() 
     
    596637            self.scene.removeItem(self.cursor_anchor_point) 
    597638            self.cursor_anchor_point = None 
     639 
     640 
     641def edit_links(scheme, source_node, sink_node, initial_links=None, 
     642               parent=None): 
     643    """ 
     644    Show and execute the `EditLinksDialog`. 
     645    Optional `initial_links` list can provide a list of initial 
     646    `(source, sink)` channel tuples to show in the view, otherwise 
     647    the dialog is populated with existing links in the scheme (passing 
     648    an empty list will disable all initial links). 
     649 
     650    """ 
     651    log.info("Constructing a Link Editor dialog.") 
     652 
     653    dlg = EditLinksDialog(parent) 
     654 
     655    # all SchemeLinks between the two nodes. 
     656    links = scheme.find_links(source_node=source_node, sink_node=sink_node) 
     657    existing_links = [(link.source_channel, link.sink_channel) 
     658                      for link in links] 
     659 
     660    if initial_links is None: 
     661        initial_links = list(existing_links) 
     662 
     663    dlg.setNodes(source_node, sink_node) 
     664    dlg.setLinks(initial_links) 
     665 
     666    log.info("Executing a Link Editor Dialog.") 
     667    rval = dlg.exec_() 
     668 
     669    if rval == EditLinksDialog.Accepted: 
     670        edited_links = dlg.links() 
     671 
     672        # Differences 
     673        links_to_add = set(edited_links) - set(existing_links) 
     674        links_to_remove = set(existing_links) - set(edited_links) 
     675        return rval, list(links_to_add), list(links_to_remove) 
     676    else: 
     677        return rval, [], [] 
     678 
     679 
     680def add_links_plan(scheme, links, force_replace=False): 
     681    """ 
     682    Return a plan for adding a list of links to the scheme. 
     683    """ 
     684    links_to_add = list(links) 
     685    links_to_remove = [conflicting_single_link(scheme, link) 
     686                       for link in links] 
     687    links_to_remove = filter(None, links_to_remove) 
     688 
     689    if not force_replace: 
     690        links_to_add, links_to_remove = remove_duplicates(links_to_add, 
     691                                                          links_to_remove) 
     692    return links_to_add, links_to_remove 
     693 
     694 
     695def conflicting_single_link(scheme, link): 
     696    """ 
     697    Find and return an existing link in `scheme` connected to the same 
     698    input channel as `link` if the channel has the 'single' flag. 
     699    If no such channel exists (or sink channel is not 'single') 
     700    return `None`. 
     701 
     702    """ 
     703 
     704    if link.sink_channel.single: 
     705        existing = scheme.find_links( 
     706            sink_node=link.sink_node, 
     707            sink_channel=link.sink_channel 
     708        ) 
     709 
     710        if existing: 
     711            assert len(existing) == 1 
     712            return existing[0] 
     713    return None 
     714 
     715 
     716def remove_duplicates(links_to_add, links_to_remove): 
     717    def link_key(link): 
     718        return (link.source_node, link.source_channel, 
     719                link.sink_node, link.sink_channel) 
     720 
     721    add_keys = map(link_key, links_to_add) 
     722    remove_keys = map(link_key, links_to_remove) 
     723    duplicate_keys = set(add_keys).intersection(remove_keys) 
     724 
     725    def not_duplicate(link): 
     726        return link_key(link) not in duplicate_keys 
     727 
     728    links_to_add = filter(not_duplicate, links_to_add) 
     729    links_to_remove = filter(not_duplicate, links_to_remove) 
     730    return links_to_add, links_to_remove 
    598731 
    599732 
     
    771904class EditNodeLinksAction(UserInteraction): 
    772905    """ 
    773     Edit multiple links between two NodeItems using a :class:`EditLinksDialog` 
     906    Edit multiple links between two :class:`SchemeNode` instances using 
     907    a :class:`EditLinksDialog` 
    774908 
    775909    Parameters 
     
    799933        log.info("Constructing a Link Editor dialog.") 
    800934 
    801         parent = self.scene.views()[0] 
    802         dlg = EditLinksDialog(parent) 
     935        dlg = EditLinksDialog(self.document) 
    803936 
    804937        links = self.scheme.find_links(source_node=self.source_node, 
Note: See TracChangeset for help on using the changeset viewer.