-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ENH] Widget Insertion #3179
[ENH] Widget Insertion #3179
Changes from 2 commits
8989b6b
57380e0
28fe821
976f9ea
cdffe81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,8 @@ | |
|
||
from AnyQt.QtWidgets import QUndoCommand | ||
|
||
from Orange.canvas.scheme import SchemeLink | ||
|
||
|
||
class AddNodeCommand(QUndoCommand): | ||
def __init__(self, scheme, node, parent=None): | ||
|
@@ -69,6 +71,38 @@ def undo(self): | |
self.scheme.add_link(self.link) | ||
|
||
|
||
class InsertNodeCommand(QUndoCommand): | ||
def __init__(self, scheme, link, new_node, parent=None): | ||
QUndoCommand.__init__(self, "Remove link", parent) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change the command name/text to 'Insert widget on link' or something similar. |
||
self.scheme = scheme | ||
self.original_link = link | ||
self.inserted_widget = new_node | ||
|
||
possible_links = (self.scheme.propose_links(link.source_node, new_node), | ||
self.scheme.propose_links(new_node, link.sink_node)) | ||
|
||
if not possible_links[0] or not possible_links[1]: | ||
raise ValueError("Cannot insert widget: links not possible") | ||
|
||
self.new_links = ( | ||
SchemeLink(link.source_node, link.source_channel, | ||
new_node, possible_links[0][0][1]), # first link, first entry, output (1) | ||
SchemeLink(new_node, possible_links[1][0][0], # second link, first entry, input (0) | ||
link.sink_node, link.sink_channel)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic in |
||
|
||
def redo(self): | ||
self.scheme.add_node(self.inserted_widget) | ||
self.scheme.remove_link(self.original_link) | ||
self.scheme.add_link(self.new_links[0]) | ||
self.scheme.add_link(self.new_links[1]) | ||
|
||
def undo(self): | ||
self.scheme.remove_link(self.new_links[0]) | ||
self.scheme.remove_link(self.new_links[1]) | ||
self.scheme.add_link(self.original_link) | ||
self.scheme.remove_node(self.inserted_widget) | ||
|
||
|
||
class AddAnnotationCommand(QUndoCommand): | ||
def __init__(self, scheme, annotation, parent=None): | ||
QUndoCommand.__init__(self, "Add annotation", parent) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,13 +29,14 @@ | |
) | ||
|
||
from AnyQt.QtCore import ( | ||
Qt, QObject, QEvent, QSignalMapper, QRectF, QCoreApplication | ||
) | ||
Qt, QObject, QEvent, QSignalMapper, QRectF, QCoreApplication, | ||
QPoint) | ||
|
||
from AnyQt.QtCore import pyqtProperty as Property, pyqtSignal as Signal | ||
|
||
from Orange.canvas.registry import WidgetDescription | ||
from .suggestions import Suggestions | ||
from ..registry.qt import whats_this_helper | ||
from ..registry.qt import whats_this_helper, QtWidgetRegistry | ||
from ..gui.quickhelp import QuickHelpTipEvent | ||
from ..gui.utils import message_information, disabled | ||
from ..scheme import ( | ||
|
@@ -171,6 +172,8 @@ def __init__(self, parent=None, ): | |
self.__linkMenu = QMenu(self.tr("Link"), self) | ||
self.__linkMenu.addAction(self.__linkEnableAction) | ||
self.__linkMenu.addSeparator() | ||
self.__linkMenu.addAction(self.__nodeInsertAction) | ||
self.__linkMenu.addSeparator() | ||
self.__linkMenu.addAction(self.__linkRemoveAction) | ||
self.__linkMenu.addAction(self.__linkResetAction) | ||
|
||
|
@@ -328,6 +331,13 @@ def color_icon(color): | |
toolTip=self.tr("Remove link."), | ||
) | ||
|
||
self.__nodeInsertAction = \ | ||
QAction(self.tr("Insert Widget"), self, | ||
objectName="node-insert-action", | ||
triggered=self.__nodeInsert, | ||
toolTip=self.tr("Insert widget."), | ||
) | ||
|
||
self.__linkResetAction = \ | ||
QAction(self.tr("Reset Signals"), self, | ||
objectName="link-reset-action", | ||
|
@@ -346,6 +356,7 @@ def color_icon(color): | |
self.__newArrowAnnotationAction, | ||
self.__linkEnableAction, | ||
self.__linkRemoveAction, | ||
self.__nodeInsertAction, | ||
self.__linkResetAction, | ||
self.__duplicateSelectedAction]) | ||
|
||
|
@@ -867,6 +878,13 @@ def removeLink(self, link): | |
command = commands.RemoveLinkCommand(self.__scheme, link) | ||
self.__undoStack.push(command) | ||
|
||
def insertNode(self, link, new_node): | ||
""" | ||
Insert a node in-between two linked nodes. | ||
""" | ||
command = commands.InsertNodeCommand(self.__scheme, link, new_node) | ||
self.__undoStack.push(command) | ||
|
||
def onNewLink(self, func): | ||
""" | ||
Runs function when new link is added to current scheme. | ||
|
@@ -1029,6 +1047,17 @@ def changeEvent(self, event): | |
|
||
QWidget.changeEvent(self, event) | ||
|
||
def tryInsertNode(self, link, new_node_desc, pos): | ||
source_node = link.source_node | ||
sink_node = link.sink_node | ||
|
||
if nodes_are_compatible(source_node.description, new_node_desc) and \ | ||
nodes_are_compatible(new_node_desc, sink_node.description): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test here should also be constrained on the existing E.g. If I use: and drop an Impute widget on the link, I get a
The set of possible links below should also be filtered correspondingly. P.S. |
||
new_node = self.newNodeHelper(new_node_desc, position=(pos.x(), pos.y())) | ||
self.insertNode(link, new_node) | ||
else: | ||
self.createNewNode(new_node_desc, position=(pos.x(), pos.y())) | ||
|
||
def eventFilter(self, obj, event): | ||
# Filter the scene's drag/drop events. | ||
if obj is self.scene(): | ||
|
@@ -1054,7 +1083,12 @@ def eventFilter(self, obj, event): | |
log.error("Unknown qualified name '%s'", qname) | ||
else: | ||
pos = event.scenePos() | ||
self.createNewNode(desc, position=(pos.x(), pos.y())) | ||
item = self.__scene.item_at(event.scenePos()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use |
||
if item and isinstance(item, items.LinkCurveItem): | ||
link = self.__scene.link_for_item(item.parent()) | ||
self.tryInsertNode(link, desc, pos) | ||
else: | ||
self.createNewNode(desc, position=(pos.x(), pos.y())) | ||
return True | ||
|
||
elif etype == QEvent.GraphicsSceneMousePress: | ||
|
@@ -1594,6 +1628,47 @@ def __linkReset(self): | |
) | ||
action.edit_links() | ||
|
||
def __nodeInsert(self): | ||
""" | ||
Node insert was requested from the context menu. | ||
""" | ||
if not self.__contextMenuTarget: | ||
return | ||
|
||
original_link = self.__contextMenuTarget | ||
source_node = original_link.source_node | ||
sink_node = original_link.sink_node | ||
|
||
def filterFunc(index): | ||
new_node_desc = index.data(QtWidgetRegistry.WIDGET_DESC_ROLE) | ||
if isinstance(new_node_desc, WidgetDescription): | ||
return nodes_are_compatible(source_node.description, new_node_desc) and \ | ||
nodes_are_compatible(new_node_desc, sink_node.description) | ||
else: | ||
return False | ||
|
||
x = (source_node.position[0] + sink_node.position[0]) / 2 | ||
y = (source_node.position[1] + sink_node.position[1]) / 2 | ||
|
||
menu = self.quickMenu() | ||
menu.setFilterFunc(filterFunc) | ||
menu.setSortingFunc(None) | ||
|
||
view = self.view() | ||
try: | ||
action = menu.exec_(view.mapToGlobal(view.mapFromScene(QPoint(x, y)))) | ||
finally: | ||
menu.setFilterFunc(None) | ||
|
||
if action: | ||
item = action.property("item") | ||
desc = item.data(QtWidgetRegistry.WIDGET_DESC_ROLE) | ||
new_node = self.newNodeHelper(desc, position=(x, y)) | ||
else: | ||
return | ||
|
||
self.insertNode(original_link, new_node) | ||
|
||
def __duplicateSelected(self): | ||
""" | ||
Duplicate currently selected nodes. | ||
|
@@ -1936,6 +2011,12 @@ def node_properties(scheme): | |
return [dict(node.properties) for node in scheme.nodes] | ||
|
||
|
||
def nodes_are_compatible(source, sink): | ||
return any(scheme.compatible_channels(output, input) \ | ||
for output in source.outputs \ | ||
for input in sink.inputs) | ||
|
||
|
||
def uniquify(item, names, pattern="{item}-{_}", start=0): | ||
candidates = (pattern.format(item=item, _=i) | ||
for i in itertools.count(start)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same as
parentItem()