diff --git a/NodeGraphQt/base/commands.py b/NodeGraphQt/base/commands.py index 5c8758a3..c1e7db57 100644 --- a/NodeGraphQt/base/commands.py +++ b/NodeGraphQt/base/commands.py @@ -1,7 +1,7 @@ #!/usr/bin/python -from NodeGraphQt import QtWidgets +from .. import QtWidgets -from NodeGraphQt.constants import IN_PORT, OUT_PORT +from ..constants import IN_PORT, OUT_PORT class PropertyChangedCmd(QtWidgets.QUndoCommand): diff --git a/NodeGraphQt/base/factory.py b/NodeGraphQt/base/factory.py index 6da3f9db..5f47ca49 100644 --- a/NodeGraphQt/base/factory.py +++ b/NodeGraphQt/base/factory.py @@ -1,6 +1,6 @@ #!/usr/bin/python -from NodeGraphQt.errors import NodeRegistrationError +from ..errors import NodeRegistrationError class NodeFactory(object): diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index bd62af80..6f4b8509 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -5,22 +5,22 @@ import re import copy -from NodeGraphQt import QtCore, QtWidgets, QtGui -from NodeGraphQt.base.commands import (NodeAddedCmd, - NodeRemovedCmd, - NodeMovedCmd, - PortConnectedCmd) -from NodeGraphQt.base.factory import NodeFactory -from NodeGraphQt.base.menu import NodeGraphMenu, NodesMenu -from NodeGraphQt.base.model import NodeGraphModel -from NodeGraphQt.base.node import NodeObject, BaseNode -from NodeGraphQt.base.port import Port -from NodeGraphQt.constants import (DRAG_DROP_ID, - PIPE_LAYOUT_CURVED, - PIPE_LAYOUT_STRAIGHT, - PIPE_LAYOUT_ANGLE, - IN_PORT, OUT_PORT) -from NodeGraphQt.widgets.viewer import NodeViewer +from .. import QtCore, QtWidgets, QtGui +from .commands import (NodeAddedCmd, + NodeRemovedCmd, + NodeMovedCmd, + PortConnectedCmd) +from .factory import NodeFactory +from .menu import NodeGraphMenu, NodesMenu +from .model import NodeGraphModel +from .node import NodeObject, BaseNode +from .port import Port +from ..constants import (DRAG_DROP_ID, + PIPE_LAYOUT_CURVED, + PIPE_LAYOUT_STRAIGHT, + PIPE_LAYOUT_ANGLE, + IN_PORT, OUT_PORT) +from ..widgets.viewer import NodeViewer class QWidgetDrops(QtWidgets.QWidget): @@ -43,7 +43,7 @@ def dropEvent(self, event): for url in event.mimeData().urls(): self.import_session(url.toLocalFile()) else: - e.ignore() + event.ignore() class NodeGraph(QtCore.QObject): @@ -119,6 +119,7 @@ class NodeGraph(QtCore.QObject): :parameters: :str :emits: new session path """ + def __init__(self, parent=None): super(NodeGraph, self).__init__(parent) self.setObjectName('NodeGraphQt') @@ -204,7 +205,7 @@ def _on_property_bin_changed(self, node_id, prop_name, prop_value): node = self.get_node_by_id(node_id) # prevent signals from causing a infinite loop. - _exc = [float, int , str, bool, None] + _exc = [float, int, str, bool, None] if node.get_property(prop_name) != prop_value: if type(node.get_property(prop_name)) in _exc: value = prop_value @@ -351,8 +352,8 @@ def widget(self): if self._widget is None: self._widget = QWidgetDrops() self._widget.import_session = self.import_session - - layout = QtWidgets.QVBoxLayout(self._widget) + + layout = QtWidgets.QVBoxLayout(self._widget) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._viewer) return self._widget @@ -1087,7 +1088,7 @@ def import_session(self, file_path): Args: file_path (str): path to the serialized layout file. """ - + file_path = file_path.strip() if not os.path.isfile(file_path): raise IOError('file does not exist.') diff --git a/NodeGraphQt/base/menu.py b/NodeGraphQt/base/menu.py index a6facc66..7017cebd 100644 --- a/NodeGraphQt/base/menu.py +++ b/NodeGraphQt/base/menu.py @@ -1,9 +1,9 @@ #!/usr/bin/python from distutils.version import LooseVersion -from NodeGraphQt import QtGui, QtCore -from NodeGraphQt.errors import NodeMenuError -from NodeGraphQt.widgets.actions import BaseMenu, GraphAction, NodeAction +from .. import QtGui, QtCore +from ..errors import NodeMenuError +from ..widgets.actions import BaseMenu, GraphAction, NodeAction class NodeGraphMenu(object): diff --git a/NodeGraphQt/base/model.py b/NodeGraphQt/base/model.py index 4da929dd..c7ba550d 100644 --- a/NodeGraphQt/base/model.py +++ b/NodeGraphQt/base/model.py @@ -2,12 +2,12 @@ import json from collections import defaultdict -from NodeGraphQt.constants import (NODE_PROP, - NODE_PROP_QLABEL, - NODE_PROP_QLINEEDIT, - NODE_PROP_QCHECKBOX, - NODE_PROP_COLORPICKER) -from NodeGraphQt.errors import NodePropertyError +from ..constants import (NODE_PROP, + NODE_PROP_QLABEL, + NODE_PROP_QLINEEDIT, + NODE_PROP_QCHECKBOX, + NODE_PROP_COLORPICKER) +from ..errors import NodePropertyError class PortModel(object): diff --git a/NodeGraphQt/base/node.py b/NodeGraphQt/base/node.py index 70b2e34f..044bb0b8 100644 --- a/NodeGraphQt/base/node.py +++ b/NodeGraphQt/base/node.py @@ -1,25 +1,25 @@ #!/usr/bin/python -from NodeGraphQt.base.commands import PropertyChangedCmd -from NodeGraphQt.base.model import NodeModel -from NodeGraphQt.base.port import Port -from NodeGraphQt.constants import (NODE_PROP, - NODE_PROP_QLINEEDIT, - NODE_PROP_QTEXTEDIT, - NODE_PROP_QCOMBO, - NODE_PROP_QCHECKBOX, - NODE_PROP_FILE, - NODE_PROP_FLOAT, - NODE_PROP_INT, - IN_PORT, OUT_PORT) -from NodeGraphQt.errors import PortRegistrationError -from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem -from NodeGraphQt.qgraphics.node_base import NodeItem -from NodeGraphQt.widgets.node_widgets import (NodeComboBox, - NodeLineEdit, - NodeFloatEdit, - NodeIntEdit, - NodeCheckBox, - NodeFilePath) +from .commands import PropertyChangedCmd +from .model import NodeModel +from .port import Port +from ..constants import (NODE_PROP, + NODE_PROP_QLINEEDIT, + NODE_PROP_QTEXTEDIT, + NODE_PROP_QCOMBO, + NODE_PROP_QCHECKBOX, + NODE_PROP_FILE, + NODE_PROP_FLOAT, + NODE_PROP_INT, + IN_PORT, OUT_PORT) +from ..errors import PortRegistrationError +from ..qgraphics.node_backdrop import BackdropNodeItem +from ..qgraphics.node_base import NodeItem +from ..widgets.node_widgets import (NodeComboBox, + NodeLineEdit, + NodeFloatEdit, + NodeIntEdit, + NodeCheckBox, + NodeFilePath) class classproperty(object): @@ -709,7 +709,7 @@ def update_combo_menu(self, name, items): return old_value = self.get_property(name) self.set_property(name, items) - _name = '_'+name+"_" + _name = '_' + name + "_" if not self.has_property(_name): self.create_property(_name, items) else: diff --git a/NodeGraphQt/base/port.py b/NodeGraphQt/base/port.py index 1f008c92..da4f5c7f 100644 --- a/NodeGraphQt/base/port.py +++ b/NodeGraphQt/base/port.py @@ -1,11 +1,11 @@ #!/usr/bin/python -from NodeGraphQt.base.commands import (PortConnectedCmd, - PortDisconnectedCmd, - PortVisibleCmd, - NodeInputConnectedCmd, - NodeInputDisconnectedCmd) -from NodeGraphQt.base.model import PortModel -from NodeGraphQt.constants import IN_PORT, OUT_PORT +from .commands import (PortConnectedCmd, + PortDisconnectedCmd, + PortVisibleCmd, + NodeInputConnectedCmd, + NodeInputDisconnectedCmd) +from .model import PortModel +from ..constants import IN_PORT, OUT_PORT class Port(object): diff --git a/NodeGraphQt/base/utils.py b/NodeGraphQt/base/utils.py index 612361c9..a8c772fc 100644 --- a/NodeGraphQt/base/utils.py +++ b/NodeGraphQt/base/utils.py @@ -1,7 +1,7 @@ #!/usr/bin/python from distutils.version import LooseVersion -from NodeGraphQt import QtGui, QtCore +from .. import QtGui, QtCore def setup_context_menu(graph): diff --git a/NodeGraphQt/constants.py b/NodeGraphQt/constants.py index 7e5c7ae2..efba1cc7 100644 --- a/NodeGraphQt/constants.py +++ b/NodeGraphQt/constants.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import os from .pkg_info import __version__ -from NodeGraphQt import QtWidgets +from . import QtWidgets #: Current version of the NodeGraphQt framework. VERSION = __version__ diff --git a/NodeGraphQt/errors.py b/NodeGraphQt/errors.py index e88e5956..1070e5ef 100644 --- a/NodeGraphQt/errors.py +++ b/NodeGraphQt/errors.py @@ -1,7 +1,17 @@ #!/usr/bin/python # -*- coding: utf-8 -*- + + class NodeMenuError(Exception): pass + + class NodePropertyError(Exception): pass + + class NodeWidgetError(Exception): pass + + class NodeRegistrationError(Exception): pass + + class PortRegistrationError(Exception): pass diff --git a/NodeGraphQt/qgraphics/node_abstract.py b/NodeGraphQt/qgraphics/node_abstract.py index 766f8ebc..0c9a6227 100644 --- a/NodeGraphQt/qgraphics/node_abstract.py +++ b/NodeGraphQt/qgraphics/node_abstract.py @@ -1,8 +1,9 @@ #!/usr/bin/python -from NodeGraphQt import QtCore, QtWidgets -from NodeGraphQt.constants import (Z_VAL_NODE, NODE_WIDTH, NODE_HEIGHT, - ITEM_CACHE_MODE) +from .. import QtCore, QtWidgets + +from ..constants import (Z_VAL_NODE, NODE_WIDTH, NODE_HEIGHT, + ITEM_CACHE_MODE) class AbstractNodeItem(QtWidgets.QGraphicsItem): diff --git a/NodeGraphQt/qgraphics/node_backdrop.py b/NodeGraphQt/qgraphics/node_backdrop.py index b23e8105..6901ba3f 100644 --- a/NodeGraphQt/qgraphics/node_backdrop.py +++ b/NodeGraphQt/qgraphics/node_backdrop.py @@ -1,12 +1,12 @@ #!/usr/bin/python -from NodeGraphQt import QtGui, QtCore, QtWidgets -from NodeGraphQt.constants import (Z_VAL_PIPE, - NODE_SEL_COLOR, - NODE_SEL_BORDER_COLOR) -from NodeGraphQt.qgraphics.node_abstract import AbstractNodeItem -from NodeGraphQt.qgraphics.pipe import Pipe -from NodeGraphQt.qgraphics.port import PortItem +from .. import QtGui, QtCore, QtWidgets +from ..constants import (Z_VAL_PIPE, + NODE_SEL_COLOR, + NODE_SEL_BORDER_COLOR) +from .node_abstract import AbstractNodeItem +from .pipe import Pipe +from .port import PortItem class BackdropSizer(QtWidgets.QGraphicsItem): diff --git a/NodeGraphQt/qgraphics/node_base.py b/NodeGraphQt/qgraphics/node_base.py index 2bae0c76..79562fb4 100644 --- a/NodeGraphQt/qgraphics/node_base.py +++ b/NodeGraphQt/qgraphics/node_base.py @@ -1,15 +1,16 @@ #!/usr/bin/python -from NodeGraphQt import QtGui, QtCore, QtWidgets -from NodeGraphQt.constants import (IN_PORT, OUT_PORT, - NODE_WIDTH, NODE_HEIGHT, - NODE_ICON_SIZE, ICON_NODE_BASE, - NODE_SEL_COLOR, NODE_SEL_BORDER_COLOR, - PORT_FALLOFF, Z_VAL_NODE, Z_VAL_NODE_WIDGET, - ITEM_CACHE_MODE) -from NodeGraphQt.errors import NodeWidgetError -from NodeGraphQt.qgraphics.node_abstract import AbstractNodeItem -from NodeGraphQt.qgraphics.port import PortItem +from .. import QtGui, QtCore, QtWidgets +from ..constants import (IN_PORT, OUT_PORT, + NODE_WIDTH, NODE_HEIGHT, + NODE_ICON_SIZE, ICON_NODE_BASE, + NODE_SEL_COLOR, NODE_SEL_BORDER_COLOR, + PORT_FALLOFF, Z_VAL_NODE, Z_VAL_NODE_WIDGET, + ITEM_CACHE_MODE) +from ..errors import NodeWidgetError +from .node_abstract import AbstractNodeItem +from .port import PortItem + class XDisabledItem(QtWidgets.QGraphicsItem): """ @@ -493,11 +494,11 @@ def post_init(self, viewer=None, pos=None): def auto_switch_mode(self): if ITEM_CACHE_MODE is QtWidgets.QGraphicsItem.ItemCoordinateCache: return - rect= self.sceneBoundingRect() + rect = self.sceneBoundingRect() l = self.viewer().mapToGlobal(self.viewer().mapFromScene(rect.topLeft())) - r = self.viewer().mapToGlobal(self.viewer().mapFromScene( rect.topRight())) + r = self.viewer().mapToGlobal(self.viewer().mapFromScene(rect.topRight())) # with is the node with in screen - width = r.x()-l.x() + width = r.x() - l.x() self.set_proxy_mode(width < self._porxy_mode_threshold) diff --git a/NodeGraphQt/qgraphics/pipe.py b/NodeGraphQt/qgraphics/pipe.py index a6b77655..1bb82a89 100644 --- a/NodeGraphQt/qgraphics/pipe.py +++ b/NodeGraphQt/qgraphics/pipe.py @@ -1,8 +1,8 @@ #!/usr/bin/python import math -from NodeGraphQt import QtCore, QtGui, QtWidgets -from NodeGraphQt.constants import ( +from .. import QtCore, QtGui, QtWidgets +from ..constants import ( PIPE_DEFAULT_COLOR, PIPE_ACTIVE_COLOR, PIPE_HIGHLIGHT_COLOR, PIPE_DISABLED_COLOR, PIPE_STYLE_DASHED, PIPE_STYLE_DEFAULT, PIPE_STYLE_DOTTED, @@ -10,7 +10,7 @@ Z_VAL_NODE_WIDGET, PIPE_LAYOUT_ANGLE, PIPE_LAYOUT_CURVED, ITEM_CACHE_MODE) -from NodeGraphQt.qgraphics.port import PortItem +from .port import PortItem PIPE_STYLES = { PIPE_STYLE_DEFAULT: QtCore.Qt.SolidLine, diff --git a/NodeGraphQt/qgraphics/port.py b/NodeGraphQt/qgraphics/port.py index 400333d3..fa934d4a 100644 --- a/NodeGraphQt/qgraphics/port.py +++ b/NodeGraphQt/qgraphics/port.py @@ -1,7 +1,7 @@ #!/usr/bin/python -from NodeGraphQt import QtGui, QtCore, QtWidgets +from .. import QtGui, QtCore, QtWidgets -from NodeGraphQt.constants import ( +from ..constants import ( IN_PORT, OUT_PORT, PORT_DEFAULT_COLOR, PORT_DEFAULT_BORDER_COLOR, diff --git a/NodeGraphQt/qgraphics/slicer.py b/NodeGraphQt/qgraphics/slicer.py index 6f99c3b6..80b1e65c 100644 --- a/NodeGraphQt/qgraphics/slicer.py +++ b/NodeGraphQt/qgraphics/slicer.py @@ -1,6 +1,6 @@ #!/usr/bin/python -from NodeGraphQt import QtCore, QtGui, QtWidgets -from NodeGraphQt.constants import Z_VAL_NODE_WIDGET, PIPE_SLICER_COLOR +from .. import QtCore, QtGui, QtWidgets +from ..constants import Z_VAL_NODE_WIDGET, PIPE_SLICER_COLOR class SlicerPipe(QtWidgets.QGraphicsPathItem): diff --git a/NodeGraphQt/widgets/actions.py b/NodeGraphQt/widgets/actions.py index d11785e3..2625f75a 100644 --- a/NodeGraphQt/widgets/actions.py +++ b/NodeGraphQt/widgets/actions.py @@ -1,6 +1,6 @@ #!/usr/bin/python -from NodeGraphQt import QtCore, QtWidgets -from NodeGraphQt.widgets.stylesheet import STYLE_QMENU +from .. import QtCore, QtWidgets +from .stylesheet import STYLE_QMENU class BaseMenu(QtWidgets.QMenu): diff --git a/NodeGraphQt/widgets/file_dialog.py b/NodeGraphQt/widgets/file_dialog.py index e3fea479..a63ae414 100644 --- a/NodeGraphQt/widgets/file_dialog.py +++ b/NodeGraphQt/widgets/file_dialog.py @@ -1,5 +1,5 @@ -from NodeGraphQt import QtWidgets -from NodeGraphQt.widgets.stylesheet import STYLE_MESSAGEBOX +from .. import QtWidgets +from .stylesheet import STYLE_MESSAGEBOX import os current_dir = os.path.expanduser('~') diff --git a/NodeGraphQt/widgets/node_tree.py b/NodeGraphQt/widgets/node_tree.py index b88c4368..f349dedb 100644 --- a/NodeGraphQt/widgets/node_tree.py +++ b/NodeGraphQt/widgets/node_tree.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -from NodeGraphQt import QtWidgets, QtCore -from NodeGraphQt.constants import DRAG_DROP_ID +from .. import QtWidgets, QtCore +from ..constants import DRAG_DROP_ID TYPE_NODE = QtWidgets.QTreeWidgetItem.UserType + 1 diff --git a/NodeGraphQt/widgets/node_widgets.py b/NodeGraphQt/widgets/node_widgets.py index 2a7e5c66..03921dd6 100644 --- a/NodeGraphQt/widgets/node_widgets.py +++ b/NodeGraphQt/widgets/node_widgets.py @@ -1,10 +1,10 @@ #!/usr/bin/python -from NodeGraphQt import QtCore, QtWidgets, QtGui +from .. import QtCore, QtWidgets -from NodeGraphQt.constants import Z_VAL_NODE_WIDGET -from NodeGraphQt.widgets.stylesheet import * -from NodeGraphQt.widgets.file_dialog import file_dialog -from NodeGraphQt.widgets.properties import _valueEdit +from ..constants import Z_VAL_NODE_WIDGET +from .stylesheet import * +from .file_dialog import file_dialog +from .properties import _valueEdit class _NodeGroupBox(QtWidgets.QGroupBox): diff --git a/NodeGraphQt/widgets/properties.py b/NodeGraphQt/widgets/properties.py index 14248421..297c44a4 100644 --- a/NodeGraphQt/widgets/properties.py +++ b/NodeGraphQt/widgets/properties.py @@ -1,23 +1,23 @@ #!/usr/bin/python from collections import defaultdict -from NodeGraphQt import QtWidgets, QtCore, QtGui -from NodeGraphQt.constants import (NODE_PROP_QLABEL, - NODE_PROP_QLINEEDIT, - NODE_PROP_QTEXTEDIT, - NODE_PROP_QCOMBO, - NODE_PROP_QCHECKBOX, - NODE_PROP_QSPINBOX, - NODE_PROP_COLORPICKER, - NODE_PROP_SLIDER, - NODE_PROP_FILE, - NODE_PROP_VECTOR2, - NODE_PROP_VECTOR3, - NODE_PROP_VECTOR4, - NODE_PROP_FLOAT, - NODE_PROP_INT, - NODE_PROP_BUTTON) -from NodeGraphQt.widgets.file_dialog import file_dialog +from .. import QtWidgets, QtCore, QtGui +from ..constants import (NODE_PROP_QLABEL, + NODE_PROP_QLINEEDIT, + NODE_PROP_QTEXTEDIT, + NODE_PROP_QCOMBO, + NODE_PROP_QCHECKBOX, + NODE_PROP_QSPINBOX, + NODE_PROP_COLORPICKER, + NODE_PROP_SLIDER, + NODE_PROP_FILE, + NODE_PROP_VECTOR2, + NODE_PROP_VECTOR3, + NODE_PROP_VECTOR4, + NODE_PROP_FLOAT, + NODE_PROP_INT, + NODE_PROP_BUTTON) +from .file_dialog import file_dialog class BaseProperty(QtWidgets.QWidget): @@ -30,77 +30,52 @@ def get_value(self): raise NotImplementedError -class _ColorSolid(QtWidgets.QWidget): - - def __init__(self, parent=None, color=None): - super(_ColorSolid, self).__init__(parent) - self.setFixedSize(15, 15) - self.color = color or (0, 0, 0) - - def paintEvent(self, event): - size = self.geometry() - rect = QtCore.QRect(1, 1, size.width() - 2, size.height() - 2) - painter = QtGui.QPainter(self) - painter.setPen(QtCore.Qt.NoPen) - painter.setBrush(QtGui.QColor(*self._color)) - painter.drawRoundedRect(rect, 1, 1) - - @property - def color(self): - return self._color - - @color.setter - def color(self, color): - self._color = color - hex = '#{0:02x}{1:02x}{2:02x}'.format(*self._color) - self.setToolTip('rgb: {}\nhex: {}'.format(self._color[0:3], hex)) - self.update() - - class PropColorPicker(BaseProperty): - def __init__(self, parent=None): super(PropColorPicker, self).__init__(parent) self._color = (0, 0, 0) - self._label = QtWidgets.QLabel() self._button = QtWidgets.QPushButton() + self._vector = PropVector3() + self._vector.set_value([0, 0, 0]) self._update_color() self._button.clicked.connect(self._on_select_color) + self._vector.value_changed.connect(self._on_vector_changed) layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 8, 0) + layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(4) - layout.addWidget(self._label, 0, QtCore.Qt.AlignCenter) - layout.addWidget(self._button, 1, QtCore.Qt.AlignLeft) + layout.addWidget(self._button, 0, QtCore.Qt.AlignLeft) + layout.addWidget(self._vector, 1, QtCore.Qt.AlignLeft) + + def _on_vector_changed(self, o, value): + self._color = tuple(value) + self._update_color() + self.value_changed.emit(self.toolTip(), value) + + def _update_vector(self): + self._vector.set_value(list(self._color)) def _on_select_color(self): - color = QtWidgets.QColorDialog.getColor(QtGui.QColor(*self.get_value()), - options=QtWidgets.QColorDialog.ShowAlphaChannel) + color = QtWidgets.QColorDialog.getColor(QtGui.QColor.fromRgbF(*self.get_value())) if color.isValid(): self.set_value(color.getRgb()) def _update_color(self): - hex = self.hex_color() - self._label.setText(hex) - self._label.setAlignment(QtCore.Qt.AlignCenter) - self._label.setMinimumWidth(60) - + c = [int(max(min(i, 255), 0)) for i in self._color] + hex_color = '#{0:02x}{1:02x}{2:02x}'.format(*c) self._button.setStyleSheet( '''QPushButton {{background-color: rgba({0}, {1}, {2}, 255);}} - QPushButton::hover {{background-color: rgba({0}, {1}, {2}, 200);}}''' - .format(*self._color)) - self._button.setToolTip('rgb: {}\nhex: {}'.format(self._color[0:3], hex)) - - def hex_color(self): - return '#{0:02x}{1:02x}{2:02x}'.format(*self._color) + QPushButton::hover {{background-color: rgba({0}, {1}, {2}, 200);}}'''.format(*c)) + self._button.setToolTip('rgb: {}\nhex: {}'.format(self._color[:3], hex_color)) def get_value(self): - return self._color + return self._color[:3] def set_value(self, value): if value != self.get_value(): self._color = value self._update_color() + self._update_vector() self.value_changed.emit(self.toolTip(), value) @@ -324,7 +299,9 @@ def _on_select_file(self): if file: self.set_value(file) - def _on_value_change(self, value): + def _on_value_change(self, value=None): + if value is None: + value = self._ledit.text() self.value_changed.emit(self.toolTip(), value) def get_value(self): @@ -338,7 +315,6 @@ def set_value(self, value): class _valueMenu(QtWidgets.QMenu): - mouseMove = QtCore.Signal(object) mouseRelease = QtCore.Signal(object) stepChange = QtCore.Signal() @@ -349,13 +325,13 @@ def __init__(self, parent=None): self.last_action = None self.steps = [] - def set_steps(self,steps): + def set_steps(self, steps): self.clear() self.steps = steps for step in steps: self._add_action(step) - def _add_action(self,step): + def _add_action(self, step): action = QtWidgets.QAction(str(step), self) action.step = step self.addAction(action) @@ -380,7 +356,7 @@ def mouseReleaseEvent(self, event): self.mouseRelease.emit(event) super(_valueMenu, self).mouseReleaseEvent(event) - def set_data_type(self,dt): + def set_data_type(self, dt): if dt is int: new_steps = [] for step in self.steps: @@ -430,23 +406,23 @@ def mouseMoveEvent(self, event): else: self.set_step(self.menu.step) delta = event.x() - self.pre_x - value = self.pre_val + int(delta*self._speed) * self._step + value = self.pre_val + int(delta * self._speed) * self._step self.setValue(value) self._on_text_changed() - super(_valueEdit,self).mouseMoveEvent(event) + super(_valueEdit, self).mouseMoveEvent(event) def mousePressEvent(self, event): if event.button() == QtCore.Qt.MiddleButton: self.mid_state = True self._reset() self.menu.exec_(QtGui.QCursor.pos()) - super(_valueEdit,self).mousePressEvent(event) + super(_valueEdit, self).mousePressEvent(event) def mouseReleaseEvent(self, event): self.menu.close() self.mid_state = False - super(_valueEdit,self).mouseReleaseEvent(event) + super(_valueEdit, self).mouseReleaseEvent(event) def set_step(self, step): self._step = step @@ -459,7 +435,7 @@ def set_data_type(self, dt): self._data_type = dt self.menu.set_data_type(dt) - def _convert_text(self,text): + def _convert_text(self, text): # int("1.0") will return error # so we use int(float("1.0")) try: @@ -472,7 +448,7 @@ def _convert_text(self,text): def value(self): if self.text().startswith("."): - text = "0"+self.text() + text = "0" + self.text() self.setText(text) return self._convert_text(self.text()) @@ -482,21 +458,21 @@ def setValue(self, value): class _slider(QtWidgets.QSlider): - def __init__(self, parent = None): - super(_slider,self).__init__(parent) + def __init__(self, parent=None): + super(_slider, self).__init__(parent) self.setOrientation(QtCore.Qt.Horizontal) self.setTickPosition(QtWidgets.QSlider.TicksBelow) self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Preferred) + QtWidgets.QSizePolicy.Preferred) - def _update_value(self,x): + def _update_value(self, x): value = (self.maximum() - self.minimum()) * x / self.width() + self.minimum() self.setValue(value) def mousePressEvent(self, event): if event.button() == QtCore.Qt.LeftButton: self._update_value(event.pos().x()) - super(_slider,self).mousePressEvent(event) + super(_slider, self).mousePressEvent(event) class _valueSliderEdit(QtWidgets.QWidget): @@ -541,7 +517,7 @@ def _set_slider_value(self, value): self._lock = True _min = self._slider.minimum() _max = self._slider.maximum() - if _min<=value<=_max: + if _min <= value <= _max: self._slider.setValue(value) elif value < _min and self._slider.value() != _min: self._slider.setValue(_min) @@ -549,14 +525,14 @@ def _set_slider_value(self, value): self._slider.setValue(_max) def set_min(self, value=0): - self._slider.setMinimum(int(value*self._mul)) + self._slider.setMinimum(int(value * self._mul)) def set_max(self, value=10): - self._slider.setMaximum(int(value*self._mul)) + self._slider.setMaximum(int(value * self._mul)) def set_data_type(self, dt): - _min = int(self._slider.minimum()/self._mul) - _max = int(self._slider.maximum()/self._mul) + _min = int(self._slider.minimum() / self._mul) + _max = int(self._slider.maximum() / self._mul) if dt is int: self._mul = 1.0 elif dt is float: @@ -569,7 +545,7 @@ def set_data_type(self, dt): def value(self): return self._edit.value() - def setValue(self,value): + def setValue(self, value): self._edit.setValue(value) self._on_edit_changed(value) @@ -618,7 +594,7 @@ def _on_value_change(self, value=None, index=None): def _update_items(self): for index, value in enumerate(self._value): - if self._items[index].value() != value: + if index < len(self._items) and self._items[index].value() != value: self._items[index].setValue(value) def get_value(self): @@ -709,6 +685,12 @@ def get_value(self): } +def registerPropType(name, prop_class, override=False): + global WIDGET_MAP + if name in WIDGET_MAP.keys() and not override: + raise Exception("Prop type {} has already exists, u can use override=True to override)".format(name)) + WIDGET_MAP[name] = prop_class + # main property widgets. @@ -971,7 +953,7 @@ def get_widget(self, name): if __name__ == '__main__': import sys - from NodeGraphQt import BaseNode, NodeGraph + from .. import BaseNode, NodeGraph class TestNode(BaseNode): diff --git a/NodeGraphQt/widgets/properties_bin.py b/NodeGraphQt/widgets/properties_bin.py index 9a10c003..2f491c22 100644 --- a/NodeGraphQt/widgets/properties_bin.py +++ b/NodeGraphQt/widgets/properties_bin.py @@ -1,7 +1,7 @@ #!/usr/bin/python -from NodeGraphQt import QtWidgets, QtCore, QtGui, QtCompat +from .. import QtWidgets, QtCore, QtGui, QtCompat -from NodeGraphQt.widgets.properties import NodePropWidget +from .properties import NodePropWidget class PropertiesDelegate(QtWidgets.QStyledItemDelegate): @@ -42,12 +42,17 @@ def __init__(self, parent=None): self.setItemDelegate(PropertiesDelegate()) self.setColumnCount(1) self.setShowGrid(False) - QtCompat.QHeaderView.setSectionResizeMode( - self.verticalHeader(), QtWidgets.QHeaderView.ResizeToContents) self.verticalHeader().hide() + self.horizontalHeader().hide() + QtCompat.QHeaderView.setSectionResizeMode( + self.verticalHeader(), QtWidgets.QHeaderView.Stretch) QtCompat.QHeaderView.setSectionResizeMode( self.horizontalHeader(), 0, QtWidgets.QHeaderView.Stretch) - self.horizontalHeader().hide() + self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) + + def wheelEvent(self, event): + delta = event.delta() * 0.2 + self.verticalScrollBar().setValue(self.verticalScrollBar().value() - delta) class PropertiesBinWidget(QtWidgets.QWidget): @@ -250,13 +255,13 @@ def prop_widget(self, node): if __name__ == '__main__': import sys - from NodeGraphQt import BaseNode, NodeGraph - from NodeGraphQt.constants import (NODE_PROP_QLABEL, - NODE_PROP_QLINEEDIT, - NODE_PROP_QCOMBO, - NODE_PROP_QSPINBOX, - NODE_PROP_COLORPICKER, - NODE_PROP_SLIDER) + from .. import BaseNode, NodeGraph + from ..constants import (NODE_PROP_QLABEL, + NODE_PROP_QLINEEDIT, + NODE_PROP_QCOMBO, + NODE_PROP_QSPINBOX, + NODE_PROP_COLORPICKER, + NODE_PROP_SLIDER) class TestNode(BaseNode): diff --git a/NodeGraphQt/widgets/scene.py b/NodeGraphQt/widgets/scene.py index 67c1550b..165a958a 100644 --- a/NodeGraphQt/widgets/scene.py +++ b/NodeGraphQt/widgets/scene.py @@ -1,10 +1,10 @@ #!/usr/bin/python -from NodeGraphQt import QtGui, QtCore, QtWidgets +from .. import QtGui, QtCore, QtWidgets -from NodeGraphQt.constants import (VIEWER_BG_COLOR, - VIEWER_GRID_SIZE, - VIEWER_GRID_OVERLAY, - VIEWER_GRID_COLOR) +from ..constants import (VIEWER_BG_COLOR, + VIEWER_GRID_SIZE, + VIEWER_GRID_OVERLAY, + VIEWER_GRID_COLOR) class NodeScene(QtWidgets.QGraphicsScene): diff --git a/NodeGraphQt/widgets/stylesheet.py b/NodeGraphQt/widgets/stylesheet.py index 8efa21b0..8273663f 100644 --- a/NodeGraphQt/widgets/stylesheet.py +++ b/NodeGraphQt/widgets/stylesheet.py @@ -1,6 +1,6 @@ import re -from NodeGraphQt.constants import ICON_DOWN_ARROW +from ..constants import ICON_DOWN_ARROW # Reformat the icon path on Windows OS. match = re.match('(\\w:)', ICON_DOWN_ARROW) diff --git a/NodeGraphQt/widgets/tab_search.py b/NodeGraphQt/widgets/tab_search.py index 0bf6b27f..77a16a87 100644 --- a/NodeGraphQt/widgets/tab_search.py +++ b/NodeGraphQt/widgets/tab_search.py @@ -1,7 +1,8 @@ #!/usr/bin/python -from NodeGraphQt import QtCore, QtWidgets,QtGui - -from NodeGraphQt.widgets.stylesheet import STYLE_TABSEARCH, STYLE_TABSEARCH_LIST, STYLE_QMENU +from .. import QtCore, QtWidgets, QtGui +from .stylesheet import STYLE_TABSEARCH, STYLE_TABSEARCH_LIST, STYLE_QMENU +from collections import OrderedDict +import re class TabSearchCompleter(QtWidgets.QCompleter): @@ -112,6 +113,18 @@ def set_nodes(self, node_dict=None): self._completer.setModel(self._model) +def fuzzyFinder(key, collection): + suggestions = [] + pattern = '.*?'.join(key.lower()) + regex = re.compile(pattern) + for item in collection: + match = regex.search(item.lower()) + if match: + suggestions.append((len(match.group()), match.start(), item)) + + return [x for _, _, x in sorted(suggestions)] + + class TabSearchMenuWidget(QtWidgets.QLineEdit): search_submitted = QtCore.Signal(str) @@ -132,7 +145,7 @@ def __init__(self, parent=None, node_dict=None): self.SearchMenu.addAction(searchWidget) self.SearchMenu.setStyleSheet(STYLE_QMENU) - self._actions = [] + self._actions = {} self._menus = {} self._searched_actions = [] @@ -151,19 +164,21 @@ def _on_text_changed(self,text): self._set_menu_visible(False) - self._searched_actions = [action for action in self._actions\ - if text.lower() in action.text().lower()] + action_names = fuzzyFinder(text, self._actions.keys()) + self._searched_actions = [self._actions[name] for name in action_names] self.SearchMenu.addActions(self._searched_actions) + if self._searched_actions: + self.SearchMenu.setActiveAction(self._searched_actions[0]) + def _clear_actions(self): for action in self._searched_actions: self.SearchMenu.removeAction(action) self._searched_actions = [] - def _set_menu_visible(self,visible): - for menu in self._menus.values(): - menu.menuAction().setVisible(visible) + def _set_menu_visible(self, visible): + [menu.menuAction().setVisible(visible) for menu in self._menus.values()] def _close(self): self._set_menu_visible(False) @@ -171,10 +186,10 @@ def _close(self): self.SearchMenu.menuAction().setVisible(False) def _show(self): - self.SearchMenu.exec_(QtGui.QCursor.pos()) self.setText("") self.setFocus() self._set_menu_visible(True) + self.SearchMenu.exec_(QtGui.QCursor.pos()) def _on_search_submitted(self): action = self.sender() @@ -192,33 +207,52 @@ def _on_search_submitted(self): self._close() - def _generate_items_from_node_dict(self): - node_names = sorted(self._node_dict.keys()) + def build_menu_tree(self): node_types = sorted(self._node_dict.values()) + node_names = sorted(self._node_dict.keys()) + menu_tree = OrderedDict() - self._menus.clear() - self._actions.clear() - self._searched_actions.clear() - + max_depth = 0 for node_type in node_types: - menu_name = ".".join(node_type.split(".")[:-1]) - if menu_name not in self._menus.keys(): - new_menu = QtWidgets.QMenu(menu_name) - new_menu.setStyleSheet(STYLE_QMENU) - self._menus[menu_name] = new_menu - self.SearchMenu.addMenu(new_menu) + trees = node_type.split(".") + trees.pop(-1) + for depth, menu_name in enumerate(trees): + menu_path = ".".join(trees[:depth+1]) + if depth in menu_tree.keys(): + if menu_name not in menu_tree[depth].keys(): + new_menu = QtWidgets.QMenu(menu_name) + new_menu.setStyleSheet(STYLE_QMENU) + menu_tree[depth][menu_path] = new_menu + else: + new_menu = QtWidgets.QMenu(menu_name) + new_menu.setStyleSheet(STYLE_QMENU) + menu_tree[depth] = {menu_path: new_menu} + if depth > 0: + new_menu.parentPath = ".".join(trees[:depth]) + + max_depth = max(max_depth,depth) + + for i in range(max_depth+1): + menus = menu_tree[i] + for menu_path, menu in menus.items(): + self._menus[menu_path] = menu + if i == 0: + self.SearchMenu.addMenu(menu) + else: + parentMenu = self._menus[menu.parentPath] + parentMenu.addMenu(menu) for name in node_names: action = QtWidgets.QAction(name, self) action.setText(name) action.triggered.connect(self._on_search_submitted) - self._actions.append(action) + self._actions[name] = action menu_name = self._node_dict[name] - menu_name = ".".join(menu_name.split(".")[:-1]) + menu_path = ".".join(menu_name.split(".")[:-1]) - if menu_name in self._menus.keys(): - self._menus[menu_name].addAction(action) + if menu_path in self._menus.keys(): + self._menus[menu_path].addAction(action) else: self.SearchMenu.addAction(action) @@ -231,7 +265,7 @@ def set_nodes(self, node_dict=None): continue for node_id in node_types: self._node_dict['{} ({})'.format(name, node_id)] = node_id - self._generate_items_from_node_dict() + self.build_menu_tree() self._show() diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index 6ca3c444..38717faf 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -1,20 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- import math -import os - -from NodeGraphQt import QtGui, QtCore, QtWidgets ,QtOpenGL -from NodeGraphQt.constants import (IN_PORT, OUT_PORT, - PIPE_LAYOUT_CURVED) -from NodeGraphQt.qgraphics.node_abstract import AbstractNodeItem -from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem -from NodeGraphQt.qgraphics.pipe import Pipe, LivePipe -from NodeGraphQt.qgraphics.port import PortItem -from NodeGraphQt.qgraphics.slicer import SlicerPipe -from NodeGraphQt.widgets.actions import BaseMenu -from NodeGraphQt.widgets.scene import NodeScene -from NodeGraphQt.widgets.tab_search import TabSearchWidget, TabSearchMenuWidget -from NodeGraphQt.widgets.file_dialog import file_dialog, messageBox + +from .. import QtGui, QtCore, QtWidgets, QtOpenGL +from ..constants import (IN_PORT, OUT_PORT, + PIPE_LAYOUT_CURVED) +from ..qgraphics.node_abstract import AbstractNodeItem +from ..qgraphics.node_backdrop import BackdropNodeItem +from ..qgraphics.pipe import Pipe, LivePipe +from ..qgraphics.port import PortItem +from ..qgraphics.slicer import SlicerPipe +from ..base.menu import BaseMenu +from .scene import NodeScene +from .tab_search import TabSearchMenuWidget +from .file_dialog import file_dialog, messageBox ZOOM_MIN = -0.95 ZOOM_MAX = 2.0 @@ -115,7 +114,7 @@ def _set_viewer_zoom(self, value, sensitivity=None, pos=None): pos = self.mapToScene(pos) if sensitivity is None: scale = 1.001 ** value - self.scale(scale,scale,pos) + self.scale(scale, scale, pos) return if value == 0.0: @@ -137,7 +136,7 @@ def _set_viewer_pan(self, pos_x, pos_y): self._scene_range.adjust(x, y, x, y) self._update_scene() - def scale(self, sx, sy, pos = None): + def scale(self, sx, sy, pos=None): scale = [sx, sx] center = pos or self._scene_range.center() @@ -145,7 +144,7 @@ def scale(self, sx, sy, pos = None): w = self._scene_range.width() / scale[0] h = self._scene_range.height() / scale[1] self._scene_range = QtCore.QRectF(center.x() - (center.x() - self._scene_range.left()) / scale[0], - center.y() - (center.y() - self._scene_range.top()) / scale[1], w, h) + center.y() - (center.y() - self._scene_range.top()) / scale[1], w, h) self._update_scene() @@ -185,8 +184,8 @@ def _on_pipes_sliced(self, path): # --- reimplemented events --- def resizeEvent(self, event): - delta = max(self.size().width()/self._last_size.width(), - self.size().height()/self._last_size.height()) + delta = max(self.size().width() / self._last_size.width(), + self.size().height() / self._last_size.height()) self._set_viewer_zoom(delta) self._last_size = self.size() super(NodeViewer, self).resizeEvent(event) @@ -227,7 +226,7 @@ def mousePressEvent(self, event): self._origin_pos = event.pos() self._previous_pos = event.pos() self._prev_selection_nodes, \ - self._prev_selection_pipes = self.selected_items() + self._prev_selection_pipes = self.selected_items() # close tab search if self._search_widget.isVisible(): @@ -340,7 +339,7 @@ def mouseMoveEvent(self, event): if self.MMB_state and self.ALT_state: pos_x = (event.x() - self._previous_pos.x()) zoom = 0.1 if pos_x > 0 else -0.1 - self._set_viewer_zoom(zoom, 0.05,pos = event.pos()) + self._set_viewer_zoom(zoom, 0.05, pos=event.pos()) elif self.MMB_state or (self.LMB_state and self.ALT_state): pos_x = (event.x() - self._previous_pos.x()) pos_y = (event.y() - self._previous_pos.y()) @@ -718,7 +717,7 @@ def context_menus(self): 'nodes': self._ctx_node_menu} def question_dialog(self, text, title='Node Graph'): - dlg = messageBox(text, title, QtWidgets.QMessageBox.Yes|QtWidgets.QMessageBox.No) + dlg = messageBox(text, title, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) return dlg == QtWidgets.QMessageBox.Yes def message_dialog(self, text, title='Node Graph'): @@ -766,13 +765,13 @@ def all_nodes(self): return nodes def selected_nodes(self): - nodes = [item for item in self.scene().selectedItems()\ - if isinstance(item,AbstractNodeItem)] + nodes = [item for item in self.scene().selectedItems() \ + if isinstance(item, AbstractNodeItem)] return nodes def selected_pipes(self): - pipes = [item for item in self.scene().selectedItems()\ - if isinstance(item,Pipe)] + pipes = [item for item in self.scene().selectedItems() \ + if isinstance(item, Pipe)] return pipes def selected_items(self): @@ -851,7 +850,7 @@ def set_pipe_layout(self, layout): for pipe in self.all_pipes(): pipe.draw_path(pipe.input_port, pipe.output_port) - def reset_zoom(self,cent=None): + def reset_zoom(self, cent=None): self._scene_range = QtCore.QRectF(0, 0, self.size().width(), self.size().height()) if cent: self._scene_range.translate(cent - self._scene_range.center()) @@ -874,7 +873,7 @@ def set_zoom(self, value=0.0): if not (ZOOM_MIN <= value <= ZOOM_MAX): return value = value - zoom - self._set_viewer_zoom(value,0.0) + self._set_viewer_zoom(value, 0.0) def zoom_to_nodes(self, nodes): self._scene_range = self._combined_rect(nodes) @@ -886,4 +885,4 @@ def zoom_to_nodes(self, nodes): def use_opengl(self): format = QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers) format.setSamples(4) - self.setViewport(QtOpenGL.QGLWidget(format)) \ No newline at end of file + self.setViewport(QtOpenGL.QGLWidget(format))