From 889953af3166bbc5fa2798071c53bfa7667bc644 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:18:33 +0100 Subject: [PATCH 01/20] tasks widget created model and proxy model with methods that can be overriden --- openpype/tools/utils/tasks_widget.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 419e77c780..699b1cf569 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -194,6 +194,8 @@ class TasksWidget(QtWidgets.QWidget): task_changed = QtCore.Signal() def __init__(self, dbcon, parent=None): + self._dbcon = dbcon + super(TasksWidget, self).__init__(parent) tasks_view = DeselectableTreeView(self) @@ -204,9 +206,8 @@ class TasksWidget(QtWidgets.QWidget): header_view = tasks_view.header() header_view.setSortIndicator(0, QtCore.Qt.AscendingOrder) - tasks_model = TasksModel(dbcon) - tasks_proxy = TasksProxyModel() - tasks_proxy.setSourceModel(tasks_model) + tasks_model = self._create_source_model() + tasks_proxy = self._create_proxy_model(tasks_model) tasks_view.setModel(tasks_proxy) layout = QtWidgets.QVBoxLayout(self) @@ -222,6 +223,14 @@ class TasksWidget(QtWidgets.QWidget): self._last_selected_task_name = None + def _create_source_model(self): + return TasksModel(self._dbcon) + + def _create_proxy_model(self, source_model): + proxy = TasksProxyModel() + proxy.setSourceModel(source_model) + return proxy + def refresh(self): self._tasks_model.refresh() From 33ae30d6ab25afa294ebd32aa4097bba03e8878e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:19:12 +0100 Subject: [PATCH 02/20] model and proxy model in assets widget is created in methods which may be overriden --- openpype/tools/utils/assets_widget.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index f310aafe89..1cb803c68a 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -582,11 +582,8 @@ class AssetsWidget(QtWidgets.QWidget): self.dbcon = dbcon # Tree View - model = AssetModel(dbcon=self.dbcon, parent=self) - proxy = RecursiveSortFilterProxyModel() - proxy.setSourceModel(model) - proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + model = self._create_source_model() + proxy = self._create_proxy_model(model) view = AssetsView(self) view.setModel(proxy) @@ -628,7 +625,6 @@ class AssetsWidget(QtWidgets.QWidget): selection_model.selectionChanged.connect(self._on_selection_change) refresh_btn.clicked.connect(self.refresh) current_asset_btn.clicked.connect(self.set_current_session_asset) - model.refreshed.connect(self._on_model_refresh) view.doubleClicked.connect(self.double_clicked) self._current_asset_btn = current_asset_btn @@ -639,6 +635,18 @@ class AssetsWidget(QtWidgets.QWidget): self.model_selection = {} + def _create_source_model(self): + model = AssetModel(dbcon=self.dbcon, parent=self) + model.refreshed.connect(self._on_model_refresh) + return model + + def _create_proxy_model(self, source_model): + proxy = RecursiveSortFilterProxyModel() + proxy.setSourceModel(source_model) + proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) + proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + return proxy + @property def refreshing(self): return self._model.refreshing From 4525cf1be4f48e81c51c8aa33434a7e8762a34de Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:19:51 +0100 Subject: [PATCH 03/20] filling of assets model is done in separated method --- openpype/tools/utils/assets_widget.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 1cb803c68a..1ae560bd2b 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -401,11 +401,18 @@ class AssetModel(QtGui.QStandardItemModel): self._clear_items() return + self._fill_assets(self._doc_payload) + + self.refreshed.emit(bool(self._items_by_asset_id)) + + self._stop_fetch_thread() + + def _fill_assets(self, asset_docs): # Collect asset documents as needed asset_ids = set() asset_docs_by_id = {} asset_ids_by_parents = collections.defaultdict(set) - for asset_doc in self._doc_payload: + for asset_doc in asset_docs: asset_id = asset_doc["_id"] asset_data = asset_doc.get("data") or {} parent_id = asset_data.get("visualParent") @@ -511,10 +518,6 @@ class AssetModel(QtGui.QStandardItemModel): except Exception: pass - self.refreshed.emit(bool(self._items_by_asset_id)) - - self._stop_fetch_thread() - def _threaded_fetch(self): asset_docs = self._fetch_asset_docs() if not self._refreshing: From c19f216128f7214735cf9f864e763e1651abb5fc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:20:17 +0100 Subject: [PATCH 04/20] assets model cares on it's own if should be cleared on refresh --- openpype/tools/utils/assets_widget.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 1ae560bd2b..9789078cb8 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -306,6 +306,8 @@ class AssetModel(QtGui.QStandardItemModel): self._items_with_color_by_id = {} self._items_by_asset_id = {} + self._last_project_name = None + @property def refreshing(self): return self._refreshing @@ -347,12 +349,11 @@ class AssetModel(QtGui.QStandardItemModel): return self.get_indexes_by_asset_ids(asset_ids) - def refresh(self, force=False, clear=False): + def refresh(self, force=False): """Refresh the data for the model. Args: force (bool): Stop currently running refresh start new refresh. - clear (bool): Clear model before refresh thread starts. """ # Skip fetch if there is already other thread fetching documents if self._refreshing: @@ -360,7 +361,13 @@ class AssetModel(QtGui.QStandardItemModel): return self.stop_refresh() - if clear: + project_name = self.dbcon.Session.get("AVALON_PROJECT") + clear_model = False + if project_name != self._last_project_name: + clear_model = True + self._last_project_name = project_name + + if clear_model: self._clear_items() # Fetch documents from mongo @@ -655,12 +662,7 @@ class AssetsWidget(QtWidgets.QWidget): return self._model.refreshing def refresh(self): - project_name = self.dbcon.Session.get("AVALON_PROJECT") - clear_model = False - if project_name != self._last_project_name: - clear_model = True - self._last_project_name = project_name - self._refresh_model(clear_model) + self._refresh_model() def stop_refresh(self): self._model.stop_refresh() @@ -706,14 +708,14 @@ class AssetsWidget(QtWidgets.QWidget): self._set_loading_state(loading=False, empty=not has_item) self.refreshed.emit() - def _refresh_model(self, clear=False): + def _refresh_model(self): # Store selection self._set_loading_state(loading=True, empty=True) # Trigger signal before refresh is called self.refresh_triggered.emit() # Refresh model - self._model.refresh(clear=clear) + self._model.refresh() def _set_loading_state(self, loading, empty): self._view.set_loading_state(loading, empty) From 06396fa038e57eb51280c1e33d58ef065e61f557 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:42:55 +0100 Subject: [PATCH 05/20] Create model for whole launcher tool --- openpype/tools/launcher/models.py | 305 +++++++++++++++++++++++++++++- 1 file changed, 303 insertions(+), 2 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 427475cb4b..d1927e2667 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -3,6 +3,10 @@ import copy import logging import collections +from Qt import QtCore, QtGui +from openpype.lib import ApplicationManager +from openpype.tools.utils.lib import DynamicQThread + from . import lib from .constants import ( ACTION_ROLE, @@ -11,10 +15,8 @@ from .constants import ( ACTION_ID_ROLE ) from .actions import ApplicationAction -from Qt import QtCore, QtGui from avalon.vendor import qtawesome from avalon import style, api -from openpype.lib import ApplicationManager log = logging.getLogger(__name__) @@ -223,6 +225,305 @@ class ActionModel(QtGui.QStandardItemModel): ) +class LauncherModel(QtCore.QObject): + # Refresh interval of projects + refresh_interval = 10000 + + # Signals + # Current project has changed + project_changed = QtCore.Signal(str) + # Filters has changed (any) + filters_changed = QtCore.Signal() + + # Projects were refreshed + projects_refreshed = QtCore.Signal() + + # Signals ONLY for assets model! + # - other objects should listen to asset model signals + # Asset refresh started + assets_refresh_started = QtCore.Signal() + # Assets refresh finished + assets_refreshed = QtCore.Signal() + + # Refresh timer timeout + # - give ability to tell parent window that this timer still runs + timer_timeout = QtCore.Signal() + + # Duplication from AssetsModel with "data.tasks" + _asset_projection = { + "name": 1, + "parent": 1, + "data.visualParent": 1, + "data.label": 1, + "data.icon": 1, + "data.color": 1, + "data.tasks": 1 + } + + def __init__(self, dbcon): + super(LauncherModel, self).__init__() + # Refresh timer + # - should affect only projects + refresh_timer = QtCore.QTimer() + refresh_timer.setInterval(self.refresh_interval) + refresh_timer.timeout.connect(self._on_timeout) + + self._refresh_timer = refresh_timer + + # Launcher is active + self._active = False + + # Global data + self._dbcon = dbcon + # Available project names + self._project_names = set() + + # Context data + self._asset_docs = [] + self._asset_docs_by_id = {} + self._asset_filter_data_by_id = {} + self._assignees = set() + self._task_types = set() + + # Filters + self._asset_name_filter = "" + self._assignee_filters = set() + self._task_type_filters = set() + + # Last project for which were assets queried + self._last_project_name = None + # Asset refresh thread is running + self._refreshing_assets = False + # Asset refresh thread + self._asset_refresh_thread = None + + def _on_timeout(self): + """Refresh timer timeout.""" + if self._active: + self.timer_timeout.emit() + self.refresh_projects() + + def set_active(self, active): + """Window change active state.""" + self._active = active + + def start_refresh_timer(self, trigger=False): + """Start refresh timer.""" + self._refresh_timer.start() + if trigger: + self._on_timeout() + + def stop_refresh_timer(self): + """Stop refresh timer.""" + self._refresh_timer.stop() + + @property + def project_name(self): + """Current project name.""" + return self._dbcon.Session.get("AVALON_PROJECT") + + @property + def refreshing_assets(self): + """Refreshing thread is running.""" + return self._refreshing_assets + + @property + def asset_docs(self): + """Access to asset docs.""" + return self._asset_docs + + @property + def project_names(self): + """Available project names.""" + return self._project_names + + @property + def asset_filter_data_by_id(self): + """Prepared filter data by asset id.""" + return self._asset_filter_data_by_id + + @property + def assignees(self): + """All assignees for all assets in current project.""" + return self._assignees + + @property + def task_types(self): + """All task types for all assets in current project. + + TODO: This could be maybe taken from project document where are all + task types... + """ + return self._task_types + + @property + def task_type_filters(self): + """Currently set task type filters.""" + return self._task_type_filters + + @property + def assignee_filters(self): + """Currently set assignee filters.""" + return self._assignee_filters + + @property + def asset_name_filter(self): + """Asset name filter (can be used as regex filter).""" + return self._asset_name_filter + + def get_asset_doc(self, asset_id): + """Get single asset document by id.""" + return self._asset_docs_by_id.get(asset_id) + + def set_project_name(self, project_name): + """Change project name and refresh asset documents.""" + if project_name == self.project_name: + return + self._dbcon.Session["AVALON_PROJECT"] = project_name + self.project_changed.emit(project_name) + + self.refresh_assets(force=True) + + def refresh(self): + """Trigger refresh of whole model.""" + self.refresh_projects() + self.refresh_assets(force=False) + + def refresh_projects(self): + """Refresh projects.""" + current_project = self.project_name + project_names = set() + for project_doc in self._dbcon.projects(only_active=True): + project_names.add(project_doc["name"]) + + self._project_names = project_names + self.projects_refreshed.emit() + if ( + current_project is not None + and current_project not in project_names + ): + self.set_project_name(None) + + def _set_asset_docs(self, asset_docs=None): + """Set asset documents and all related data. + + Method extract and prepare data needed for assets and tasks widget and + prepare filtering data. + """ + if asset_docs is None: + asset_docs = [] + + all_task_types = set() + all_assignees = set() + asset_docs_by_id = {} + asset_filter_data_by_id = {} + for asset_doc in asset_docs: + task_types = set() + assignees = set() + asset_id = asset_doc["_id"] + asset_docs_by_id[asset_id] = asset_doc + asset_tasks = asset_doc.get("data", {}).get("tasks") + asset_filter_data_by_id[asset_id] = { + "assignees": assignees, + "task_types": task_types + } + if not asset_tasks: + continue + + for task_data in asset_tasks.values(): + task_assignees = set() + _task_assignees = task_data.get("assignees") + if _task_assignees: + for assignee in _task_assignees: + task_assignees.add(assignee["username"]) + + task_type = task_data.get("type") + if task_assignees: + assignees |= set(task_assignees) + if task_type: + task_types.add(task_type) + + all_task_types |= task_types + all_assignees |= assignees + + self._asset_docs_by_id = asset_docs_by_id + self._asset_docs = asset_docs + self._asset_filter_data_by_id = asset_filter_data_by_id + self._assignees = all_assignees + self._task_types = all_task_types + + self.assets_refreshed.emit() + + def set_task_type_filter(self, task_types): + """Change task type filter. + + Args: + task_types (set): Set of task types that should be visible. + Pass empty set to turn filter off. + """ + self._task_type_filters = task_types + self.filters_changed.emit() + + def set_assignee_filter(self, assignees): + """Change assignees filter. + + Args: + assignees (set): Set of assignees that should be visible. + Pass empty set to turn filter off. + """ + self._assignee_filters = assignees + self.filters_changed.emit() + + def set_asset_name_filter(self, text_filter): + """Change asset name filter. + + Args: + text_filter (str): Asset name filter. Pass empty string to + turn filter off. + """ + self._asset_name_filter = text_filter + self.filters_changed.emit() + + def refresh_assets(self, force=True): + """Refresh assets.""" + self.assets_refresh_started.emit() + + if self.project_name is None: + self._set_asset_docs() + return + + if ( + not force + and self._last_project_name == self.project_name + ): + return + + self._stop_fetch_thread() + + self._refreshing_assets = True + self._last_project_name = self.project_name + self._asset_refresh_thread = DynamicQThread(self._refresh_assets) + self._asset_refresh_thread.start() + + def _stop_fetch_thread(self): + self._refreshing_assets = False + if self._asset_refresh_thread is not None: + while self._asset_refresh_thread.isRunning(): + time.sleep(0.01) + self._asset_refresh_thread = None + + def _refresh_assets(self): + asset_docs = list(self._dbcon.find( + {"type": "asset"}, + self._asset_projection + )) + time.sleep(5) + if not self._refreshing_assets: + return + self._refreshing_assets = False + self._set_asset_docs(asset_docs) + + class ProjectModel(QtGui.QStandardItemModel): """List of projects""" From 557de8c3aa51858e40145bbf9ea4b9e14d0da687 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:43:40 +0100 Subject: [PATCH 06/20] create slightly modified task model --- openpype/tools/launcher/models.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index d1927e2667..1c61bd5012 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -4,8 +4,13 @@ import logging import collections from Qt import QtCore, QtGui +from avalon.vendor import qtawesome +from avalon import style, api from openpype.lib import ApplicationManager from openpype.tools.utils.lib import DynamicQThread +from openpype.tools.utils.tasks_widget import ( + TasksModel, +) from . import lib from .constants import ( @@ -15,8 +20,6 @@ from .constants import ( ACTION_ID_ROLE ) from .actions import ApplicationAction -from avalon.vendor import qtawesome -from avalon import style, api log = logging.getLogger(__name__) @@ -524,6 +527,18 @@ class LauncherModel(QtCore.QObject): self._set_asset_docs(asset_docs) +class LauncherTaskModel(TasksModel): + def __init__(self, launcher_model, *args, **kwargs): + self._launcher_model = launcher_model + super(LauncherTaskModel, self).__init__(*args, **kwargs) + + def set_asset_id(self, asset_id): + asset_doc = None + if self._context_is_valid(): + asset_doc = self._launcher_model.get_asset_doc(asset_id) + self._set_asset(asset_doc) + + class ProjectModel(QtGui.QStandardItemModel): """List of projects""" From c874adfcb6e16e9d6bb239ea225c0184f2799849 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:47:11 +0100 Subject: [PATCH 07/20] tasks widget also can know about assignees --- openpype/tools/utils/tasks_widget.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 699b1cf569..204719e739 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -9,6 +9,7 @@ from .views import DeselectableTreeView TASK_NAME_ROLE = QtCore.Qt.UserRole + 1 TASK_TYPE_ROLE = QtCore.Qt.UserRole + 2 TASK_ORDER_ROLE = QtCore.Qt.UserRole + 3 +TASK_ASSIGNEE_ROLE = QtCore.Qt.UserRole + 4 class TasksModel(QtGui.QStandardItemModel): @@ -144,11 +145,19 @@ class TasksModel(QtGui.QStandardItemModel): task_type_icon = task_type_info.get("icon") icon = self._get_icon(task_icon, task_type_icon) + task_assignees = set() + assignees_data = task_info.get("assignees") or [] + for assignee in assignees_data: + username = assignee.get("username") + if username: + task_assignees.add(username) + label = "{} ({})".format(task_name, task_type or "type N/A") item = QtGui.QStandardItem(label) item.setData(task_name, TASK_NAME_ROLE) item.setData(task_type, TASK_TYPE_ROLE) item.setData(task_order, TASK_ORDER_ROLE) + item.setData(task_assignees, TASK_ASSIGNEE_ROLE) item.setData(icon, QtCore.Qt.DecorationRole) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) items.append(item) From 6d55d5d11a5342c360f912eaa8f82eefa763c78d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:49:44 +0100 Subject: [PATCH 08/20] created task proxy model with more filtering --- openpype/tools/launcher/models.py | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 1c61bd5012..c4836fb1af 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -10,6 +10,9 @@ from openpype.lib import ApplicationManager from openpype.tools.utils.lib import DynamicQThread from openpype.tools.utils.tasks_widget import ( TasksModel, + TasksProxyModel, + TASK_TYPE_ROLE, + TASK_ASSIGNEE_ROLE ) from . import lib @@ -527,6 +530,48 @@ class LauncherModel(QtCore.QObject): self._set_asset_docs(asset_docs) +class LauncherTasksProxyModel(TasksProxyModel): + """Tasks proxy model with more filtering. + + TODO: + This can be (with few modifications) used in default tasks widget too. + """ + def __init__(self, launcher_model, *args, **kwargs): + self._launcher_model = launcher_model + super(LauncherTasksProxyModel, self).__init__(*args, **kwargs) + + launcher_model.filters_changed.connect(self._on_filter_change) + + self._task_types_filter = set() + self._assignee_filter = set() + + def _on_filter_change(self): + self._task_types_filter = self._launcher_model.task_type_filters + self._assignee_filter = self._launcher_model.assignee_filters + self.invalidateFilter() + + def filterAcceptsRow(self, row, parent): + if not self._task_types_filter and not self._assignee_filter: + return True + + model = self.sourceModel() + source_index = model.index(row, self.filterKeyColumn(), parent) + if not source_index.isValid(): + return False + + # Check current index itself + if self._task_types_filter: + task_type = model.data(source_index, TASK_TYPE_ROLE) + if task_type not in self._task_types_filter: + return False + + if self._assignee_filter: + assignee = model.data(source_index, TASK_ASSIGNEE_ROLE) + if not self._assignee_filter.intersection(assignee): + return False + return True + + class LauncherTaskModel(TasksModel): def __init__(self, launcher_model, *args, **kwargs): self._launcher_model = launcher_model From e749a8ee53671b3cadb7a7e85cfbd9a76a3b0057 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:50:23 +0100 Subject: [PATCH 09/20] added modified assets model --- openpype/tools/launcher/models.py | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index c4836fb1af..13ca95fe43 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -8,6 +8,10 @@ from avalon.vendor import qtawesome from avalon import style, api from openpype.lib import ApplicationManager from openpype.tools.utils.lib import DynamicQThread +from openpype.tools.utils.assets_widget import ( + AssetModel, + ASSET_NAME_ROLE +) from openpype.tools.utils.tasks_widget import ( TasksModel, TasksProxyModel, @@ -26,6 +30,10 @@ from .actions import ApplicationAction log = logging.getLogger(__name__) +# Must be different than roles in default asset model +ASSET_TASK_TYPES_ROLE = QtCore.Qt.UserRole + 10 +ASSET_ASSIGNEE_ROLE = QtCore.Qt.UserRole + 11 + class ActionModel(QtGui.QStandardItemModel): def __init__(self, dbcon, parent=None): @@ -584,6 +592,54 @@ class LauncherTaskModel(TasksModel): self._set_asset(asset_doc) +class LauncherAssetsModel(AssetModel): + def __init__(self, launcher_model, dbcon, parent=None): + self._launcher_model = launcher_model + # Make sure that variable is available (even if is in AssetModel) + self._last_project_name = None + + super(LauncherAssetsModel, self).__init__(dbcon, parent) + + launcher_model.project_changed.connect(self._on_project_change) + launcher_model.assets_refresh_started.connect( + self._on_launcher_refresh_start + ) + launcher_model.assets_refreshed.connect(self._on_launcher_refresh) + + def _on_launcher_refresh_start(self): + self._refreshing = True + project_name = self._launcher_model.project_name + if self._last_project_name != project_name: + self._clear_items() + self._last_project_name = project_name + + def _on_launcher_refresh(self): + self._fill_assets(self._launcher_model.asset_docs) + self._refreshing = False + self.refreshed.emit(bool(self._items_by_asset_id)) + + def _fill_assets(self, *args, **kwargs): + super(LauncherAssetsModel, self)._fill_assets(*args, **kwargs) + asset_filter_data_by_id = self._launcher_model.asset_filter_data_by_id + for asset_id, item in self._items_by_asset_id.items(): + filter_data = asset_filter_data_by_id.get(asset_id) + + assignees = filter_data["assignees"] + task_types = filter_data["task_types"] + + item.setData(assignees, ASSET_ASSIGNEE_ROLE) + item.setData(task_types, ASSET_TASK_TYPES_ROLE) + + def _on_project_change(self): + self._clear_items() + + def refresh(self, *args, **kwargs): + raise ValueError("This is a bug!") + + def stop_refresh(self, *args, **kwargs): + raise ValueError("This is a bug!") + + class ProjectModel(QtGui.QStandardItemModel): """List of projects""" From 577a82ca7b67b923d64a6799e509219a9a2a758e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:50:48 +0100 Subject: [PATCH 10/20] created new recursive assets proxy with more filters --- openpype/tools/launcher/models.py | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 13ca95fe43..dc06ec4e28 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -1,3 +1,4 @@ +import re import uuid import copy import logging @@ -592,6 +593,65 @@ class LauncherTaskModel(TasksModel): self._set_asset(asset_doc) +class AssetRecursiveSortFilterModel(QtCore.QSortFilterProxyModel): + def __init__(self, launcher_model, *args, **kwargs): + self._launcher_model = launcher_model + + super(AssetRecursiveSortFilterModel, self).__init__(*args, **kwargs) + + launcher_model.filters_changed.connect(self._on_filter_change) + self._name_filter = "" + self._task_types_filter = set() + self._assignee_filter = set() + + def _on_filter_change(self): + self._name_filter = self._launcher_model.asset_name_filter + self._task_types_filter = self._launcher_model.task_type_filters + self._assignee_filter = self._launcher_model.assignee_filters + self.invalidateFilter() + + """Filters to the regex if any of the children matches allow parent""" + def filterAcceptsRow(self, row, parent): + if ( + not self._name_filter + and not self._task_types_filter + and not self._assignee_filter + ): + return True + + model = self.sourceModel() + source_index = model.index(row, self.filterKeyColumn(), parent) + if not source_index.isValid(): + return False + + # Check current index itself + valid = True + if self._name_filter: + name = model.data(source_index, ASSET_NAME_ROLE) + if not re.search(self._name_filter, name, re.IGNORECASE): + valid = False + + if valid and self._task_types_filter: + task_types = model.data(source_index, ASSET_TASK_TYPES_ROLE) + if not self._task_types_filter.intersection(task_types): + valid = False + + if valid and self._assignee_filter: + assignee = model.data(source_index, ASSET_ASSIGNEE_ROLE) + if not self._assignee_filter.intersection(assignee): + valid = False + + if valid: + return True + + # Check children + rows = model.rowCount(source_index) + for child_row in range(rows): + if self.filterAcceptsRow(child_row, source_index): + return True + return False + + class LauncherAssetsModel(AssetModel): def __init__(self, launcher_model, dbcon, parent=None): self._launcher_model = launcher_model From ccf3b1de219a2a8c5fe55c1e6f644e76442b08a8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:51:53 +0100 Subject: [PATCH 11/20] modified ProjectModel to use launcher model --- openpype/tools/launcher/models.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index dc06ec4e28..f518ac78a7 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -3,6 +3,7 @@ import uuid import copy import logging import collections +import time from Qt import QtCore, QtGui from avalon.vendor import qtawesome @@ -524,6 +525,7 @@ class LauncherModel(QtCore.QObject): self._refreshing_assets = False if self._asset_refresh_thread is not None: while self._asset_refresh_thread.isRunning(): + # TODO this is blocking UI should be done in a different way time.sleep(0.01) self._asset_refresh_thread = None @@ -532,7 +534,6 @@ class LauncherModel(QtCore.QObject): {"type": "asset"}, self._asset_projection )) - time.sleep(5) if not self._refreshing_assets: return self._refreshing_assets = False @@ -703,18 +704,17 @@ class LauncherAssetsModel(AssetModel): class ProjectModel(QtGui.QStandardItemModel): """List of projects""" - def __init__(self, dbcon, parent=None): + def __init__(self, launcher_model, parent=None): super(ProjectModel, self).__init__(parent=parent) - self.dbcon = dbcon + self._launcher_model = launcher_model self.project_icon = qtawesome.icon("fa.map", color="white") self._project_names = set() - def refresh(self): - project_names = set() - for project_doc in self.get_projects(): - project_names.add(project_doc["name"]) + launcher_model.projects_refreshed.connect(self._on_refresh) + def _on_refresh(self): + project_names = set(self._launcher_model.project_names) origin_project_names = set(self._project_names) self._project_names = project_names @@ -757,7 +757,3 @@ class ProjectModel(QtGui.QStandardItemModel): items.append(item) self.invisibleRootItem().insertRows(row, items) - - def get_projects(self): - return sorted(self.dbcon.projects(only_active=True), - key=lambda x: x["name"]) From 258dfbffd9e8c923a07e65cca0d3dae22d654b1f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:54:45 +0100 Subject: [PATCH 12/20] ProjectBar and ActionBar are using launcher model --- openpype/tools/launcher/widgets.py | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index edda8d08b5..33118e03be 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -4,10 +4,14 @@ import collections from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome +from openpype.tools.flickcharm import FlickCharm + from .delegates import ActionDelegate from . import lib -from .models import ActionModel -from openpype.tools.flickcharm import FlickCharm +from .models import ( + ActionModel, + ProjectModel, +) from .constants import ( ACTION_ROLE, GROUP_ROLE, @@ -20,15 +24,15 @@ from .constants import ( class ProjectBar(QtWidgets.QWidget): - def __init__(self, project_handler, parent=None): + def __init__(self, launcher_model, parent=None): super(ProjectBar, self).__init__(parent) project_combobox = QtWidgets.QComboBox(self) # Change delegate so stylysheets are applied project_delegate = QtWidgets.QStyledItemDelegate(project_combobox) project_combobox.setItemDelegate(project_delegate) - - project_combobox.setModel(project_handler.model) + model = ProjectModel(launcher_model) + project_combobox.setModel(model) project_combobox.setRootModelIndex(QtCore.QModelIndex()) layout = QtWidgets.QHBoxLayout(self) @@ -40,16 +44,17 @@ class ProjectBar(QtWidgets.QWidget): QtWidgets.QSizePolicy.Maximum ) - self.project_handler = project_handler + self._launcher_model = launcher_model self.project_delegate = project_delegate self.project_combobox = project_combobox + self._model = model # Signals self.project_combobox.currentIndexChanged.connect(self.on_index_change) - project_handler.project_changed.connect(self._on_project_change) + launcher_model.project_changed.connect(self._on_project_change) # Set current project by default if it's set. - project_name = project_handler.current_project + project_name = launcher_model.project_name if project_name: self.set_project(project_name) @@ -65,7 +70,7 @@ class ProjectBar(QtWidgets.QWidget): index = self.project_combobox.findText(project_name) if index < 0: # Try refresh combobox model - self.project_handler.refresh_model() + self._launcher_model.refresh_projects() index = self.project_combobox.findText(project_name) if index >= 0: @@ -76,7 +81,7 @@ class ProjectBar(QtWidgets.QWidget): return project_name = self.get_current_project() - self.project_handler.set_project(project_name) + self._launcher_model.set_project_name(project_name) class ActionBar(QtWidgets.QWidget): @@ -84,10 +89,10 @@ class ActionBar(QtWidgets.QWidget): action_clicked = QtCore.Signal(object) - def __init__(self, project_handler, dbcon, parent=None): + def __init__(self, launcher_model, dbcon, parent=None): super(ActionBar, self).__init__(parent) - self.project_handler = project_handler + self._launcher_model = launcher_model self.dbcon = dbcon view = QtWidgets.QListView(self) @@ -133,7 +138,7 @@ class ActionBar(QtWidgets.QWidget): self.set_row_height(1) - project_handler.projects_refreshed.connect(self._on_projects_refresh) + launcher_model.projects_refreshed.connect(self._on_projects_refresh) view.clicked.connect(self.on_clicked) def discover_actions(self): @@ -172,7 +177,7 @@ class ActionBar(QtWidgets.QWidget): def _start_animation(self, index): # Offset refresh timout - self.project_handler.start_timer() + self.launcher_model.start_refresh_timer() action_id = index.data(ACTION_ID_ROLE) item = self.model.items_by_id.get(action_id) if item: @@ -194,7 +199,7 @@ class ActionBar(QtWidgets.QWidget): return # Offset refresh timout - self.project_handler.start_timer() + self.launcher_model.start_refresh_timer() actions = index.data(ACTION_ROLE) From cde8c69cbabca6b9471cc30996b7a4a8000cfb06 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:55:12 +0100 Subject: [PATCH 13/20] modify tasks widget to use different models --- openpype/tools/launcher/widgets.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 33118e03be..4408219628 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -5,12 +5,15 @@ from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome from openpype.tools.flickcharm import FlickCharm +from openpype.tools.utils.tasks_widget import TasksWidget from .delegates import ActionDelegate from . import lib from .models import ( ActionModel, ProjectModel, + LauncherTaskModel, + LauncherTasksProxyModel ) from .constants import ( ACTION_ROLE, @@ -84,6 +87,21 @@ class ProjectBar(QtWidgets.QWidget): self._launcher_model.set_project_name(project_name) +class LauncherTaskWidget(TasksWidget): + def __init__(self, launcher_model, *args, **kwargs): + self._launcher_model = launcher_model + + super(LauncherTaskWidget, self).__init__(*args, **kwargs) + + def _create_source_model(self): + return LauncherTaskModel(self._launcher_model, self._dbcon) + + def _create_proxy_model(self, source_model): + proxy = LauncherTasksProxyModel(self._launcher_model) + proxy.setSourceModel(source_model) + return proxy + + class ActionBar(QtWidgets.QWidget): """Launcher interface""" From a232f8bc58b918e1e31741ea06c715b2ee97b992 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:55:33 +0100 Subject: [PATCH 14/20] modified assets widget to use new models nad modified what is called when --- openpype/tools/launcher/widgets.py | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 4408219628..397b29a1b0 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -5,6 +5,7 @@ from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome from openpype.tools.flickcharm import FlickCharm +from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget from openpype.tools.utils.tasks_widget import TasksWidget from .delegates import ActionDelegate @@ -12,6 +13,8 @@ from . import lib from .models import ( ActionModel, ProjectModel, + LauncherAssetsModel, + AssetRecursiveSortFilterModel, LauncherTaskModel, LauncherTasksProxyModel ) @@ -102,6 +105,54 @@ class LauncherTaskWidget(TasksWidget): return proxy +class LauncherAssetsWidget(SingleSelectAssetsWidget): + def __init__(self, launcher_model, *args, **kwargs): + self._launcher_model = launcher_model + + super(LauncherAssetsWidget, self).__init__(*args, **kwargs) + + launcher_model.assets_refresh_started.connect(self._on_refresh_start) + + self.set_current_asset_btn_visibility(False) + + def _on_refresh_start(self): + self._set_loading_state(loading=True, empty=True) + self.refresh_triggered.emit() + + @property + def refreshing(self): + return self._model.refreshing + + def refresh(self): + self._launcher_model.refresh_assets(force=True) + + def stop_refresh(self): + raise ValueError("bug stop_refresh called") + + def _refresh_model(self, clear=False): + raise ValueError("bug _refresh_model called") + + def _create_source_model(self): + model = LauncherAssetsModel(self._launcher_model, self.dbcon) + model.refreshed.connect(self._on_model_refresh) + return model + + def _create_proxy_model(self, source_model): + proxy = AssetRecursiveSortFilterModel(self._launcher_model) + proxy.setSourceModel(source_model) + proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) + proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + return proxy + + def _on_model_refresh(self, has_item): + self._proxy.sort(0) + self._set_loading_state(loading=False, empty=not has_item) + self.refreshed.emit() + + def _on_filter_text_change(self, new_text): + self._launcher_model.set_asset_name_filter(new_text) + + class ActionBar(QtWidgets.QWidget): """Launcher interface""" From 27d067fbdf66a2f61ef005236b2fadde4e758829 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:56:47 +0100 Subject: [PATCH 15/20] project panel is using launcher model --- openpype/tools/launcher/window.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/tools/launcher/window.py b/openpype/tools/launcher/window.py index a8f65894f2..9b5a21ebc2 100644 --- a/openpype/tools/launcher/window.py +++ b/openpype/tools/launcher/window.py @@ -89,15 +89,15 @@ class ProjectIconView(QtWidgets.QListView): class ProjectsPanel(QtWidgets.QWidget): """Projects Page""" - def __init__(self, project_handler, parent=None): + def __init__(self, launcher_model, parent=None): super(ProjectsPanel, self).__init__(parent=parent) view = ProjectIconView(parent=self) view.setSelectionMode(QtWidgets.QListView.NoSelection) flick = FlickCharm(parent=self) flick.activateOn(view) - - view.setModel(project_handler.model) + model = ProjectModel(launcher_model) + view.setModel(model) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -105,13 +105,14 @@ class ProjectsPanel(QtWidgets.QWidget): view.clicked.connect(self.on_clicked) + self._model = model self.view = view - self.project_handler = project_handler + self._launcher_model = launcher_model def on_clicked(self, index): if index.isValid(): project_name = index.data(QtCore.Qt.DisplayRole) - self.project_handler.set_project(project_name) + self._launcher_model.set_project_name(project_name) class AssetsPanel(QtWidgets.QWidget): @@ -119,7 +120,7 @@ class AssetsPanel(QtWidgets.QWidget): back_clicked = QtCore.Signal() session_changed = QtCore.Signal() - def __init__(self, project_handler, dbcon, parent=None): + def __init__(self, launcher_model, dbcon, parent=None): super(AssetsPanel, self).__init__(parent=parent) self.dbcon = dbcon @@ -129,7 +130,7 @@ class AssetsPanel(QtWidgets.QWidget): btn_back = QtWidgets.QPushButton(self) btn_back.setIcon(btn_back_icon) - project_bar = ProjectBar(project_handler, self) + project_bar = ProjectBar(launcher_model, self) project_bar_layout = QtWidgets.QHBoxLayout() project_bar_layout.setContentsMargins(0, 0, 0, 0) From 9b044ffd828f3c8015fdb031e67d371599c9606d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:57:07 +0100 Subject: [PATCH 16/20] use new widgets and models in launcher window --- openpype/tools/launcher/window.py | 52 ++++++++++++++++--------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/openpype/tools/launcher/window.py b/openpype/tools/launcher/window.py index 9b5a21ebc2..b5b6368865 100644 --- a/openpype/tools/launcher/window.py +++ b/openpype/tools/launcher/window.py @@ -8,17 +8,19 @@ from avalon.api import AvalonMongoDB from openpype import style from openpype.api import resources -from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget -from openpype.tools.utils.tasks_widget import TasksWidget - from avalon.vendor import qtawesome -from .models import ProjectModel -from .lib import get_action_label, ProjectHandler +from .models import ( + LauncherModel, + ProjectModel +) +from .lib import get_action_label from .widgets import ( ProjectBar, ActionBar, ActionHistory, - SlidePageWidget + SlidePageWidget, + LauncherAssetsWidget, + LauncherTaskWidget ) from openpype.tools.flickcharm import FlickCharm @@ -139,12 +141,14 @@ class AssetsPanel(QtWidgets.QWidget): project_bar_layout.addWidget(project_bar) # Assets widget - assets_widget = SingleSelectAssetsWidget(dbcon=self.dbcon, parent=self) + assets_widget = LauncherAssetsWidget( + launcher_model, dbcon=self.dbcon, parent=self + ) # Make assets view flickable assets_widget.activate_flick_charm() # Tasks widget - tasks_widget = TasksWidget(self.dbcon, self) + tasks_widget = LauncherTaskWidget(launcher_model, self.dbcon, self) # Body body = QtWidgets.QSplitter(self) @@ -166,19 +170,20 @@ class AssetsPanel(QtWidgets.QWidget): layout.addWidget(body) # signals - project_handler.project_changed.connect(self._on_project_changed) + launcher_model.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) - self.project_handler = project_handler self.project_bar = project_bar self.assets_widget = assets_widget self._tasks_widget = tasks_widget self._btn_back = btn_back + self._launcher_model = launcher_model + def select_asset(self, asset_name): self.assets_widget.select_asset_by_name(asset_name) @@ -197,8 +202,6 @@ class AssetsPanel(QtWidgets.QWidget): def _on_project_changed(self): self.session_changed.emit() - self.assets_widget.refresh() - def _on_asset_changed(self): """Callback on asset selection changed @@ -251,18 +254,17 @@ class LauncherWindow(QtWidgets.QDialog): | QtCore.Qt.WindowCloseButtonHint ) - project_model = ProjectModel(self.dbcon) - project_handler = ProjectHandler(self.dbcon, project_model) + launcher_model = LauncherModel(self.dbcon) - project_panel = ProjectsPanel(project_handler) - asset_panel = AssetsPanel(project_handler, self.dbcon) + project_panel = ProjectsPanel(launcher_model) + asset_panel = AssetsPanel(launcher_model, self.dbcon) page_slider = SlidePageWidget() page_slider.addWidget(project_panel) page_slider.addWidget(asset_panel) # actions - actions_bar = ActionBar(project_handler, self.dbcon, self) + actions_bar = ActionBar(launcher_model, self.dbcon, self) # statusbar message_label = QtWidgets.QLabel(self) @@ -304,8 +306,8 @@ class LauncherWindow(QtWidgets.QDialog): # signals actions_bar.action_clicked.connect(self.on_action_clicked) action_history.trigger_history.connect(self.on_history_action) - project_handler.project_changed.connect(self.on_project_change) - project_handler.timer_timeout.connect(self._on_refresh_timeout) + launcher_model.project_changed.connect(self.on_project_change) + launcher_model.timer_timeout.connect(self._on_refresh_timeout) asset_panel.back_clicked.connect(self.on_back_clicked) asset_panel.session_changed.connect(self.on_session_changed) @@ -315,7 +317,7 @@ class LauncherWindow(QtWidgets.QDialog): self._message_timer = message_timer - self.project_handler = project_handler + self._launcher_model = launcher_model self._message_label = message_label self.project_panel = project_panel @@ -325,19 +327,19 @@ class LauncherWindow(QtWidgets.QDialog): self.page_slider = page_slider def showEvent(self, event): - self.project_handler.set_active(True) - self.project_handler.start_timer(True) + self._launcher_model.set_active(True) + self._launcher_model.start_refresh_timer(True) super(LauncherWindow, self).showEvent(event) def _on_refresh_timeout(self): # Stop timer if widget is not visible if not self.isVisible(): - self.project_handler.stop_timer() + self._launcher_model.stop_refresh_timer() def changeEvent(self, event): if event.type() == QtCore.QEvent.ActivationChange: - self.project_handler.set_active(self.isActiveWindow()) + self._launcher_model.set_active(self.isActiveWindow()) super(LauncherWindow, self).changeEvent(event) def set_page(self, page): @@ -372,7 +374,7 @@ class LauncherWindow(QtWidgets.QDialog): self.discover_actions() def on_back_clicked(self): - self.project_handler.set_project(None) + self._launcher_model.set_project_name(None) self.set_page(0) self.discover_actions() From c776ee07ba6f7e357f12ef4f53d829182130d11e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 21:57:14 +0100 Subject: [PATCH 17/20] removed unused project handler --- openpype/tools/launcher/lib.py | 71 ---------------------------------- 1 file changed, 71 deletions(-) diff --git a/openpype/tools/launcher/lib.py b/openpype/tools/launcher/lib.py index d6374f49d2..a7c686a7d0 100644 --- a/openpype/tools/launcher/lib.py +++ b/openpype/tools/launcher/lib.py @@ -23,77 +23,6 @@ ICON_CACHE = {} NOT_FOUND = type("NotFound", (object, ), {}) -class ProjectHandler(QtCore.QObject): - """Handler of project model and current project in Launcher tool. - - Helps to organize two separate widgets handling current project selection. - - It is easier to trigger project change callbacks from one place than from - multiple differect places without proper handling or sequence changes. - - Args: - dbcon(AvalonMongoDB): Mongo connection with Session. - model(ProjectModel): Object of projects model which is shared across - all widgets using projects. Arg dbcon should be used as source for - the model. - """ - # Project list will be refreshed each 10000 msecs - # - this is not part of helper implementation but should be used by widgets - # that may require reshing of projects - refresh_interval = 10000 - - # Signal emmited when project has changed - project_changed = QtCore.Signal(str) - projects_refreshed = QtCore.Signal() - timer_timeout = QtCore.Signal() - - def __init__(self, dbcon, model): - super(ProjectHandler, self).__init__() - self._active = False - # Store project model for usage - self.model = model - # Store dbcon - self.dbcon = dbcon - - self.current_project = dbcon.Session.get("AVALON_PROJECT") - - refresh_timer = QtCore.QTimer() - refresh_timer.setInterval(self.refresh_interval) - refresh_timer.timeout.connect(self._on_timeout) - - self.refresh_timer = refresh_timer - - def _on_timeout(self): - if self._active: - self.timer_timeout.emit() - self.refresh_model() - - def set_active(self, active): - self._active = active - - def start_timer(self, trigger=False): - self.refresh_timer.start() - if trigger: - self._on_timeout() - - def stop_timer(self): - self.refresh_timer.stop() - - def set_project(self, project_name): - # Change current project of this handler - self.current_project = project_name - # Change session project to take effect for other widgets using the - # dbcon object. - self.dbcon.Session["AVALON_PROJECT"] = project_name - - # Trigger change signal when everything is updated to new project - self.project_changed.emit(project_name) - - def refresh_model(self): - self.model.refresh() - self.projects_refreshed.emit() - - def get_action_icon(action): icon_name = action.icon if not icon_name: From 856a8e2b02ecb3937c354ac5eb67826dcf7d633a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Feb 2022 14:22:50 +0100 Subject: [PATCH 18/20] fix imports after merge --- openpype/tools/launcher/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index ab22809f7c..eea32e1aee 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -8,8 +8,8 @@ import time import appdirs from Qt import QtCore, QtGui from avalon.vendor import qtawesome -from avalon import style, api -from openpype.lib import ApplicationManager +from avalon import api +from openpype.lib import ApplicationManager, JSONSettingRegistry from openpype.tools.utils.lib import DynamicQThread from openpype.tools.utils.assets_widget import ( AssetModel, From 48242818e36aa41fd2637ad8543d4986b4e6a5e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 22 Feb 2022 16:36:36 +0100 Subject: [PATCH 19/20] fix attr name typo --- openpype/tools/launcher/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 7437dc1453..30e6531843 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -257,7 +257,7 @@ class ActionBar(QtWidgets.QWidget): def _start_animation(self, index): # Offset refresh timout - self.launcher_model.start_refresh_timer() + self._launcher_model.start_refresh_timer() action_id = index.data(ACTION_ID_ROLE) item = self.model.items_by_id.get(action_id) if item: @@ -325,7 +325,7 @@ class ActionBar(QtWidgets.QWidget): return # Offset refresh timout - self.launcher_model.start_refresh_timer() + self._launcher_model.start_refresh_timer() actions = index.data(ACTION_ROLE) From c6df75827508d4e64f48f79e6669d5f19ed78089 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 22 Feb 2022 17:18:04 +0100 Subject: [PATCH 20/20] fix filter bug --- openpype/tools/launcher/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index c9f222a7d8..effa283318 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -736,7 +736,10 @@ class AssetRecursiveSortFilterModel(QtCore.QSortFilterProxyModel): valid = True if self._name_filter: name = model.data(source_index, ASSET_NAME_ROLE) - if not re.search(self._name_filter, name, re.IGNORECASE): + if ( + name is None + or not re.search(self._name_filter, name, re.IGNORECASE) + ): valid = False if valid and self._task_types_filter: