initial changes of window

This commit is contained in:
Jakub Trllo 2023-10-13 18:12:21 +02:00
parent edf5c52541
commit 78eebdaca1

View file

@ -1,369 +1,19 @@
import collections
from qtpy import QtWidgets, QtGui, QtCore
from openpype.style import load_stylesheet, get_app_icon_path
from openpype.tools.utils import (
PlaceholderLineEdit,
SeparatorWidget,
get_asset_icon_by_name,
set_style_property,
)
from openpype.tools.utils.views import DeselectableTreeView
from openpype.tools.ayon_push_to_project import PushToContextController
PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1
ASSET_NAME_ROLE = QtCore.Qt.UserRole + 2
ASSET_ID_ROLE = QtCore.Qt.UserRole + 3
TASK_NAME_ROLE = QtCore.Qt.UserRole + 4
TASK_TYPE_ROLE = QtCore.Qt.UserRole + 5
class ProjectsModel(QtGui.QStandardItemModel):
empty_text = "< Empty >"
select_project_text = "< Select Project >"
refreshed = QtCore.Signal()
def __init__(self, controller):
super(ProjectsModel, self).__init__()
self._controller = controller
self.event_system.add_callback(
"projects.refresh.finished", self._on_refresh_finish
)
placeholder_item = QtGui.QStandardItem(self.empty_text)
root_item = self.invisibleRootItem()
root_item.appendRows([placeholder_item])
items = {None: placeholder_item}
self._placeholder_item = placeholder_item
self._items = items
@property
def event_system(self):
return self._controller.event_system
def _on_refresh_finish(self):
root_item = self.invisibleRootItem()
project_names = self._controller.model.get_projects()
if not project_names:
placeholder_text = self.empty_text
else:
placeholder_text = self.select_project_text
self._placeholder_item.setData(placeholder_text, QtCore.Qt.DisplayRole)
new_items = []
if None not in self._items:
new_items.append(self._placeholder_item)
current_project_names = set(self._items.keys())
for project_name in current_project_names - set(project_names):
if project_name is None:
continue
item = self._items.pop(project_name)
root_item.takeRow(item.row())
for project_name in project_names:
if project_name in self._items:
continue
item = QtGui.QStandardItem(project_name)
item.setData(project_name, PROJECT_NAME_ROLE)
new_items.append(item)
if new_items:
root_item.appendRows(new_items)
self.refreshed.emit()
class ProjectProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self):
super(ProjectProxyModel, self).__init__()
self._filter_empty_projects = False
def set_filter_empty_project(self, filter_empty_projects):
if filter_empty_projects == self._filter_empty_projects:
return
self._filter_empty_projects = filter_empty_projects
self.invalidate()
def filterAcceptsRow(self, row, parent):
if not self._filter_empty_projects:
return True
model = self.sourceModel()
source_index = model.index(row, self.filterKeyColumn(), parent)
if model.data(source_index, PROJECT_NAME_ROLE) is None:
return False
return True
class AssetsModel(QtGui.QStandardItemModel):
items_changed = QtCore.Signal()
empty_text = "< Empty >"
def __init__(self, controller):
super(AssetsModel, self).__init__()
self._controller = controller
placeholder_item = QtGui.QStandardItem(self.empty_text)
placeholder_item.setFlags(QtCore.Qt.ItemIsEnabled)
root_item = self.invisibleRootItem()
root_item.appendRows([placeholder_item])
self.event_system.add_callback(
"project.changed", self._on_project_change
)
self.event_system.add_callback(
"assets.refresh.started", self._on_refresh_start
)
self.event_system.add_callback(
"assets.refresh.finished", self._on_refresh_finish
)
self._items = {None: placeholder_item}
self._placeholder_item = placeholder_item
self._last_project = None
@property
def event_system(self):
return self._controller.event_system
def _clear(self):
placeholder_in = False
root_item = self.invisibleRootItem()
for row in reversed(range(root_item.rowCount())):
item = root_item.child(row)
asset_id = item.data(ASSET_ID_ROLE)
if asset_id is None:
placeholder_in = True
continue
root_item.removeRow(item.row())
for key in tuple(self._items.keys()):
if key is not None:
self._items.pop(key)
if not placeholder_in:
root_item.appendRows([self._placeholder_item])
self._items[None] = self._placeholder_item
def _on_project_change(self, event):
project_name = event["project_name"]
if project_name == self._last_project:
return
self._last_project = project_name
self._clear()
self.items_changed.emit()
def _on_refresh_start(self, event):
pass
def _on_refresh_finish(self, event):
event_project_name = event["project_name"]
project_name = self._controller.selection_model.project_name
if event_project_name != project_name:
return
self._last_project = event["project_name"]
if project_name is None:
if None not in self._items:
self._clear()
self.items_changed.emit()
return
asset_items_by_id = self._controller.model.get_assets(project_name)
if not asset_items_by_id:
self._clear()
self.items_changed.emit()
return
assets_by_parent_id = collections.defaultdict(list)
for asset_item in asset_items_by_id.values():
assets_by_parent_id[asset_item.parent_id].append(asset_item)
root_item = self.invisibleRootItem()
if None in self._items:
self._items.pop(None)
root_item.takeRow(self._placeholder_item.row())
items_to_remove = set(self._items) - set(asset_items_by_id.keys())
hierarchy_queue = collections.deque()
hierarchy_queue.append((None, root_item))
while hierarchy_queue:
parent_id, parent_item = hierarchy_queue.popleft()
new_items = []
for asset_item in assets_by_parent_id[parent_id]:
item = self._items.get(asset_item.id)
if item is None:
item = QtGui.QStandardItem()
item.setFlags(
QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsEnabled
)
new_items.append(item)
self._items[asset_item.id] = item
elif item.parent() is not parent_item:
new_items.append(item)
icon = get_asset_icon_by_name(
asset_item.icon_name, asset_item.icon_color
)
item.setData(asset_item.name, QtCore.Qt.DisplayRole)
item.setData(icon, QtCore.Qt.DecorationRole)
item.setData(asset_item.id, ASSET_ID_ROLE)
hierarchy_queue.append((asset_item.id, item))
if new_items:
parent_item.appendRows(new_items)
for item_id in items_to_remove:
item = self._items.pop(item_id, None)
if item is None:
continue
row = item.row()
if row < 0:
continue
parent = item.parent()
if parent is None:
parent = root_item
parent.takeRow(row)
self.items_changed.emit()
class TasksModel(QtGui.QStandardItemModel):
items_changed = QtCore.Signal()
empty_text = "< Empty >"
def __init__(self, controller):
super(TasksModel, self).__init__()
self._controller = controller
placeholder_item = QtGui.QStandardItem(self.empty_text)
placeholder_item.setFlags(QtCore.Qt.ItemIsEnabled)
root_item = self.invisibleRootItem()
root_item.appendRows([placeholder_item])
self.event_system.add_callback(
"project.changed", self._on_project_change
)
self.event_system.add_callback(
"assets.refresh.finished", self._on_asset_refresh_finish
)
self.event_system.add_callback(
"asset.changed", self._on_asset_change
)
self._items = {None: placeholder_item}
self._placeholder_item = placeholder_item
self._last_project = None
@property
def event_system(self):
return self._controller.event_system
def _clear(self):
placeholder_in = False
root_item = self.invisibleRootItem()
for row in reversed(range(root_item.rowCount())):
item = root_item.child(row)
task_name = item.data(TASK_NAME_ROLE)
if task_name is None:
placeholder_in = True
continue
root_item.removeRow(item.row())
for key in tuple(self._items.keys()):
if key is not None:
self._items.pop(key)
if not placeholder_in:
root_item.appendRows([self._placeholder_item])
self._items[None] = self._placeholder_item
def _on_project_change(self, event):
project_name = event["project_name"]
if project_name == self._last_project:
return
self._last_project = project_name
self._clear()
self.items_changed.emit()
def _on_asset_refresh_finish(self, event):
self._refresh(event["project_name"])
def _on_asset_change(self, event):
self._refresh(event["project_name"])
def _refresh(self, new_project_name):
project_name = self._controller.selection_model.project_name
if new_project_name != project_name:
return
self._last_project = project_name
if project_name is None:
if None not in self._items:
self._clear()
self.items_changed.emit()
return
asset_id = self._controller.selection_model.asset_id
task_items = self._controller.model.get_tasks(
project_name, asset_id
)
if not task_items:
self._clear()
self.items_changed.emit()
return
root_item = self.invisibleRootItem()
if None in self._items:
self._items.pop(None)
root_item.takeRow(self._placeholder_item.row())
new_items = []
task_names = set()
for task_item in task_items:
task_name = task_item.name
item = self._items.get(task_name)
if item is None:
item = QtGui.QStandardItem()
item.setFlags(
QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsEnabled
)
new_items.append(item)
self._items[task_name] = item
item.setData(task_name, QtCore.Qt.DisplayRole)
item.setData(task_name, TASK_NAME_ROLE)
item.setData(task_item.task_type, TASK_TYPE_ROLE)
if new_items:
root_item.appendRows(new_items)
items_to_remove = set(self._items) - task_names
for item_id in items_to_remove:
item = self._items.pop(item_id, None)
if item is None:
continue
parent = item.parent()
if parent is not None:
parent.removeRow(item.row())
self.items_changed.emit()
from openpype.tools.ayon_utils.widgets import (
ProjectsCombobox,
FoldersWidget,
TasksWidget,
)
from openpype.tools.ayon_push_to_project.control import (
PushToContextController,
)
class PushToContextSelectWindow(QtWidgets.QWidget):
@ -380,7 +30,10 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
header_widget = QtWidgets.QWidget(main_context_widget)
header_label = QtWidgets.QLabel(controller.src_label, header_widget)
header_label = QtWidgets.QLabel(
controller.get_source_label(),
header_widget
)
header_layout = QtWidgets.QHBoxLayout(header_widget)
header_layout.setContentsMargins(0, 0, 0, 0)
@ -392,48 +45,32 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
context_widget = QtWidgets.QWidget(main_splitter)
project_combobox = QtWidgets.QComboBox(context_widget)
project_model = ProjectsModel(controller)
project_proxy = ProjectProxyModel()
project_proxy.setSourceModel(project_model)
project_proxy.setDynamicSortFilter(True)
project_delegate = QtWidgets.QStyledItemDelegate()
project_combobox.setItemDelegate(project_delegate)
project_combobox.setModel(project_proxy)
projects_combobox = ProjectsCombobox(controller, context_widget)
projects_combobox.set_select_item_visible(True)
projects_combobox.set_standard_filter_enabled(True)
asset_task_splitter = QtWidgets.QSplitter(
context_splitter = QtWidgets.QSplitter(
QtCore.Qt.Vertical, context_widget
)
asset_view = DeselectableTreeView(asset_task_splitter)
asset_view.setHeaderHidden(True)
asset_model = AssetsModel(controller)
asset_proxy = QtCore.QSortFilterProxyModel()
asset_proxy.setSourceModel(asset_model)
asset_proxy.setDynamicSortFilter(True)
asset_view.setModel(asset_proxy)
folders_widget = FoldersWidget(controller, context_splitter)
folders_widget.set_deselectable(True)
tasks_widget = TasksWidget(controller, context_splitter)
task_view = QtWidgets.QListView(asset_task_splitter)
task_proxy = QtCore.QSortFilterProxyModel()
task_model = TasksModel(controller)
task_proxy.setSourceModel(task_model)
task_proxy.setDynamicSortFilter(True)
task_view.setModel(task_proxy)
asset_task_splitter.addWidget(asset_view)
asset_task_splitter.addWidget(task_view)
context_splitter.addWidget(folders_widget)
context_splitter.addWidget(tasks_widget)
context_layout = QtWidgets.QVBoxLayout(context_widget)
context_layout.setContentsMargins(0, 0, 0, 0)
context_layout.addWidget(project_combobox, 0)
context_layout.addWidget(asset_task_splitter, 1)
context_layout.addWidget(projects_combobox, 0)
context_layout.addWidget(context_splitter, 1)
# --- Inputs widget ---
inputs_widget = QtWidgets.QWidget(main_splitter)
asset_name_input = PlaceholderLineEdit(inputs_widget)
asset_name_input.setPlaceholderText("< Name of new asset >")
asset_name_input.setObjectName("ValidatedLineEdit")
folder_name_input = PlaceholderLineEdit(inputs_widget)
folder_name_input.setPlaceholderText("< Name of new folder >")
folder_name_input.setObjectName("ValidatedLineEdit")
variant_input = PlaceholderLineEdit(inputs_widget)
variant_input.setPlaceholderText("< Variant >")
@ -444,7 +81,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
inputs_layout = QtWidgets.QFormLayout(inputs_widget)
inputs_layout.setContentsMargins(0, 0, 0, 0)
inputs_layout.addRow("New asset name", asset_name_input)
inputs_layout.addRow("New folder name", folder_name_input)
inputs_layout.addRow("Variant", variant_input)
inputs_layout.addRow("Comment", comment_input)
@ -509,7 +146,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
main_layout.setCurrentWidget(main_context_widget)
show_timer = QtCore.QTimer()
show_timer.setInterval(1)
show_timer.setInterval(0)
main_thread_timer = QtCore.QTimer()
main_thread_timer.setInterval(10)
@ -521,46 +158,38 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
main_thread_timer.timeout.connect(self._on_main_thread_timer)
show_timer.timeout.connect(self._on_show_timer)
user_input_changed_timer.timeout.connect(self._on_user_input_timer)
asset_name_input.textChanged.connect(self._on_new_asset_change)
folder_name_input.textChanged.connect(self._on_new_asset_change)
variant_input.textChanged.connect(self._on_variant_change)
comment_input.textChanged.connect(self._on_comment_change)
project_model.refreshed.connect(self._on_projects_refresh)
project_combobox.currentIndexChanged.connect(self._on_project_change)
asset_view.selectionModel().selectionChanged.connect(
self._on_asset_change
)
asset_model.items_changed.connect(self._on_asset_model_change)
task_view.selectionModel().selectionChanged.connect(
self._on_task_change
)
task_model.items_changed.connect(self._on_task_model_change)
publish_btn.clicked.connect(self._on_select_click)
cancel_btn.clicked.connect(self._on_close_click)
overlay_close_btn.clicked.connect(self._on_close_click)
overlay_try_btn.clicked.connect(self._on_try_again_click)
controller.event_system.add_callback(
"new_asset_name.changed", self._on_controller_new_asset_change
controller.register_event_callback(
"new_folder_name.changed",
self._on_controller_new_asset_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"variant.changed", self._on_controller_variant_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"comment.changed", self._on_controller_comment_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"submission.enabled.changed", self._on_submission_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"source.changed", self._on_controller_source_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"submit.started", self._on_controller_submit_start
)
controller.event_system.add_callback(
controller.register_event_callback(
"submit.finished", self._on_controller_submit_end
)
controller.event_system.add_callback(
controller.register_event_callback(
"push.message.added", self._on_push_message
)
@ -571,20 +200,12 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
self._header_label = header_label
self._main_splitter = main_splitter
self._project_combobox = project_combobox
self._project_model = project_model
self._project_proxy = project_proxy
self._project_delegate = project_delegate
self._asset_view = asset_view
self._asset_model = asset_model
self._asset_proxy_model = asset_proxy
self._task_view = task_view
self._task_proxy_model = task_proxy
self._projects_combobox = projects_combobox
self._folders_widget = folders_widget
self._tasks_widget = tasks_widget
self._variant_input = variant_input
self._asset_name_input = asset_name_input
self._folder_name_input = folder_name_input
self._comment_input = comment_input
self._publish_btn = publish_btn
@ -600,60 +221,78 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
# The goal is to have controll over changes happened during user change
# in UI and controller auto-changes
self._variant_input_text = None
self._new_asset_name_input_text = None
self._new_folder_name_input_text = None
self._comment_input_text = None
self._show_timer = show_timer
self._show_counter = 2
self._first_show = True
self._show_timer = show_timer
self._show_counter = 0
self._main_thread_timer = main_thread_timer
self._main_thread_timer_can_stop = True
self._last_submit_message = None
self._process_item = None
self._variant_is_valid = None
self._folder_is_valid = None
publish_btn.setEnabled(False)
overlay_close_btn.setVisible(False)
overlay_try_btn.setVisible(False)
if controller.user_values.new_asset_name:
asset_name_input.setText(controller.user_values.new_asset_name)
if controller.user_values.variant:
variant_input.setText(controller.user_values.variant)
self._invalidate_variant()
self._invalidate_new_asset_name()
# Support of public api function of controller
def set_source(self, project_name, version_id):
"""Set source project and version.
@property
def controller(self):
return self._controller
Call the method on controller.
Args:
project_name (Union[str, None]): Name of project.
version_id (Union[str, None]): Version id.
"""
self._controller.set_source(project_name, version_id)
def showEvent(self, event):
super(PushToContextSelectWindow, self).showEvent(event)
if self._first_show:
self._first_show = False
self.setStyleSheet(load_stylesheet())
self._invalidate_variant()
self._show_timer.start()
self._on_first_show()
def refresh(self):
user_values = self._controller.get_user_values()
new_folder_name = user_values["new_folder_name"]
variant = user_values["variant"]
self._folder_name_input.setText(new_folder_name or "")
self._variant_input.setText(variant or "")
self._invalidate_variant(user_values["is_variant_valid"])
self._invalidate_new_folder_name(
new_folder_name, user_values["is_new_folder_name_valid"]
)
self._projects_combobox.refresh()
def _on_first_show(self):
width = 740
height = 640
inputs_width = 360
self.setStyleSheet(load_stylesheet())
self.resize(width, height)
self._main_splitter.setSizes([width - inputs_width, inputs_width])
self._show_timer.start()
def _on_show_timer(self):
if self._show_counter == 0:
self._show_timer.stop()
if self._show_counter < 3:
self._show_counter += 1
return
self._show_timer.stop()
self._show_counter -= 1
if self._show_counter == 1:
width = 740
height = 640
inputs_width = 360
self.resize(width, height)
self._main_splitter.setSizes([width - inputs_width, inputs_width])
self._show_counter = 0
if self._show_counter > 0:
return
self._controller.model.refresh_projects()
self.refresh()
def _on_new_asset_change(self, text):
self._new_asset_name_input_text = text
self._new_folder_name_input_text = text
self._user_input_changed_timer.start()
def _on_variant_change(self, text):
@ -665,42 +304,41 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
self._user_input_changed_timer.start()
def _on_user_input_timer(self):
asset_name = self._new_asset_name_input_text
if asset_name is not None:
self._new_asset_name_input_text = None
self._controller.user_values.set_new_asset(asset_name)
folder_name = self._new_folder_name_input_text
if folder_name is not None:
self._new_folder_name_input_text = None
self._controller.set_user_value_folder_name(folder_name)
variant = self._variant_input_text
if variant is not None:
self._variant_input_text = None
self._controller.user_values.set_variant(variant)
self._controller.set_user_value_variant(variant)
comment = self._comment_input_text
if comment is not None:
self._comment_input_text = None
self._controller.user_values.set_comment(comment)
self._controller.set_user_value_comment(comment)
def _on_controller_new_asset_change(self, event):
asset_name = event["changes"]["new_asset_name"]["new"]
folder_name = event["new_folder_name"]
if (
self._new_asset_name_input_text is None
and asset_name != self._asset_name_input.text()
self._new_folder_name_input_text is None
and folder_name != self._folder_name_input.text()
):
self._asset_name_input.setText(asset_name)
self._folder_name_input.setText(folder_name)
self._invalidate_new_asset_name()
self._invalidate_new_folder_name(folder_name, event["is_valid"])
def _on_controller_variant_change(self, event):
is_valid_changes = event["changes"]["is_valid"]
variant = event["changes"]["variant"]["new"]
is_valid = event["is_valid"]
variant = event["variant"]
if (
self._variant_input_text is None
and variant != self._variant_input.text()
):
self._variant_input.setText(variant)
if is_valid_changes["old"] != is_valid_changes["new"]:
self._invalidate_variant()
self._invalidate_variant(is_valid)
def _on_controller_comment_change(self, event):
comment = event["comment"]
@ -711,66 +349,30 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
self._comment_input.setText(comment)
def _on_controller_source_change(self):
self._header_label.setText(self._controller.src_label)
self._header_label.setText(self._controller.get_source_label())
def _invalidate_new_asset_name(self):
asset_name = self._controller.user_values.new_asset_name
self._task_view.setVisible(not asset_name)
valid = None
if asset_name:
valid = self._controller.user_values.is_new_asset_name_valid
state = ""
if valid is True:
state = "valid"
elif valid is False:
state = "invalid"
set_style_property(self._asset_name_input, "state", state)
def _invalidate_variant(self):
valid = self._controller.user_values.is_variant_valid
state = "invalid"
if valid is True:
state = "valid"
set_style_property(self._variant_input, "state", state)
def _on_projects_refresh(self):
self._project_proxy.sort(0, QtCore.Qt.AscendingOrder)
def _on_project_change(self):
idx = self._project_combobox.currentIndex()
if idx < 0:
self._project_proxy.set_filter_empty_project(False)
def _invalidate_new_folder_name(self, folder_name, is_valid):
print(folder_name)
self._tasks_widget.setVisible(not folder_name)
if self._folder_is_valid is is_valid:
return
self._folder_is_valid = is_valid
state = ""
if folder_name:
if is_valid is True:
state = "valid"
elif is_valid is False:
state = "invalid"
set_style_property(
self._folder_name_input, "state", state
)
project_name = self._project_combobox.itemData(idx, PROJECT_NAME_ROLE)
self._project_proxy.set_filter_empty_project(project_name is not None)
self._controller.selection_model.select_project(project_name)
def _on_asset_change(self):
indexes = self._asset_view.selectedIndexes()
index = next(iter(indexes), None)
asset_id = None
if index is not None:
model = self._asset_view.model()
asset_id = model.data(index, ASSET_ID_ROLE)
self._controller.selection_model.select_asset(asset_id)
def _on_asset_model_change(self):
self._asset_proxy_model.sort(0, QtCore.Qt.AscendingOrder)
def _on_task_model_change(self):
self._task_proxy_model.sort(0, QtCore.Qt.AscendingOrder)
def _on_task_change(self):
indexes = self._task_view.selectedIndexes()
index = next(iter(indexes), None)
task_name = None
if index is not None:
model = self._task_view.model()
task_name = model.data(index, TASK_NAME_ROLE)
self._controller.selection_model.select_task(task_name)
def _invalidate_variant(self, is_valid):
if self._variant_is_valid is is_valid:
return
self._variant_is_valid = is_valid
state = "valid" if is_valid else "invalid"
set_style_property(self._variant_input, "state", state)
def _on_submission_change(self, event):
self._publish_btn.setEnabled(event["enabled"])