diff --git a/NodeGraphQt/constants.py b/NodeGraphQt/constants.py
index 59ef50e4..7e5c7ae2 100644
--- a/NodeGraphQt/constants.py
+++ b/NodeGraphQt/constants.py
@@ -81,6 +81,8 @@
NODE_PROP_FLOAT = 14
#: Property type represented with int widget in the properties bin.
NODE_PROP_INT = 15
+#: Property type represented with button widget in the properties bin.
+NODE_PROP_BUTTON = 16
# === NODE VIEWER ===
diff --git a/NodeGraphQt/widgets/node_widgets.py b/NodeGraphQt/widgets/node_widgets.py
index 33c8ec8e..47ab743a 100644
--- a/NodeGraphQt/widgets/node_widgets.py
+++ b/NodeGraphQt/widgets/node_widgets.py
@@ -417,7 +417,7 @@ def __init__(self, parent=None, name='', label='', text='', ext="*"):
self._ext = ext
def _on_select_file(self):
- file_path = file_dialog.getOpenFileName()
+ file_path = file_dialog.getOpenFileName(ext_filter=self._ext)
file = file_path[0] or None
if file:
self.value = file
diff --git a/NodeGraphQt/widgets/properties.py b/NodeGraphQt/widgets/properties.py
index 819914f2..588c10e1 100644
--- a/NodeGraphQt/widgets/properties.py
+++ b/NodeGraphQt/widgets/properties.py
@@ -15,7 +15,8 @@
NODE_PROP_VECTOR3,
NODE_PROP_VECTOR4,
NODE_PROP_FLOAT,
- NODE_PROP_INT)
+ NODE_PROP_INT,
+ NODE_PROP_BUTTON)
from NodeGraphQt.widgets.file_dialog import file_dialog
@@ -333,6 +334,7 @@ class _valueMenu(QtWidgets.QMenu):
mouseMove = QtCore.Signal(object)
mouseRelease = QtCore.Signal(object)
+ stepChange = QtCore.Signal()
def __init__(self, parent=None):
super(_valueMenu, self).__init__(parent)
@@ -357,6 +359,8 @@ def mouseMoveEvent(self, event):
action = self.actionAt(event.pos())
if action:
+ if action is not self.last_action:
+ self.stepChange.emit()
self.last_action = action
self.step = action.step
elif self.last_action:
@@ -389,9 +393,9 @@ def __init__(self, parent=None):
self._data_type = float
self.setText("0")
- self.pre_x = 0
+ self.pre_x = None
+ self.pre_val = None
self._step = 1
- self._tmp_value = 0
self._speed = 0.1
self.textChanged.connect(self._on_text_changed)
@@ -399,6 +403,7 @@ def __init__(self, parent=None):
self.menu = _valueMenu()
self.menu.mouseMove.connect(self.mouseMoveEvent)
self.menu.mouseRelease.connect(self.mouseReleaseEvent)
+ self.menu.stepChange.connect(self._reset)
steps = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
self.menu.set_steps(steps)
@@ -407,25 +412,26 @@ def __init__(self, parent=None):
def _on_text_changed(self, value):
self.valueChanged.emit(self.value())
+ def _reset(self):
+ self.pre_x = None
+
def mouseMoveEvent(self, event):
if self.mid_state:
if self.pre_x is None:
self.pre_x = event.x()
- self.set_step(self.menu.step)
- delta = (event.x() - self.pre_x)
- self._tmp_value += delta * self._speed * self._step
- if abs(self._tmp_value) > self._step:
- value = self.value() + delta * self._step
+ self.pre_val = self.value()
+ else:
+ self.set_step(self.menu.step)
+ delta = event.x() - self.pre_x
+ value = self.pre_val + int(delta*self._speed) * self._step
self.setValue(value)
- self._tmp_value = 0
- self.pre_x = event.x()
+
super(_valueEdit,self).mouseMoveEvent(event)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton:
self.mid_state = True
- self.pre_x = None
- self._tmp_value = 0
+ self._reset()
self.menu.exec_(QtGui.QCursor.pos())
super(_valueEdit,self).mousePressEvent(event)
@@ -448,8 +454,10 @@ def set_data_type(self, dt):
def _convert_text(self,text):
# int("1.0") will return error
# so we use int(float("1.0"))
-
- value = float(text)
+ try:
+ value = float(text)
+ except:
+ value = 0.0
if self._data_type is int:
value = int(value)
return value
@@ -506,14 +514,13 @@ def __init__(self, parent=None):
self._lock = False
def _on_edit_changed(self,value):
- if self._lock:
- return
- self._lock = True
self._set_slider_value(value)
self.valueChanged.emit(self._edit.value())
- self._lock = False
def _on_slider_changed(self,value):
+ if self._lock:
+ self._lock = False
+ return
value = value / float(self._mul)
self._edit.setValue(value)
@@ -522,7 +529,7 @@ def _set_slider_value(self,value):
if value == self._slider.value():
return
-
+ self._lock = True
_min = self._slider.minimum()
_max = self._slider.maximum()
if _min<=value<=_max:
@@ -532,6 +539,7 @@ def _set_slider_value(self,value):
elif value > _max and self._slider.value() != _max:
self._slider.setValue(_max)
+
def set_min(self, value=0):
self._slider.setMinimum(int(value*self._mul))
@@ -656,6 +664,21 @@ def __init__(self, parent=None):
self.set_data_type(int)
+class PropButton(QtWidgets.QPushButton):
+ value_changed = QtCore.Signal(str, object)
+
+ def __init__(self, parent=None):
+ super(PropButton, self).__init__(parent)
+
+ def set_value(self, value):
+ # value: list of functions
+ for func in value:
+ self.clicked.connect(func)
+
+ def get_value(self):
+ return None
+
+
WIDGET_MAP = {
NODE_PROP_QLABEL: PropLabel,
NODE_PROP_QLINEEDIT: PropLineEdit,
@@ -671,6 +694,7 @@ def __init__(self, parent=None):
NODE_PROP_VECTOR4: PropVector4,
NODE_PROP_FLOAT: PropFloat,
NODE_PROP_INT: PropInt,
+ NODE_PROP_BUTTON: PropButton
}
@@ -710,11 +734,14 @@ def add_widget(self, name, widget, value, label=None):
if row > 0:
row += 1
+ label = QtWidgets.QLabel(label)
label_flags = QtCore.Qt.AlignCenter | QtCore.Qt.AlignRight
if widget.__class__.__name__ == 'PropTextEdit':
label_flags = label_flags | QtCore.Qt.AlignTop
-
- self.__layout.addWidget(QtWidgets.QLabel(label), row, 0, label_flags)
+ elif widget.__class__.__name__ == 'PropButton':
+ label.setVisible(False)
+ widget.setText(name)
+ self.__layout.addWidget(label, row, 0, label_flags)
self.__layout.addWidget(widget, row, 1)
def get_widget(self, name):
diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py
index 2482bae1..0725f222 100644
--- a/NodeGraphQt/widgets/viewer.py
+++ b/NodeGraphQt/widgets/viewer.py
@@ -246,6 +246,9 @@ def mousePressEvent(self, event):
nodes = [i for i in items if isinstance(i, AbstractNodeItem)]
pipes = [i for i in items if isinstance(i, Pipe)]
+ if nodes:
+ self.MMB_state = False
+
# toggle extend node selection.
if self.LMB_state:
if self.SHIFT_state:
diff --git a/example_auto_nodes/node_base/auto_node.py b/example_auto_nodes/node_base/auto_node.py
index fd0aeffc..fc6b5159 100644
--- a/example_auto_nodes/node_base/auto_node.py
+++ b/example_auto_nodes/node_base/auto_node.py
@@ -4,7 +4,7 @@
from NodeGraphQt import QtCore
import random
import copy
-
+import time
def rand_color(seed_type):
seed = id(seed_type)
@@ -35,6 +35,9 @@ def __init__(self, defaultInputType=None, defaultOutputType=None):
self.defaultInputType = defaultInputType
self.defaultOutputType = defaultOutputType
+ self._cookTime = 0.0
+ self._toolTip = self._setup_tool_tip()
+
@property
def autoCook(self):
return self._autoCook
@@ -51,11 +54,24 @@ def autoCook(self, mode):
self.defaultColor = self.get_property("color")
self.set_property('color', self.stopCookColor)
+ @property
+ def cookTime(self):
+ return self._cookTime
+
+ @autoCook.setter
+ def cookTime(self, time):
+ self._cookTime = time
+ self._update_tool_tip()
+
def cookNextNode(self):
for nodeList in self.connected_output_nodes().values():
for n in nodeList:
n.cook()
+ def getData(self, port):
+ # for custom output data
+ return self.get_property(port.name())
+
def getInputData(self, port):
# get input data by input Port,the type of "port" can be :
# int : Port index
@@ -77,38 +93,41 @@ def getInputData(self, port):
return copy.deepcopy(self.defaultValue)
for from_port in from_ports:
- data = from_port.node().get_property(from_port.name())
+ data = from_port.node().getData(from_port)
return copy.deepcopy(data)
+ def when_disabled(self):
+ num = len(self.input_ports())
+ for index, out_port in enumerate(self.output_ports()):
+ self.set_property(out_port.name(), self.getInputData(index % num))
+
def cook(self, forceCook=False):
if not self._autoCook and forceCook is not True:
return
- _tmp = self._autoCook
- self._autoCook = False
-
- if self.disabled():
- num = len(self.input_ports())
- for index, out_port in enumerate(self.output_ports()):
- self.set_property(out_port.name(), self.getInputData(index % num))
- self.cookNextNode()
- return
-
if not self.needCook:
return
+ _tmp = self._autoCook
+ self._autoCook = False
+
if self.error():
self._close_error()
+ _start_time = time.time()
+
try:
self.run()
except Exception as error:
self.error(error)
+
self._autoCook = _tmp
if self.error():
return
+ self.cookTime = time.time() - _start_time
+
self.cooked.emit()
self.cookNextNode()
@@ -128,11 +147,6 @@ def on_input_disconnected(self, to_port, from_port):
return
self.cook()
- def set_disabled(self, mode=False):
- super(AutoNode, self).set_disabled(mode)
- if self.input_ports():
- self.cook()
-
def checkPortType(self, to_port, from_port):
# None type port can connect with any other type port
# types in self.matchTypes can connect with each other
@@ -203,10 +217,20 @@ def add_output(self, name='output', data_type=None, multi_output=True, display_n
self.set_port_type(new_port, self.defaultOutputType)
return new_port
+ def set_disabled(self, mode=False):
+ super(AutoNode, self).set_disabled(mode)
+ self._autoCook = not mode
+ if mode is True:
+ self.when_disabled()
+ self.cookNextNode()
+ else:
+ self.cook()
+
+
def _close_error(self):
self._error = False
self.set_property('color', self.defaultColor)
- self._view._tooltip_disable(False)
+ self._update_tool_tip()
def _show_error(self, message):
if not self._error:
@@ -214,10 +238,22 @@ def _show_error(self, message):
self._error = True
self.set_property('color', self.errorColor)
- tooltip = '{}'.format(self.name())
- tooltip += '
({})'.format(message)
- tooltip += '
{}
'.format(self._view.type_)
- self._view.setToolTip(tooltip)
+ tooltip = '
({})'.format(message)
+ self._update_tool_tip(tooltip)
+
+ def _update_tool_tip(self, message = None):
+ if message is None:
+ tooltip = self._toolTip.format(self._cookTime)
+ else:
+ tooltip = '{}'.format(self.name())
+ tooltip += message
+ tooltip += '
{}
'.format(self._view.type_)
+ self.view.setToolTip(tooltip)
+ return tooltip
+
+ def _setup_tool_tip(self):
+ tooltip = '
last cook used: {}s'
+ return self._update_tool_tip(tooltip)
def error(self, message=None):
if message is None: