diff --git a/NodeGraphQt/custom_widgets/properties_bin/custom_widget_color_picker.py b/NodeGraphQt/custom_widgets/properties_bin/custom_widget_color_picker.py index 79d0813b..a0004496 100644 --- a/NodeGraphQt/custom_widgets/properties_bin/custom_widget_color_picker.py +++ b/NodeGraphQt/custom_widgets/properties_bin/custom_widget_color_picker.py @@ -15,7 +15,11 @@ def __init__(self, parent=None): self._color = (0, 0, 0) self._button = QtWidgets.QPushButton() self._vector = PropVector3() + self._vector.set_steps([1, 10, 100]) + self._vector.set_data_type(int) self._vector.set_value([0, 0, 0]) + self._vector.set_min(0) + self._vector.set_max(255) self._update_color() self._button.clicked.connect(self._on_select_color) @@ -53,6 +57,15 @@ def _update_color(self): 'rgb: {}\nhex: {}'.format(self._color[:3], hex_color) ) + def set_data_type(self, data_type): + """ + Sets the input line edit fields to either display in float or int. + + Args: + data_type(int or float): int or float data type object. + """ + self._vector.set_data_type(data_type) + def get_value(self): return self._color[:3] @@ -74,7 +87,11 @@ def __init__(self, parent=None): self._color = (0, 0, 0, 255) self._button = QtWidgets.QPushButton() self._vector = PropVector4() + self._vector.set_steps([1, 10, 100]) + self._vector.set_data_type(int) self._vector.set_value([0, 0, 0, 255]) + self._vector.set_min(0) + self._vector.set_max(255) self._update_color() self._button.clicked.connect(self._on_select_color) diff --git a/NodeGraphQt/custom_widgets/properties_bin/custom_widget_value_edit.py b/NodeGraphQt/custom_widgets/properties_bin/custom_widget_value_edit.py index b2ba0f67..5bc49b2f 100644 --- a/NodeGraphQt/custom_widgets/properties_bin/custom_widget_value_edit.py +++ b/NodeGraphQt/custom_widgets/properties_bin/custom_widget_value_edit.py @@ -1,6 +1,10 @@ #!/usr/bin/python +import re + from Qt import QtWidgets, QtCore, QtGui +_NUMB_REGEX = re.compile(r'^((?:\-)*\d+)*([\.,])*(\d+(?:[eE](?:[\-\+])*\d+)*)*') + class _NumberValueMenu(QtWidgets.QMenu): @@ -83,16 +87,17 @@ def __init__(self, parent=None, data_type=float): self._previous_x = None self._previous_value = None self._step = 1 - self._speed = 0.1 + self._speed = 0.05 self._data_type = float + self._min = None + self._max = None self._menu = _NumberValueMenu() self._menu.mouseMove.connect(self.mouseMoveEvent) self._menu.mouseRelease.connect(self.mouseReleaseEvent) self._menu.stepChange.connect(self._reset_previous_x) - self._menu.set_steps([0.001, 0.01, 0.1, 1, 10, 100, 1000]) - self.editingFinished.connect(self._on_text_changed) + self.editingFinished.connect(self._on_editing_finished) self.set_data_type(data_type) @@ -113,7 +118,7 @@ def mouseMoveEvent(self, event): value = self._previous_value value = value + int(delta * self._speed) * self._step self.set_value(value) - self._on_text_changed() + self._on_mmb_mouse_move() super(_NumberValueEdit, self).mouseMoveEvent(event) def mousePressEvent(self, event): @@ -140,15 +145,37 @@ def keyPressEvent(self, event): def _reset_previous_x(self): self._previous_x = None - def _on_text_changed(self): + def _on_mmb_mouse_move(self): + self.value_changed.emit(self.get_value()) + + def _on_editing_finished(self): + if self._data_type is float: + match = _NUMB_REGEX.match(self.text()) + if match: + val1, point, val2 = match.groups() + if point: + val1 = val1 or '0' + val2 = val2 or '0' + self.setText(val1 + point + val2) self.value_changed.emit(self.get_value()) def _convert_text(self, text): - # int("1.0") will return error - # so we use int(float("1.0")) - try: - value = float(text) - except: + """ + Convert text to int or float. + + Args: + text (str): input text. + + Returns: + int or float: converted value. + """ + match = _NUMB_REGEX.match(text) + if match: + val1, _, val2 = match.groups() + val1 = val1 or '0' + val2 = val2 or '0' + value = float(val1 + '.' + val2) + else: value = 0.0 if self._data_type is int: value = int(value) @@ -157,26 +184,90 @@ def _convert_text(self, text): # public def set_data_type(self, data_type): + """ + Sets the line edit to either display value in float or int. + + Args: + data_type(int or float): int or float data type object. + """ self._data_type = data_type - self._menu.set_data_type(data_type) if data_type is int: - self.setValidator(QtGui.QIntValidator()) + regexp = QtCore.QRegExp(r'\d+') + validator = QtGui.QRegExpValidator(regexp, self) + steps = [1, 10, 100, 1000] elif data_type is float: - self.setValidator(QtGui.QDoubleValidator()) + regexp = QtCore.QRegExp(r'\d+[\.,]\d+(?:[eE](?:[\-\+]|)\d+)*') + validator = QtGui.QRegExpValidator(regexp, self) + steps = [0.001, 0.01, 0.1, 1] + self.setValidator(validator) + if not self._menu.steps: + self._menu.set_steps(steps) + self._menu.set_data_type(data_type) def set_steps(self, steps=None): - steps = steps or [0.001, 0.01, 0.1, 1, 10, 100, 1000] + """ + Sets the step items in the MMB context menu. + + Args: + steps (list[int] or list[float]): list of ints or floats. + """ + step_types = { + int: [1, 10, 100, 1000], + float: [0.001, 0.01, 0.1, 1] + } + steps = steps or step_types.get(self._data_type) self._menu.set_steps(steps) + def set_min(self, value=None): + """ + Set the minimum range for the input field. + + Args: + value (int or float): minimum range value. + """ + if self._data_type is int: + self._min = int(value) + elif self._data_type is float: + self._min = float(value) + else: + self._min = value + + def set_max(self, value=None): + """ + Set the maximum range for the input field. + + Args: + value (int or float): maximum range value. + """ + if self._data_type is int: + self._max = int(value) + elif self._data_type is float: + self._max = float(value) + else: + self._max = value + def get_value(self): - if self.text().startswith('.'): - text = '0' + self.text() - self.setText(text) - return self._convert_text(self.text()) + value = self._convert_text(self.text()) + return value def set_value(self, value): - if value != self.get_value(): - self.setText(str(self._convert_text(value))) + text = str(value) + converted = self._convert_text(text) + current = self.get_value() + if converted == current: + return + point = None + if isinstance(converted, float): + point = _NUMB_REGEX.match(str(value)).groups(2) + if self._min is not None and converted < self._min: + text = str(self._min) + if point and point not in text: + text = str(self._min).replace('.', point) + if self._max is not None and converted > self._max: + text = str(self._max) + if point and point not in text: + text = text.replace('.', point) + self.setText(text) class IntValueEdit(_NumberValueEdit): diff --git a/NodeGraphQt/custom_widgets/properties_bin/custom_widget_vectors.py b/NodeGraphQt/custom_widgets/properties_bin/custom_widget_vectors.py index a6bb8f52..b13f23d0 100644 --- a/NodeGraphQt/custom_widgets/properties_bin/custom_widget_vectors.py +++ b/NodeGraphQt/custom_widgets/properties_bin/custom_widget_vectors.py @@ -51,9 +51,45 @@ def _update_items(self): self._items[index].set_value(value) def set_data_type(self, data_type): + """ + Sets the input line edit fields to either display in float or int. + + Args: + data_type(int or float): int or float data type object. + """ for item in self._items: item.set_data_type(data_type) + def set_steps(self, steps): + """ + Sets the step items in the MMB context menu. + + Args: + steps (list[int] or list[float]): list of ints or floats. + """ + for item in self._items: + item.set_steps(steps) + + def set_min(self, value): + """ + Set the minimum range for the input fields. + + Args: + value (int or float): minimum range value. + """ + for item in self._items: + item.set_min(value) + + def set_max(self, value): + """ + Set the maximum range for the input fields. + + Args: + value (int or float): maximum range value. + """ + for item in self._items: + item.set_max(value) + def get_value(self): return self._value diff --git a/NodeGraphQt/nodes/base_node.py b/NodeGraphQt/nodes/base_node.py index 1d1650ff..4b689d85 100644 --- a/NodeGraphQt/nodes/base_node.py +++ b/NodeGraphQt/nodes/base_node.py @@ -697,9 +697,9 @@ def connected_output_nodes(self): def add_accept_port_type(self, port, port_type_data): """ - Add a accept constrain to a specified node port. + Add an accept constrain to a specified node port. - Once a constrain has been added only ports of that type specified will + Once a constraint has been added only ports of that type specified will be allowed a pipe connection. port type data example @@ -759,7 +759,7 @@ def add_reject_port_type(self, port, port_type_data): """ Add a reject constrain to a specified node port. - Once a constrain has been added only ports of that type specified will + Once a constraint has been added only ports of that type specified will NOT be allowed a pipe connection. port type data example diff --git a/NodeGraphQt/pkg_info.py b/NodeGraphQt/pkg_info.py index b6b146d7..c51e203e 100644 --- a/NodeGraphQt/pkg_info.py +++ b/NodeGraphQt/pkg_info.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -__version__ = '0.6.21' +__version__ = '0.6.22' __status__ = 'Work in Progress' __license__ = 'MIT'