mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-26 05:42:15 +01:00
Merge pull request #547 from pypeclub/feature/enumerator_item
Enumerator item in settings GUI
This commit is contained in:
commit
ae59f9c04a
8 changed files with 511 additions and 5 deletions
|
|
@ -168,6 +168,29 @@
|
|||
}
|
||||
```
|
||||
|
||||
### enum
|
||||
- returns value of single on multiple items from predefined values
|
||||
- multiselection can be allowed with setting key `"multiselection"` to `True` (Default: `False`)
|
||||
- values are defined under value of key `"enum_items"` as list
|
||||
- each item in list is simple dictionary where value is label and key is value which will be stored
|
||||
- should be possible to enter single dictionary if order of items doesn't matter
|
||||
|
||||
```
|
||||
{
|
||||
"key": "tags",
|
||||
"label": "Tags",
|
||||
"type": "enum",
|
||||
"multiselection": true,
|
||||
"enum_items": [
|
||||
{"burnin": "Add burnins"},
|
||||
{"ftrackreview": "Add to Ftrack"},
|
||||
{"delete": "Delete output"},
|
||||
{"slate-frame": "Add slate frame"},
|
||||
{"no-hnadles": "Skip handle frames"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Inputs for setting value using Pure inputs
|
||||
- these inputs also have required `"key"` and `"label"`
|
||||
- they use Pure inputs "as widgets"
|
||||
|
|
|
|||
|
|
@ -202,8 +202,15 @@
|
|||
}, {
|
||||
"key": "tags",
|
||||
"label": "Tags",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
"type": "enum",
|
||||
"multiselection": true,
|
||||
"enum_items": [
|
||||
{"burnin": "Add burnins"},
|
||||
{"ftrackreview": "Add to Ftrack"},
|
||||
{"delete": "Delete output"},
|
||||
{"slate-frame": "Add slate frame"},
|
||||
{"no-hnadles": "Skip handle frames"}
|
||||
]
|
||||
}, {
|
||||
"key": "ffmpeg_args",
|
||||
"label": "FFmpeg arguments",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,25 @@
|
|||
"type": "dict-invisible",
|
||||
"children": [
|
||||
{
|
||||
"type": "enum",
|
||||
"key": "test_enum_singleselection",
|
||||
"label": "Enum Single Selection",
|
||||
"enum_items": [
|
||||
{"value_1": "Label 1"},
|
||||
{"value_2": "Label 2"},
|
||||
{"value_3": "Label 3"}
|
||||
]
|
||||
}, {
|
||||
"type": "enum",
|
||||
"key": "test_enum_multiselection",
|
||||
"label": "Enum Multi Selection",
|
||||
"multiselection": true,
|
||||
"enum_items": [
|
||||
{"value_1": "Label 1"},
|
||||
{"value_2": "Label 2"},
|
||||
{"value_3": "Label 3"}
|
||||
]
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "bool",
|
||||
"label": "Boolean checkbox"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,18 @@ QLineEdit:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QPlainTextEdit:d
|
|||
QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus {
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
|
||||
QComboBox {
|
||||
border: 1px solid #aaaaaa;
|
||||
border-radius: 3px;
|
||||
padding: 2px 2px 4px 4px;
|
||||
background: #1d272f;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
QToolButton {
|
||||
background: transparent;
|
||||
}
|
||||
|
|
@ -102,6 +114,10 @@ QPushButton[btn-type="expand-toggle"] {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
#MultiSelectionComboBox {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#DictKey[state="studio"] {border-color: #bfccd6;}
|
||||
#DictKey[state="modified"] {border-color: #137cbd;}
|
||||
#DictKey[state="overriden"] {border-color: #00f;}
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ class SystemWidget(QtWidgets.QWidget):
|
|||
klass = lib.TypeToKlass.types.get(item_type)
|
||||
item = klass(child_configuration, self)
|
||||
self.input_fields.append(item)
|
||||
self.content_layout.addWidget(item)
|
||||
self.content_layout.addWidget(item, 0)
|
||||
|
||||
# Add spacer to stretch children guis
|
||||
spacer = QtWidgets.QWidget(self.content_widget)
|
||||
|
|
@ -532,7 +532,7 @@ class ProjectWidget(QtWidgets.QWidget):
|
|||
klass = lib.TypeToKlass.types.get(item_type)
|
||||
item = klass(child_configuration, self)
|
||||
self.input_fields.append(item)
|
||||
self.content_layout.addWidget(item)
|
||||
self.content_layout.addWidget(item, 0)
|
||||
|
||||
# Add spacer to stretch children guis
|
||||
spacer = QtWidgets.QWidget(self.content_widget)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ from .widgets import (
|
|||
ExpandingWidget,
|
||||
NumberSpinBox,
|
||||
PathInput,
|
||||
GridLabelWidget
|
||||
GridLabelWidget,
|
||||
ComboBox
|
||||
)
|
||||
from .multiselection_combobox import MultiSelectionComboBox
|
||||
from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET
|
||||
from avalon.vendor import qtawesome
|
||||
|
||||
|
|
@ -1072,6 +1074,105 @@ class PathInputWidget(QtWidgets.QWidget, InputObject):
|
|||
return self.path_input.text()
|
||||
|
||||
|
||||
class EnumeratorWidget(QtWidgets.QWidget, InputObject):
|
||||
default_input_value = True
|
||||
value_changed = QtCore.Signal(object)
|
||||
|
||||
def __init__(
|
||||
self, input_data, parent,
|
||||
as_widget=False, label_widget=None, parent_widget=None
|
||||
):
|
||||
if parent_widget is None:
|
||||
parent_widget = parent
|
||||
super(EnumeratorWidget, self).__init__(parent_widget)
|
||||
|
||||
self.initial_attributes(input_data, parent, as_widget)
|
||||
self.multiselection = input_data.get("multiselection")
|
||||
self.enum_items = input_data["enum_items"]
|
||||
if not self.enum_items:
|
||||
raise ValueError("Attribute `enum_items` is not defined.")
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
if not self._as_widget:
|
||||
self.key = input_data["key"]
|
||||
if not label_widget:
|
||||
label = input_data["label"]
|
||||
label_widget = QtWidgets.QLabel(label)
|
||||
label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
layout.addWidget(label_widget, 0)
|
||||
self.label_widget = label_widget
|
||||
|
||||
if self.multiselection:
|
||||
placeholder = input_data.get("placeholder")
|
||||
self.input_field = MultiSelectionComboBox(
|
||||
placeholder=placeholder, parent=self
|
||||
)
|
||||
else:
|
||||
self.input_field = ComboBox(self)
|
||||
|
||||
first_value = NOT_SET
|
||||
for enum_item in self.enum_items:
|
||||
for value, label in enum_item.items():
|
||||
if first_value is NOT_SET:
|
||||
first_value = value
|
||||
self.input_field.addItem(label, value)
|
||||
self._first_value = first_value
|
||||
|
||||
if self.multiselection:
|
||||
model = self.input_field.model()
|
||||
for idx in range(self.input_field.count()):
|
||||
model.item(idx).setCheckable(True)
|
||||
|
||||
layout.addWidget(self.input_field, 0)
|
||||
|
||||
self.setFocusProxy(self.input_field)
|
||||
|
||||
self.input_field.value_changed.connect(self._on_value_change)
|
||||
|
||||
@property
|
||||
def default_input_value(self):
|
||||
if self.multiselection:
|
||||
return []
|
||||
return self._first_value
|
||||
|
||||
def set_value(self, value):
|
||||
# Ignore value change because if `self.isChecked()` has same
|
||||
# value as `value` the `_on_value_change` is not triggered
|
||||
self.input_field.set_value(value)
|
||||
|
||||
def update_style(self):
|
||||
if self.as_widget:
|
||||
state = self.style_state(
|
||||
False,
|
||||
self._is_invalid,
|
||||
False,
|
||||
self._is_modified
|
||||
)
|
||||
else:
|
||||
state = self.style_state(
|
||||
self.has_studio_override,
|
||||
self.is_invalid,
|
||||
self.is_overriden,
|
||||
self.is_modified
|
||||
)
|
||||
|
||||
if self._state == state:
|
||||
return
|
||||
|
||||
self._state = state
|
||||
self.input_field.setProperty("input-state", state)
|
||||
self.input_field.style().polish(self.input_field)
|
||||
if self.label_widget:
|
||||
self.label_widget.setProperty("state", state)
|
||||
self.label_widget.style().polish(self.label_widget)
|
||||
|
||||
def item_value(self):
|
||||
return self.input_field.value()
|
||||
|
||||
|
||||
class RawJsonInput(QtWidgets.QPlainTextEdit):
|
||||
tab_length = 4
|
||||
|
||||
|
|
@ -3496,6 +3597,7 @@ TypeToKlass.types["path-input"] = PathInputWidget
|
|||
TypeToKlass.types["raw-json"] = RawJsonWidget
|
||||
TypeToKlass.types["list"] = ListWidget
|
||||
TypeToKlass.types["list-strict"] = ListStrictWidget
|
||||
TypeToKlass.types["enum"] = EnumeratorWidget
|
||||
TypeToKlass.types["dict-modifiable"] = ModifiableDict
|
||||
# DEPRECATED - remove when removed from schemas
|
||||
TypeToKlass.types["dict-item"] = DictWidget
|
||||
|
|
|
|||
317
pype/tools/settings/settings/widgets/multiselection_combobox.py
Normal file
317
pype/tools/settings/settings/widgets/multiselection_combobox.py
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
from Qt import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class ComboItemDelegate(QtWidgets.QStyledItemDelegate):
|
||||
"""
|
||||
Helper styled delegate (mostly based on existing private Qt's
|
||||
delegate used by the QtWidgets.QComboBox). Used to style the popup like a
|
||||
list view (e.g windows style).
|
||||
"""
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
option = QtWidgets.QStyleOptionViewItem(option)
|
||||
option.showDecorationSelected = True
|
||||
|
||||
# option.state &= (
|
||||
# ~QtWidgets.QStyle.State_HasFocus
|
||||
# & ~QtWidgets.QStyle.State_MouseOver
|
||||
# )
|
||||
super(ComboItemDelegate, self).paint(painter, option, index)
|
||||
|
||||
|
||||
class MultiSelectionComboBox(QtWidgets.QComboBox):
|
||||
value_changed = QtCore.Signal()
|
||||
ignored_keys = {
|
||||
QtCore.Qt.Key_Up,
|
||||
QtCore.Qt.Key_Down,
|
||||
QtCore.Qt.Key_PageDown,
|
||||
QtCore.Qt.Key_PageUp,
|
||||
QtCore.Qt.Key_Home,
|
||||
QtCore.Qt.Key_End
|
||||
}
|
||||
|
||||
top_bottom_padding = 2
|
||||
left_right_padding = 3
|
||||
left_offset = 4
|
||||
top_bottom_margins = 2
|
||||
item_spacing = 5
|
||||
|
||||
item_bg_color = QtGui.QColor("#31424e")
|
||||
|
||||
def __init__(
|
||||
self, parent=None, placeholder="", separator=", ", **kwargs
|
||||
):
|
||||
super(MultiSelectionComboBox, self).__init__(parent=parent, **kwargs)
|
||||
self.setObjectName("MultiSelectionComboBox")
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
self._popup_is_shown = False
|
||||
self._block_mouse_release_timer = QtCore.QTimer(self, singleShot=True)
|
||||
self._initial_mouse_pos = None
|
||||
self._separator = separator
|
||||
self.placeholder_text = placeholder
|
||||
self.delegate = ComboItemDelegate(self)
|
||||
self.setItemDelegate(self.delegate)
|
||||
|
||||
self.lines = {}
|
||||
self.item_height = (
|
||||
self.fontMetrics().height()
|
||||
+ (2 * self.top_bottom_padding)
|
||||
+ (2 * self.top_bottom_margins)
|
||||
)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Reimplemented."""
|
||||
self._popup_is_shown = False
|
||||
super(MultiSelectionComboBox, self).mousePressEvent(event)
|
||||
if self._popup_is_shown:
|
||||
self._initial_mouse_pos = self.mapToGlobal(event.pos())
|
||||
self._block_mouse_release_timer.start(
|
||||
QtWidgets.QApplication.doubleClickInterval()
|
||||
)
|
||||
|
||||
def showPopup(self):
|
||||
"""Reimplemented."""
|
||||
super(MultiSelectionComboBox, self).showPopup()
|
||||
view = self.view()
|
||||
view.installEventFilter(self)
|
||||
view.viewport().installEventFilter(self)
|
||||
self._popup_is_shown = True
|
||||
|
||||
def hidePopup(self):
|
||||
"""Reimplemented."""
|
||||
self.view().removeEventFilter(self)
|
||||
self.view().viewport().removeEventFilter(self)
|
||||
self._popup_is_shown = False
|
||||
self._initial_mouse_pos = None
|
||||
super(MultiSelectionComboBox, self).hidePopup()
|
||||
self.view().clearFocus()
|
||||
|
||||
def _event_popup_shown(self, obj, event):
|
||||
if not self._popup_is_shown:
|
||||
return
|
||||
|
||||
current_index = self.view().currentIndex()
|
||||
model = self.model()
|
||||
|
||||
if event.type() == QtCore.QEvent.MouseMove:
|
||||
if (
|
||||
self.view().isVisible()
|
||||
and self._initial_mouse_pos is not None
|
||||
and self._block_mouse_release_timer.isActive()
|
||||
):
|
||||
diff = obj.mapToGlobal(event.pos()) - self._initial_mouse_pos
|
||||
if diff.manhattanLength() > 9:
|
||||
self._block_mouse_release_timer.stop()
|
||||
return
|
||||
|
||||
index_flags = current_index.flags()
|
||||
state = current_index.data(QtCore.Qt.CheckStateRole)
|
||||
new_state = None
|
||||
|
||||
if event.type() == QtCore.QEvent.MouseButtonRelease:
|
||||
if (
|
||||
self._block_mouse_release_timer.isActive()
|
||||
or not current_index.isValid()
|
||||
or not self.view().isVisible()
|
||||
or not self.view().rect().contains(event.pos())
|
||||
or not index_flags & QtCore.Qt.ItemIsSelectable
|
||||
or not index_flags & QtCore.Qt.ItemIsEnabled
|
||||
or not index_flags & QtCore.Qt.ItemIsUserCheckable
|
||||
):
|
||||
return
|
||||
|
||||
if state == QtCore.Qt.Unchecked:
|
||||
new_state = QtCore.Qt.Checked
|
||||
else:
|
||||
new_state = QtCore.Qt.Unchecked
|
||||
|
||||
elif event.type() == QtCore.QEvent.KeyPress:
|
||||
# TODO: handle QtCore.Qt.Key_Enter, Key_Return?
|
||||
if event.key() == QtCore.Qt.Key_Space:
|
||||
# toogle the current items check state
|
||||
if (
|
||||
index_flags & QtCore.Qt.ItemIsUserCheckable
|
||||
and index_flags & QtCore.Qt.ItemIsTristate
|
||||
):
|
||||
new_state = QtCore.Qt.CheckState((int(state) + 1) % 3)
|
||||
|
||||
elif index_flags & QtCore.Qt.ItemIsUserCheckable:
|
||||
if state != QtCore.Qt.Checked:
|
||||
new_state = QtCore.Qt.Checked
|
||||
else:
|
||||
new_state = QtCore.Qt.Unchecked
|
||||
|
||||
if new_state is not None:
|
||||
model.setData(current_index, new_state, QtCore.Qt.CheckStateRole)
|
||||
self.view().update(current_index)
|
||||
self.update_size_hint()
|
||||
self.value_changed.emit()
|
||||
return True
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
"""Reimplemented."""
|
||||
result = self._event_popup_shown(obj, event)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return super(MultiSelectionComboBox, self).eventFilter(obj, event)
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""Reimplemented."""
|
||||
painter = QtWidgets.QStylePainter(self)
|
||||
option = QtWidgets.QStyleOptionComboBox()
|
||||
self.initStyleOption(option)
|
||||
painter.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, option)
|
||||
|
||||
# draw the icon and text
|
||||
items = self.checked_items_text()
|
||||
if not items:
|
||||
option.currentText = self.placeholder_text
|
||||
option.palette.setCurrentColorGroup(QtGui.QPalette.Disabled)
|
||||
painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option)
|
||||
return
|
||||
|
||||
font_metricts = self.fontMetrics()
|
||||
for line, items in self.lines.items():
|
||||
top_y = (
|
||||
option.rect.top()
|
||||
+ (line * self.item_height)
|
||||
+ self.top_bottom_margins
|
||||
)
|
||||
left_x = option.rect.left() + self.left_offset
|
||||
for item in items:
|
||||
label_rect = font_metricts.boundingRect(item)
|
||||
label_height = label_rect.height()
|
||||
|
||||
label_rect.moveTop(top_y)
|
||||
label_rect.moveLeft(left_x)
|
||||
label_rect.setHeight(self.item_height)
|
||||
|
||||
bg_rect = QtCore.QRectF(label_rect)
|
||||
bg_rect.setWidth(
|
||||
label_rect.width()
|
||||
+ (2 * self.left_right_padding)
|
||||
)
|
||||
left_x = bg_rect.right() + self.item_spacing
|
||||
|
||||
label_rect.moveLeft(label_rect.x() + self.left_right_padding)
|
||||
|
||||
bg_rect.setHeight(label_height + (2 * self.top_bottom_padding))
|
||||
bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins)
|
||||
|
||||
path = QtGui.QPainterPath()
|
||||
path.addRoundedRect(bg_rect, 5, 5)
|
||||
|
||||
painter.fillPath(path, self.item_bg_color)
|
||||
|
||||
painter.drawText(
|
||||
label_rect,
|
||||
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter,
|
||||
item
|
||||
)
|
||||
|
||||
def resizeEvent(self, *args, **kwargs):
|
||||
super(MultiSelectionComboBox, self).resizeEvent(*args, **kwargs)
|
||||
self.update_size_hint()
|
||||
|
||||
def update_size_hint(self):
|
||||
self.lines = {}
|
||||
|
||||
items = self.checked_items_text()
|
||||
if not items:
|
||||
self.update()
|
||||
return
|
||||
|
||||
option = QtWidgets.QStyleOptionComboBox()
|
||||
self.initStyleOption(option)
|
||||
btn_rect = self.style().subControlRect(
|
||||
QtWidgets.QStyle.CC_ComboBox,
|
||||
option,
|
||||
QtWidgets.QStyle.SC_ComboBoxArrow
|
||||
)
|
||||
total_width = option.rect.width() - btn_rect.width()
|
||||
font_metricts = self.fontMetrics()
|
||||
|
||||
line = 0
|
||||
self.lines = {line: []}
|
||||
|
||||
font_metricts = self.fontMetrics()
|
||||
default_left_x = 0 + self.left_offset
|
||||
left_x = int(default_left_x)
|
||||
for item in items:
|
||||
rect = font_metricts.boundingRect(item)
|
||||
width = rect.width() + (2 * self.left_right_padding)
|
||||
right_x = left_x + width
|
||||
if right_x > total_width:
|
||||
left_x = int(default_left_x)
|
||||
if self.lines.get(line):
|
||||
line += 1
|
||||
self.lines[line] = [item]
|
||||
left_x += width
|
||||
else:
|
||||
self.lines[line] = [item]
|
||||
line += 1
|
||||
else:
|
||||
self.lines[line].append(item)
|
||||
left_x = left_x + width + self.item_spacing
|
||||
|
||||
self.update()
|
||||
self.updateGeometry()
|
||||
|
||||
def sizeHint(self):
|
||||
value = super(MultiSelectionComboBox, self).sizeHint()
|
||||
lines = len(self.lines)
|
||||
if lines == 0:
|
||||
lines = 1
|
||||
value.setHeight(
|
||||
(lines * self.item_height)
|
||||
+ (2 * self.top_bottom_margins)
|
||||
)
|
||||
return value
|
||||
|
||||
def setItemCheckState(self, index, state):
|
||||
self.setItemData(index, state, QtCore.Qt.CheckStateRole)
|
||||
|
||||
def set_value(self, values):
|
||||
for idx in range(self.count()):
|
||||
value = self.itemData(idx, role=QtCore.Qt.UserRole)
|
||||
if value in values:
|
||||
check_state = QtCore.Qt.Checked
|
||||
else:
|
||||
check_state = QtCore.Qt.Unchecked
|
||||
self.setItemData(idx, check_state, QtCore.Qt.CheckStateRole)
|
||||
self.update_size_hint()
|
||||
|
||||
def value(self):
|
||||
items = list()
|
||||
for idx in range(self.count()):
|
||||
state = self.itemData(idx, role=QtCore.Qt.CheckStateRole)
|
||||
if state == QtCore.Qt.Checked:
|
||||
items.append(
|
||||
self.itemData(idx, role=QtCore.Qt.UserRole)
|
||||
)
|
||||
return items
|
||||
|
||||
def checked_items_text(self):
|
||||
items = list()
|
||||
for idx in range(self.count()):
|
||||
state = self.itemData(idx, role=QtCore.Qt.CheckStateRole)
|
||||
if state == QtCore.Qt.Checked:
|
||||
items.append(self.itemText(idx))
|
||||
return items
|
||||
|
||||
def wheelEvent(self, event):
|
||||
event.ignore()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if (
|
||||
event.key() == QtCore.Qt.Key_Down
|
||||
and event.modifiers() & QtCore.Qt.AltModifier
|
||||
):
|
||||
return self.showPopup()
|
||||
|
||||
if event.key() in self.ignored_keys:
|
||||
return event.ignore()
|
||||
|
||||
return super(MultiSelectionComboBox, self).keyPressEvent(event)
|
||||
|
|
@ -25,6 +25,28 @@ class NumberSpinBox(QtWidgets.QDoubleSpinBox):
|
|||
return output
|
||||
|
||||
|
||||
class ComboBox(QtWidgets.QComboBox):
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ComboBox, self).__init__(*args, **kwargs)
|
||||
|
||||
self.currentIndexChanged.connect(self._on_change)
|
||||
|
||||
def _on_change(self, *args, **kwargs):
|
||||
self.value_changed.emit()
|
||||
|
||||
def set_value(self, value):
|
||||
for idx in range(self.count()):
|
||||
_value = self.itemData(idx, role=QtCore.Qt.UserRole)
|
||||
if _value == value:
|
||||
self.setCurrentIndex(idx)
|
||||
break
|
||||
|
||||
def value(self):
|
||||
return self.itemData(self.currentIndex(), role=QtCore.Qt.UserRole)
|
||||
|
||||
|
||||
class PathInput(QtWidgets.QLineEdit):
|
||||
def clear_end_path(self):
|
||||
value = self.text().strip()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue