Merge pull request #2536 from pypeclub/feature/OP-1117_Disable-workfile-autoload-in-Launcher

Launcher: Added context menu to to skip opening last workfile
This commit is contained in:
Petr Kalis 2022-01-31 16:06:49 +01:00 committed by GitHub
commit ad0abfbdd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 192 additions and 10 deletions

View file

@ -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
workfile. This property is set explicitly in Launcher.
"""
# Execute after workfile template copy

View file

@ -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

View file

@ -40,7 +40,10 @@ class NonPythonHostHook(PreLaunchHook):
)
# Add workfile path if exists
workfile_path = self.data["last_workfile_path"]
if os.path.exists(workfile_path):
if (
self.data.get("start_last_workfile")
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

View file

@ -1490,6 +1490,7 @@ def _prepare_last_workfile(data, workdir):
import avalon.api
log = data["log"]
_workdir_data = data.get("workdir_data")
if not _workdir_data:
log.info(
@ -1503,9 +1504,15 @@ 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
)
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
)
else:
log.info("Opening of last workfile was disabled by user")
data["start_last_workfile"] = start_last_workfile
workfile_startup = should_workfile_tool_start(

View file

@ -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:

View file

@ -7,6 +7,8 @@ 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
ACTION_TOOLTIP_ROLE = QtCore.Qt.UserRole + 7
# Animation length in seconds
ANIMATION_LEN = 7

View file

@ -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)

View file

@ -2,19 +2,21 @@ import uuid
import copy
import logging
import collections
import appdirs
from . import lib
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
from avalon.vendor import qtawesome
from avalon import style, api
from openpype.lib import ApplicationManager
from openpype.lib import ApplicationManager, JSONSettingRegistry
log = logging.getLogger(__name__)
@ -30,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", "pypeclub")
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."""
@ -75,7 +84,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 +189,17 @@ class ActionModel(QtGui.QStandardItemModel):
self.beginResetModel()
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]:
item_id = str(uuid.uuid4())
item.setData(item_id, ACTION_ID_ROLE)
if self.is_force_not_open_workfile(item,
stored):
self.change_action_item(item, True)
self.items_by_id[item_id] = item
items.append(item)
@ -222,6 +238,90 @@ class ActionModel(QtGui.QStandardItemModel):
key=lambda action: (action.order, action.name)
)
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_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")
if is_checked:
stored.append(actual_data)
else:
final_values = []
for config in stored:
if config != actual_data:
final_values.append(config)
stored = final_values
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
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
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)
stored (list) of dict
"""
action = item.data(ACTION_ROLE)
if not self.is_application_action(action):
return False
actual_data = self._prepare_compare_data(action)
for config in stored:
if config == actual_data:
return True
return False
def _prepare_compare_data(self, action):
if isinstance(action, list) and action:
action = action[0]
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):
"""List of projects"""

View file

@ -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,8 +137,16 @@ 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)
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()
@ -181,6 +191,46 @@ 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)
if not self.model.is_application_action(action_item):
return
menu = QtWidgets.QMenu(self.view)
checkbox = QtWidgets.QCheckBox("Skip opening last workfile.",
menu)
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_id))
action = QtWidgets.QWidgetAction(menu)
action.setDefaultWidget(checkbox)
menu.addAction(action)
self._context_menu = menu
global_point = self.mapToGlobal(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_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():
return
@ -189,6 +239,10 @@ 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
else:
action.data.pop("start_last_workfile", None)
self._start_animation(index)
self.action_clicked.emit(action)
return