diff --git a/python/context_selector/context_widget.py b/python/context_selector/context_widget.py index eb9a451c..7b518a9f 100644 --- a/python/context_selector/context_widget.py +++ b/python/context_selector/context_widget.py @@ -13,6 +13,8 @@ from sgtk.platform.qt import QtCore, QtGui from .ui.context_editor_widget import Ui_ContextWidget +from collections import OrderedDict + # framework imports shotgun_globals = sgtk.platform.import_framework( "tk-framework-shotgunutils", "shotgun_globals" @@ -97,12 +99,43 @@ def __init__(self, parent): self.ui = Ui_ContextWidget() self.ui.setupUi(self) + + self._initialize_task_statuses = True + self._status_permissions = {} + #self._complex_populate_status_display() + + # Todo: read all availabvle 'sg_status_list' fields from shotgrid + self._status_dict = { + "wtg": "Waiting to Start", + "rdy": "Ready to Start", + "ip": "In Progress", + "hld": "On Hold", + "rev": "Ready For Review", + "apr": "Approved", + "fin": "Final", + "na": "N/A" + } + + self._tasks = {} # Loads the style sheet for the widget qss_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "style.qss") with open(qss_file, "rt") as f: # apply to widget (and all its children) self.setStyleSheet(f.read()) + def display_publish_name(self): + """ + Display the publish name widget + """ + self.ui.publish_name_label.show() + self.ui.publish_name_display.show() + self.ui.publish_token_label.show() + self.ui.publish_token_display.show() + + def set_publish_name(self, publish_name): + self.ui.publish_name_display.setText(publish_name) + self.ui.publish_name_display.setCursorPosition(0) + def eventFilter(self, widget, event): """ Filter out and handle some key/click events on the search widgets. @@ -226,7 +259,7 @@ def set_context( task_display_override=task_display_override, link_display_override=link_display_override, ) - + self._show_status(context) # ensure the new context is added to the list of recents. if context: self._add_to_recents(context) @@ -251,6 +284,11 @@ def set_up(self, task_manager): self.ui.task_search_btn.toggled.connect(self._on_task_search_toggled) self.ui.task_search.hide() + #self.ui.task_display.textchanged.connect(self._task_display_update) + + # Update task status + self.ui.status_display.currentIndexChanged.connect(self._save_task_status) + # setup the search toggle self.ui.link_search_btn.toggled.connect(self._on_link_search_toggled) self.ui.link_search.hide() @@ -296,6 +334,85 @@ def set_up(self, task_manager): # get recent contexts from user settings self._get_recent_contexts() + def _complex_populate_status_display(self, context): + """ + Populate status display + """ + self.ui.status_display.blockSignals(True) + if self._initialize_task_statuses: + # self._check_status_permissions(context) + self._initialize_task_statuses = False + + self.ui.status_display.clear() + + for status_short_code, status_name in self._status_dict.items(): + self.ui.status_display.addItem(status_name) + + for index, status_short_code in enumerate(self._status_dict.keys()): + if status_short_code == "na": + self.ui.status_display.model().item(index).setEnabled(False) + try: + if self._context and self._context.task: + task_id = self._context.task["id"] + if task_id in self._tasks: + status_name = self._tasks[task_id]["new_status_name"] + self.ui.status_display.setCurrentText(status_name) + else: + self.ui.status_display.setCurrentText("N/A") + except: + _log("Unable to set current task status") + pass + self.ui.status_display.blockSignals(False) + + def _simple_populate_status_display(self): + """ + Populate status display + """ + self.ui.status_display.blockSignals(True) + + self.ui.status_display.clear() + #self.ui.status_display.addItem("testing") + + for status_short_code, status_name in self._status_dict.items(): + self.ui.status_display.addItem(status_name) + self.ui.status_display.setCurrentText("N/A") + self.ui.status_display.blockSignals(False) + + def _check_status_permissions(self, context): + """ + Check if current user have permission to change each status + Todo: find a better way to do this + """ + sg = sgtk.platform.current_bundle().shotgun + if context and context.task: + task_status_short_code = _get_task_status(context) + + for status_short_code in self._status_dict.keys(): + try: + result = sg.update('Task', context.task['id'], {'sg_status_list': status_short_code}) + if result['sg_status_list'] == status_short_code: + self._status_permissions[status_short_code] = True + else: + self._status_permissions[status_short_code] = False + except: + self._status_permissions[status_short_code] = False + pass + # reset task status + sg.update('Task', context.task['id'], {'sg_status_list': task_status_short_code}) + + def _get_status_name(self, short_code): + if not short_code: + return "N/A" + return self._status_dict[short_code] + + def _get_status_short_code(self, name): + if not name: + return "na" + if name: + for key, value in self._status_dict.items(): + if value == name: + return key + def restrict_entity_types_by_link(self, entity_type, field_name): """ Specify what entries should show up in the list of links when @@ -615,14 +732,17 @@ def _on_context_activated(self, context): """ Called when a new context is set via the menu or one of the completers. """ - - logger.debug("Context changed to: %s" % (context,)) - + msg = "Context changed to: %s" % (context,) + logger.debug(msg) + _log(msg) + self._context = context # update the widget to display the new context and alert listeners that # a new context was selected self._show_context(context) + self._show_status(context) self.context_changed.emit(context) + def _on_entity_activated(self, entity_type, entity_id, entity_name): """ Slot called when an entity is selected via one of the search completers. @@ -666,6 +786,61 @@ def _on_task_search_toggled(self, checked): self.ui.task_menu_btn.show() self.ui.task_search.hide() + def _save_task_status(self): + """ + Update task status + """ + self.ui.status_display.blockSignals(True) + try: + if self._context and self._context.task: + task_id = self._context.task["id"] + if task_id not in self._tasks: + self._tasks[task_id] = {} + task_name = self._context.task["name"] + original_status_code = _get_task_status(self._context) + new_status_name = self.ui.status_display.currentText() + new_status_code = self._get_status_short_code(new_status_name) + self._tasks[task_id]["task_name"] = task_name + self._tasks[task_id]["original_status_code"] = original_status_code + self._tasks[task_id]["new_status_name"] = new_status_name + self._tasks[task_id]["new_status_code"] = new_status_code + # _log("self._context is: %s" % self._context) + # _log("self._context.entity is: %s" % self._context.entity["name"]) + _log("task name is: %s" % self._context.task["name"]) + _log("Task are: %s" % self._tasks) + except: + _log("Unable to get task or task status" ) + pass + self.ui.status_display.blockSignals(False) + + def update_task_status(self): + """ + Update task status + """ + try: + sg = sgtk.platform.current_bundle().shotgun + for task_id in self._tasks.keys(): + + try: + # _log("----------------------------------------") + task_name = self._tasks[task_id]["task_name"] + original_status_code = self._tasks[task_id]["original_status_code"] + new_status_code = self._tasks[task_id]["new_status_code"] + if original_status_code != new_status_code: + result = sg.update('Task', task_id, {'sg_status_list': new_status_code}) + + _log("Updating status of task \'%s\' from %s to %s ..." % (task_name, original_status_code, new_status_code)) + if result['sg_status_list'] == new_status_code: + _log("Task status updated successfully") + else: + _log("Task status update was not successful") + except: + _log("Error when updating status of task %s", task_name) + pass + except: + _log("Error when updating task statuses ") + pass + def _on_link_search_toggled(self, checked): """ Slot called when the user clicks the link display or the link search @@ -828,7 +1003,6 @@ def _show_context( """ Show the supplied context in the UI. """ - if task_display_override: task_display = task_display_override else: @@ -841,6 +1015,7 @@ def _show_context( # update the task display/state self.ui.task_display.setText(task_display) + self.ui.task_search_btn.setChecked(False) self.ui.task_search_btn.setDown(False) @@ -855,6 +1030,48 @@ def _show_context( self._query_related_tasks, task_args=[context] ) + def _show_status(self, context): + """ + Show task status. + """ + self.ui.status_display.blockSignals(True) + try: + if context and context.task: + self._complex_populate_status_display(context) + + _log("Get task status ...") + task_id = context.task["id"] + if task_id in self._tasks.keys(): + task_status_name = self._tasks[task_id]["new_status_name"] + else: + task_status_short_code = _get_task_status(context) + task_status_name = self._get_status_name(task_status_short_code) + + + try: + self.ui.status_display.setCurrentText(task_status_name) + except: + _log("Unable to update show task status") + pass + + else: + self.ui.status_display.setCurrentText("N/A") + except: + _log("Unable to update show task status") + pass + self.ui.status_display.blockSignals(False) + + def _update_task_display(self, context, task_display_override=None): + """ + Update the task display + """ + if task_display_override: + task_display = task_display_override + else: + task_display = _get_task_display(context) + + # update the task display/state + self.ui.task_display.setText(task_display) def _get_task_display(context, plain_text=False): """ @@ -883,6 +1100,28 @@ def _get_task_display(context, plain_text=False): return display_name +def _get_task_status(context): + """ + Gat task status + """ + + sg = sgtk.platform.current_bundle().shotgun + + if not context or not context.task: + return "" + task_id = context.task["id"] + + entity = 'Task' + filters = [['id', 'is', task_id]] + fields = ['entity', 'sg_status_list'] + item = sg.find_one(entity, filters, fields) + _log("item is: %s" % item) + task_status = item['sg_status_list'] + _log("task_status is: %s" % task_status) + + return task_status + + def _get_link_display(context, plain_text=False): """ Build a display string for the link of the supplied context. @@ -1016,3 +1255,12 @@ def _query_entity_schema(entity_type, field_name): return bundle.shotgun.schema_field_read( entity_type, field_name=field_name, project_entity=project ) + + +def _log(msg, error=0): + if logger: + if error: + logger.warn(msg) + else: + logger.info(msg) + print(msg) diff --git a/python/context_selector/ui/context_editor_widget.py b/python/context_selector/ui/context_editor_widget.py index 6f036579..d4846c59 100644 --- a/python/context_selector/ui/context_editor_widget.py +++ b/python/context_selector/ui/context_editor_widget.py @@ -7,6 +7,7 @@ # WARNING! All changes made in this file will be lost! from tank.platform.qt import QtCore, QtGui +from tank.platform.qt5 import QtWidgets class Ui_ContextWidget(object): def setupUi(self, ContextWidget): @@ -37,6 +38,7 @@ def setupUi(self, ContextWidget): self.gridLayout = QtGui.QGridLayout() self.gridLayout.setSpacing(0) self.gridLayout.setObjectName("gridLayout") + self.gridLayout.setContentsMargins(0, 0, 0, 0) self.task_label = QtGui.QLabel(self.edit_widget) self.task_label.setMinimumSize(QtCore.QSize(0, 0)) self.task_label.setMaximumSize(QtCore.QSize(16777215, 32)) @@ -48,7 +50,8 @@ def setupUi(self, ContextWidget): self.gridLayout.addWidget(self.task_label, 0, 0, 1, 1) self.task_widgets_layout = QtGui.QHBoxLayout() self.task_widgets_layout.setSpacing(4) - self.task_widgets_layout.setContentsMargins(-1, -1, -1, 1) + #self.task_widgets_layout.setContentsMargins(-1, -1, -1, 1) + self.task_widgets_layout.setContentsMargins(-1, -1, -1, 8) self.task_widgets_layout.setObjectName("task_widgets_layout") self.task_display = QtGui.QLabel(self.edit_widget) self.task_display.setMinimumSize(QtCore.QSize(0, 0)) @@ -98,15 +101,40 @@ def setupUi(self, ContextWidget): self.task_widgets_layout.setStretch(1, 100) self.task_widgets_layout.setStretch(2, 1) self.gridLayout.addLayout(self.task_widgets_layout, 0, 1, 1, 1) + + self.status_label = QtGui.QLabel(self.edit_widget) + self.status_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) + self.status_label.setOpenExternalLinks(True) + self.status_label.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.status_label.setObjectName("status_label") + self.gridLayout.addWidget(self.status_label, 1, 0, 1, 1) + self.status_widgets_layout = QtGui.QHBoxLayout() + self.status_widgets_layout.setSpacing(4) + #self.status_widgets_layout.setContentsMargins(-1, 1, -1, -1) + self.status_widgets_layout.setContentsMargins(-1, 1, -1, 8) + self.status_widgets_layout.setObjectName("status_widgets_layout") + self.status_display = QtGui.QComboBox(self.edit_widget) + self.status_display.setToolTip('Display or change current task status.') + self.status_display.setMinimumSize(QtCore.QSize(0, 0)) + self.status_display.setMaximumSize(QtCore.QSize(16777215, 32)) + self.status_display.setObjectName("status_display") + self.status_widgets_layout.addWidget(self.status_display) + + self.status_widgets_layout.setStretch(0, 1) + self.status_widgets_layout.setStretch(1, 100) + self.status_widgets_layout.setStretch(2, 1) + self.gridLayout.addLayout(self.status_widgets_layout, 1, 1, 1, 1) + self.link_label = QtGui.QLabel(self.edit_widget) self.link_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.link_label.setOpenExternalLinks(True) self.link_label.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.link_label.setObjectName("link_label") - self.gridLayout.addWidget(self.link_label, 1, 0, 1, 1) + self.gridLayout.addWidget(self.link_label, 2, 0, 1, 1) self.link_widgets_layout = QtGui.QHBoxLayout() self.link_widgets_layout.setSpacing(4) - self.link_widgets_layout.setContentsMargins(-1, 1, -1, -1) + #self.link_widgets_layout.setContentsMargins(-1, 1, -1, -1) + self.link_widgets_layout.setContentsMargins(-1, 1, -1, 8) self.link_widgets_layout.setObjectName("link_widgets_layout") self.link_display = QtGui.QLabel(self.edit_widget) self.link_display.setMinimumSize(QtCore.QSize(0, 0)) @@ -133,7 +161,67 @@ def setupUi(self, ContextWidget): self.link_widgets_layout.setStretch(0, 1) self.link_widgets_layout.setStretch(1, 100) self.link_widgets_layout.setStretch(2, 1) - self.gridLayout.addLayout(self.link_widgets_layout, 1, 1, 1, 1) + self.gridLayout.addLayout(self.link_widgets_layout, 2, 1, 1, 1) + + self.publish_name_label = QtGui.QLabel(self.edit_widget) + self.publish_name_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) + self.publish_name_label.setOpenExternalLinks(True) + self.publish_name_label.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.publish_name_label.setObjectName("publish_name_label") + self.gridLayout.addWidget(self.publish_name_label, 3, 0, 1, 1) + self.publish_name_widgets_layout = QtGui.QHBoxLayout() + self.publish_name_widgets_layout.setSpacing(4) + # self.publish_name_widgets_layout.setContentsMargins(-1, 1, -1, -1) + self.publish_name_widgets_layout.setContentsMargins(-1, 1, -1, 8) + self.publish_name_widgets_layout.setObjectName("publish_name_widgets_layout") + self.publish_name_display = QtWidgets.QLineEdit(self.edit_widget) + self.publish_name_display.setCursorPosition(0) + self.publish_name_display.setToolTip('Display potential publish version name.') + self.publish_name_display.setPlaceholderText('Potential publish version name') + # self.publish_name_display.setMinimumSize(QtCore.QSize(0, 0)) + # self.publish_name_display.setMaximumSize(QtCore.QSize(16777215, 32)) + self.publish_name_display.setObjectName("publish_name_display") + self.publish_name_display.setEnabled(False) + self.publish_name_widgets_layout.addWidget(self.publish_name_display) + + self.publish_name_label.hide() + self.publish_name_display.hide() + + self.publish_name_widgets_layout.setStretch(0, 1) + self.publish_name_widgets_layout.setStretch(1, 100) + self.publish_name_widgets_layout.setStretch(2, 1) + self.gridLayout.addLayout(self.publish_name_widgets_layout, 3, 1, 1, 1) + + self.publish_token_label = QtGui.QLabel(self.edit_widget) + self.publish_token_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) + self.publish_token_label.setOpenExternalLinks(True) + self.publish_token_label.setTextInteractionFlags( + QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.publish_token_label.setObjectName("publish_token_label") + self.gridLayout.addWidget(self.publish_token_label, 4, 0, 1, 1) + self.publish_token_widgets_layout = QtGui.QHBoxLayout() + self.publish_token_widgets_layout.setSpacing(4) + # self.publish_token_widgets_layout.setContentsMargins(-1, 1, -1, -1) + self.publish_token_widgets_layout.setContentsMargins(-1, 1, -1, 8) + self.publish_token_widgets_layout.setObjectName("publish_token_widgets_layout") + self.publish_token_display = QtWidgets.QLineEdit(self.edit_widget) + self.publish_token_display.setCursorPosition(0) + self.publish_token_display.setToolTip('Type version token then press enter') + self.publish_token_display.setPlaceholderText('Type version token then press enter') + # self.publish_token_display.setMinimumSize(QtCore.QSize(0, 0)) + # self.publish_token_display.setMaximumSize(QtCore.QSize(16777215, 32)) + self.publish_token_display.setObjectName("publish_token_display") + self.publish_token_display.setEnabled(True) + self.publish_token_widgets_layout.addWidget(self.publish_token_display) + + self.publish_token_label.hide() + self.publish_token_display.hide() + + self.publish_token_widgets_layout.setStretch(0, 1) + self.publish_token_widgets_layout.setStretch(1, 100) + self.publish_token_widgets_layout.setStretch(2, 1) + self.gridLayout.addLayout(self.publish_token_widgets_layout, 4, 1, 1, 1) + self.gridLayout.setColumnStretch(0, 1) self.gridLayout.setColumnStretch(1, 100) self.verticalLayout.addLayout(self.gridLayout) @@ -149,10 +237,14 @@ def retranslateUi(self, ContextWidget): self.task_display.setText(QtGui.QApplication.translate("ContextWidget", "Loading...", None, QtGui.QApplication.UnicodeUTF8)) self.task_search_btn.setToolTip(QtGui.QApplication.translate("ContextWidget", "

Toggle this button to allow searching for a Task to associate with the selected item.

", None, QtGui.QApplication.UnicodeUTF8)) self.task_search_btn.setText(QtGui.QApplication.translate("ContextWidget", "...", None, QtGui.QApplication.UnicodeUTF8)) + self.status_label.setText(QtGui.QApplication.translate("ContextWidget", "Task status: ", None, QtGui.QApplication.UnicodeUTF8)) + #self.status_display.setText(QtGui.QApplication.translate("ContextWidget", "Loading...", None, QtGui.QApplication.UnicodeUTF8)) self.link_label.setText(QtGui.QApplication.translate("ContextWidget", "Link: ", None, QtGui.QApplication.UnicodeUTF8)) self.link_display.setText(QtGui.QApplication.translate("ContextWidget", "Loading...", None, QtGui.QApplication.UnicodeUTF8)) self.link_search_btn.setToolTip(QtGui.QApplication.translate("ContextWidget", "

Toggle this button to allow searching for an entity to link to the selected item.

", None, QtGui.QApplication.UnicodeUTF8)) self.link_search_btn.setText(QtGui.QApplication.translate("ContextWidget", "...", None, QtGui.QApplication.UnicodeUTF8)) + self.publish_name_label.setText(QtGui.QApplication.translate("ContextWidget", "Version name: ", None, QtGui.QApplication.UnicodeUTF8)) + self.publish_token_label.setText(QtGui.QApplication.translate("ContextWidget", "Version token: ", None, QtGui.QApplication.UnicodeUTF8)) from ..qtwidgets import GlobalSearchWidget from . import resources_rc