mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
prepared context dialog using AYON calls
This commit is contained in:
parent
915502989c
commit
9868b09c9b
3 changed files with 1188 additions and 393 deletions
783
openpype/tools/context_dialog/_ayon_window.py
Normal file
783
openpype/tools/context_dialog/_ayon_window.py
Normal file
|
|
@ -0,0 +1,783 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
import ayon_api
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype import style
|
||||
from openpype.lib.events import QueuedEventSystem
|
||||
from openpype.tools.ayon_utils.models import (
|
||||
ProjectsModel,
|
||||
HierarchyModel,
|
||||
)
|
||||
from openpype.tools.ayon_utils.widgets import (
|
||||
ProjectsCombobox,
|
||||
FoldersWidget,
|
||||
TasksWidget,
|
||||
)
|
||||
from openpype.tools.utils.lib import (
|
||||
center_window,
|
||||
get_openpype_qt_app,
|
||||
)
|
||||
|
||||
|
||||
class SelectionModel(object):
|
||||
"""Model handling selection changes.
|
||||
|
||||
Triggering events:
|
||||
- "selection.project.changed"
|
||||
- "selection.folder.changed"
|
||||
- "selection.task.changed"
|
||||
"""
|
||||
|
||||
event_source = "selection.model"
|
||||
|
||||
def __init__(self, controller):
|
||||
self._controller = controller
|
||||
|
||||
self._project_name = None
|
||||
self._folder_id = None
|
||||
self._task_id = None
|
||||
self._task_name = None
|
||||
|
||||
def get_selected_project_name(self):
|
||||
return self._project_name
|
||||
|
||||
def set_selected_project(self, project_name):
|
||||
self._project_name = project_name
|
||||
self._controller.emit_event(
|
||||
"selection.project.changed",
|
||||
{"project_name": project_name},
|
||||
self.event_source
|
||||
)
|
||||
|
||||
def get_selected_folder_id(self):
|
||||
return self._folder_id
|
||||
|
||||
def set_selected_folder(self, folder_id):
|
||||
if folder_id == self._folder_id:
|
||||
return
|
||||
self._folder_id = folder_id
|
||||
self._controller.emit_event(
|
||||
"selection.folder.changed",
|
||||
{
|
||||
"project_name": self._project_name,
|
||||
"folder_id": folder_id,
|
||||
},
|
||||
self.event_source
|
||||
)
|
||||
|
||||
def get_selected_task_name(self):
|
||||
return self._task_name
|
||||
|
||||
def get_selected_task_id(self):
|
||||
return self._task_id
|
||||
|
||||
def set_selected_task(self, task_id, task_name):
|
||||
if task_id == self._task_id:
|
||||
return
|
||||
|
||||
self._task_name = task_name
|
||||
self._task_id = task_id
|
||||
self._controller.emit_event(
|
||||
"selection.task.changed",
|
||||
{
|
||||
"project_name": self._project_name,
|
||||
"folder_id": self._folder_id,
|
||||
"task_name": task_name,
|
||||
"task_id": task_id,
|
||||
},
|
||||
self.event_source
|
||||
)
|
||||
|
||||
|
||||
class ExpectedSelection:
|
||||
def __init__(self, controller):
|
||||
self._project_name = None
|
||||
self._folder_id = None
|
||||
|
||||
self._project_selected = True
|
||||
self._folder_selected = True
|
||||
|
||||
self._controller = controller
|
||||
|
||||
def _emit_change(self):
|
||||
self._controller.emit_event(
|
||||
"expected_selection_changed",
|
||||
self.get_expected_selection_data(),
|
||||
)
|
||||
|
||||
def set_expected_selection(self, project_name, folder_id):
|
||||
self._project_name = project_name
|
||||
self._folder_id = folder_id
|
||||
|
||||
self._project_selected = False
|
||||
self._folder_selected = False
|
||||
self._emit_change()
|
||||
|
||||
def get_expected_selection_data(self):
|
||||
project_current = False
|
||||
folder_current = False
|
||||
if not self._project_selected:
|
||||
project_current = True
|
||||
elif not self._folder_selected:
|
||||
folder_current = True
|
||||
return {
|
||||
"project": {
|
||||
"name": self._project_name,
|
||||
"current": project_current,
|
||||
"selected": self._project_selected,
|
||||
},
|
||||
"folder": {
|
||||
"id": self._folder_id,
|
||||
"current": folder_current,
|
||||
"selected": self._folder_selected,
|
||||
},
|
||||
}
|
||||
|
||||
def is_expected_project_selected(self, project_name):
|
||||
return project_name == self._project_name and self._project_selected
|
||||
|
||||
def is_expected_folder_selected(self, folder_id):
|
||||
return folder_id == self._folder_id and self._folder_selected
|
||||
|
||||
def expected_project_selected(self, project_name):
|
||||
if project_name != self._project_name:
|
||||
return False
|
||||
self._project_selected = True
|
||||
self._emit_change()
|
||||
return True
|
||||
|
||||
def expected_folder_selected(self, folder_id):
|
||||
if folder_id != self._folder_id:
|
||||
return False
|
||||
self._folder_selected = True
|
||||
self._emit_change()
|
||||
return True
|
||||
|
||||
|
||||
class ContextDialogController:
|
||||
def __init__(self):
|
||||
self._event_system = None
|
||||
|
||||
self._projects_model = ProjectsModel(self)
|
||||
self._hierarchy_model = HierarchyModel(self)
|
||||
self._selection_model = SelectionModel(self)
|
||||
self._expected_selection = ExpectedSelection(self)
|
||||
|
||||
self._confirmed = False
|
||||
self._is_strict = False
|
||||
self._output_path = None
|
||||
|
||||
self._initial_project_name = None
|
||||
self._initial_folder_id = None
|
||||
self._initial_folder_label = None
|
||||
self._initial_project_found = True
|
||||
self._initial_folder_found = True
|
||||
self._initial_tasks_found = True
|
||||
|
||||
def reset(self):
|
||||
self._emit_event("controller.reset.started")
|
||||
|
||||
self._confirmed = False
|
||||
self._output_path = None
|
||||
|
||||
self._initial_project_name = None
|
||||
self._initial_folder_id = None
|
||||
self._initial_folder_label = None
|
||||
self._initial_project_found = True
|
||||
self._initial_folder_found = True
|
||||
self._initial_tasks_found = True
|
||||
|
||||
self._projects_model.reset()
|
||||
self._hierarchy_model.reset()
|
||||
|
||||
self._emit_event("controller.reset.finished")
|
||||
|
||||
def refresh(self):
|
||||
self._emit_event("controller.refresh.started")
|
||||
|
||||
self._projects_model.reset()
|
||||
self._hierarchy_model.reset()
|
||||
|
||||
self._emit_event("controller.refresh.finished")
|
||||
|
||||
# Event handling
|
||||
def emit_event(self, topic, data=None, source=None):
|
||||
"""Use implemented event system to trigger event."""
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
self._get_event_system().emit(topic, data, source)
|
||||
|
||||
def register_event_callback(self, topic, callback):
|
||||
self._get_event_system().add_callback(topic, callback)
|
||||
|
||||
def set_output_json_path(self, output_path):
|
||||
self._output_path = output_path
|
||||
|
||||
def is_strict(self):
|
||||
return self._is_strict
|
||||
|
||||
def set_strict(self, enabled):
|
||||
if self._is_strict is enabled:
|
||||
return
|
||||
self._is_strict = enabled
|
||||
self._emit_event("strict.changed", {"strict": enabled})
|
||||
|
||||
# Data model functions
|
||||
def get_project_items(self, sender=None):
|
||||
return self._projects_model.get_project_items(sender)
|
||||
|
||||
def get_folder_items(self, project_name, sender=None):
|
||||
return self._hierarchy_model.get_folder_items(project_name, sender)
|
||||
|
||||
def get_task_items(self, project_name, folder_id, sender=None):
|
||||
return self._hierarchy_model.get_task_items(
|
||||
project_name, folder_id, sender
|
||||
)
|
||||
|
||||
# Expected selection helpers
|
||||
def set_expected_selection(self, project_name, folder_id):
|
||||
return self._expected_selection.set_expected_selection(
|
||||
project_name, folder_id
|
||||
)
|
||||
|
||||
def get_expected_selection_data(self):
|
||||
return self._expected_selection.get_expected_selection_data()
|
||||
|
||||
def expected_project_selected(self, project_name):
|
||||
self._expected_selection.expected_project_selected(project_name)
|
||||
|
||||
def expected_folder_selected(self, folder_id):
|
||||
self._expected_selection.expected_folder_selected(folder_id)
|
||||
|
||||
# Selection handling
|
||||
def get_selected_project_name(self):
|
||||
return self._selection_model.get_selected_project_name()
|
||||
|
||||
def set_selected_project(self, project_name):
|
||||
self._selection_model.set_selected_project(project_name)
|
||||
|
||||
def get_selected_folder_id(self):
|
||||
return self._selection_model.get_selected_folder_id()
|
||||
|
||||
def set_selected_folder(self, folder_id):
|
||||
self._selection_model.set_selected_folder(folder_id)
|
||||
|
||||
def get_selected_task_name(self):
|
||||
return self._selection_model.get_selected_task_name()
|
||||
|
||||
def get_selected_task_id(self):
|
||||
return self._selection_model.get_selected_task_id()
|
||||
|
||||
def set_selected_task(self, task_id, task_name):
|
||||
self._selection_model.set_selected_task(task_id, task_name)
|
||||
|
||||
def is_initial_context_valid(self):
|
||||
return self._initial_folder_found and self._initial_project_found
|
||||
|
||||
def set_initial_context(
|
||||
self, project_name=None, asset_name=None, folder_path=None
|
||||
):
|
||||
if project_name is None:
|
||||
project_found = True
|
||||
asset_name = None
|
||||
folder_path = None
|
||||
|
||||
else:
|
||||
project = ayon_api.get_project(project_name)
|
||||
project_found = project is not None
|
||||
|
||||
folder_id = None
|
||||
folder_found = True
|
||||
folder_label = None
|
||||
if folder_path:
|
||||
folder_label = folder_path
|
||||
folder = ayon_api.get_folder_by_path(project_name, folder_path)
|
||||
if folder:
|
||||
folder_id = folder["id"]
|
||||
else:
|
||||
folder_found = False
|
||||
elif asset_name:
|
||||
folder_label = asset_name
|
||||
for folder in ayon_api.get_folders(
|
||||
project_name, folder_names=[asset_name]
|
||||
):
|
||||
folder_id = folder["id"]
|
||||
break
|
||||
if not folder_id:
|
||||
folder_found = False
|
||||
|
||||
tasks_found = True
|
||||
if folder_found and (folder_path or asset_name):
|
||||
tasks = list(ayon_api.get_tasks(
|
||||
project_name, folder_ids=[folder_id], fields=["id"]
|
||||
))
|
||||
if not tasks:
|
||||
tasks_found = False
|
||||
|
||||
self._initial_project_name = project_name
|
||||
self._initial_folder_id = folder_id
|
||||
self._initial_folder_label = folder_label
|
||||
self._initial_folder_found = project_found
|
||||
self._initial_folder_found = folder_found
|
||||
self._initial_tasks_found = tasks_found
|
||||
self._emit_event(
|
||||
"initial.context.changed",
|
||||
self.get_initial_context()
|
||||
)
|
||||
|
||||
def get_initial_context(self):
|
||||
return {
|
||||
"project_name": self._initial_project_name,
|
||||
"folder_id": self._initial_folder_id,
|
||||
"folder_label": self._initial_folder_label,
|
||||
"project_found": self._initial_project_found,
|
||||
"folder_found": self._initial_folder_found,
|
||||
"tasks_found": self._initial_tasks_found,
|
||||
"valid": (
|
||||
self._initial_project_found
|
||||
and self._initial_folder_found
|
||||
and self._initial_tasks_found
|
||||
)
|
||||
}
|
||||
|
||||
# Result of this tool
|
||||
def get_selected_context(self):
|
||||
return {
|
||||
"project": None,
|
||||
"project_name": None,
|
||||
"asset": None,
|
||||
"folder_id": None,
|
||||
"folder_path": None,
|
||||
"task": None,
|
||||
"task_id": None,
|
||||
"task_name": None,
|
||||
}
|
||||
|
||||
def window_closed(self):
|
||||
if not self._confirmed and not self._is_strict:
|
||||
return
|
||||
|
||||
self._store_output()
|
||||
|
||||
def confirm_selection(self):
|
||||
self._confirmed = True
|
||||
self._emit_event(
|
||||
"selection.confirmed",
|
||||
{"confirmed": True}
|
||||
)
|
||||
|
||||
def _store_output(self):
|
||||
if not self._output_path:
|
||||
return
|
||||
|
||||
dirpath = os.path.dirname(self._output_path)
|
||||
os.makedirs(dirpath, exist_ok=True)
|
||||
with open(self._output_path, "w") as stream:
|
||||
json.dump(self.get_selected_context(), stream)
|
||||
|
||||
def _get_event_system(self):
|
||||
"""Inner event system for workfiles tool controller.
|
||||
|
||||
Is used for communication with UI. Event system is created on demand.
|
||||
|
||||
Returns:
|
||||
QueuedEventSystem: Event system which can trigger callbacks
|
||||
for topics.
|
||||
"""
|
||||
|
||||
if self._event_system is None:
|
||||
self._event_system = QueuedEventSystem()
|
||||
return self._event_system
|
||||
|
||||
def _emit_event(self, topic, data=None):
|
||||
self.emit_event(topic, data, "controller")
|
||||
|
||||
|
||||
class InvalidContextOverlay(QtWidgets.QFrame):
|
||||
confirmed = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(InvalidContextOverlay, self).__init__(parent)
|
||||
self.setObjectName("OverlayFrame")
|
||||
|
||||
mid_widget = QtWidgets.QWidget(self)
|
||||
label_widget = QtWidgets.QLabel(
|
||||
"Requested context was not found...",
|
||||
mid_widget
|
||||
)
|
||||
|
||||
confirm_btn = QtWidgets.QPushButton("Close", mid_widget)
|
||||
|
||||
mid_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
mid_layout = QtWidgets.QVBoxLayout(mid_widget)
|
||||
mid_layout.setContentsMargins(0, 0, 0, 0)
|
||||
mid_layout.addWidget(label_widget, 0)
|
||||
mid_layout.addSpacing(30)
|
||||
mid_layout.addWidget(confirm_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QGridLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(mid_widget, 1, 1)
|
||||
main_layout.setRowStretch(0, 1)
|
||||
main_layout.setRowStretch(1, 0)
|
||||
main_layout.setRowStretch(2, 1)
|
||||
main_layout.setColumnStretch(0, 1)
|
||||
main_layout.setColumnStretch(1, 0)
|
||||
main_layout.setColumnStretch(2, 1)
|
||||
|
||||
confirm_btn.clicked.connect(self.confirmed)
|
||||
|
||||
self._label_widget = label_widget
|
||||
self._confirm_btn = confirm_btn
|
||||
|
||||
def set_context(
|
||||
self,
|
||||
project_name,
|
||||
folder_label,
|
||||
project_found,
|
||||
folder_found,
|
||||
tasks_found,
|
||||
):
|
||||
lines = []
|
||||
if not project_found:
|
||||
lines.extend([
|
||||
"Requested project {} was not found...".format(project_name),
|
||||
])
|
||||
|
||||
elif not folder_found:
|
||||
lines.extend([
|
||||
"Requested folder was not found...",
|
||||
"",
|
||||
"Project: {}".format(project_name),
|
||||
"Folder: {}".format(folder_label),
|
||||
])
|
||||
elif not tasks_found:
|
||||
lines.extend([
|
||||
"Requested folder does not have any tasks...",
|
||||
"",
|
||||
"Project: {}".format(project_name),
|
||||
"Folder: {}".format(folder_label),
|
||||
])
|
||||
else:
|
||||
lines.append("Requested context was not found...")
|
||||
self._label_widget.setText("<br/>".join(lines))
|
||||
|
||||
|
||||
class ContextDialog(QtWidgets.QDialog):
|
||||
"""Dialog to select a context.
|
||||
|
||||
Context has 3 parts:
|
||||
- Project
|
||||
- Asset
|
||||
- Task
|
||||
|
||||
It is possible to predefine project and asset. In that case their widgets
|
||||
will have passed preselected values and will be disabled.
|
||||
"""
|
||||
def __init__(self, controller=None, parent=None):
|
||||
super(ContextDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Select Context")
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
if controller is None:
|
||||
controller = ContextDialogController()
|
||||
|
||||
# Enable minimize and maximize for app
|
||||
window_flags = QtCore.Qt.Window
|
||||
if not parent:
|
||||
window_flags |= QtCore.Qt.WindowStaysOnTopHint
|
||||
self.setWindowFlags(window_flags)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
# UI initialization
|
||||
main_splitter = QtWidgets.QSplitter(self)
|
||||
|
||||
# Left side widget contains project combobox and asset widget
|
||||
left_side_widget = QtWidgets.QWidget(main_splitter)
|
||||
|
||||
project_combobox = ProjectsCombobox(
|
||||
controller,
|
||||
parent=left_side_widget,
|
||||
handle_expected_selection=True
|
||||
)
|
||||
|
||||
# Assets widget
|
||||
folders_widget = FoldersWidget(
|
||||
controller,
|
||||
parent=left_side_widget,
|
||||
handle_expected_selection=True
|
||||
)
|
||||
|
||||
left_side_layout = QtWidgets.QVBoxLayout(left_side_widget)
|
||||
left_side_layout.setContentsMargins(0, 0, 0, 0)
|
||||
left_side_layout.addWidget(project_combobox, 0)
|
||||
left_side_layout.addWidget(folders_widget, 1)
|
||||
|
||||
# Right side of window contains only tasks
|
||||
tasks_widget = TasksWidget(controller, parent=main_splitter)
|
||||
|
||||
# Add widgets to main splitter
|
||||
main_splitter.addWidget(left_side_widget)
|
||||
main_splitter.addWidget(tasks_widget)
|
||||
|
||||
# Set stretch of both sides
|
||||
main_splitter.setStretchFactor(0, 7)
|
||||
main_splitter.setStretchFactor(1, 3)
|
||||
|
||||
# Add confimation button to bottom right
|
||||
ok_btn = QtWidgets.QPushButton("OK", self)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.setContentsMargins(0, 0, 0, 0)
|
||||
buttons_layout.addStretch(1)
|
||||
buttons_layout.addWidget(ok_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(main_splitter, 1)
|
||||
main_layout.addLayout(buttons_layout, 0)
|
||||
|
||||
overlay_widget = InvalidContextOverlay(self)
|
||||
overlay_widget.setVisible(False)
|
||||
|
||||
ok_btn.clicked.connect(self._on_ok_click)
|
||||
project_combobox.refreshed.connect(self._on_projects_refresh)
|
||||
overlay_widget.confirmed.connect(self._on_overlay_confirm)
|
||||
|
||||
controller.register_event_callback(
|
||||
"selection.project.changed",
|
||||
self._on_project_selection_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"selection.folder.changed",
|
||||
self._on_folder_selection_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"selection.task.changed",
|
||||
self._on_task_selection_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"initial.context.changed",
|
||||
self._on_init_context_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"strict.changed",
|
||||
self._on_strict_changed
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"controller.reset.finished",
|
||||
self._on_controller_reset
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"controller.refresh.finished",
|
||||
self._on_controller_refresh
|
||||
)
|
||||
|
||||
# Set stylehseet and resize window on first show
|
||||
self._first_show = True
|
||||
self._visible = False
|
||||
|
||||
self._controller = controller
|
||||
|
||||
self._project_combobox = project_combobox
|
||||
self._folders_widget = folders_widget
|
||||
self._tasks_widget = tasks_widget
|
||||
|
||||
self._ok_btn = ok_btn
|
||||
|
||||
self._overlay_widget = overlay_widget
|
||||
|
||||
self._apply_strict_changes(self.is_strict())
|
||||
|
||||
def is_strict(self):
|
||||
return self._controller.is_strict()
|
||||
|
||||
def showEvent(self, event):
|
||||
"""Override show event to do some callbacks."""
|
||||
super(ContextDialog, self).showEvent(event)
|
||||
self._visible = True
|
||||
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
# Set stylesheet and resize
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
self.resize(600, 700)
|
||||
center_window(self)
|
||||
self._controller.refresh()
|
||||
|
||||
initial_context = self._controller.get_initial_context()
|
||||
self._set_init_context(initial_context)
|
||||
self._overlay_widget.resize(self.size())
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(ContextDialog, self).resizeEvent(event)
|
||||
self._overlay_widget.resize(self.size())
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Ignore close event if is in strict state and context is not done."""
|
||||
if self.is_strict() and not self._ok_btn.isEnabled():
|
||||
# Allow to close window when initial context is not valid
|
||||
if self._controller.is_initial_context_valid():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if self.is_strict():
|
||||
self._controller.confirm_selection()
|
||||
self._visible = False
|
||||
super(ContextDialog, self).closeEvent(event)
|
||||
|
||||
def set_strict(self, enabled):
|
||||
"""Change strictness of dialog."""
|
||||
|
||||
self._controller.set_strict(enabled)
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh all widget one by one.
|
||||
|
||||
When asset refresh is triggered we have to wait when is done so
|
||||
this method continues with `_on_asset_widget_refresh_finished`.
|
||||
"""
|
||||
|
||||
self._controller.reset()
|
||||
|
||||
def get_context(self):
|
||||
"""Result of dialog."""
|
||||
return self._controller.get_selected_context()
|
||||
|
||||
def set_context(self, project_name=None, asset_name=None):
|
||||
"""Set context which will be used and locked in dialog."""
|
||||
|
||||
self._controller.set_initial_context(project_name, asset_name)
|
||||
|
||||
def _on_projects_refresh(self):
|
||||
initial_context = self._controller.get_initial_context()
|
||||
self._controller.set_expected_selection(
|
||||
initial_context["project_name"],
|
||||
initial_context["folder_id"]
|
||||
)
|
||||
|
||||
def _on_overlay_confirm(self):
|
||||
self.close()
|
||||
|
||||
def _on_ok_click(self):
|
||||
# Store values to output
|
||||
self._controller.confirm_selection()
|
||||
# Close dialog
|
||||
self.accept()
|
||||
|
||||
def _on_project_selection_change(self, event):
|
||||
self._on_selection_change(
|
||||
event["project_name"],
|
||||
)
|
||||
|
||||
def _on_folder_selection_change(self, event):
|
||||
self._on_selection_change(
|
||||
event["project_name"],
|
||||
event["folder_id"],
|
||||
)
|
||||
|
||||
def _on_task_selection_change(self, event):
|
||||
self._on_selection_change(
|
||||
event["project_name"],
|
||||
event["folder_id"],
|
||||
event["task_name"],
|
||||
)
|
||||
|
||||
def _on_selection_change(
|
||||
self, project_name, folder_id=None, task_name=None
|
||||
):
|
||||
self._validate_strict(project_name, folder_id, task_name)
|
||||
|
||||
def _on_init_context_change(self, event):
|
||||
self._set_init_context(event.data)
|
||||
if self._visible:
|
||||
self._controller.set_expected_selection(
|
||||
event["project_name"], event["folder_id"]
|
||||
)
|
||||
|
||||
def _set_init_context(self, init_context):
|
||||
project_name = init_context["project_name"]
|
||||
if not init_context["valid"]:
|
||||
self._overlay_widget.setVisible(True)
|
||||
self._overlay_widget.set_context(
|
||||
project_name,
|
||||
init_context["folder_label"],
|
||||
init_context["project_found"],
|
||||
init_context["folder_found"],
|
||||
init_context["tasks_found"]
|
||||
)
|
||||
return
|
||||
|
||||
self._overlay_widget.setVisible(False)
|
||||
if project_name:
|
||||
self._project_combobox.setEnabled(False)
|
||||
if init_context["folder_id"]:
|
||||
self._folders_widget.setEnabled(False)
|
||||
else:
|
||||
self._project_combobox.setEnabled(True)
|
||||
self._folders_widget.setEnabled(True)
|
||||
|
||||
def _on_strict_changed(self, event):
|
||||
self._apply_strict_changes(event["strict"])
|
||||
|
||||
def _on_controller_reset(self):
|
||||
self._apply_strict_changes(self.is_strict())
|
||||
self._project_combobox.refresh()
|
||||
|
||||
def _on_controller_refresh(self):
|
||||
self._project_combobox.refresh()
|
||||
|
||||
def _apply_strict_changes(self, is_strict):
|
||||
if not is_strict:
|
||||
if not self._ok_btn.isEnabled():
|
||||
self._ok_btn.setEnabled(True)
|
||||
return
|
||||
context = self._controller.get_selected_context()
|
||||
self._validate_strict(
|
||||
context["project_name"],
|
||||
context["folder_id"],
|
||||
context["task_name"]
|
||||
)
|
||||
|
||||
def _validate_strict(self, project_name, folder_id, task_name):
|
||||
if not self.is_strict():
|
||||
return
|
||||
|
||||
enabled = True
|
||||
if not project_name or not folder_id or not task_name:
|
||||
enabled = False
|
||||
self._ok_btn.setEnabled(enabled)
|
||||
|
||||
|
||||
def main(
|
||||
path_to_store,
|
||||
project_name=None,
|
||||
asset_name=None,
|
||||
strict=True
|
||||
):
|
||||
# Run Qt application
|
||||
app = get_openpype_qt_app()
|
||||
controller = ContextDialogController()
|
||||
controller.set_strict(strict)
|
||||
controller.set_initial_context(project_name, asset_name)
|
||||
controller.set_output_json_path(path_to_store)
|
||||
window = ContextDialog(controller=controller)
|
||||
window.show()
|
||||
app.exec_()
|
||||
|
||||
# Get result from window
|
||||
data = window.get_context()
|
||||
|
||||
# Make sure json filepath directory exists
|
||||
file_dir = os.path.dirname(path_to_store)
|
||||
if not os.path.exists(file_dir):
|
||||
os.makedirs(file_dir)
|
||||
|
||||
# Store result into json file
|
||||
with open(path_to_store, "w") as stream:
|
||||
json.dump(data, stream)
|
||||
396
openpype/tools/context_dialog/_openpype_window.py
Normal file
396
openpype/tools/context_dialog/_openpype_window.py
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype import style
|
||||
from openpype.pipeline import AvalonMongoDB
|
||||
from openpype.tools.utils.lib import center_window, get_openpype_qt_app
|
||||
from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget
|
||||
from openpype.tools.utils.constants import (
|
||||
PROJECT_NAME_ROLE
|
||||
)
|
||||
from openpype.tools.utils.tasks_widget import TasksWidget
|
||||
from openpype.tools.utils.models import (
|
||||
ProjectModel,
|
||||
ProjectSortFilterProxy
|
||||
)
|
||||
|
||||
|
||||
class ContextDialog(QtWidgets.QDialog):
|
||||
"""Dialog to select a context.
|
||||
|
||||
Context has 3 parts:
|
||||
- Project
|
||||
- Asset
|
||||
- Task
|
||||
|
||||
It is possible to predefine project and asset. In that case their widgets
|
||||
will have passed preselected values and will be disabled.
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super(ContextDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Select Context")
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
# Enable minimize and maximize for app
|
||||
window_flags = QtCore.Qt.Window
|
||||
if not parent:
|
||||
window_flags |= QtCore.Qt.WindowStaysOnTopHint
|
||||
self.setWindowFlags(window_flags)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
dbcon = AvalonMongoDB()
|
||||
|
||||
# UI initialization
|
||||
main_splitter = QtWidgets.QSplitter(self)
|
||||
|
||||
# Left side widget contains project combobox and asset widget
|
||||
left_side_widget = QtWidgets.QWidget(main_splitter)
|
||||
|
||||
project_combobox = QtWidgets.QComboBox(left_side_widget)
|
||||
# Styled delegate to propagate stylessheet
|
||||
project_delegate = QtWidgets.QStyledItemDelegate(project_combobox)
|
||||
project_combobox.setItemDelegate(project_delegate)
|
||||
# Project model with only active projects without default item
|
||||
project_model = ProjectModel(
|
||||
dbcon,
|
||||
only_active=True,
|
||||
add_default_project=False
|
||||
)
|
||||
# Sorting proxy model
|
||||
project_proxy = ProjectSortFilterProxy()
|
||||
project_proxy.setSourceModel(project_model)
|
||||
project_combobox.setModel(project_proxy)
|
||||
|
||||
# Assets widget
|
||||
assets_widget = SingleSelectAssetsWidget(
|
||||
dbcon, parent=left_side_widget
|
||||
)
|
||||
|
||||
left_side_layout = QtWidgets.QVBoxLayout(left_side_widget)
|
||||
left_side_layout.setContentsMargins(0, 0, 0, 0)
|
||||
left_side_layout.addWidget(project_combobox)
|
||||
left_side_layout.addWidget(assets_widget)
|
||||
|
||||
# Right side of window contains only tasks
|
||||
tasks_widget = TasksWidget(dbcon, main_splitter)
|
||||
|
||||
# Add widgets to main splitter
|
||||
main_splitter.addWidget(left_side_widget)
|
||||
main_splitter.addWidget(tasks_widget)
|
||||
|
||||
# Set stretch of both sides
|
||||
main_splitter.setStretchFactor(0, 7)
|
||||
main_splitter.setStretchFactor(1, 3)
|
||||
|
||||
# Add confimation button to bottom right
|
||||
ok_btn = QtWidgets.QPushButton("OK", self)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.setContentsMargins(0, 0, 0, 0)
|
||||
buttons_layout.addStretch(1)
|
||||
buttons_layout.addWidget(ok_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(main_splitter, 1)
|
||||
main_layout.addLayout(buttons_layout, 0)
|
||||
|
||||
# Timer which will trigger asset refresh
|
||||
# - this is needed because asset widget triggers
|
||||
# finished refresh before hides spin box so we need to trigger
|
||||
# refreshing in small offset if we want re-refresh asset widget
|
||||
assets_timer = QtCore.QTimer()
|
||||
assets_timer.setInterval(50)
|
||||
assets_timer.setSingleShot(True)
|
||||
|
||||
assets_timer.timeout.connect(self._on_asset_refresh_timer)
|
||||
|
||||
project_combobox.currentIndexChanged.connect(
|
||||
self._on_project_combo_change
|
||||
)
|
||||
assets_widget.selection_changed.connect(self._on_asset_change)
|
||||
assets_widget.refresh_triggered.connect(self._on_asset_refresh_trigger)
|
||||
assets_widget.refreshed.connect(self._on_asset_widget_refresh_finished)
|
||||
tasks_widget.task_changed.connect(self._on_task_change)
|
||||
ok_btn.clicked.connect(self._on_ok_click)
|
||||
|
||||
self._dbcon = dbcon
|
||||
|
||||
self._project_combobox = project_combobox
|
||||
self._project_model = project_model
|
||||
self._project_proxy = project_proxy
|
||||
self._project_delegate = project_delegate
|
||||
|
||||
self._assets_widget = assets_widget
|
||||
|
||||
self._tasks_widget = tasks_widget
|
||||
|
||||
self._ok_btn = ok_btn
|
||||
|
||||
self._strict = False
|
||||
|
||||
# Values set by `set_context` method
|
||||
self._set_context_project = None
|
||||
self._set_context_asset = None
|
||||
|
||||
# Requirements for asset widget refresh
|
||||
self._assets_timer = assets_timer
|
||||
self._rerefresh_assets = True
|
||||
self._assets_refreshing = False
|
||||
|
||||
# Set stylehseet and resize window on first show
|
||||
self._first_show = True
|
||||
|
||||
# Helper attributes for handling of refresh
|
||||
self._ignore_value_changes = False
|
||||
self._refresh_on_next_show = True
|
||||
|
||||
# Output of dialog
|
||||
self._context_to_store = {
|
||||
"project": None,
|
||||
"asset": None,
|
||||
"task": None
|
||||
}
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Ignore close event if is in strict state and context is not done."""
|
||||
if self._strict and not self._ok_btn.isEnabled():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if self._strict:
|
||||
self._confirm_values()
|
||||
super(ContextDialog, self).closeEvent(event)
|
||||
|
||||
def set_strict(self, strict):
|
||||
"""Change strictness of dialog."""
|
||||
self._strict = strict
|
||||
self._validate_strict()
|
||||
|
||||
def _set_refresh_on_next_show(self):
|
||||
"""Refresh will be called on next showEvent.
|
||||
|
||||
If window is already visible then just execute refresh.
|
||||
"""
|
||||
self._refresh_on_next_show = True
|
||||
if self.isVisible():
|
||||
self.refresh()
|
||||
|
||||
def _refresh_assets(self):
|
||||
"""Trigger refreshing of asset widget.
|
||||
|
||||
This will set mart to rerefresh asset when current refreshing is done
|
||||
or do it immidietely if asset widget is not refreshing at the time.
|
||||
"""
|
||||
if self._assets_refreshing:
|
||||
self._rerefresh_assets = True
|
||||
else:
|
||||
self._on_asset_refresh_timer()
|
||||
|
||||
def showEvent(self, event):
|
||||
"""Override show event to do some callbacks."""
|
||||
super(ContextDialog, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
# Set stylesheet and resize
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
self.resize(600, 700)
|
||||
center_window(self)
|
||||
|
||||
if self._refresh_on_next_show:
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh all widget one by one.
|
||||
|
||||
When asset refresh is triggered we have to wait when is done so
|
||||
this method continues with `_on_asset_widget_refresh_finished`.
|
||||
"""
|
||||
# Change state of refreshing (no matter how refresh was called)
|
||||
self._refresh_on_next_show = False
|
||||
|
||||
# Ignore changes of combobox and asset widget
|
||||
self._ignore_value_changes = True
|
||||
|
||||
# Get current project name to be able set it afterwards
|
||||
select_project_name = self._dbcon.Session.get("AVALON_PROJECT")
|
||||
# Trigger project refresh
|
||||
self._project_model.refresh()
|
||||
# Sort projects
|
||||
self._project_proxy.sort(0)
|
||||
|
||||
# Disable combobox if project was passed to `set_context`
|
||||
if self._set_context_project:
|
||||
select_project_name = self._set_context_project
|
||||
self._project_combobox.setEnabled(False)
|
||||
else:
|
||||
# Find new project to select
|
||||
self._project_combobox.setEnabled(True)
|
||||
if (
|
||||
select_project_name is None
|
||||
and self._project_proxy.rowCount() > 0
|
||||
):
|
||||
index = self._project_proxy.index(0, 0)
|
||||
select_project_name = index.data(PROJECT_NAME_ROLE)
|
||||
|
||||
self._ignore_value_changes = False
|
||||
|
||||
idx = self._project_combobox.findText(select_project_name)
|
||||
if idx >= 0:
|
||||
self._project_combobox.setCurrentIndex(idx)
|
||||
self._dbcon.Session["AVALON_PROJECT"] = (
|
||||
self._project_combobox.currentText()
|
||||
)
|
||||
|
||||
# Trigger asset refresh
|
||||
self._refresh_assets()
|
||||
|
||||
def _on_asset_refresh_timer(self):
|
||||
"""This is only way how to trigger refresh asset widget.
|
||||
|
||||
Use `_refresh_assets` method to refresh asset widget.
|
||||
"""
|
||||
self._assets_widget.refresh()
|
||||
|
||||
def _on_asset_widget_refresh_finished(self):
|
||||
"""Catch when asset widget finished refreshing."""
|
||||
# If should refresh again then skip all other callbacks and trigger
|
||||
# assets timer directly.
|
||||
self._assets_refreshing = False
|
||||
if self._rerefresh_assets:
|
||||
self._rerefresh_assets = False
|
||||
self._assets_timer.start()
|
||||
return
|
||||
|
||||
self._ignore_value_changes = True
|
||||
if self._set_context_asset:
|
||||
self._dbcon.Session["AVALON_ASSET"] = self._set_context_asset
|
||||
self._assets_widget.setEnabled(False)
|
||||
self._assets_widget.select_asset_by_name(self._set_context_asset)
|
||||
self._set_asset_to_tasks_widget()
|
||||
else:
|
||||
self._assets_widget.setEnabled(True)
|
||||
self._assets_widget.set_current_asset_btn_visibility(False)
|
||||
|
||||
# Refresh tasks
|
||||
self._tasks_widget.refresh()
|
||||
|
||||
self._ignore_value_changes = False
|
||||
|
||||
self._validate_strict()
|
||||
|
||||
def _on_project_combo_change(self):
|
||||
if self._ignore_value_changes:
|
||||
return
|
||||
project_name = self._project_combobox.currentText()
|
||||
|
||||
if self._dbcon.Session.get("AVALON_PROJECT") == project_name:
|
||||
return
|
||||
|
||||
self._dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
|
||||
self._refresh_assets()
|
||||
self._validate_strict()
|
||||
|
||||
def _on_asset_refresh_trigger(self):
|
||||
self._assets_refreshing = True
|
||||
self._on_asset_change()
|
||||
|
||||
def _on_asset_change(self):
|
||||
"""Selected assets have changed"""
|
||||
if self._ignore_value_changes:
|
||||
return
|
||||
self._set_asset_to_tasks_widget()
|
||||
|
||||
def _on_task_change(self):
|
||||
self._validate_strict()
|
||||
|
||||
def _set_asset_to_tasks_widget(self):
|
||||
asset_id = self._assets_widget.get_selected_asset_id()
|
||||
|
||||
self._tasks_widget.set_asset_id(asset_id)
|
||||
|
||||
def _confirm_values(self):
|
||||
"""Store values to output."""
|
||||
self._context_to_store["project"] = self.get_selected_project()
|
||||
self._context_to_store["asset"] = self.get_selected_asset()
|
||||
self._context_to_store["task"] = self.get_selected_task()
|
||||
|
||||
def _on_ok_click(self):
|
||||
# Store values to output
|
||||
self._confirm_values()
|
||||
# Close dialog
|
||||
self.accept()
|
||||
|
||||
def get_selected_project(self):
|
||||
"""Get selected project."""
|
||||
return self._project_combobox.currentText()
|
||||
|
||||
def get_selected_asset(self):
|
||||
"""Currently selected asset in asset widget."""
|
||||
return self._assets_widget.get_selected_asset_name()
|
||||
|
||||
def get_selected_task(self):
|
||||
"""Currently selected task."""
|
||||
return self._tasks_widget.get_selected_task_name()
|
||||
|
||||
def _validate_strict(self):
|
||||
if not self._strict:
|
||||
if not self._ok_btn.isEnabled():
|
||||
self._ok_btn.setEnabled(True)
|
||||
return
|
||||
|
||||
enabled = True
|
||||
if not self._set_context_project and not self.get_selected_project():
|
||||
enabled = False
|
||||
elif not self._set_context_asset and not self.get_selected_asset():
|
||||
enabled = False
|
||||
elif not self.get_selected_task():
|
||||
enabled = False
|
||||
self._ok_btn.setEnabled(enabled)
|
||||
|
||||
def set_context(self, project_name=None, asset_name=None):
|
||||
"""Set context which will be used and locked in dialog."""
|
||||
if project_name is None:
|
||||
asset_name = None
|
||||
|
||||
self._set_context_project = project_name
|
||||
self._set_context_asset = asset_name
|
||||
|
||||
self._context_to_store["project"] = project_name
|
||||
self._context_to_store["asset"] = asset_name
|
||||
|
||||
self._set_refresh_on_next_show()
|
||||
|
||||
def get_context(self):
|
||||
"""Result of dialog."""
|
||||
return self._context_to_store
|
||||
|
||||
|
||||
def main(
|
||||
path_to_store,
|
||||
project_name=None,
|
||||
asset_name=None,
|
||||
strict=True
|
||||
):
|
||||
# Run Qt application
|
||||
app = get_openpype_qt_app()
|
||||
window = ContextDialog()
|
||||
window.set_strict(strict)
|
||||
window.set_context(project_name, asset_name)
|
||||
window.show()
|
||||
app.exec_()
|
||||
|
||||
# Get result from window
|
||||
data = window.get_context()
|
||||
|
||||
# Make sure json filepath directory exists
|
||||
file_dir = os.path.dirname(path_to_store)
|
||||
if not os.path.exists(file_dir):
|
||||
os.makedirs(file_dir)
|
||||
|
||||
# Store result into json file
|
||||
with open(path_to_store, "w") as stream:
|
||||
json.dump(data, stream)
|
||||
|
|
@ -1,396 +1,12 @@
|
|||
import os
|
||||
import json
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
if AYON_SERVER_ENABLED:
|
||||
from ._ayon_window import ContextDialog, main
|
||||
else:
|
||||
from ._openpype_window import ContextDialog, main
|
||||
|
||||
from openpype import style
|
||||
from openpype.pipeline import AvalonMongoDB
|
||||
from openpype.tools.utils.lib import center_window, get_openpype_qt_app
|
||||
from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget
|
||||
from openpype.tools.utils.constants import (
|
||||
PROJECT_NAME_ROLE
|
||||
|
||||
__all__ = (
|
||||
"ContextDialog",
|
||||
"main",
|
||||
)
|
||||
from openpype.tools.utils.tasks_widget import TasksWidget
|
||||
from openpype.tools.utils.models import (
|
||||
ProjectModel,
|
||||
ProjectSortFilterProxy
|
||||
)
|
||||
|
||||
|
||||
class ContextDialog(QtWidgets.QDialog):
|
||||
"""Dialog to select a context.
|
||||
|
||||
Context has 3 parts:
|
||||
- Project
|
||||
- Aseet
|
||||
- Task
|
||||
|
||||
It is possible to predefine project and asset. In that case their widgets
|
||||
will have passed preselected values and will be disabled.
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super(ContextDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Select Context")
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
# Enable minimize and maximize for app
|
||||
window_flags = QtCore.Qt.Window
|
||||
if not parent:
|
||||
window_flags |= QtCore.Qt.WindowStaysOnTopHint
|
||||
self.setWindowFlags(window_flags)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
dbcon = AvalonMongoDB()
|
||||
|
||||
# UI initialization
|
||||
main_splitter = QtWidgets.QSplitter(self)
|
||||
|
||||
# Left side widget contains project combobox and asset widget
|
||||
left_side_widget = QtWidgets.QWidget(main_splitter)
|
||||
|
||||
project_combobox = QtWidgets.QComboBox(left_side_widget)
|
||||
# Styled delegate to propagate stylessheet
|
||||
project_delegate = QtWidgets.QStyledItemDelegate(project_combobox)
|
||||
project_combobox.setItemDelegate(project_delegate)
|
||||
# Project model with only active projects without default item
|
||||
project_model = ProjectModel(
|
||||
dbcon,
|
||||
only_active=True,
|
||||
add_default_project=False
|
||||
)
|
||||
# Sorting proxy model
|
||||
project_proxy = ProjectSortFilterProxy()
|
||||
project_proxy.setSourceModel(project_model)
|
||||
project_combobox.setModel(project_proxy)
|
||||
|
||||
# Assets widget
|
||||
assets_widget = SingleSelectAssetsWidget(
|
||||
dbcon, parent=left_side_widget
|
||||
)
|
||||
|
||||
left_side_layout = QtWidgets.QVBoxLayout(left_side_widget)
|
||||
left_side_layout.setContentsMargins(0, 0, 0, 0)
|
||||
left_side_layout.addWidget(project_combobox)
|
||||
left_side_layout.addWidget(assets_widget)
|
||||
|
||||
# Right side of window contains only tasks
|
||||
tasks_widget = TasksWidget(dbcon, main_splitter)
|
||||
|
||||
# Add widgets to main splitter
|
||||
main_splitter.addWidget(left_side_widget)
|
||||
main_splitter.addWidget(tasks_widget)
|
||||
|
||||
# Set stretch of both sides
|
||||
main_splitter.setStretchFactor(0, 7)
|
||||
main_splitter.setStretchFactor(1, 3)
|
||||
|
||||
# Add confimation button to bottom right
|
||||
ok_btn = QtWidgets.QPushButton("OK", self)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.setContentsMargins(0, 0, 0, 0)
|
||||
buttons_layout.addStretch(1)
|
||||
buttons_layout.addWidget(ok_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(main_splitter, 1)
|
||||
main_layout.addLayout(buttons_layout, 0)
|
||||
|
||||
# Timer which will trigger asset refresh
|
||||
# - this is needed because asset widget triggers
|
||||
# finished refresh before hides spin box so we need to trigger
|
||||
# refreshing in small offset if we want re-refresh asset widget
|
||||
assets_timer = QtCore.QTimer()
|
||||
assets_timer.setInterval(50)
|
||||
assets_timer.setSingleShot(True)
|
||||
|
||||
assets_timer.timeout.connect(self._on_asset_refresh_timer)
|
||||
|
||||
project_combobox.currentIndexChanged.connect(
|
||||
self._on_project_combo_change
|
||||
)
|
||||
assets_widget.selection_changed.connect(self._on_asset_change)
|
||||
assets_widget.refresh_triggered.connect(self._on_asset_refresh_trigger)
|
||||
assets_widget.refreshed.connect(self._on_asset_widget_refresh_finished)
|
||||
tasks_widget.task_changed.connect(self._on_task_change)
|
||||
ok_btn.clicked.connect(self._on_ok_click)
|
||||
|
||||
self._dbcon = dbcon
|
||||
|
||||
self._project_combobox = project_combobox
|
||||
self._project_model = project_model
|
||||
self._project_proxy = project_proxy
|
||||
self._project_delegate = project_delegate
|
||||
|
||||
self._assets_widget = assets_widget
|
||||
|
||||
self._tasks_widget = tasks_widget
|
||||
|
||||
self._ok_btn = ok_btn
|
||||
|
||||
self._strict = False
|
||||
|
||||
# Values set by `set_context` method
|
||||
self._set_context_project = None
|
||||
self._set_context_asset = None
|
||||
|
||||
# Requirements for asset widget refresh
|
||||
self._assets_timer = assets_timer
|
||||
self._rerefresh_assets = True
|
||||
self._assets_refreshing = False
|
||||
|
||||
# Set stylehseet and resize window on first show
|
||||
self._first_show = True
|
||||
|
||||
# Helper attributes for handling of refresh
|
||||
self._ignore_value_changes = False
|
||||
self._refresh_on_next_show = True
|
||||
|
||||
# Output of dialog
|
||||
self._context_to_store = {
|
||||
"project": None,
|
||||
"asset": None,
|
||||
"task": None
|
||||
}
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Ignore close event if is in strict state and context is not done."""
|
||||
if self._strict and not self._ok_btn.isEnabled():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if self._strict:
|
||||
self._confirm_values()
|
||||
super(ContextDialog, self).closeEvent(event)
|
||||
|
||||
def set_strict(self, strict):
|
||||
"""Change strictness of dialog."""
|
||||
self._strict = strict
|
||||
self._validate_strict()
|
||||
|
||||
def _set_refresh_on_next_show(self):
|
||||
"""Refresh will be called on next showEvent.
|
||||
|
||||
If window is already visible then just execute refresh.
|
||||
"""
|
||||
self._refresh_on_next_show = True
|
||||
if self.isVisible():
|
||||
self.refresh()
|
||||
|
||||
def _refresh_assets(self):
|
||||
"""Trigger refreshing of asset widget.
|
||||
|
||||
This will set mart to rerefresh asset when current refreshing is done
|
||||
or do it immidietely if asset widget is not refreshing at the time.
|
||||
"""
|
||||
if self._assets_refreshing:
|
||||
self._rerefresh_assets = True
|
||||
else:
|
||||
self._on_asset_refresh_timer()
|
||||
|
||||
def showEvent(self, event):
|
||||
"""Override show event to do some callbacks."""
|
||||
super(ContextDialog, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
# Set stylesheet and resize
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
self.resize(600, 700)
|
||||
center_window(self)
|
||||
|
||||
if self._refresh_on_next_show:
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh all widget one by one.
|
||||
|
||||
When asset refresh is triggered we have to wait when is done so
|
||||
this method continues with `_on_asset_widget_refresh_finished`.
|
||||
"""
|
||||
# Change state of refreshing (no matter how refresh was called)
|
||||
self._refresh_on_next_show = False
|
||||
|
||||
# Ignore changes of combobox and asset widget
|
||||
self._ignore_value_changes = True
|
||||
|
||||
# Get current project name to be able set it afterwards
|
||||
select_project_name = self._dbcon.Session.get("AVALON_PROJECT")
|
||||
# Trigger project refresh
|
||||
self._project_model.refresh()
|
||||
# Sort projects
|
||||
self._project_proxy.sort(0)
|
||||
|
||||
# Disable combobox if project was passed to `set_context`
|
||||
if self._set_context_project:
|
||||
select_project_name = self._set_context_project
|
||||
self._project_combobox.setEnabled(False)
|
||||
else:
|
||||
# Find new project to select
|
||||
self._project_combobox.setEnabled(True)
|
||||
if (
|
||||
select_project_name is None
|
||||
and self._project_proxy.rowCount() > 0
|
||||
):
|
||||
index = self._project_proxy.index(0, 0)
|
||||
select_project_name = index.data(PROJECT_NAME_ROLE)
|
||||
|
||||
self._ignore_value_changes = False
|
||||
|
||||
idx = self._project_combobox.findText(select_project_name)
|
||||
if idx >= 0:
|
||||
self._project_combobox.setCurrentIndex(idx)
|
||||
self._dbcon.Session["AVALON_PROJECT"] = (
|
||||
self._project_combobox.currentText()
|
||||
)
|
||||
|
||||
# Trigger asset refresh
|
||||
self._refresh_assets()
|
||||
|
||||
def _on_asset_refresh_timer(self):
|
||||
"""This is only way how to trigger refresh asset widget.
|
||||
|
||||
Use `_refresh_assets` method to refresh asset widget.
|
||||
"""
|
||||
self._assets_widget.refresh()
|
||||
|
||||
def _on_asset_widget_refresh_finished(self):
|
||||
"""Catch when asset widget finished refreshing."""
|
||||
# If should refresh again then skip all other callbacks and trigger
|
||||
# assets timer directly.
|
||||
self._assets_refreshing = False
|
||||
if self._rerefresh_assets:
|
||||
self._rerefresh_assets = False
|
||||
self._assets_timer.start()
|
||||
return
|
||||
|
||||
self._ignore_value_changes = True
|
||||
if self._set_context_asset:
|
||||
self._dbcon.Session["AVALON_ASSET"] = self._set_context_asset
|
||||
self._assets_widget.setEnabled(False)
|
||||
self._assets_widget.select_assets(self._set_context_asset)
|
||||
self._set_asset_to_tasks_widget()
|
||||
else:
|
||||
self._assets_widget.setEnabled(True)
|
||||
self._assets_widget.set_current_asset_btn_visibility(False)
|
||||
|
||||
# Refresh tasks
|
||||
self._tasks_widget.refresh()
|
||||
|
||||
self._ignore_value_changes = False
|
||||
|
||||
self._validate_strict()
|
||||
|
||||
def _on_project_combo_change(self):
|
||||
if self._ignore_value_changes:
|
||||
return
|
||||
project_name = self._project_combobox.currentText()
|
||||
|
||||
if self._dbcon.Session.get("AVALON_PROJECT") == project_name:
|
||||
return
|
||||
|
||||
self._dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
|
||||
self._refresh_assets()
|
||||
self._validate_strict()
|
||||
|
||||
def _on_asset_refresh_trigger(self):
|
||||
self._assets_refreshing = True
|
||||
self._on_asset_change()
|
||||
|
||||
def _on_asset_change(self):
|
||||
"""Selected assets have changed"""
|
||||
if self._ignore_value_changes:
|
||||
return
|
||||
self._set_asset_to_tasks_widget()
|
||||
|
||||
def _on_task_change(self):
|
||||
self._validate_strict()
|
||||
|
||||
def _set_asset_to_tasks_widget(self):
|
||||
asset_id = self._assets_widget.get_selected_asset_id()
|
||||
|
||||
self._tasks_widget.set_asset_id(asset_id)
|
||||
|
||||
def _confirm_values(self):
|
||||
"""Store values to output."""
|
||||
self._context_to_store["project"] = self.get_selected_project()
|
||||
self._context_to_store["asset"] = self.get_selected_asset()
|
||||
self._context_to_store["task"] = self.get_selected_task()
|
||||
|
||||
def _on_ok_click(self):
|
||||
# Store values to output
|
||||
self._confirm_values()
|
||||
# Close dialog
|
||||
self.accept()
|
||||
|
||||
def get_selected_project(self):
|
||||
"""Get selected project."""
|
||||
return self._project_combobox.currentText()
|
||||
|
||||
def get_selected_asset(self):
|
||||
"""Currently selected asset in asset widget."""
|
||||
return self._assets_widget.get_selected_asset_name()
|
||||
|
||||
def get_selected_task(self):
|
||||
"""Currently selected task."""
|
||||
return self._tasks_widget.get_selected_task_name()
|
||||
|
||||
def _validate_strict(self):
|
||||
if not self._strict:
|
||||
if not self._ok_btn.isEnabled():
|
||||
self._ok_btn.setEnabled(True)
|
||||
return
|
||||
|
||||
enabled = True
|
||||
if not self._set_context_project and not self.get_selected_project():
|
||||
enabled = False
|
||||
elif not self._set_context_asset and not self.get_selected_asset():
|
||||
enabled = False
|
||||
elif not self.get_selected_task():
|
||||
enabled = False
|
||||
self._ok_btn.setEnabled(enabled)
|
||||
|
||||
def set_context(self, project_name=None, asset_name=None):
|
||||
"""Set context which will be used and locked in dialog."""
|
||||
if project_name is None:
|
||||
asset_name = None
|
||||
|
||||
self._set_context_project = project_name
|
||||
self._set_context_asset = asset_name
|
||||
|
||||
self._context_to_store["project"] = project_name
|
||||
self._context_to_store["asset"] = asset_name
|
||||
|
||||
self._set_refresh_on_next_show()
|
||||
|
||||
def get_context(self):
|
||||
"""Result of dialog."""
|
||||
return self._context_to_store
|
||||
|
||||
|
||||
def main(
|
||||
path_to_store,
|
||||
project_name=None,
|
||||
asset_name=None,
|
||||
strict=True
|
||||
):
|
||||
# Run Qt application
|
||||
app = get_openpype_qt_app()
|
||||
window = ContextDialog()
|
||||
window.set_strict(strict)
|
||||
window.set_context(project_name, asset_name)
|
||||
window.show()
|
||||
app.exec_()
|
||||
|
||||
# Get result from window
|
||||
data = window.get_context()
|
||||
|
||||
# Make sure json filepath directory exists
|
||||
file_dir = os.path.dirname(path_to_store)
|
||||
if not os.path.exists(file_dir):
|
||||
os.makedirs(file_dir)
|
||||
|
||||
# Store result into json file
|
||||
with open(path_to_store, "w") as stream:
|
||||
json.dump(data, stream)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue