From 6491c48d3c8e735dae22a4f5c1fbfd15f4b548bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janez=20Dem=C5=A1ar?= Date: Fri, 9 Dec 2016 17:09:02 +0100 Subject: [PATCH] Merge pull request #1810 from ales-erjavec/input-output-signal-replacement-declarations [ENH] Input/output signal replacement declarations (cherry picked from commit 3a501f11d7c505501b4a00050ef744c9c6c01385) --- Orange/canvas/registry/base.py | 2 +- Orange/canvas/registry/description.py | 12 ++++--- Orange/canvas/scheme/readwrite.py | 41 ++++++++++++++++++++++-- Orange/widgets/visualize/owtreeviewer.py | 18 +++++++++-- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Orange/canvas/registry/base.py b/Orange/canvas/registry/base.py index 53b74b79acd..720352decf3 100644 --- a/Orange/canvas/registry/base.py +++ b/Orange/canvas/registry/base.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) # Registry hex version -VERSION_HEX = 0x000102 +VERSION_HEX = 0x000103 class WidgetRegistry(object): diff --git a/Orange/canvas/registry/description.py b/Orange/canvas/registry/description.py index 0af02836857..4be071fb780 100644 --- a/Orange/canvas/registry/description.py +++ b/Orange/canvas/registry/description.py @@ -69,15 +69,17 @@ class InputSignal(object): A unique id of the input signal. doc : str, optional A docstring documenting the channel. - + replaces : List[str] + A list of names this input replaces. """ def __init__(self, name, type, handler, flags=Single + NonDefault, - id=None, doc=None): + id=None, doc=None, replaces=[]): self.name = name self.type = type self.handler = handler self.id = id self.doc = doc + self.replaces = list(replaces) if isinstance(flags, str): # flags are stored as strings @@ -130,14 +132,16 @@ class OutputSignal(object): A unique id of the output signal. doc : str, optional A docstring documenting the channel. - + replaces : List[str] + A list of names this output replaces. """ def __init__(self, name, type, flags=Single + NonDefault, - id=None, doc=None): + id=None, doc=None, replaces=[]): self.name = name self.type = type self.id = id self.doc = doc + self.replaces = list(replaces) if isinstance(flags, str): # flags are stored as strings diff --git a/Orange/canvas/scheme/readwrite.py b/Orange/canvas/scheme/readwrite.py index 1ece429c6e6..accfd0005db 100644 --- a/Orange/canvas/scheme/readwrite.py +++ b/Orange/canvas/scheme/readwrite.py @@ -25,6 +25,7 @@ from .errors import IncompatibleChannelTypeError from ..registry import global_registry +from ..registry import WidgetDescription, InputSignal, OutputSignal log = logging.getLogger(__name__) @@ -607,12 +608,26 @@ def resolve_1_0(scheme_desc, registry): def resolve_replaced(scheme_desc, registry): widgets = registry.widgets() + nodes_by_id = {} # type: Dict[str, _node] replacements = {} - for desc in widgets: + replacements_channels = {} # type: Dict[str, Tuple[dict, dict]] + # collect all the replacement mappings + for desc in widgets: # type: WidgetDescription if desc.replaces: for repl_qname in desc.replaces: replacements[repl_qname] = desc.qualified_name + input_repl = {} + for idesc in desc.inputs or []: # type: InputSignal + for repl_qname in idesc.replaces or []: # type: str + input_repl[repl_qname] = idesc.name + output_repl = {} + for odesc in desc.outputs: # type: OutputSignal + for repl_qname in odesc.replaces or []: # type: str + output_repl[repl_qname] = odesc.name + replacements_channels[desc.qualified_name] = (input_repl, output_repl) + + # replace the nodes nodes = scheme_desc.nodes for i, node in list(enumerate(nodes)): if not registry.has_widget(node.qualified_name) and \ @@ -621,8 +636,28 @@ def resolve_replaced(scheme_desc, registry): desc = registry.widget(qname) nodes[i] = node._replace(qualified_name=desc.qualified_name, project_name=desc.project_name) - - return scheme_desc._replace(nodes=nodes) + nodes_by_id[node.id] = nodes[i] + + # replace links + links = scheme_desc.links + for i, link in list(enumerate(links)): # type: _link + nsource = nodes_by_id[link.source_node_id] + nsink = nodes_by_id[link.sink_node_id] + + _, source_rep = replacements_channels.get( + nsource.qualified_name, ({}, {})) + sink_rep, _ = replacements_channels.get( + nsink.qualified_name, ({}, {})) + + if link.source_channel in source_rep: + link = link._replace( + source_channel=source_rep[link.source_channel]) + if link.sink_channel in sink_rep: + link = link._replace( + sink_channel=sink_rep[link.sink_channel]) + links[i] = link + + return scheme_desc._replace(nodes=nodes, links=links) def scheme_load(scheme, stream, registry=None, error_handler=None): diff --git a/Orange/widgets/visualize/owtreeviewer.py b/Orange/widgets/visualize/owtreeviewer.py index fcddee79770..1ad9db8d039 100644 --- a/Orange/widgets/visualize/owtreeviewer.py +++ b/Orange/widgets/visualize/owtreeviewer.py @@ -155,9 +155,21 @@ class OWTreeGraph(OWTreeViewer2D): name = "Tree Viewer" icon = "icons/TreeViewer.svg" priority = 35 - inputs = [("Tree", TreeModel, "ctree")] - outputs = [("Selected Data", Table, widget.Default), - (ANNOTATED_DATA_SIGNAL_NAME, Table)] + inputs = [ + widget.InputSignal( + "Tree", TreeModel, "ctree", + # Had different input names before merging from + # Classification/Regression tree variants + replaces=["Classification Tree", "Regression Tree"]) + ] + outputs = [ + widget.OutputSignal( + "Selected Data", Table, widget.Default, id="selected-data", + ), + widget.OutputSignal( + ANNOTATED_DATA_SIGNAL_NAME, Table, id="annotated-data") + ] + settingsHandler = ClassValuesContextHandler() target_class_index = ContextSetting(0)