mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
* set 'QT_SCALE_FACTOR_ROUNDING_POLICY' to 'PassThrough' * implemented 'get_openpype_qt_app' which set all openpype related attributes * implemented get app functions in igniter and ayon common * removed env varaibles 'QT_SCALE_FACTOR_ROUNDING_POLICY' * formatting fixes * fix line length * fix args
396 lines
13 KiB
Python
396 lines
13 KiB
Python
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
|
|
- 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)
|