diff --git a/openpype/modules/sync_server/tray/delegates.py b/openpype/modules/sync_server/tray/delegates.py new file mode 100644 index 0000000000..0e694f1f9e --- /dev/null +++ b/openpype/modules/sync_server/tray/delegates.py @@ -0,0 +1,111 @@ +import os +from Qt import QtCore, QtWidgets, QtGui +from avalon.vendor import qtawesome + +from openpype.lib import PypeLogger +from openpype.modules.sync_server.tray import lib + +log = PypeLogger().get_logger("SyncServer") + + +class PriorityDelegate(QtWidgets.QStyledItemDelegate): + """Creates editable line edit to set priority on representation""" + def paint(self, painter, option, index): + super(PriorityDelegate, self).paint(painter, option, index) + + if option.widget.selectionModel().isSelected(index) or \ + option.state & QtWidgets.QStyle.State_MouseOver: + edit_icon = qtawesome.icon("fa.edit", color="white") + state = QtGui.QIcon.On + mode = QtGui.QIcon.Selected + + icon_side = 16 + icon_rect = QtCore.QRect( + option.rect.left() + option.rect.width() - icon_side - 4, + option.rect.top() + ((option.rect.height() - icon_side) / 2), + icon_side, + icon_side + ) + + edit_icon.paint( + painter, icon_rect, + QtCore.Qt.AlignRight, mode, state + ) + + def createEditor(self, parent, option, index): + editor = PriorityLineEdit( + parent, + option.widget.selectionModel().selectedRows()) + editor.setValidator(QtGui.QIntValidator(0, 1000, self)) + editor.setFocus(True) + return editor + + def setModelData(self, editor, model, index): + for index in editor.selected_idxs: + model.set_priority_data(index, editor.text()) + + +class PriorityLineEdit(QtWidgets.QLineEdit): + """Special LineEdit to consume Enter and store selected indexes""" + def __init__(self, parent=None, selected_idxs=None): + self.selected_idxs = selected_idxs + super(PriorityLineEdit, self).__init__(parent) + + def keyPressEvent(self, event): + result = super(PriorityLineEdit, self).keyPressEvent(event) + if ( + event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter) + ): + return event.accept() + + return result + + +class ImageDelegate(QtWidgets.QStyledItemDelegate): + """ + Prints icon of site and progress of synchronization + """ + + def __init__(self, parent=None): + super(ImageDelegate, self).__init__(parent) + self.icons = {} + + def paint(self, painter, option, index): + super(ImageDelegate, self).paint(painter, option, index) + option = QtWidgets.QStyleOptionViewItem(option) + option.showDecorationSelected = True + + provider = index.data(lib.ProviderRole) + value = index.data(lib.ProgressRole) + date_value = index.data(lib.DateRole) + is_failed = index.data(lib.FailedRole) + + if not self.icons.get(provider): + resource_path = os.path.dirname(__file__) + resource_path = os.path.join(resource_path, "..", + "providers", "resources") + pix_url = "{}/{}.png".format(resource_path, provider) + pixmap = QtGui.QPixmap(pix_url) + self.icons[provider] = pixmap + else: + pixmap = self.icons[provider] + + padding = 10 + point = QtCore.QPoint(option.rect.x() + padding, + option.rect.y() + + (option.rect.height() - pixmap.height()) / 2) + painter.drawPixmap(point, pixmap) + + overlay_rect = option.rect.translated(0, 0) + overlay_rect.setHeight(overlay_rect.height() * (1.0 - float(value))) + painter.fillRect(overlay_rect, + QtGui.QBrush(QtGui.QColor(0, 0, 0, 100))) + text_rect = option.rect.translated(10, 0) + painter.drawText(text_rect, + QtCore.Qt.AlignCenter, + date_value) + + if is_failed: + overlay_rect = option.rect.translated(0, 0) + painter.fillRect(overlay_rect, + QtGui.QBrush(QtGui.QColor(255, 0, 0, 35))) diff --git a/openpype/modules/sync_server/tray/lib.py b/openpype/modules/sync_server/tray/lib.py index 5e59d653ac..49d224d61f 100644 --- a/openpype/modules/sync_server/tray/lib.py +++ b/openpype/modules/sync_server/tray/lib.py @@ -1,11 +1,9 @@ from Qt import QtCore, QtWidgets, QtGui -from Qt.QtCore import Qt import attr import abc import six from openpype.lib import PypeLogger -from avalon.vendor import qtawesome log = PypeLogger().get_logger("SyncServer") @@ -167,55 +165,3 @@ def get_item_by_id(model, object_id): item = model.data(index, FullItemRole) return item - -class PriorityDelegate(QtWidgets.QStyledItemDelegate): - - def paint(self, painter, option, index): - super(PriorityDelegate, self).paint(painter, option, index) - - if option.widget.selectionModel().isSelected(index) or \ - option.state & QtWidgets.QStyle.State_MouseOver: - edit_icon = qtawesome.icon("fa.edit", color="white") - state = QtGui.QIcon.On - mode = QtGui.QIcon.Selected - - icon_side = 16 - icon_rect = QtCore.QRect( - option.rect.left() + option.rect.width() - icon_side - 4, - option.rect.top() + ((option.rect.height() - icon_side) / 2), - icon_side, - icon_side - ) - - edit_icon.paint( - painter, icon_rect, - QtCore.Qt.AlignRight, mode, state - ) - - def createEditor(self, parent, option, index): - editor = PriorityLineEdit( - parent, - option.widget.selectionModel().selectedRows()) - editor.setValidator(QtGui.QIntValidator(0, 1000, self)) - editor.setFocus(True) - return editor - - def setModelData(self, editor, model, index): - for index in editor.selected_idxs: - model.set_priority_data(index, editor.text()) - - -class PriorityLineEdit(QtWidgets.QLineEdit): - """Special LineEdit to consume Enter and store selected indexes""" - def __init__(self, parent=None, selected_idxs=None): - self.selected_idxs = selected_idxs - super(PriorityLineEdit, self).__init__(parent) - - def keyPressEvent(self, event): - result = super(PriorityLineEdit, self).keyPressEvent(event) - if ( - event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter) - ): - return event.accept() - - return result diff --git a/openpype/modules/sync_server/tray/widgets.py b/openpype/modules/sync_server/tray/widgets.py index 838b325c73..4ca06d3d07 100644 --- a/openpype/modules/sync_server/tray/widgets.py +++ b/openpype/modules/sync_server/tray/widgets.py @@ -23,6 +23,7 @@ from openpype.modules.sync_server.tray.models import ( ) from openpype.modules.sync_server.tray import lib +from openpype.modules.sync_server.tray import delegates log = PypeLogger().get_logger("SyncServer") @@ -145,10 +146,10 @@ class _SyncRepresentationWidget(QtWidgets.QWidget): def _selection_changed(self, _new_selected, _all_selected): idxs = self.selection_model.selectedRows() - self._selected_ids = [] + self._selected_ids = set() for index in idxs: - self._selected_ids.append(self.model.data(index, Qt.UserRole)) + self._selected_ids.add(self.model.data(index, Qt.UserRole)) def _set_selection(self): """ @@ -156,14 +157,14 @@ class _SyncRepresentationWidget(QtWidgets.QWidget): Keep selection during model refresh. """ - existing_ids = [] + existing_ids = set() for selected_id in self._selected_ids: index = self.model.get_index(selected_id) if index and index.isValid(): mode = QtCore.QItemSelectionModel.Select | \ QtCore.QItemSelectionModel.Rows self.selection_model.select(index, mode) - existing_ids.append(selected_id) + existing_ids.add(selected_id) self._selected_ids = existing_ids @@ -196,7 +197,7 @@ class _SyncRepresentationWidget(QtWidgets.QWidget): return if is_multi: - index = self.model.get_index(self._selected_ids[0]) + index = self.model.get_index(list(self._selected_ids)[0]) item = self.model.data(index, lib.FullItemRole) else: item = self.model.data(point_index, lib.FullItemRole) @@ -412,8 +413,9 @@ class _SyncRepresentationWidget(QtWidgets.QWidget): def _change_priority(self, **kwargs): """Open editor to change priority on first selected row""" if self._selected_ids: - index = self.model.get_index(self._selected_ids[0]) # column = 0 - column_no = self.model.get_header_index("priority") # real column + # get_index returns dummy index with column equals to 0 + index = self.model.get_index(list(self._selected_ids)[0]) + column_no = self.model.get_header_index("priority") # real column real_index = self.model.index(index.row(), column_no) self.model.is_editing = True self.table_view.openPersistentEditor(real_index) @@ -462,7 +464,7 @@ class SyncRepresentationSummaryWidget(_SyncRepresentationWidget): self.sync_server = sync_server - self._selected_ids = [] # keep last selected _id + self._selected_ids = set() # keep last selected _id txt_filter = QtWidgets.QLineEdit() txt_filter.setPlaceholderText("Quick filter representations..") @@ -494,15 +496,15 @@ class SyncRepresentationSummaryWidget(_SyncRepresentationWidget): table_view.viewport().setAttribute(QtCore.Qt.WA_Hover, True) column = table_view.model().get_header_index("local_site") - delegate = ImageDelegate(self) + delegate = delegates.ImageDelegate(self) table_view.setItemDelegateForColumn(column, delegate) column = table_view.model().get_header_index("remote_site") - delegate = ImageDelegate(self) + delegate = delegates.ImageDelegate(self) table_view.setItemDelegateForColumn(column, delegate) column = table_view.model().get_header_index("priority") - priority_delegate = lib.PriorityDelegate(self) + priority_delegate = delegates.PriorityDelegate(self) table_view.setItemDelegateForColumn(column, priority_delegate) layout = QtWidgets.QVBoxLayout(self) @@ -624,7 +626,7 @@ class SyncRepresentationDetailWidget(_SyncRepresentationWidget): self.sync_server = sync_server self.representation_id = _id - self._selected_ids = [] + self._selected_ids = set() self.txt_filter = QtWidgets.QLineEdit() self.txt_filter.setPlaceholderText("Quick filter representation..") @@ -654,13 +656,17 @@ class SyncRepresentationDetailWidget(_SyncRepresentationWidget): table_view.verticalHeader().hide() column = model.get_header_index("local_site") - delegate = ImageDelegate(self) + delegate = delegates.ImageDelegate(self) table_view.setItemDelegateForColumn(column, delegate) column = model.get_header_index("remote_site") - delegate = ImageDelegate(self) + delegate = delegates.ImageDelegate(self) table_view.setItemDelegateForColumn(column, delegate) + column = table_view.model().get_header_index("priority") + priority_delegate = delegates.PriorityDelegate(self) + table_view.setItemDelegateForColumn(column, priority_delegate) + layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addLayout(top_bar_layout) @@ -684,12 +690,24 @@ class SyncRepresentationDetailWidget(_SyncRepresentationWidget): self.txt_filter.textChanged.connect(lambda: model.set_word_filter( self.txt_filter.text())) + table_view.doubleClicked.connect(self._double_clicked) table_view.customContextMenuRequested.connect(self._on_context_menu) model.refresh_started.connect(self._save_scrollbar) model.refresh_finished.connect(self._set_scrollbar) model.modelReset.connect(self._set_selection) + def _double_clicked(self, index): + """ + Opens representation dialog with all files after doubleclick + """ + # priority editing + column_name = self.model.get_column(index.column()) + if column_name[0] in self.model.EDITABLE_COLUMNS: + self.model.is_editing = True + self.table_view.openPersistentEditor(index) + return + def _show_detail(self, selected_ids=None): """ Shows windows with error message for failed sync of a file. @@ -804,72 +822,6 @@ class SyncRepresentationErrorWidget(QtWidgets.QWidget): layout.addWidget(text_area) -class ImageDelegate(QtWidgets.QStyledItemDelegate): - """ - Prints icon of site and progress of synchronization - """ - - def __init__(self, parent=None): - super(ImageDelegate, self).__init__(parent) - self.icons = {} - - def paint(self, painter, option, index): - super(ImageDelegate, self).paint(painter, option, index) - option = QtWidgets.QStyleOptionViewItem(option) - option.showDecorationSelected = True - - provider = index.data(lib.ProviderRole) - value = index.data(lib.ProgressRole) - date_value = index.data(lib.DateRole) - is_failed = index.data(lib.FailedRole) - - if not self.icons.get(provider): - resource_path = os.path.dirname(__file__) - resource_path = os.path.join(resource_path, "..", - "providers", "resources") - pix_url = "{}/{}.png".format(resource_path, provider) - pixmap = QtGui.QPixmap(pix_url) - self.icons[provider] = pixmap - else: - pixmap = self.icons[provider] - - padding = 10 - point = QtCore.QPoint(option.rect.x() + padding, - option.rect.y() + - (option.rect.height() - pixmap.height()) / 2) - painter.drawPixmap(point, pixmap) - - overlay_rect = option.rect.translated(0, 0) - overlay_rect.setHeight(overlay_rect.height() * (1.0 - float(value))) - painter.fillRect(overlay_rect, - QtGui.QBrush(QtGui.QColor(0, 0, 0, 100))) - text_rect = option.rect.translated(10, 0) - painter.drawText(text_rect, - QtCore.Qt.AlignCenter, - date_value) - - if is_failed: - overlay_rect = option.rect.translated(0, 0) - painter.fillRect(overlay_rect, - QtGui.QBrush(QtGui.QColor(255, 0, 0, 35))) - - -class TransparentWidget(QtWidgets.QWidget): - """Used for header cell for resizing to work properly""" - clicked = QtCore.Signal(str) - - def __init__(self, column_name, *args, **kwargs): - super(TransparentWidget, self).__init__(*args, **kwargs) - self.column_name = column_name - # self.setStyleSheet("background: red;") - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - self.clicked.emit(self.column_name) - - super(TransparentWidget, self).mouseReleaseEvent(event) - - class HorizontalHeader(QtWidgets.QHeaderView): """Reiplemented QHeaderView to contain clickable changeable button""" def __init__(self, parent=None):