mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
removed attribute defs from widgets
This commit is contained in:
parent
c78b7d1d0c
commit
72e166066e
3 changed files with 0 additions and 1468 deletions
|
|
@ -1,10 +0,0 @@
|
|||
from .widgets import (
|
||||
create_widget_for_attr_def,
|
||||
AttributeDefinitionsWidget,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"create_widget_for_attr_def",
|
||||
"AttributeDefinitionsWidget",
|
||||
)
|
||||
|
|
@ -1,968 +0,0 @@
|
|||
import os
|
||||
import collections
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype.lib import FileDefItem
|
||||
from openpype.tools.utils import (
|
||||
paint_image_with_color,
|
||||
ClickableLabel,
|
||||
)
|
||||
# TODO change imports
|
||||
from openpype.tools.resources import get_image
|
||||
from openpype.tools.utils import (
|
||||
IconButton,
|
||||
PixmapLabel
|
||||
)
|
||||
|
||||
ITEM_ID_ROLE = QtCore.Qt.UserRole + 1
|
||||
ITEM_LABEL_ROLE = QtCore.Qt.UserRole + 2
|
||||
ITEM_ICON_ROLE = QtCore.Qt.UserRole + 3
|
||||
FILENAMES_ROLE = QtCore.Qt.UserRole + 4
|
||||
DIRPATH_ROLE = QtCore.Qt.UserRole + 5
|
||||
IS_DIR_ROLE = QtCore.Qt.UserRole + 6
|
||||
IS_SEQUENCE_ROLE = QtCore.Qt.UserRole + 7
|
||||
EXT_ROLE = QtCore.Qt.UserRole + 8
|
||||
|
||||
|
||||
def convert_bytes_to_json(bytes_value):
|
||||
if isinstance(bytes_value, QtCore.QByteArray):
|
||||
# Raw data are already QByteArray and we don't have to load them
|
||||
encoded_data = bytes_value
|
||||
else:
|
||||
encoded_data = QtCore.QByteArray.fromRawData(bytes_value)
|
||||
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
|
||||
text = stream.readQString()
|
||||
try:
|
||||
return json.loads(text)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def convert_data_to_bytes(data):
|
||||
bytes_value = QtCore.QByteArray()
|
||||
stream = QtCore.QDataStream(bytes_value, QtCore.QIODevice.WriteOnly)
|
||||
stream.writeQString(json.dumps(data))
|
||||
return bytes_value
|
||||
|
||||
|
||||
class SupportLabel(QtWidgets.QLabel):
|
||||
pass
|
||||
|
||||
|
||||
class DropEmpty(QtWidgets.QWidget):
|
||||
_empty_extensions = "Any file"
|
||||
|
||||
def __init__(self, single_item, allow_sequences, extensions_label, parent):
|
||||
super(DropEmpty, self).__init__(parent)
|
||||
|
||||
drop_label_widget = QtWidgets.QLabel("Drag & Drop files here", self)
|
||||
|
||||
items_label_widget = SupportLabel(self)
|
||||
items_label_widget.setWordWrap(True)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addSpacing(20)
|
||||
layout.addWidget(
|
||||
drop_label_widget, 0, alignment=QtCore.Qt.AlignCenter
|
||||
)
|
||||
layout.addSpacing(30)
|
||||
layout.addStretch(1)
|
||||
layout.addWidget(
|
||||
items_label_widget, 0, alignment=QtCore.Qt.AlignCenter
|
||||
)
|
||||
layout.addSpacing(10)
|
||||
|
||||
for widget in (
|
||||
drop_label_widget,
|
||||
items_label_widget,
|
||||
):
|
||||
widget.setAlignment(QtCore.Qt.AlignCenter)
|
||||
widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
update_size_timer = QtCore.QTimer()
|
||||
update_size_timer.setInterval(10)
|
||||
update_size_timer.setSingleShot(True)
|
||||
|
||||
update_size_timer.timeout.connect(self._on_update_size_timer)
|
||||
|
||||
self._update_size_timer = update_size_timer
|
||||
|
||||
if extensions_label and not extensions_label.startswith(" "):
|
||||
extensions_label = " " + extensions_label
|
||||
|
||||
self._single_item = single_item
|
||||
self._extensions_label = extensions_label
|
||||
self._allow_sequences = allow_sequences
|
||||
self._allowed_extensions = set()
|
||||
self._allow_folders = None
|
||||
|
||||
self._drop_label_widget = drop_label_widget
|
||||
self._items_label_widget = items_label_widget
|
||||
|
||||
self.set_allow_folders(False)
|
||||
|
||||
def set_extensions(self, extensions):
|
||||
if extensions:
|
||||
extensions = {
|
||||
ext.replace(".", "")
|
||||
for ext in extensions
|
||||
}
|
||||
if extensions == self._allowed_extensions:
|
||||
return
|
||||
self._allowed_extensions = extensions
|
||||
|
||||
self._update_items_label()
|
||||
|
||||
def set_allow_folders(self, allowed):
|
||||
if self._allow_folders == allowed:
|
||||
return
|
||||
|
||||
self._allow_folders = allowed
|
||||
self._update_items_label()
|
||||
|
||||
def _update_items_label(self):
|
||||
allowed_items = []
|
||||
if self._allow_folders:
|
||||
allowed_items.append("folder")
|
||||
|
||||
if self._allowed_extensions:
|
||||
allowed_items.append("file")
|
||||
if self._allow_sequences:
|
||||
allowed_items.append("sequence")
|
||||
|
||||
if not self._single_item:
|
||||
allowed_items = [item + "s" for item in allowed_items]
|
||||
|
||||
if not allowed_items:
|
||||
self._items_label_widget.setText(
|
||||
"It is not allowed to add anything here!"
|
||||
)
|
||||
return
|
||||
|
||||
items_label = "Multiple "
|
||||
if self._single_item:
|
||||
items_label = "Single "
|
||||
|
||||
if len(allowed_items) == 1:
|
||||
extensions_label = allowed_items[0]
|
||||
elif len(allowed_items) == 2:
|
||||
extensions_label = " or ".join(allowed_items)
|
||||
else:
|
||||
last_item = allowed_items.pop(-1)
|
||||
new_last_item = " or ".join(last_item, allowed_items.pop(-1))
|
||||
allowed_items.append(new_last_item)
|
||||
extensions_label = ", ".join(allowed_items)
|
||||
|
||||
allowed_items_label = extensions_label
|
||||
|
||||
items_label += allowed_items_label
|
||||
label_tooltip = None
|
||||
if self._allowed_extensions:
|
||||
items_label += " of\n{}".format(
|
||||
", ".join(sorted(self._allowed_extensions))
|
||||
)
|
||||
|
||||
if self._extensions_label:
|
||||
label_tooltip = items_label
|
||||
items_label = self._extensions_label
|
||||
|
||||
if self._items_label_widget.text() == items_label:
|
||||
return
|
||||
|
||||
self._items_label_widget.setToolTip(label_tooltip)
|
||||
self._items_label_widget.setText(items_label)
|
||||
self._update_size_timer.start()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(DropEmpty, self).resizeEvent(event)
|
||||
self._update_size_timer.start()
|
||||
|
||||
def _on_update_size_timer(self):
|
||||
"""Recalculate height of label with extensions.
|
||||
|
||||
Dynamic QLabel with word wrap does not handle properly it's sizeHint
|
||||
calculations on show. This way it is recalculated. It is good practice
|
||||
to trigger this method with small offset using '_update_size_timer'.
|
||||
"""
|
||||
|
||||
width = self._items_label_widget.width()
|
||||
height = self._items_label_widget.heightForWidth(width)
|
||||
self._items_label_widget.setMinimumHeight(height)
|
||||
self._items_label_widget.updateGeometry()
|
||||
|
||||
def paintEvent(self, event):
|
||||
super(DropEmpty, self).paintEvent(event)
|
||||
painter = QtGui.QPainter(self)
|
||||
pen = QtGui.QPen()
|
||||
pen.setWidth(1)
|
||||
pen.setBrush(QtCore.Qt.darkGray)
|
||||
pen.setStyle(QtCore.Qt.DashLine)
|
||||
painter.setPen(pen)
|
||||
content_margins = self.layout().contentsMargins()
|
||||
|
||||
left_m = content_margins.left()
|
||||
top_m = content_margins.top()
|
||||
rect = QtCore.QRect(
|
||||
left_m,
|
||||
top_m,
|
||||
(
|
||||
self.rect().width()
|
||||
- (left_m + content_margins.right() + pen.width())
|
||||
),
|
||||
(
|
||||
self.rect().height()
|
||||
- (top_m + content_margins.bottom() + pen.width())
|
||||
)
|
||||
)
|
||||
painter.drawRect(rect)
|
||||
|
||||
|
||||
class FilesModel(QtGui.QStandardItemModel):
|
||||
def __init__(self, single_item, allow_sequences):
|
||||
super(FilesModel, self).__init__()
|
||||
|
||||
self._id = str(uuid.uuid4())
|
||||
self._single_item = single_item
|
||||
self._multivalue = False
|
||||
self._allow_sequences = allow_sequences
|
||||
|
||||
self._items_by_id = {}
|
||||
self._file_items_by_id = {}
|
||||
self._filenames_by_dirpath = collections.defaultdict(set)
|
||||
self._items_by_dirpath = collections.defaultdict(list)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
def set_multivalue(self, multivalue):
|
||||
"""Disable filtering."""
|
||||
|
||||
if self._multivalue == multivalue:
|
||||
return
|
||||
self._multivalue = multivalue
|
||||
|
||||
def add_filepaths(self, items):
|
||||
if not items:
|
||||
return
|
||||
|
||||
file_items = FileDefItem.from_value(items, self._allow_sequences)
|
||||
if not file_items:
|
||||
return
|
||||
|
||||
if not self._multivalue and self._single_item:
|
||||
file_items = [file_items[0]]
|
||||
current_ids = list(self._file_items_by_id.keys())
|
||||
if current_ids:
|
||||
self.remove_item_by_ids(current_ids)
|
||||
|
||||
new_model_items = []
|
||||
for file_item in file_items:
|
||||
item_id, model_item = self._create_item(file_item)
|
||||
new_model_items.append(model_item)
|
||||
self._file_items_by_id[item_id] = file_item
|
||||
self._items_by_id[item_id] = model_item
|
||||
|
||||
if new_model_items:
|
||||
roow_item = self.invisibleRootItem()
|
||||
roow_item.appendRows(new_model_items)
|
||||
|
||||
def remove_item_by_ids(self, item_ids):
|
||||
if not item_ids:
|
||||
return
|
||||
|
||||
items = []
|
||||
for item_id in set(item_ids):
|
||||
if item_id not in self._items_by_id:
|
||||
continue
|
||||
item = self._items_by_id.pop(item_id)
|
||||
self._file_items_by_id.pop(item_id)
|
||||
items.append(item)
|
||||
|
||||
if items:
|
||||
for item in items:
|
||||
self.removeRows(item.row(), 1)
|
||||
|
||||
def get_file_item_by_id(self, item_id):
|
||||
return self._file_items_by_id.get(item_id)
|
||||
|
||||
def _create_item(self, file_item):
|
||||
if file_item.is_dir:
|
||||
icon_pixmap = paint_image_with_color(
|
||||
get_image(filename="folder.png"), QtCore.Qt.white
|
||||
)
|
||||
else:
|
||||
icon_pixmap = paint_image_with_color(
|
||||
get_image(filename="file.png"), QtCore.Qt.white
|
||||
)
|
||||
|
||||
item = QtGui.QStandardItem()
|
||||
item_id = str(uuid.uuid4())
|
||||
item.setData(item_id, ITEM_ID_ROLE)
|
||||
item.setData(file_item.label or "< empty >", ITEM_LABEL_ROLE)
|
||||
item.setData(file_item.filenames, FILENAMES_ROLE)
|
||||
item.setData(file_item.directory, DIRPATH_ROLE)
|
||||
item.setData(icon_pixmap, ITEM_ICON_ROLE)
|
||||
item.setData(file_item.ext, EXT_ROLE)
|
||||
item.setData(file_item.is_dir, IS_DIR_ROLE)
|
||||
item.setData(file_item.is_sequence, IS_SEQUENCE_ROLE)
|
||||
|
||||
return item_id, item
|
||||
|
||||
def mimeData(self, indexes):
|
||||
item_ids = [
|
||||
index.data(ITEM_ID_ROLE)
|
||||
for index in indexes
|
||||
]
|
||||
|
||||
item_ids_data = convert_data_to_bytes(item_ids)
|
||||
mime_data = super(FilesModel, self).mimeData(indexes)
|
||||
mime_data.setData("files_widget/internal_move", item_ids_data)
|
||||
|
||||
file_items = []
|
||||
for item_id in item_ids:
|
||||
file_item = self.get_file_item_by_id(item_id)
|
||||
if file_item:
|
||||
file_items.append(file_item.to_dict())
|
||||
|
||||
full_item_data = convert_data_to_bytes({
|
||||
"items": file_items,
|
||||
"id": self._id
|
||||
})
|
||||
mime_data.setData("files_widget/full_data", full_item_data)
|
||||
return mime_data
|
||||
|
||||
def dropMimeData(self, mime_data, action, row, col, index):
|
||||
item_ids = convert_bytes_to_json(
|
||||
mime_data.data("files_widget/internal_move")
|
||||
)
|
||||
if item_ids is None:
|
||||
return False
|
||||
|
||||
# Find matching item after which will be items moved
|
||||
# - store item before moved items are removed
|
||||
root = self.invisibleRootItem()
|
||||
if row >= 0:
|
||||
src_item = self.item(row)
|
||||
else:
|
||||
src_item_id = index.data(ITEM_ID_ROLE)
|
||||
src_item = self._items_by_id.get(src_item_id)
|
||||
|
||||
# Take out items that should be moved
|
||||
items = []
|
||||
for item_id in item_ids:
|
||||
item = self._items_by_id.get(item_id)
|
||||
if item:
|
||||
self.takeRow(item.row())
|
||||
items.append(item)
|
||||
|
||||
# Skip if there are not items that can be moved
|
||||
if not items:
|
||||
return False
|
||||
|
||||
# Calculate row where items should be inserted
|
||||
if src_item:
|
||||
src_row = src_item.row()
|
||||
else:
|
||||
src_row = root.rowCount()
|
||||
|
||||
root.insertRow(src_row, items)
|
||||
return True
|
||||
|
||||
|
||||
class FilesProxyModel(QtCore.QSortFilterProxyModel):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FilesProxyModel, self).__init__(*args, **kwargs)
|
||||
self._allow_folders = False
|
||||
self._allowed_extensions = None
|
||||
self._multivalue = False
|
||||
|
||||
def set_multivalue(self, multivalue):
|
||||
"""Disable filtering."""
|
||||
|
||||
if self._multivalue == multivalue:
|
||||
return
|
||||
self._multivalue = multivalue
|
||||
self.invalidateFilter()
|
||||
|
||||
def set_allow_folders(self, allow=None):
|
||||
if allow is None:
|
||||
allow = not self._allow_folders
|
||||
|
||||
if allow == self._allow_folders:
|
||||
return
|
||||
self._allow_folders = allow
|
||||
self.invalidateFilter()
|
||||
|
||||
def set_allowed_extensions(self, extensions=None):
|
||||
if extensions is not None:
|
||||
_extensions = set()
|
||||
for ext in set(extensions):
|
||||
if not ext.startswith("."):
|
||||
ext = ".{}".format(ext)
|
||||
_extensions.add(ext.lower())
|
||||
extensions = _extensions
|
||||
|
||||
if self._allowed_extensions != extensions:
|
||||
self._allowed_extensions = extensions
|
||||
self.invalidateFilter()
|
||||
|
||||
def are_valid_files(self, filepaths):
|
||||
for filepath in filepaths:
|
||||
if os.path.isfile(filepath):
|
||||
_, ext = os.path.splitext(filepath)
|
||||
if ext in self._allowed_extensions:
|
||||
return True
|
||||
|
||||
elif self._allow_folders:
|
||||
return True
|
||||
return False
|
||||
|
||||
def filter_valid_files(self, filepaths):
|
||||
filtered_paths = []
|
||||
for filepath in filepaths:
|
||||
if os.path.isfile(filepath):
|
||||
_, ext = os.path.splitext(filepath)
|
||||
if ext in self._allowed_extensions:
|
||||
filtered_paths.append(filepath)
|
||||
|
||||
elif self._allow_folders:
|
||||
filtered_paths.append(filepath)
|
||||
return filtered_paths
|
||||
|
||||
def filterAcceptsRow(self, row, parent_index):
|
||||
# Skip filtering if multivalue is set
|
||||
if self._multivalue:
|
||||
return True
|
||||
|
||||
model = self.sourceModel()
|
||||
index = model.index(row, self.filterKeyColumn(), parent_index)
|
||||
# First check if item is folder and if folders are enabled
|
||||
if index.data(IS_DIR_ROLE):
|
||||
if not self._allow_folders:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if there are any allowed extensions
|
||||
if self._allowed_extensions is None:
|
||||
return False
|
||||
|
||||
if index.data(EXT_ROLE) not in self._allowed_extensions:
|
||||
return False
|
||||
return True
|
||||
|
||||
def lessThan(self, left, right):
|
||||
left_comparison = left.data(DIRPATH_ROLE)
|
||||
right_comparison = right.data(DIRPATH_ROLE)
|
||||
if left_comparison == right_comparison:
|
||||
left_comparison = left.data(ITEM_LABEL_ROLE)
|
||||
right_comparison = right.data(ITEM_LABEL_ROLE)
|
||||
|
||||
if sorted((left_comparison, right_comparison))[0] == left_comparison:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ItemWidget(QtWidgets.QWidget):
|
||||
context_menu_requested = QtCore.Signal(QtCore.QPoint)
|
||||
|
||||
def __init__(
|
||||
self, item_id, label, pixmap_icon, is_sequence, multivalue, parent=None
|
||||
):
|
||||
self._item_id = item_id
|
||||
|
||||
super(ItemWidget, self).__init__(parent)
|
||||
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
icon_widget = PixmapLabel(pixmap_icon, self)
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
|
||||
label_size_hint = label_widget.sizeHint()
|
||||
height = label_size_hint.height()
|
||||
actions_menu_pix = paint_image_with_color(
|
||||
get_image(filename="menu.png"), QtCore.Qt.white
|
||||
)
|
||||
|
||||
split_btn = ClickableLabel(self)
|
||||
split_btn.setFixedSize(height, height)
|
||||
split_btn.setPixmap(actions_menu_pix)
|
||||
if multivalue:
|
||||
split_btn.setVisible(False)
|
||||
else:
|
||||
split_btn.setVisible(is_sequence)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.addWidget(icon_widget, 0)
|
||||
layout.addWidget(label_widget, 1)
|
||||
layout.addWidget(split_btn, 0)
|
||||
|
||||
split_btn.clicked.connect(self._on_actions_clicked)
|
||||
|
||||
self._icon_widget = icon_widget
|
||||
self._label_widget = label_widget
|
||||
self._split_btn = split_btn
|
||||
self._actions_menu_pix = actions_menu_pix
|
||||
self._last_scaled_pix_height = None
|
||||
|
||||
def _update_btn_size(self):
|
||||
label_size_hint = self._label_widget.sizeHint()
|
||||
height = label_size_hint.height()
|
||||
if height == self._last_scaled_pix_height:
|
||||
return
|
||||
self._last_scaled_pix_height = height
|
||||
self._split_btn.setFixedSize(height, height)
|
||||
pix = self._actions_menu_pix.scaled(
|
||||
height, height,
|
||||
QtCore.Qt.KeepAspectRatio,
|
||||
QtCore.Qt.SmoothTransformation
|
||||
)
|
||||
self._split_btn.setPixmap(pix)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(ItemWidget, self).showEvent(event)
|
||||
self._update_btn_size()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(ItemWidget, self).resizeEvent(event)
|
||||
self._update_btn_size()
|
||||
|
||||
def _on_actions_clicked(self):
|
||||
pos = self._split_btn.rect().bottomLeft()
|
||||
point = self._split_btn.mapToGlobal(pos)
|
||||
self.context_menu_requested.emit(point)
|
||||
|
||||
|
||||
class InViewButton(IconButton):
|
||||
pass
|
||||
|
||||
|
||||
class FilesView(QtWidgets.QListView):
|
||||
"""View showing instances and their groups."""
|
||||
|
||||
remove_requested = QtCore.Signal()
|
||||
context_menu_requested = QtCore.Signal(QtCore.QPoint)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FilesView, self).__init__(*args, **kwargs)
|
||||
|
||||
self.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
|
||||
self.setSelectionMode(
|
||||
QtWidgets.QAbstractItemView.ExtendedSelection
|
||||
)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.setAcceptDrops(True)
|
||||
self.setDragEnabled(True)
|
||||
self.setDragDropMode(self.InternalMove)
|
||||
|
||||
remove_btn = InViewButton(self)
|
||||
pix_enabled = paint_image_with_color(
|
||||
get_image(filename="delete.png"), QtCore.Qt.white
|
||||
)
|
||||
pix_disabled = paint_image_with_color(
|
||||
get_image(filename="delete.png"), QtCore.Qt.gray
|
||||
)
|
||||
icon = QtGui.QIcon(pix_enabled)
|
||||
icon.addPixmap(pix_disabled, icon.Disabled, icon.Off)
|
||||
remove_btn.setIcon(icon)
|
||||
remove_btn.setEnabled(False)
|
||||
|
||||
remove_btn.clicked.connect(self._on_remove_clicked)
|
||||
self.customContextMenuRequested.connect(self._on_context_menu_request)
|
||||
|
||||
self._remove_btn = remove_btn
|
||||
|
||||
def setSelectionModel(self, *args, **kwargs):
|
||||
"""Catch selection model set to register signal callback.
|
||||
|
||||
Selection model is not available during initialization.
|
||||
"""
|
||||
|
||||
super(FilesView, self).setSelectionModel(*args, **kwargs)
|
||||
selection_model = self.selectionModel()
|
||||
selection_model.selectionChanged.connect(self._on_selection_change)
|
||||
|
||||
def set_multivalue(self, multivalue):
|
||||
"""Disable remove button on multivalue."""
|
||||
|
||||
self._remove_btn.setVisible(not multivalue)
|
||||
|
||||
def has_selected_item_ids(self):
|
||||
"""Is any index selected."""
|
||||
for index in self.selectionModel().selectedIndexes():
|
||||
instance_id = index.data(ITEM_ID_ROLE)
|
||||
if instance_id is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_selected_item_ids(self):
|
||||
"""Ids of selected instances."""
|
||||
|
||||
selected_item_ids = set()
|
||||
for index in self.selectionModel().selectedIndexes():
|
||||
instance_id = index.data(ITEM_ID_ROLE)
|
||||
if instance_id is not None:
|
||||
selected_item_ids.add(instance_id)
|
||||
return selected_item_ids
|
||||
|
||||
def has_selected_sequence(self):
|
||||
for index in self.selectionModel().selectedIndexes():
|
||||
if index.data(IS_SEQUENCE_ROLE):
|
||||
return True
|
||||
return False
|
||||
|
||||
def event(self, event):
|
||||
if event.type() == QtCore.QEvent.KeyPress:
|
||||
if (
|
||||
event.key() == QtCore.Qt.Key_Delete
|
||||
and self.has_selected_item_ids()
|
||||
):
|
||||
self.remove_requested.emit()
|
||||
return True
|
||||
|
||||
return super(FilesView, self).event(event)
|
||||
|
||||
def _on_context_menu_request(self, pos):
|
||||
index = self.indexAt(pos)
|
||||
if index.isValid():
|
||||
point = self.viewport().mapToGlobal(pos)
|
||||
self.context_menu_requested.emit(point)
|
||||
|
||||
def _on_selection_change(self):
|
||||
self._remove_btn.setEnabled(self.has_selected_item_ids())
|
||||
|
||||
def _on_remove_clicked(self):
|
||||
self.remove_requested.emit()
|
||||
|
||||
def _update_remove_btn(self):
|
||||
"""Position remove button to bottom right."""
|
||||
|
||||
viewport = self.viewport()
|
||||
height = viewport.height()
|
||||
pos_x = viewport.width() - self._remove_btn.width() - 5
|
||||
pos_y = height - self._remove_btn.height() - 5
|
||||
self._remove_btn.move(max(0, pos_x), max(0, pos_y))
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(FilesView, self).resizeEvent(event)
|
||||
self._update_remove_btn()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(FilesView, self).showEvent(event)
|
||||
self._update_remove_btn()
|
||||
|
||||
|
||||
class FilesWidget(QtWidgets.QFrame):
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, single_item, allow_sequences, extensions_label, parent):
|
||||
super(FilesWidget, self).__init__(parent)
|
||||
self.setAcceptDrops(True)
|
||||
|
||||
empty_widget = DropEmpty(
|
||||
single_item, allow_sequences, extensions_label, self
|
||||
)
|
||||
|
||||
files_model = FilesModel(single_item, allow_sequences)
|
||||
files_proxy_model = FilesProxyModel()
|
||||
files_proxy_model.setSourceModel(files_model)
|
||||
files_view = FilesView(self)
|
||||
files_view.setModel(files_proxy_model)
|
||||
files_view.setVisible(False)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(empty_widget, 1)
|
||||
layout.addWidget(files_view, 1)
|
||||
|
||||
files_proxy_model.rowsInserted.connect(self._on_rows_inserted)
|
||||
files_proxy_model.rowsRemoved.connect(self._on_rows_removed)
|
||||
files_view.remove_requested.connect(self._on_remove_requested)
|
||||
files_view.context_menu_requested.connect(
|
||||
self._on_context_menu_requested
|
||||
)
|
||||
|
||||
self._in_set_value = False
|
||||
self._single_item = single_item
|
||||
self._multivalue = False
|
||||
|
||||
self._empty_widget = empty_widget
|
||||
self._files_model = files_model
|
||||
self._files_proxy_model = files_proxy_model
|
||||
self._files_view = files_view
|
||||
|
||||
self._widgets_by_id = {}
|
||||
|
||||
def _set_multivalue(self, multivalue):
|
||||
if self._multivalue == multivalue:
|
||||
return
|
||||
self._multivalue = multivalue
|
||||
self._files_view.set_multivalue(multivalue)
|
||||
self._files_model.set_multivalue(multivalue)
|
||||
self._files_proxy_model.set_multivalue(multivalue)
|
||||
|
||||
def set_value(self, value, multivalue):
|
||||
self._in_set_value = True
|
||||
|
||||
widget_ids = set(self._widgets_by_id.keys())
|
||||
self._remove_item_by_ids(widget_ids)
|
||||
|
||||
self._set_multivalue(multivalue)
|
||||
|
||||
self._add_filepaths(value)
|
||||
|
||||
self._in_set_value = False
|
||||
|
||||
def current_value(self):
|
||||
model = self._files_proxy_model
|
||||
item_ids = set()
|
||||
for row in range(model.rowCount()):
|
||||
index = model.index(row, 0)
|
||||
item_ids.add(index.data(ITEM_ID_ROLE))
|
||||
|
||||
file_items = []
|
||||
for item_id in item_ids:
|
||||
file_item = self._files_model.get_file_item_by_id(item_id)
|
||||
if file_item is not None:
|
||||
file_items.append(file_item.to_dict())
|
||||
|
||||
if not self._single_item:
|
||||
return file_items
|
||||
if file_items:
|
||||
return file_items[0]
|
||||
|
||||
empty_item = FileDefItem.create_empty_item()
|
||||
return empty_item.to_dict()
|
||||
|
||||
def set_filters(self, folders_allowed, exts_filter):
|
||||
self._files_proxy_model.set_allow_folders(folders_allowed)
|
||||
self._files_proxy_model.set_allowed_extensions(exts_filter)
|
||||
self._empty_widget.set_extensions(exts_filter)
|
||||
self._empty_widget.set_allow_folders(folders_allowed)
|
||||
|
||||
def _on_rows_inserted(self, parent_index, start_row, end_row):
|
||||
for row in range(start_row, end_row + 1):
|
||||
index = self._files_proxy_model.index(row, 0, parent_index)
|
||||
item_id = index.data(ITEM_ID_ROLE)
|
||||
if item_id in self._widgets_by_id:
|
||||
continue
|
||||
label = index.data(ITEM_LABEL_ROLE)
|
||||
pixmap_icon = index.data(ITEM_ICON_ROLE)
|
||||
is_sequence = index.data(IS_SEQUENCE_ROLE)
|
||||
|
||||
widget = ItemWidget(
|
||||
item_id,
|
||||
label,
|
||||
pixmap_icon,
|
||||
is_sequence,
|
||||
self._multivalue
|
||||
)
|
||||
widget.context_menu_requested.connect(
|
||||
self._on_context_menu_requested
|
||||
)
|
||||
self._files_view.setIndexWidget(index, widget)
|
||||
self._files_proxy_model.setData(
|
||||
index, widget.sizeHint(), QtCore.Qt.SizeHintRole
|
||||
)
|
||||
self._widgets_by_id[item_id] = widget
|
||||
|
||||
if not self._in_set_value:
|
||||
self.value_changed.emit()
|
||||
|
||||
def _on_rows_removed(self, parent_index, start_row, end_row):
|
||||
available_item_ids = set()
|
||||
for row in range(self._files_proxy_model.rowCount()):
|
||||
index = self._files_proxy_model.index(row, 0)
|
||||
item_id = index.data(ITEM_ID_ROLE)
|
||||
available_item_ids.add(index.data(ITEM_ID_ROLE))
|
||||
|
||||
widget_ids = set(self._widgets_by_id.keys())
|
||||
for item_id in available_item_ids:
|
||||
if item_id in widget_ids:
|
||||
widget_ids.remove(item_id)
|
||||
|
||||
for item_id in widget_ids:
|
||||
widget = self._widgets_by_id.pop(item_id)
|
||||
widget.setVisible(False)
|
||||
widget.deleteLater()
|
||||
|
||||
if not self._in_set_value:
|
||||
self.value_changed.emit()
|
||||
|
||||
def _on_split_request(self):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
item_ids = self._files_view.get_selected_item_ids()
|
||||
if not item_ids:
|
||||
return
|
||||
|
||||
for item_id in item_ids:
|
||||
file_item = self._files_model.get_file_item_by_id(item_id)
|
||||
if not file_item:
|
||||
return
|
||||
|
||||
new_items = file_item.split_sequence()
|
||||
self._add_filepaths(new_items)
|
||||
self._remove_item_by_ids(item_ids)
|
||||
|
||||
def _on_remove_requested(self):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
items_to_delete = self._files_view.get_selected_item_ids()
|
||||
if items_to_delete:
|
||||
self._remove_item_by_ids(items_to_delete)
|
||||
|
||||
def _on_context_menu_requested(self, pos):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
menu = QtWidgets.QMenu(self._files_view)
|
||||
|
||||
if self._files_view.has_selected_sequence():
|
||||
split_action = QtWidgets.QAction("Split sequence", menu)
|
||||
split_action.triggered.connect(self._on_split_request)
|
||||
menu.addAction(split_action)
|
||||
|
||||
remove_action = QtWidgets.QAction("Remove", menu)
|
||||
remove_action.triggered.connect(self._on_remove_requested)
|
||||
menu.addAction(remove_action)
|
||||
|
||||
menu.popup(pos)
|
||||
|
||||
def sizeHint(self):
|
||||
# Get size hints of widget and visible widgets
|
||||
result = super(FilesWidget, self).sizeHint()
|
||||
if not self._files_view.isVisible():
|
||||
not_visible_hint = self._files_view.sizeHint()
|
||||
else:
|
||||
not_visible_hint = self._empty_widget.sizeHint()
|
||||
|
||||
# Get margins of this widget
|
||||
margins = self.layout().contentsMargins()
|
||||
|
||||
# Change size hint based on result of maximum size hint of widgets
|
||||
result.setWidth(max(
|
||||
result.width(),
|
||||
not_visible_hint.width() + margins.left() + margins.right()
|
||||
))
|
||||
result.setHeight(max(
|
||||
result.height(),
|
||||
not_visible_hint.height() + margins.top() + margins.bottom()
|
||||
))
|
||||
|
||||
return result
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
mime_data = event.mimeData()
|
||||
if mime_data.hasUrls():
|
||||
filepaths = []
|
||||
for url in mime_data.urls():
|
||||
filepath = url.toLocalFile()
|
||||
if os.path.exists(filepath):
|
||||
filepaths.append(filepath)
|
||||
|
||||
if self._files_proxy_model.are_valid_files(filepaths):
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
|
||||
full_data_value = mime_data.data("files_widget/full_data")
|
||||
if self._handle_full_data_drag(full_data_value):
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
|
||||
def dragLeaveEvent(self, event):
|
||||
event.accept()
|
||||
|
||||
def dropEvent(self, event):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
mime_data = event.mimeData()
|
||||
if mime_data.hasUrls():
|
||||
event.accept()
|
||||
# event.setDropAction(QtCore.Qt.CopyAction)
|
||||
filepaths = []
|
||||
for url in mime_data.urls():
|
||||
filepath = url.toLocalFile()
|
||||
if os.path.exists(filepath):
|
||||
filepaths.append(filepath)
|
||||
|
||||
# Filter filepaths before passing it to model
|
||||
filepaths = self._files_proxy_model.filter_valid_files(filepaths)
|
||||
if filepaths:
|
||||
self._add_filepaths(filepaths)
|
||||
|
||||
if self._handle_full_data_drop(
|
||||
mime_data.data("files_widget/full_data")
|
||||
):
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
|
||||
super(FilesWidget, self).dropEvent(event)
|
||||
|
||||
def _handle_full_data_drag(self, value):
|
||||
if value is None:
|
||||
return False
|
||||
|
||||
full_data = convert_bytes_to_json(value)
|
||||
if full_data is None:
|
||||
return False
|
||||
|
||||
if full_data["id"] == self._files_model.id:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _handle_full_data_drop(self, value):
|
||||
if value is None:
|
||||
return False
|
||||
|
||||
full_data = convert_bytes_to_json(value)
|
||||
if full_data is None:
|
||||
return False
|
||||
|
||||
if full_data["id"] == self._files_model.id:
|
||||
return False
|
||||
|
||||
for item in full_data["items"]:
|
||||
filepaths = [
|
||||
os.path.join(item["directory"], filename)
|
||||
for filename in item["filenames"]
|
||||
]
|
||||
filepaths = self._files_proxy_model.filter_valid_files(filepaths)
|
||||
if filepaths:
|
||||
self._add_filepaths(filepaths)
|
||||
|
||||
if self._copy_modifiers_enabled():
|
||||
return False
|
||||
return True
|
||||
|
||||
def _copy_modifiers_enabled(self):
|
||||
if (
|
||||
QtWidgets.QApplication.keyboardModifiers()
|
||||
& QtCore.Qt.ControlModifier
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _add_filepaths(self, filepaths):
|
||||
self._files_model.add_filepaths(filepaths)
|
||||
self._update_visibility()
|
||||
|
||||
def _remove_item_by_ids(self, item_ids):
|
||||
self._files_model.remove_item_by_ids(item_ids)
|
||||
self._update_visibility()
|
||||
|
||||
def _update_visibility(self):
|
||||
files_exists = self._files_proxy_model.rowCount() > 0
|
||||
self._files_view.setVisible(files_exists)
|
||||
self._empty_widget.setVisible(not files_exists)
|
||||
|
|
@ -1,490 +0,0 @@
|
|||
import uuid
|
||||
import copy
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from openpype.lib.attribute_definitions import (
|
||||
AbtractAttrDef,
|
||||
UnknownDef,
|
||||
NumberDef,
|
||||
TextDef,
|
||||
EnumDef,
|
||||
BoolDef,
|
||||
FileDef,
|
||||
UIDef,
|
||||
UISeparatorDef,
|
||||
UILabelDef
|
||||
)
|
||||
from openpype.tools.utils import CustomTextComboBox
|
||||
from openpype.widgets.nice_checkbox import NiceCheckbox
|
||||
|
||||
from .files_widget import FilesWidget
|
||||
|
||||
|
||||
def create_widget_for_attr_def(attr_def, parent=None):
|
||||
if not isinstance(attr_def, AbtractAttrDef):
|
||||
raise TypeError("Unexpected type \"{}\" expected \"{}\"".format(
|
||||
str(type(attr_def)), AbtractAttrDef
|
||||
))
|
||||
|
||||
if isinstance(attr_def, NumberDef):
|
||||
return NumberAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, TextDef):
|
||||
return TextAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, EnumDef):
|
||||
return EnumAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, BoolDef):
|
||||
return BoolAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, UnknownDef):
|
||||
return UnknownAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, FileDef):
|
||||
return FileAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, UISeparatorDef):
|
||||
return SeparatorAttrWidget(attr_def, parent)
|
||||
|
||||
if isinstance(attr_def, UILabelDef):
|
||||
return LabelAttrWidget(attr_def, parent)
|
||||
|
||||
raise ValueError("Unknown attribute definition \"{}\"".format(
|
||||
str(type(attr_def))
|
||||
))
|
||||
|
||||
|
||||
class AttributeDefinitionsWidget(QtWidgets.QWidget):
|
||||
"""Create widgets for attribute definitions in grid layout.
|
||||
|
||||
Widget creates input widgets for passed attribute definitions.
|
||||
|
||||
Widget can't handle multiselection values.
|
||||
"""
|
||||
|
||||
def __init__(self, attr_defs=None, parent=None):
|
||||
super(AttributeDefinitionsWidget, self).__init__(parent)
|
||||
|
||||
self._widgets = []
|
||||
self._current_keys = set()
|
||||
|
||||
self.set_attr_defs(attr_defs)
|
||||
|
||||
def clear_attr_defs(self):
|
||||
"""Remove all existing widgets and reset layout if needed."""
|
||||
self._widgets = []
|
||||
self._current_keys = set()
|
||||
|
||||
layout = self.layout()
|
||||
if layout is not None:
|
||||
if layout.count() == 0:
|
||||
return
|
||||
|
||||
while layout.count():
|
||||
item = layout.takeAt(0)
|
||||
widget = item.widget()
|
||||
if widget:
|
||||
widget.setVisible(False)
|
||||
widget.deleteLater()
|
||||
|
||||
layout.deleteLater()
|
||||
|
||||
new_layout = QtWidgets.QGridLayout()
|
||||
new_layout.setColumnStretch(0, 0)
|
||||
new_layout.setColumnStretch(1, 1)
|
||||
self.setLayout(new_layout)
|
||||
|
||||
def set_attr_defs(self, attr_defs):
|
||||
"""Replace current attribute definitions with passed."""
|
||||
self.clear_attr_defs()
|
||||
if attr_defs:
|
||||
self.add_attr_defs(attr_defs)
|
||||
|
||||
def add_attr_defs(self, attr_defs):
|
||||
"""Add attribute definitions to current."""
|
||||
layout = self.layout()
|
||||
|
||||
row = 0
|
||||
for attr_def in attr_defs:
|
||||
if attr_def.key in self._current_keys:
|
||||
raise KeyError("Duplicated key \"{}\"".format(attr_def.key))
|
||||
|
||||
self._current_keys.add(attr_def.key)
|
||||
widget = create_widget_for_attr_def(attr_def, self)
|
||||
|
||||
expand_cols = 2
|
||||
if attr_def.is_value_def and attr_def.is_label_horizontal:
|
||||
expand_cols = 1
|
||||
|
||||
col_num = 2 - expand_cols
|
||||
|
||||
if attr_def.label:
|
||||
label_widget = QtWidgets.QLabel(attr_def.label, self)
|
||||
layout.addWidget(
|
||||
label_widget, row, 0, 1, expand_cols
|
||||
)
|
||||
if not attr_def.is_label_horizontal:
|
||||
row += 1
|
||||
|
||||
layout.addWidget(
|
||||
widget, row, col_num, 1, expand_cols
|
||||
)
|
||||
self._widgets.append(widget)
|
||||
row += 1
|
||||
|
||||
def set_value(self, value):
|
||||
new_value = copy.deepcopy(value)
|
||||
unused_keys = set(new_value.keys())
|
||||
for widget in self._widgets:
|
||||
attr_def = widget.attr_def
|
||||
if attr_def.key not in new_value:
|
||||
continue
|
||||
unused_keys.remove(attr_def.key)
|
||||
|
||||
widget_value = new_value[attr_def.key]
|
||||
if widget_value is None:
|
||||
widget_value = copy.deepcopy(attr_def.default)
|
||||
widget.set_value(widget_value)
|
||||
|
||||
def current_value(self):
|
||||
output = {}
|
||||
for widget in self._widgets:
|
||||
attr_def = widget.attr_def
|
||||
if not isinstance(attr_def, UIDef):
|
||||
output[attr_def.key] = widget.current_value()
|
||||
|
||||
return output
|
||||
|
||||
|
||||
class _BaseAttrDefWidget(QtWidgets.QWidget):
|
||||
# Type 'object' may not work with older PySide versions
|
||||
value_changed = QtCore.Signal(object, uuid.UUID)
|
||||
|
||||
def __init__(self, attr_def, parent):
|
||||
super(_BaseAttrDefWidget, self).__init__(parent)
|
||||
|
||||
self.attr_def = attr_def
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.main_layout = main_layout
|
||||
|
||||
self._ui_init()
|
||||
|
||||
def _ui_init(self):
|
||||
raise NotImplementedError(
|
||||
"Method '_ui_init' is not implemented. {}".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def current_value(self):
|
||||
raise NotImplementedError(
|
||||
"Method 'current_value' is not implemented. {}".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
raise NotImplementedError(
|
||||
"Method 'set_value' is not implemented. {}".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class SeparatorAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
input_widget = QtWidgets.QWidget(self)
|
||||
input_widget.setObjectName("Separator")
|
||||
input_widget.setMinimumHeight(2)
|
||||
input_widget.setMaximumHeight(2)
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
|
||||
class LabelAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
input_widget = QtWidgets.QLabel(self)
|
||||
label = self.attr_def.label
|
||||
if label:
|
||||
input_widget.setText(str(label))
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
|
||||
class NumberAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
decimals = self.attr_def.decimals
|
||||
if decimals > 0:
|
||||
input_widget = QtWidgets.QDoubleSpinBox(self)
|
||||
input_widget.setDecimals(decimals)
|
||||
else:
|
||||
input_widget = QtWidgets.QSpinBox(self)
|
||||
|
||||
if self.attr_def.tooltip:
|
||||
input_widget.setToolTip(self.attr_def.tooltip)
|
||||
|
||||
input_widget.setMinimum(self.attr_def.minimum)
|
||||
input_widget.setMaximum(self.attr_def.maximum)
|
||||
input_widget.setValue(self.attr_def.default)
|
||||
|
||||
input_widget.setButtonSymbols(
|
||||
QtWidgets.QAbstractSpinBox.ButtonSymbols.NoButtons
|
||||
)
|
||||
|
||||
input_widget.valueChanged.connect(self._on_value_change)
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
def _on_value_change(self, new_value):
|
||||
self.value_changed.emit(new_value, self.attr_def.id)
|
||||
|
||||
def current_value(self):
|
||||
return self._input_widget.value()
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
if multivalue:
|
||||
set_value = set(value)
|
||||
if None in set_value:
|
||||
set_value.remove(None)
|
||||
set_value.add(self.attr_def.default)
|
||||
|
||||
if len(set_value) > 1:
|
||||
self._input_widget.setSpecialValueText("Multiselection")
|
||||
return
|
||||
value = tuple(set_value)[0]
|
||||
|
||||
if self.current_value != value:
|
||||
self._input_widget.setValue(value)
|
||||
|
||||
|
||||
class TextAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
# TODO Solve how to handle regex
|
||||
# self.attr_def.regex
|
||||
|
||||
self.multiline = self.attr_def.multiline
|
||||
if self.multiline:
|
||||
input_widget = QtWidgets.QPlainTextEdit(self)
|
||||
else:
|
||||
input_widget = QtWidgets.QLineEdit(self)
|
||||
|
||||
if (
|
||||
self.attr_def.placeholder
|
||||
and hasattr(input_widget, "setPlaceholderText")
|
||||
):
|
||||
input_widget.setPlaceholderText(self.attr_def.placeholder)
|
||||
|
||||
if self.attr_def.tooltip:
|
||||
input_widget.setToolTip(self.attr_def.tooltip)
|
||||
|
||||
if self.attr_def.default:
|
||||
if self.multiline:
|
||||
input_widget.setPlainText(self.attr_def.default)
|
||||
else:
|
||||
input_widget.setText(self.attr_def.default)
|
||||
|
||||
input_widget.textChanged.connect(self._on_value_change)
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
def _on_value_change(self):
|
||||
if self.multiline:
|
||||
new_value = self._input_widget.toPlainText()
|
||||
else:
|
||||
new_value = self._input_widget.text()
|
||||
self.value_changed.emit(new_value, self.attr_def.id)
|
||||
|
||||
def current_value(self):
|
||||
if self.multiline:
|
||||
return self._input_widget.toPlainText()
|
||||
return self._input_widget.text()
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
if multivalue:
|
||||
set_value = set(value)
|
||||
if None in set_value:
|
||||
set_value.remove(None)
|
||||
set_value.add(self.attr_def.default)
|
||||
|
||||
if len(set_value) == 1:
|
||||
value = tuple(set_value)[0]
|
||||
else:
|
||||
value = "< Multiselection >"
|
||||
|
||||
if value != self.current_value():
|
||||
if self.multiline:
|
||||
self._input_widget.setPlainText(value)
|
||||
else:
|
||||
self._input_widget.setText(value)
|
||||
|
||||
|
||||
class BoolAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
input_widget = NiceCheckbox(parent=self)
|
||||
input_widget.setChecked(self.attr_def.default)
|
||||
|
||||
if self.attr_def.tooltip:
|
||||
input_widget.setToolTip(self.attr_def.tooltip)
|
||||
|
||||
input_widget.stateChanged.connect(self._on_value_change)
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
self.main_layout.addStretch(1)
|
||||
|
||||
def _on_value_change(self):
|
||||
new_value = self._input_widget.isChecked()
|
||||
self.value_changed.emit(new_value, self.attr_def.id)
|
||||
|
||||
def current_value(self):
|
||||
return self._input_widget.isChecked()
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
if multivalue:
|
||||
set_value = set(value)
|
||||
if None in set_value:
|
||||
set_value.remove(None)
|
||||
set_value.add(self.attr_def.default)
|
||||
|
||||
if len(set_value) > 1:
|
||||
self._input_widget.setCheckState(QtCore.Qt.PartiallyChecked)
|
||||
return
|
||||
value = tuple(set_value)[0]
|
||||
|
||||
if value != self.current_value():
|
||||
self._input_widget.setChecked(value)
|
||||
|
||||
|
||||
class EnumAttrWidget(_BaseAttrDefWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._multivalue = False
|
||||
super(EnumAttrWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
def _ui_init(self):
|
||||
input_widget = CustomTextComboBox(self)
|
||||
combo_delegate = QtWidgets.QStyledItemDelegate(input_widget)
|
||||
input_widget.setItemDelegate(combo_delegate)
|
||||
|
||||
if self.attr_def.tooltip:
|
||||
input_widget.setToolTip(self.attr_def.tooltip)
|
||||
|
||||
items = self.attr_def.items
|
||||
for key, label in items.items():
|
||||
input_widget.addItem(label, key)
|
||||
|
||||
idx = input_widget.findData(self.attr_def.default)
|
||||
if idx >= 0:
|
||||
input_widget.setCurrentIndex(idx)
|
||||
|
||||
input_widget.currentIndexChanged.connect(self._on_value_change)
|
||||
|
||||
self._combo_delegate = combo_delegate
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
def _on_value_change(self):
|
||||
new_value = self.current_value()
|
||||
if self._multivalue:
|
||||
self._multivalue = False
|
||||
self._input_widget.set_custom_text(None)
|
||||
self.value_changed.emit(new_value, self.attr_def.id)
|
||||
|
||||
def current_value(self):
|
||||
idx = self._input_widget.currentIndex()
|
||||
return self._input_widget.itemData(idx)
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
if multivalue:
|
||||
set_value = set(value)
|
||||
if len(set_value) == 1:
|
||||
multivalue = False
|
||||
value = tuple(set_value)[0]
|
||||
|
||||
if not multivalue:
|
||||
idx = self._input_widget.findData(value)
|
||||
cur_idx = self._input_widget.currentIndex()
|
||||
if idx != cur_idx and idx >= 0:
|
||||
self._input_widget.setCurrentIndex(idx)
|
||||
|
||||
custom_text = None
|
||||
if multivalue:
|
||||
custom_text = "< Multiselection >"
|
||||
self._input_widget.set_custom_text(custom_text)
|
||||
self._multivalue = multivalue
|
||||
|
||||
|
||||
class UnknownAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
input_widget = QtWidgets.QLabel(self)
|
||||
self._value = self.attr_def.default
|
||||
input_widget.setText(str(self._value))
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
def current_value(self):
|
||||
raise ValueError(
|
||||
"{} can't hold real value.".format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
if multivalue:
|
||||
set_value = set(value)
|
||||
if len(set_value) == 1:
|
||||
value = tuple(set_value)[0]
|
||||
else:
|
||||
value = "< Multiselection >"
|
||||
|
||||
str_value = str(value)
|
||||
if str_value != self._value:
|
||||
self._value = str_value
|
||||
self._input_widget.setText(str_value)
|
||||
|
||||
|
||||
class FileAttrWidget(_BaseAttrDefWidget):
|
||||
def _ui_init(self):
|
||||
input_widget = FilesWidget(
|
||||
self.attr_def.single_item,
|
||||
self.attr_def.allow_sequences,
|
||||
self.attr_def.extensions_label,
|
||||
self
|
||||
)
|
||||
|
||||
if self.attr_def.tooltip:
|
||||
input_widget.setToolTip(self.attr_def.tooltip)
|
||||
|
||||
input_widget.set_filters(
|
||||
self.attr_def.folders, self.attr_def.extensions
|
||||
)
|
||||
|
||||
input_widget.value_changed.connect(self._on_value_change)
|
||||
|
||||
self._input_widget = input_widget
|
||||
|
||||
self.main_layout.addWidget(input_widget, 0)
|
||||
|
||||
def _on_value_change(self):
|
||||
new_value = self.current_value()
|
||||
self.value_changed.emit(new_value, self.attr_def.id)
|
||||
|
||||
def current_value(self):
|
||||
return self._input_widget.current_value()
|
||||
|
||||
def set_value(self, value, multivalue=False):
|
||||
self._input_widget.set_value(value, multivalue)
|
||||
Loading…
Add table
Add a link
Reference in a new issue