Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Launcher fix actions discover #896

Merged
merged 17 commits into from
Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 18 additions & 19 deletions pype/tools/launcher/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,27 +115,21 @@ def __init__(self, dbcon, parent=None):
super(ActionModel, self).__init__(parent=parent)
self.dbcon = dbcon

self._session = {}
self._groups = {}
self.default_icon = qtawesome.icon("fa.cube", color="white")
# Cache of available actions
self._registered_actions = list()

self.discover()

def discover(self):
"""Set up Actions cache. Run this for each new project."""
if not self.dbcon.Session.get("AVALON_PROJECT"):
self._registered_actions = list()
return

# Discover all registered actions
actions = api.discover(api.Action)

# Get available project actions and the application actions
project_doc = self.dbcon.find_one({"type": "project"})
app_actions = lib.get_application_actions(project_doc)
actions.extend(app_actions)
if self.dbcon.Session.get("AVALON_PROJECT"):
project_doc = self.dbcon.find_one({"type": "project"})
app_actions = lib.get_application_actions(project_doc)
actions.extend(app_actions)

self._registered_actions = actions

Expand All @@ -145,7 +139,7 @@ def get_icon(self, action, skip_default=False):
return self.default_icon
return icon

def refresh(self):
def filter_actions(self):
# Validate actions based on compatibility
self.clear()

Expand Down Expand Up @@ -202,14 +196,17 @@ def refresh(self):
if icon is None:
icon = self.default_icon

item = QtGui.QStandardItem(icon, action.label)
item = QtGui.QStandardItem(icon, label)
item.setData(label, QtCore.Qt.ToolTipRole)
item.setData(actions, self.ACTION_ROLE)
item.setData(True, self.VARIANT_GROUP_ROLE)
items_by_order[order].append(item)

for action in single_actions:
icon = self.get_icon(action)
item = QtGui.QStandardItem(icon, lib.get_action_label(action))
label = lib.get_action_label(action)
item = QtGui.QStandardItem(icon, label)
item.setData(label, QtCore.Qt.ToolTipRole)
item.setData(action, self.ACTION_ROLE)
items_by_order[action.order].append(item)

Expand Down Expand Up @@ -240,11 +237,6 @@ def refresh(self):

self.endResetModel()

def set_session(self, session):
assert isinstance(session, dict)
self._session = copy.deepcopy(session)
self.refresh()

def filter_compatible_actions(self, actions):
"""Collect all actions which are compatible with the environment

Expand All @@ -259,8 +251,15 @@ def filter_compatible_actions(self, actions):
"""

compatible = []
_session = copy.deepcopy(self.dbcon.Session)
session = {
key: value
for key, value in _session.items()
if value
}

for action in actions:
if action().is_compatible(self._session):
if action().is_compatible(session):
compatible.append(action)

# Sort by order and name
Expand Down
6 changes: 6 additions & 0 deletions pype/tools/launcher/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ def __init__(self, dbcon, parent=None):

view.clicked.connect(self.on_clicked)

def discover_actions(self):
self.model.discover()

def filter_actions(self):
self.model.filter_actions()

def set_row_height(self, rows):
self.setMinimumHeight(rows * 75)

Expand Down
135 changes: 70 additions & 65 deletions pype/tools/launcher/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
from avalon.vendor import qtawesome
from .models import ProjectModel
from .widgets import (
ProjectBar, ActionBar, TasksWidget, ActionHistory, SlidePageWidget
ProjectBar,
ActionBar,
TasksWidget,
ActionHistory,
SlidePageWidget
)

from .flickcharm import FlickCharm
Expand Down Expand Up @@ -119,6 +123,7 @@ def on_clicked(self, index):
class AssetsPanel(QtWidgets.QWidget):
"""Assets page"""
back_clicked = QtCore.Signal()
session_changed = QtCore.Signal()

def __init__(self, dbcon, parent=None):
super(AssetsPanel, self).__init__(parent=parent)
Expand Down Expand Up @@ -186,6 +191,9 @@ def __init__(self, dbcon, parent=None):
# signals
project_bar.project_changed.connect(self.on_project_changed)
assets_widget.selection_changed.connect(self.on_asset_changed)
assets_widget.refreshed.connect(self.on_asset_changed)
tasks_widget.task_changed.connect(self.on_task_change)

btn_back.clicked.connect(self.back_clicked)

# Force initial refresh for the assets since we might not be
Expand All @@ -196,18 +204,20 @@ def __init__(self, dbcon, parent=None):

def set_project(self, project):
before = self.project_bar.get_current_project()
self.project_bar.set_project(project)
if project == before:
# Force a refresh on the assets if the project hasn't changed
if before == project:
self.assets_widget.refresh()
return

self.project_bar.set_project(project)
self.on_project_changed()

def on_project_changed(self):
project_name = self.project_bar.get_current_project()
self.dbcon.Session["AVALON_PROJECT"] = project_name
self.assets_widget.refresh()

# Force asset change callback to ensure tasks are correctly reset
self.assets_widget.refreshed.connect(self.on_asset_changed)
self.session_changed.emit()

self.assets_widget.refresh()

def on_asset_changed(self):
"""Callback on asset selection changed
Expand All @@ -218,28 +228,41 @@ def on_asset_changed(self):

print("Asset changed..")

asset_name = None
asset_silo = None

# Check asset on current index and selected assets
asset_doc = self.assets_widget.get_active_asset_document()
selected_asset_docs = self.assets_widget.get_selected_assets()
# If there are not asset selected docs then active asset is not
# selected
if not selected_asset_docs:
asset_doc = None
elif asset_doc:
# If selected asset doc and current asset are not same than
# something bad happened
if selected_asset_docs[0]["_id"] != asset_doc["_id"]:
asset_doc = None

if asset_doc:
self.tasks_widget.set_asset(asset_doc["_id"])
else:
self.tasks_widget.set_asset(None)
asset_name = asset_doc["name"]
asset_silo = asset_doc.get("silo")

def get_current_session(self):
asset_doc = self.assets_widget.get_active_asset_document()
session = copy.deepcopy(self.dbcon.Session)
self.dbcon.Session["AVALON_TASK"] = None
self.dbcon.Session["AVALON_ASSET"] = asset_name
self.dbcon.Session["AVALON_SILO"] = asset_silo

# Clear some values that we are about to collect if available
session.pop("AVALON_SILO", None)
session.pop("AVALON_ASSET", None)
session.pop("AVALON_TASK", None)
self.session_changed.emit()

asset_id = None
if asset_doc:
session["AVALON_ASSET"] = asset_doc["name"]
task_name = self.tasks_widget.get_current_task()
if task_name:
session["AVALON_TASK"] = task_name
asset_id = asset_doc["_id"]
self.tasks_widget.set_asset(asset_id)

return session
def on_task_change(self):
task_name = self.tasks_widget.get_current_task()
self.dbcon.Session["AVALON_TASK"] = task_name
self.session_changed.emit()


class LauncherWindow(QtWidgets.QDialog):
Expand Down Expand Up @@ -325,14 +348,7 @@ def __init__(self, parent=None):
action_history.trigger_history.connect(self.on_history_action)
project_panel.project_clicked.connect(self.on_project_clicked)
asset_panel.back_clicked.connect(self.on_back_clicked)

# Add some signals to propagate from the asset panel
for signal in (
asset_panel.project_bar.project_changed,
asset_panel.assets_widget.selection_changed,
asset_panel.tasks_widget.task_changed
):
signal.connect(self.on_session_changed)
asset_panel.session_changed.connect(self.on_session_changed)

# todo: Simplify this callback connection
asset_panel.project_bar.project_changed.connect(
Expand All @@ -341,6 +357,11 @@ def __init__(self, parent=None):

self.resize(520, 740)

def showEvent(self, event):
super().showEvent(event)
# TODO implement refresh/reset which will trigger updating
self.discover_actions()

def set_page(self, page):
current = self.page_slider.currentIndex()
if current == page and self._page == page:
Expand All @@ -350,10 +371,6 @@ def set_page(self, page):
self._page = page
self.page_slider.slide_view(page, direction=direction)

def refresh(self):
self.asset_panel.assets_widget.refresh()
self.refresh_actions()

def echo(self, message):
self.message_label.setText(str(message))
QtCore.QTimer.singleShot(5000, lambda: self.message_label.setText(""))
Expand All @@ -364,30 +381,30 @@ def on_project_changed(self):
self.dbcon.Session["AVALON_PROJECT"] = project_name

# Update the Action plug-ins available for the current project
self.actions_bar.model.discover()
self.discover_actions()

def on_session_changed(self):
self.refresh_actions()
self.filter_actions()

def discover_actions(self):
self.actions_bar.discover_actions()
self.filter_actions()

def refresh_actions(self, delay=1):
tools_lib.schedule(self.on_refresh_actions, delay)
def filter_actions(self):
self.actions_bar.filter_actions()

def on_project_clicked(self, project_name):
self.dbcon.Session["AVALON_PROJECT"] = project_name
# Refresh projects
self.asset_panel.set_project(project_name)
self.set_page(1)
self.refresh_actions()
self.discover_actions()

def on_back_clicked(self):
self.dbcon.Session["AVALON_PROJECT"] = None
self.set_page(0)
self.project_panel.model.refresh() # Refresh projects
self.refresh_actions()

def on_refresh_actions(self):
session = self.get_current_session()
self.actions_bar.model.set_session(session)
self.actions_bar.model.refresh()
self.discover_actions()

def on_action_clicked(self, action):
self.echo("Running action: {}".format(action.name))
Expand All @@ -406,33 +423,21 @@ def on_history_action(self, history_data):
# User is holding control, rerun the action
self.run_action(action, session=session)

def get_current_session(self):
if self._page == 1:
# Assets page
return self.asset_panel.get_current_session()

session = copy.deepcopy(self.dbcon.Session)

# Remove some potential invalid session values
# that we know are not set when not browsing in
# a project.
session.pop("AVALON_PROJECT", None)
session.pop("AVALON_ASSET", None)
session.pop("AVALON_SILO", None)
session.pop("AVALON_TASK", None)

return session

def run_action(self, action, session=None):
if session is None:
session = self.get_current_session()
session = copy.deepcopy(self.dbcon.Session)

filtered_session = {
key: value
for key, value in session.items()
if value
}
# Add to history
self.action_history.add_action(action, session)
self.action_history.add_action(action, filtered_session)

# Process the Action
try:
action().process(session)
action().process(filtered_session)
except Exception as exc:
self.log.warning("Action launch failed.", exc_info=True)
self.echo("Failed: {}".format(str(exc)))
Expand Down