From 75d55b9ac5aee789ed6f6c1cb41677b67a34ad4e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 14 Jan 2022 18:09:41 +0100 Subject: [PATCH 01/15] OP-1117 - added context menu to Launcher actions to skip opening last workfile Choice is persistent in Local Setting (only in DB, not exposed to GUI yet.) --- openpype/hooks/pre_global_host_data.py | 1 + openpype/hooks/pre_non_python_host_launch.py | 2 +- openpype/lib/applications.py | 5 ++ openpype/tools/launcher/actions.py | 4 +- openpype/tools/launcher/constants.py | 1 + openpype/tools/launcher/delegates.py | 13 ++- openpype/tools/launcher/models.py | 86 +++++++++++++++++++- openpype/tools/launcher/widgets.py | 39 ++++++++- 8 files changed, 145 insertions(+), 6 deletions(-) diff --git a/openpype/hooks/pre_global_host_data.py b/openpype/hooks/pre_global_host_data.py index 6b08cdb444..bae967e25f 100644 --- a/openpype/hooks/pre_global_host_data.py +++ b/openpype/hooks/pre_global_host_data.py @@ -43,6 +43,7 @@ class GlobalHostDataHook(PreLaunchHook): "env": self.launch_context.env, + "start_last_workfile": self.data.get("start_last_workfile"), "last_workfile_path": self.data.get("last_workfile_path"), "log": self.log diff --git a/openpype/hooks/pre_non_python_host_launch.py b/openpype/hooks/pre_non_python_host_launch.py index 29e40d28c8..8aa61a9027 100644 --- a/openpype/hooks/pre_non_python_host_launch.py +++ b/openpype/hooks/pre_non_python_host_launch.py @@ -40,7 +40,7 @@ class NonPythonHostHook(PreLaunchHook): ) # Add workfile path if exists workfile_path = self.data["last_workfile_path"] - if os.path.exists(workfile_path): + if workfile_path and os.path.exists(workfile_path): new_launch_args.append(workfile_path) # Append as whole list as these areguments should not be separated diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index d0438e12a6..ab20d812e7 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1490,6 +1490,11 @@ def _prepare_last_workfile(data, workdir): import avalon.api log = data["log"] + + if not data.get("start_last_workfile", True): + log.info("Explicitly forbidden to open last workfile, skipping") + return + _workdir_data = data.get("workdir_data") if not _workdir_data: log.info( diff --git a/openpype/tools/launcher/actions.py b/openpype/tools/launcher/actions.py index 4d86970f9c..fbaef05261 100644 --- a/openpype/tools/launcher/actions.py +++ b/openpype/tools/launcher/actions.py @@ -62,6 +62,7 @@ class ApplicationAction(api.Action): icon = None color = None order = 0 + data = {} _log = None required_session_keys = ( @@ -103,7 +104,8 @@ class ApplicationAction(api.Action): self.application.launch( project_name=project_name, asset_name=asset_name, - task_name=task_name + task_name=task_name, + **self.data ) except ApplictionExecutableNotFound as exc: diff --git a/openpype/tools/launcher/constants.py b/openpype/tools/launcher/constants.py index 7f394cb5ac..3747b0f0a4 100644 --- a/openpype/tools/launcher/constants.py +++ b/openpype/tools/launcher/constants.py @@ -7,6 +7,7 @@ VARIANT_GROUP_ROLE = QtCore.Qt.UserRole + 2 ACTION_ID_ROLE = QtCore.Qt.UserRole + 3 ANIMATION_START_ROLE = QtCore.Qt.UserRole + 4 ANIMATION_STATE_ROLE = QtCore.Qt.UserRole + 5 +FORCE_NOT_OPEN_WORKFILE_ROLE = QtCore.Qt.UserRole + 6 # Animation length in seconds ANIMATION_LEN = 7 diff --git a/openpype/tools/launcher/delegates.py b/openpype/tools/launcher/delegates.py index cef0f5e1a2..7b53658727 100644 --- a/openpype/tools/launcher/delegates.py +++ b/openpype/tools/launcher/delegates.py @@ -2,7 +2,8 @@ import time from Qt import QtCore, QtWidgets, QtGui from .constants import ( ANIMATION_START_ROLE, - ANIMATION_STATE_ROLE + ANIMATION_STATE_ROLE, + FORCE_NOT_OPEN_WORKFILE_ROLE ) @@ -69,6 +70,16 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate): self._draw_animation(painter, option, index) super(ActionDelegate, self).paint(painter, option, index) + + if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): + rect = QtCore.QRectF(option.rect.x(), option.rect.height(), + 5, 5) + painter.setPen(QtCore.Qt.transparent) + painter.setBrush(QtGui.QColor(200, 0, 0)) + painter.drawEllipse(rect) + + painter.setBrush(self.extender_bg_brush) + is_group = False for group_role in self.group_roles: is_group = index.data(group_role) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 427475cb4b..d3fd7ac3b9 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -8,7 +8,8 @@ from .constants import ( ACTION_ROLE, GROUP_ROLE, VARIANT_GROUP_ROLE, - ACTION_ID_ROLE + ACTION_ID_ROLE, + FORCE_NOT_OPEN_WORKFILE_ROLE ) from .actions import ApplicationAction from Qt import QtCore, QtGui @@ -16,6 +17,8 @@ from avalon.vendor import qtawesome from avalon import style, api from openpype.lib import ApplicationManager +from openpype.settings.lib import get_local_settings, save_local_settings + log = logging.getLogger(__name__) @@ -75,7 +78,8 @@ class ActionModel(QtGui.QStandardItemModel): "group": None, "icon": app.icon, "color": getattr(app, "color", None), - "order": getattr(app, "order", None) or 0 + "order": getattr(app, "order", None) or 0, + "data": {} } ) @@ -179,11 +183,20 @@ class ActionModel(QtGui.QStandardItemModel): self.beginResetModel() + local_settings = get_local_settings() items = [] for order in sorted(items_by_order.keys()): for item in items_by_order[order]: item_id = str(uuid.uuid4()) item.setData(item_id, ACTION_ID_ROLE) + + if self.is_force_not_open_workfile(item, + local_settings): + label = item.text() + label += " (Not opening last workfile)" + item.setData(label, QtCore.Qt.ToolTipRole) + item.setData(True, FORCE_NOT_OPEN_WORKFILE_ROLE) + self.items_by_id[item_id] = item items.append(item) @@ -222,6 +235,75 @@ class ActionModel(QtGui.QStandardItemModel): key=lambda action: (action.order, action.name) ) + def update_force_not_open_workfile_settings(self, is_checked, action): + """Store/remove config for forcing to skip opening last workfile. + + Args: + is_checked (bool): True to add, False to remove + action (ApplicationAction) + """ + local_settings = get_local_settings() + + actual_data = self._prepare_compare_data(action) + + force_not_open_workfile = local_settings.get("force_not_open_workfile", + []) + final_local_sett = local_settings + if is_checked: + if not force_not_open_workfile: + final_local_sett["force_not_open_workfile"] = [] + + final_local_sett["force_not_open_workfile"].append(actual_data) + else: + final_local_sett["force_not_open_workfile"] = [] + for config in force_not_open_workfile: + if config != actual_data: + final_local_sett["force_not_open_workfile"].append(config) + + if not final_local_sett["force_not_open_workfile"]: + final_local_sett.pop("force_not_open_workfile") + + save_local_settings(final_local_sett) + + def is_force_not_open_workfile(self, item, local_settings): + """Checks if application for task is marked to not open workfile + + There might be specific tasks where is unwanted to open workfile right + always (broken file, low performance). This allows artist to mark to + skip opening for combination (project, asset, task_name, app) + + Args: + item (QStandardItem) + local_settings (dict) + """ + action = item.data(ACTION_ROLE) + actual_data = self._prepare_compare_data(action) + for config in local_settings.get("force_not_open_workfile", []): + if config == actual_data: + return True + + return False + + def _prepare_compare_data(self, action): + if isinstance(action, list) and action: + action = action[0] + + _session = copy.deepcopy(self.dbcon.Session) + session = { + key: value + for key, value in _session.items() + if value + } + + actual_data = { + "app_label": action.label.lower(), + "project_name": session["AVALON_PROJECT"], + "asset": session["AVALON_ASSET"], + "task_name": session["AVALON_TASK"] + } + + return actual_data + class ProjectModel(QtGui.QStandardItemModel): """List of projects""" diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index edda8d08b5..0c21fcb33d 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -15,7 +15,8 @@ from .constants import ( ACTION_ID_ROLE, ANIMATION_START_ROLE, ANIMATION_STATE_ROLE, - ANIMATION_LEN + ANIMATION_LEN, + FORCE_NOT_OPEN_WORKFILE_ROLE ) @@ -96,6 +97,7 @@ class ActionBar(QtWidgets.QWidget): view.setViewMode(QtWidgets.QListView.IconMode) view.setResizeMode(QtWidgets.QListView.Adjust) view.setSelectionMode(QtWidgets.QListView.NoSelection) + view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) view.setEditTriggers(QtWidgets.QListView.NoEditTriggers) view.setWrapping(True) view.setGridSize(QtCore.QSize(70, 75)) @@ -135,6 +137,7 @@ class ActionBar(QtWidgets.QWidget): project_handler.projects_refreshed.connect(self._on_projects_refresh) view.clicked.connect(self.on_clicked) + view.customContextMenuRequested.connect(self.on_context_menu) def discover_actions(self): if self._animation_timer.isActive(): @@ -181,6 +184,38 @@ class ActionBar(QtWidgets.QWidget): self._animated_items.add(action_id) self._animation_timer.start() + def on_context_menu(self, point): + """Creates menu to force skip opening last workfile.""" + index = self.view.indexAt(point) + if not index.isValid(): + return + + action_item = index.data(ACTION_ROLE) + menu = QtWidgets.QMenu(self.view) + checkbox = QtWidgets.QCheckBox("Force not open last workfile", + menu) + if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): + checkbox.setChecked(True) + + checkbox.stateChanged.connect( + lambda: self.on_checkbox_changed(checkbox.isChecked(), + action_item)) + action = QtWidgets.QWidgetAction(menu) + action.setDefaultWidget(checkbox) + + menu.addAction(action) + + global_point = self.mapToGlobal(point) + action = menu.exec_(global_point) + if not action or not action.data(): + return + + return + + def on_checkbox_changed(self, is_checked, action): + self.model.update_force_not_open_workfile_settings(is_checked, action) + self.discover_actions() # repaint + def on_clicked(self, index): if not index or not index.isValid(): return @@ -189,6 +224,8 @@ class ActionBar(QtWidgets.QWidget): is_variant_group = index.data(VARIANT_GROUP_ROLE) if not is_group and not is_variant_group: action = index.data(ACTION_ROLE) + if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): + action.data["start_last_workfile"] = False self._start_animation(index) self.action_clicked.emit(action) return From c9b93837d6167c8c4111eb8a154b46c23049de0c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 12:17:36 +0100 Subject: [PATCH 02/15] OP-1117 - switched from local settings to JSONRegistry LS change in UI might be destructive, create json file in user dir instead --- openpype/tools/launcher/models.py | 45 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index d3fd7ac3b9..b872dda8c2 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -2,6 +2,7 @@ import uuid import copy import logging import collections +import appdirs from . import lib from .constants import ( @@ -15,9 +16,7 @@ from .actions import ApplicationAction from Qt import QtCore, QtGui from avalon.vendor import qtawesome from avalon import style, api -from openpype.lib import ApplicationManager - -from openpype.settings.lib import get_local_settings, save_local_settings +from openpype.lib import ApplicationManager, JSONSettingRegistry log = logging.getLogger(__name__) @@ -33,6 +32,13 @@ class ActionModel(QtGui.QStandardItemModel): # Cache of available actions self._registered_actions = list() self.items_by_id = {} + path = appdirs.user_data_dir("openpype", "pype_club") + self.launcher_registry = JSONSettingRegistry("launcher", path) + + try: + _ = self.launcher_registry.get_item("force_not_open_workfile") + except ValueError: + self.launcher_registry.set_item("force_not_open_workfile", []) def discover(self): """Set up Actions cache. Run this for each new project.""" @@ -183,7 +189,7 @@ class ActionModel(QtGui.QStandardItemModel): self.beginResetModel() - local_settings = get_local_settings() + stored = self.launcher_registry.get_item("force_not_open_workfile") items = [] for order in sorted(items_by_order.keys()): for item in items_by_order[order]: @@ -191,7 +197,7 @@ class ActionModel(QtGui.QStandardItemModel): item.setData(item_id, ACTION_ID_ROLE) if self.is_force_not_open_workfile(item, - local_settings): + stored): label = item.text() label += " (Not opening last workfile)" item.setData(label, QtCore.Qt.ToolTipRole) @@ -242,30 +248,21 @@ class ActionModel(QtGui.QStandardItemModel): is_checked (bool): True to add, False to remove action (ApplicationAction) """ - local_settings = get_local_settings() - actual_data = self._prepare_compare_data(action) - force_not_open_workfile = local_settings.get("force_not_open_workfile", - []) - final_local_sett = local_settings + stored = self.launcher_registry.get_item("force_not_open_workfile") if is_checked: - if not force_not_open_workfile: - final_local_sett["force_not_open_workfile"] = [] - - final_local_sett["force_not_open_workfile"].append(actual_data) + stored.append(actual_data) else: - final_local_sett["force_not_open_workfile"] = [] - for config in force_not_open_workfile: + final_values = [] + for config in stored: if config != actual_data: - final_local_sett["force_not_open_workfile"].append(config) + final_values.append(config) + stored = final_values - if not final_local_sett["force_not_open_workfile"]: - final_local_sett.pop("force_not_open_workfile") + self.launcher_registry.set_item("force_not_open_workfile", stored) - save_local_settings(final_local_sett) - - def is_force_not_open_workfile(self, item, local_settings): + def is_force_not_open_workfile(self, item, stored): """Checks if application for task is marked to not open workfile There might be specific tasks where is unwanted to open workfile right @@ -274,11 +271,11 @@ class ActionModel(QtGui.QStandardItemModel): Args: item (QStandardItem) - local_settings (dict) + stored (list) of dict """ action = item.data(ACTION_ROLE) actual_data = self._prepare_compare_data(action) - for config in local_settings.get("force_not_open_workfile", []): + for config in stored: if config == actual_data: return True From 14d05f5f077dc53f8c95fc5861c3122070d5eaf8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 13:14:35 +0100 Subject: [PATCH 03/15] OP-1117 - apply only on ApplicationAction --- openpype/tools/launcher/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index b872dda8c2..1df41fbc9d 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -274,6 +274,12 @@ class ActionModel(QtGui.QStandardItemModel): stored (list) of dict """ action = item.data(ACTION_ROLE) + if isinstance(action, list) and action: + action = action[0] + + if ApplicationAction not in action.__bases__: + return False + actual_data = self._prepare_compare_data(action) for config in stored: if config == actual_data: From 66e032dc6fb507b54ede43a1fb964307ad8f46a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 13:44:33 +0100 Subject: [PATCH 04/15] Update openpype/tools/launcher/models.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/tools/launcher/models.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 1df41fbc9d..0d57114e46 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -291,22 +291,13 @@ class ActionModel(QtGui.QStandardItemModel): if isinstance(action, list) and action: action = action[0] - _session = copy.deepcopy(self.dbcon.Session) - session = { - key: value - for key, value in _session.items() - if value - } - - actual_data = { + return { "app_label": action.label.lower(), - "project_name": session["AVALON_PROJECT"], - "asset": session["AVALON_ASSET"], - "task_name": session["AVALON_TASK"] + "project_name": self.dbcon.Session["AVALON_PROJECT"], + "asset": self.dbcon.Session["AVALON_ASSET"], + "task_name": self.dbcon.Session["AVALON_TASK"] } - return actual_data - class ProjectModel(QtGui.QStandardItemModel): """List of projects""" From 69f97476f921ab86ca984cea934889ede207606a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 14:45:39 +0100 Subject: [PATCH 05/15] OP-1117 - add check for ApplicationAction on context menu --- openpype/tools/launcher/models.py | 17 +++++++++++++---- openpype/tools/launcher/widgets.py | 3 +++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 0d57114e46..86c3d816b2 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -261,6 +261,18 @@ class ActionModel(QtGui.QStandardItemModel): stored = final_values self.launcher_registry.set_item("force_not_open_workfile", stored) + self.launcher_registry._get_item.cache_clear() + + def is_application_action(self, action): + """Checks if item is of a ApplicationAction type + + Args: + action (action) + """ + if isinstance(action, list) and action: + action = action[0] + + return ApplicationAction in action.__bases__ def is_force_not_open_workfile(self, item, stored): """Checks if application for task is marked to not open workfile @@ -274,10 +286,7 @@ class ActionModel(QtGui.QStandardItemModel): stored (list) of dict """ action = item.data(ACTION_ROLE) - if isinstance(action, list) and action: - action = action[0] - - if ApplicationAction not in action.__bases__: + if not self.is_application_action(action): return False actual_data = self._prepare_compare_data(action) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 0c21fcb33d..907aff91d4 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -191,6 +191,9 @@ class ActionBar(QtWidgets.QWidget): return action_item = index.data(ACTION_ROLE) + if not self.model.is_application_action(action_item): + return + menu = QtWidgets.QMenu(self.view) checkbox = QtWidgets.QCheckBox("Force not open last workfile", menu) From 0bb4d317c45fc33072acf4333d670bda000b157a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 14:55:52 +0100 Subject: [PATCH 06/15] OP-1117 - remove unneeded code --- openpype/tools/launcher/widgets.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 907aff91d4..da9218e4ea 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -209,11 +209,7 @@ class ActionBar(QtWidgets.QWidget): menu.addAction(action) global_point = self.mapToGlobal(point) - action = menu.exec_(global_point) - if not action or not action.data(): - return - - return + _ = menu.exec_(global_point) def on_checkbox_changed(self, is_checked, action): self.model.update_force_not_open_workfile_settings(is_checked, action) From 276aff358b3c7da0ae9c7e4fa2920f1ee5064004 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 17:03:30 +0100 Subject: [PATCH 07/15] OP-1117 - better handling of setting of start_last_workfile --- openpype/lib/applications.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index ab20d812e7..772514823e 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1491,10 +1491,6 @@ def _prepare_last_workfile(data, workdir): log = data["log"] - if not data.get("start_last_workfile", True): - log.info("Explicitly forbidden to open last workfile, skipping") - return - _workdir_data = data.get("workdir_data") if not _workdir_data: log.info( @@ -1511,7 +1507,11 @@ def _prepare_last_workfile(data, workdir): start_last_workfile = should_start_last_workfile( project_name, app.host_name, task_name, task_type ) - data["start_last_workfile"] = start_last_workfile + if not data.get("start_last_workfile", True): + log.info("Explicitly forbidden to open last workfile") + start_last_workfile = False + else: + data["start_last_workfile"] = start_last_workfile workfile_startup = should_workfile_tool_start( project_name, app.host_name, task_name, task_type From 419555ec08b437761f7afc6ac2438f75f7169c7c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 17:06:26 +0100 Subject: [PATCH 08/15] OP-1117 - handle empty action --- openpype/tools/launcher/models.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 86c3d816b2..305707319d 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -300,12 +300,15 @@ class ActionModel(QtGui.QStandardItemModel): if isinstance(action, list) and action: action = action[0] - return { - "app_label": action.label.lower(), - "project_name": self.dbcon.Session["AVALON_PROJECT"], - "asset": self.dbcon.Session["AVALON_ASSET"], - "task_name": self.dbcon.Session["AVALON_TASK"] - } + compare_data = {} + if action: + compare_data = { + "app_label": action.label.lower(), + "project_name": self.dbcon.Session["AVALON_PROJECT"], + "asset": self.dbcon.Session["AVALON_ASSET"], + "task_name": self.dbcon.Session["AVALON_TASK"] + } + return compare_data class ProjectModel(QtGui.QStandardItemModel): From 3e64f2d0ce224af2d3adabb518be4689c2e1e9c8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 18 Jan 2022 12:41:05 +0100 Subject: [PATCH 09/15] OP-1117 - fixed start_last_workfile logic It wasnt setting this property all the time. --- openpype/lib/applications.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 772514823e..152dc6af6b 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1504,14 +1504,16 @@ def _prepare_last_workfile(data, workdir): project_name = data["project_name"] task_name = data["task_name"] task_type = data["task_type"] - start_last_workfile = should_start_last_workfile( - project_name, app.host_name, task_name, task_type - ) - if not data.get("start_last_workfile", True): - log.info("Explicitly forbidden to open last workfile") - start_last_workfile = False + + start_last_workfile = data.get("start_last_workfile", True) + if start_last_workfile: + start_last_workfile = should_start_last_workfile( + project_name, app.host_name, task_name, task_type + ) else: - data["start_last_workfile"] = start_last_workfile + log.info("Explicitly forbidden to open last workfile") + + data["start_last_workfile"] = start_last_workfile workfile_startup = should_workfile_tool_start( project_name, app.host_name, task_name, task_type From 650294b65657d7bbfa5b28b3bbcd2265e44926ce Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Jan 2022 12:30:06 +0100 Subject: [PATCH 10/15] OP-1117 - fix for Adobe products --- openpype/hooks/pre_add_last_workfile_arg.py | 4 +++- openpype/hooks/pre_global_host_data.py | 2 +- openpype/hooks/pre_non_python_host_launch.py | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index 5ca2a42510..4ff5e17edc 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -6,6 +6,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): """Add last workfile path to launch arguments. This is not possible to do for all applications the same way. + Checks 'start_last_workfile', if set to False, it will not open last + wokfile. This property is set explicitly in Launcher. """ # Execute after workfile template copy @@ -23,7 +25,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): ] def execute(self): - if not self.data.get("start_last_workfile"): + if not self.data.get("start_last_workfile", True): self.log.info("It is set to not start last workfile on start.") return diff --git a/openpype/hooks/pre_global_host_data.py b/openpype/hooks/pre_global_host_data.py index bae967e25f..3f56c47a42 100644 --- a/openpype/hooks/pre_global_host_data.py +++ b/openpype/hooks/pre_global_host_data.py @@ -43,7 +43,7 @@ class GlobalHostDataHook(PreLaunchHook): "env": self.launch_context.env, - "start_last_workfile": self.data.get("start_last_workfile"), + "start_last_workfile": self.data.get("start_last_workfile", True), "last_workfile_path": self.data.get("last_workfile_path"), "log": self.log diff --git a/openpype/hooks/pre_non_python_host_launch.py b/openpype/hooks/pre_non_python_host_launch.py index 8aa61a9027..5bbc0a4834 100644 --- a/openpype/hooks/pre_non_python_host_launch.py +++ b/openpype/hooks/pre_non_python_host_launch.py @@ -40,7 +40,10 @@ class NonPythonHostHook(PreLaunchHook): ) # Add workfile path if exists workfile_path = self.data["last_workfile_path"] - if workfile_path and os.path.exists(workfile_path): + if ( + self.data.get("start_last_workfile", True) + and workfile_path + and os.path.exists(workfile_path)): new_launch_args.append(workfile_path) # Append as whole list as these areguments should not be separated From f6ccc6aee10360a88ea0bba1b66ecce3b8958dd0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Jan 2022 14:51:14 +0100 Subject: [PATCH 11/15] OP-1117 - updated setting of state on action item Updated handling of menu display and repaint --- openpype/tools/launcher/constants.py | 1 + openpype/tools/launcher/models.py | 26 +++++++++++++++++++------- openpype/tools/launcher/widgets.py | 26 +++++++++++++++++++++----- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/openpype/tools/launcher/constants.py b/openpype/tools/launcher/constants.py index 3747b0f0a4..61f631759b 100644 --- a/openpype/tools/launcher/constants.py +++ b/openpype/tools/launcher/constants.py @@ -8,6 +8,7 @@ ACTION_ID_ROLE = QtCore.Qt.UserRole + 3 ANIMATION_START_ROLE = QtCore.Qt.UserRole + 4 ANIMATION_STATE_ROLE = QtCore.Qt.UserRole + 5 FORCE_NOT_OPEN_WORKFILE_ROLE = QtCore.Qt.UserRole + 6 +ACTION_TOOLTIP_ROLE = QtCore.Qt.UserRole + 7 # Animation length in seconds ANIMATION_LEN = 7 diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 305707319d..10922e42cc 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -32,7 +32,7 @@ class ActionModel(QtGui.QStandardItemModel): # Cache of available actions self._registered_actions = list() self.items_by_id = {} - path = appdirs.user_data_dir("openpype", "pype_club") + path = appdirs.user_data_dir("openpype", "pypeclub") self.launcher_registry = JSONSettingRegistry("launcher", path) try: @@ -198,10 +198,7 @@ class ActionModel(QtGui.QStandardItemModel): if self.is_force_not_open_workfile(item, stored): - label = item.text() - label += " (Not opening last workfile)" - item.setData(label, QtCore.Qt.ToolTipRole) - item.setData(True, FORCE_NOT_OPEN_WORKFILE_ROLE) + self.change_action_item(item, True) self.items_by_id[item_id] = item items.append(item) @@ -241,13 +238,18 @@ class ActionModel(QtGui.QStandardItemModel): key=lambda action: (action.order, action.name) ) - def update_force_not_open_workfile_settings(self, is_checked, action): + def update_force_not_open_workfile_settings(self, is_checked, action_id): """Store/remove config for forcing to skip opening last workfile. Args: is_checked (bool): True to add, False to remove - action (ApplicationAction) + action_id (str) """ + action_item = self.items_by_id.get(action_id) + if not action_item: + return + + action = action_item.data(ACTION_ROLE) actual_data = self._prepare_compare_data(action) stored = self.launcher_registry.get_item("force_not_open_workfile") @@ -262,6 +264,16 @@ class ActionModel(QtGui.QStandardItemModel): self.launcher_registry.set_item("force_not_open_workfile", stored) self.launcher_registry._get_item.cache_clear() + self.change_action_item(action_item, is_checked) + + def change_action_item(self, item, checked): + """Modifies tooltip and sets if opening of last workfile forbidden""" + tooltip = item.data(QtCore.Qt.ToolTipRole) + if checked: + tooltip += " (Not opening last workfile)" + + item.setData(tooltip, QtCore.Qt.ToolTipRole) + item.setData(checked, FORCE_NOT_OPEN_WORKFILE_ROLE) def is_application_action(self, action): """Checks if item is of a ApplicationAction type diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index da9218e4ea..ae95e2e8d8 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -139,7 +139,14 @@ class ActionBar(QtWidgets.QWidget): view.clicked.connect(self.on_clicked) view.customContextMenuRequested.connect(self.on_context_menu) + self._context_menu = None + self._discover_on_menu = False + def discover_actions(self): + if self._context_menu is not None: + self._discover_on_menu = True + return + if self._animation_timer.isActive(): self._animation_timer.stop() self.model.discover() @@ -200,20 +207,29 @@ class ActionBar(QtWidgets.QWidget): if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): checkbox.setChecked(True) + action_id = index.data(ACTION_ID_ROLE) checkbox.stateChanged.connect( lambda: self.on_checkbox_changed(checkbox.isChecked(), - action_item)) + action_id)) action = QtWidgets.QWidgetAction(menu) action.setDefaultWidget(checkbox) menu.addAction(action) + self._context_menu = menu global_point = self.mapToGlobal(point) - _ = menu.exec_(global_point) + menu.exec_(global_point) + self._context_menu = None + if self._discover_on_menu: + self._discover_on_menu = False + self.discover_actions() - def on_checkbox_changed(self, is_checked, action): - self.model.update_force_not_open_workfile_settings(is_checked, action) - self.discover_actions() # repaint + def on_checkbox_changed(self, is_checked, action_id): + self.model.update_force_not_open_workfile_settings(is_checked, + action_id) + self.view.update() + if self._context_menu is not None: + self._context_menu.close() def on_clicked(self, index): if not index or not index.isValid(): From c71e071a55cc04a065f5f6060eef6e676ff650cc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 25 Jan 2022 15:33:55 +0100 Subject: [PATCH 12/15] Update openpype/tools/launcher/widgets.py Co-authored-by: Milan Kolar --- openpype/tools/launcher/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index ae95e2e8d8..beff2548f2 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -202,7 +202,7 @@ class ActionBar(QtWidgets.QWidget): return menu = QtWidgets.QMenu(self.view) - checkbox = QtWidgets.QCheckBox("Force not open last workfile", + checkbox = QtWidgets.QCheckBox("Skip opening last workfile.", menu) if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): checkbox.setChecked(True) From d07dc4ac999084b640cc2ec4834f2c17655cbccb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 25 Jan 2022 17:22:24 +0100 Subject: [PATCH 13/15] OP-1117 - fixed typos --- openpype/hooks/pre_add_last_workfile_arg.py | 2 +- openpype/lib/applications.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index 4ff5e17edc..048545a150 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -7,7 +7,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): This is not possible to do for all applications the same way. Checks 'start_last_workfile', if set to False, it will not open last - wokfile. This property is set explicitly in Launcher. + workfile. This property is set explicitly in Launcher. """ # Execute after workfile template copy diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 152dc6af6b..1278ed396e 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1511,7 +1511,7 @@ def _prepare_last_workfile(data, workdir): project_name, app.host_name, task_name, task_type ) else: - log.info("Explicitly forbidden to open last workfile") + log.info("Opening of last workfile was disabled by user") data["start_last_workfile"] = start_last_workfile From e836416513477d9640c64e26c6066a6a8db3398e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 26 Jan 2022 17:56:19 +0100 Subject: [PATCH 14/15] OP-1117 - cleaned up use of default value --- openpype/hooks/pre_add_last_workfile_arg.py | 2 +- openpype/hooks/pre_global_host_data.py | 2 +- openpype/hooks/pre_non_python_host_launch.py | 2 +- openpype/lib/applications.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index 048545a150..653f97b3dd 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -25,7 +25,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): ] def execute(self): - if not self.data.get("start_last_workfile", True): + if not self.data.get("start_last_workfile"): self.log.info("It is set to not start last workfile on start.") return diff --git a/openpype/hooks/pre_global_host_data.py b/openpype/hooks/pre_global_host_data.py index 3f56c47a42..bae967e25f 100644 --- a/openpype/hooks/pre_global_host_data.py +++ b/openpype/hooks/pre_global_host_data.py @@ -43,7 +43,7 @@ class GlobalHostDataHook(PreLaunchHook): "env": self.launch_context.env, - "start_last_workfile": self.data.get("start_last_workfile", True), + "start_last_workfile": self.data.get("start_last_workfile"), "last_workfile_path": self.data.get("last_workfile_path"), "log": self.log diff --git a/openpype/hooks/pre_non_python_host_launch.py b/openpype/hooks/pre_non_python_host_launch.py index 5bbc0a4834..dd193616e6 100644 --- a/openpype/hooks/pre_non_python_host_launch.py +++ b/openpype/hooks/pre_non_python_host_launch.py @@ -41,7 +41,7 @@ class NonPythonHostHook(PreLaunchHook): # Add workfile path if exists workfile_path = self.data["last_workfile_path"] if ( - self.data.get("start_last_workfile", True) + self.data.get("start_last_workfile") and workfile_path and os.path.exists(workfile_path)): new_launch_args.append(workfile_path) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 930b433ac0..a704c3ae68 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1505,8 +1505,8 @@ def _prepare_last_workfile(data, workdir): task_name = data["task_name"] task_type = data["task_type"] - start_last_workfile = data.get("start_last_workfile", True) - if start_last_workfile: + start_last_workfile = data.get("start_last_workfile") + if start_last_workfile is None: start_last_workfile = should_start_last_workfile( project_name, app.host_name, task_name, task_type ) From e68b9acb1ed018fb0d129d2c5a2453e58d309480 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 26 Jan 2022 18:04:16 +0100 Subject: [PATCH 15/15] Update openpype/tools/launcher/widgets.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/tools/launcher/widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index beff2548f2..ce3f128496 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -241,6 +241,8 @@ class ActionBar(QtWidgets.QWidget): action = index.data(ACTION_ROLE) if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): action.data["start_last_workfile"] = False + else: + action.data.pop("start_last_workfile", None) self._start_animation(index) self.action_clicked.emit(action) return