diff --git a/openpype/modules/default_modules/sync_server/sync_server_module.py b/openpype/modules/default_modules/sync_server/sync_server_module.py index 4c54f25c02..7dabd45bae 100644 --- a/openpype/modules/default_modules/sync_server/sync_server_module.py +++ b/openpype/modules/default_modules/sync_server/sync_server_module.py @@ -812,17 +812,22 @@ class SyncServerModule(OpenPypeModule, ITrayModule): def _prepare_sync_project_settings(self, exclude_locals): sync_project_settings = {} system_sites = self.get_all_site_configs() - for collection in self.connection.database.collection_names(False): + project_docs = self.connection.projects( + projection={"name": 1}, + only_active=True + ) + for project_doc in project_docs: + project_name = project_doc["name"] sites = copy.deepcopy(system_sites) # get all configured sites proj_settings = self._parse_sync_settings_from_settings( - get_project_settings(collection, + get_project_settings(project_name, exclude_locals=exclude_locals)) sites.update(self._get_default_site_configs( - proj_settings["enabled"], collection)) + proj_settings["enabled"], project_name)) sites.update(proj_settings['sites']) proj_settings["sites"] = sites - sync_project_settings[collection] = proj_settings + sync_project_settings[project_name] = proj_settings if not sync_project_settings: log.info("No enabled and configured projects for sync.") return sync_project_settings diff --git a/openpype/modules/default_modules/sync_server/tray/app.py b/openpype/modules/default_modules/sync_server/tray/app.py index a5f73db5d5..0db1c10b6a 100644 --- a/openpype/modules/default_modules/sync_server/tray/app.py +++ b/openpype/modules/default_modules/sync_server/tray/app.py @@ -77,8 +77,8 @@ class SyncServerWindow(QtWidgets.QDialog): self.setWindowTitle("Sync Queue") self.projects.project_changed.connect( - lambda: repres.table_view.model().set_project( - self.projects.current_project)) + self._on_project_change + ) self.pause_btn.clicked.connect(self._pause) self.pause_btn.setAutoDefault(False) @@ -87,6 +87,13 @@ class SyncServerWindow(QtWidgets.QDialog): self.representationWidget = repres + def _on_project_change(self): + if self.projects.current_project is None: + return + self.representationWidget.table_view.model().set_project( + self.projects.current_project + ) + def showEvent(self, event): self.representationWidget.model.set_project( self.projects.current_project) diff --git a/openpype/modules/default_modules/sync_server/tray/models.py b/openpype/modules/default_modules/sync_server/tray/models.py index c2c63c68ea..c00c1d9d8b 100644 --- a/openpype/modules/default_modules/sync_server/tray/models.py +++ b/openpype/modules/default_modules/sync_server/tray/models.py @@ -17,25 +17,6 @@ from . import lib log = PypeLogger().get_logger("SyncServer") -class ProjectModel(QtCore.QAbstractListModel): - def __init__(self, *args, projects=None, **kwargs): - super(ProjectModel, self).__init__(*args, **kwargs) - self.projects = projects or [] - - def data(self, index, role): - if role == Qt.DisplayRole: - # See below for the data structure. - status, text = self.projects[index.row()] - # Return the todo text only. - return text - - def rowCount(self, _index): - return len(self.todos) - - def columnCount(self, _index): - return len(self._header) - - class _SyncRepresentationModel(QtCore.QAbstractTableModel): COLUMN_LABELS = [] diff --git a/openpype/modules/default_modules/sync_server/tray/widgets.py b/openpype/modules/default_modules/sync_server/tray/widgets.py index c9b58ebe7c..22b33ca4c7 100644 --- a/openpype/modules/default_modules/sync_server/tray/widgets.py +++ b/openpype/modules/default_modules/sync_server/tray/widgets.py @@ -6,10 +6,7 @@ from functools import partial from Qt import QtWidgets, QtCore, QtGui from Qt.QtCore import Qt -from openpype.tools.settings import ( - ProjectListWidget, - style -) +from openpype.tools.settings import style from openpype.api import get_local_site_id from openpype.lib import PypeLogger @@ -28,28 +25,56 @@ from . import delegates log = PypeLogger().get_logger("SyncServer") -class SyncProjectListWidget(ProjectListWidget): +class SyncProjectListWidget(QtWidgets.QWidget): """ Lists all projects that are synchronized to choose from """ + project_changed = QtCore.Signal() def __init__(self, sync_server, parent): super(SyncProjectListWidget, self).__init__(parent) + self.setObjectName("ProjectListWidget") + + self._parent = parent + + label_widget = QtWidgets.QLabel("Projects", self) + project_list = QtWidgets.QListView(self) + project_model = QtGui.QStandardItemModel() + project_list.setModel(project_model) + project_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + + # Do not allow editing + project_list.setEditTriggers( + QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers + ) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(3) + layout.addWidget(label_widget, 0) + layout.addWidget(project_list, 1) + + project_list.customContextMenuRequested.connect(self._on_context_menu) + project_list.selectionModel().currentChanged.connect( + self._on_index_change + ) + + self.project_model = project_model + self.project_list = project_list self.sync_server = sync_server - self.project_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.project_list.customContextMenuRequested.connect( - self._on_context_menu) + self.current_project = None self.project_name = None self.local_site = None self.icons = {} - self.layout().setContentsMargins(0, 0, 0, 0) + def _on_index_change(self, new_idx, _old_idx): + project_name = new_idx.data(QtCore.Qt.DisplayRole) - def validate_context_change(self): - return True + self.current_project = project_name + self.project_changed.emit() def refresh(self): - model = self.project_list.model() + model = self.project_model model.clear() project_name = None @@ -70,8 +95,7 @@ class SyncProjectListWidget(ProjectListWidget): QtCore.Qt.DisplayRole ) if not self.current_project: - self.current_project = self.project_list.model().item(0). \ - data(QtCore.Qt.DisplayRole) + self.current_project = model.item(0).data(QtCore.Qt.DisplayRole) if project_name: self.local_site = self.sync_server.get_active_site(project_name) diff --git a/openpype/settings/defaults/project_anatomy/attributes.json b/openpype/settings/defaults/project_anatomy/attributes.json index 387e12bcea..983ac603f9 100644 --- a/openpype/settings/defaults/project_anatomy/attributes.json +++ b/openpype/settings/defaults/project_anatomy/attributes.json @@ -22,5 +22,6 @@ "aftereffects/2021", "unreal/4-26" ], - "tools_env": [] + "tools_env": [], + "active": true } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json index 7391108a02..a2a566da0e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json @@ -69,6 +69,11 @@ "type": "tools-enum", "key": "tools_env", "label": "Tools" + }, + { + "type": "boolean", + "key": "active", + "label": "Active Project" } ] } diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 4988829c11..f87871409e 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -326,8 +326,6 @@ class ProjectModel(QtGui.QStandardItemModel): super(ProjectModel, self).__init__(parent=parent) self.dbcon = dbcon - - self.hide_invisible = False self.project_icon = qtawesome.icon("fa.map", color="white") self._project_names = set() @@ -380,16 +378,5 @@ class ProjectModel(QtGui.QStandardItemModel): self.invisibleRootItem().insertRows(row, items) def get_projects(self): - project_docs = [] - - for project_doc in sorted( - self.dbcon.projects(), key=lambda x: x["name"] - ): - if ( - self.hide_invisible - and not project_doc["data"].get("visible", True) - ): - continue - project_docs.append(project_doc) - - return project_docs + return sorted(self.dbcon.projects(only_active=True), + key=lambda x: x["name"]) diff --git a/openpype/tools/launcher/window.py b/openpype/tools/launcher/window.py index 1a753db16a..9b839fb2bc 100644 --- a/openpype/tools/launcher/window.py +++ b/openpype/tools/launcher/window.py @@ -271,7 +271,6 @@ class LauncherWindow(QtWidgets.QDialog): ) project_model = ProjectModel(self.dbcon) - project_model.hide_invisible = True project_handler = ProjectHandler(self.dbcon, project_model) project_panel = ProjectsPanel(project_handler) diff --git a/openpype/tools/project_manager/project_manager/model.py b/openpype/tools/project_manager/project_manager/model.py index 7ee43a6b61..7036b65f87 100644 --- a/openpype/tools/project_manager/project_manager/model.py +++ b/openpype/tools/project_manager/project_manager/model.py @@ -43,18 +43,14 @@ class ProjectModel(QtGui.QStandardItemModel): none_project.setData(None) project_items.append(none_project) - database = self.dbcon.database project_names = set() - for project_name in database.collection_names(): - # Each collection will have exactly one project document - project_doc = database[project_name].find_one( - {"type": "project"}, - {"name": 1} - ) - if not project_doc: - continue - project_name = project_doc.get("name") + for doc in sorted( + self.dbcon.projects(projection={"name": 1}, only_active=True), + key=lambda x: x["name"] + ): + + project_name = doc.get("name") if project_name: project_names.add(project_name) project_items.append(QtGui.QStandardItem(project_name)) diff --git a/openpype/tools/settings/local_settings/projects_widget.py b/openpype/tools/settings/local_settings/projects_widget.py index a48c504d59..9cd3b9a38e 100644 --- a/openpype/tools/settings/local_settings/projects_widget.py +++ b/openpype/tools/settings/local_settings/projects_widget.py @@ -809,7 +809,7 @@ class ProjectSettingsWidget(QtWidgets.QWidget): self.modules_manager = modules_manager - projects_widget = _ProjectListWidget(self) + projects_widget = _ProjectListWidget(self, only_active=True) roos_site_widget = RootSiteWidget( modules_manager, project_settings, self ) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index 2caf8c33ba..a461f3e675 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -602,6 +602,12 @@ class NiceCheckbox(QtWidgets.QFrame): return super(NiceCheckbox, self).mouseReleaseEvent(event) +class ProjectListModel(QtGui.QStandardItemModel): + sort_role = QtCore.Qt.UserRole + 10 + filter_role = QtCore.Qt.UserRole + 11 + selected_role = QtCore.Qt.UserRole + 12 + + class ProjectListView(QtWidgets.QListView): left_mouse_released_at = QtCore.Signal(QtCore.QModelIndex) @@ -612,11 +618,35 @@ class ProjectListView(QtWidgets.QListView): super(ProjectListView, self).mouseReleaseEvent(event) +class ProjectListSortFilterProxy(QtCore.QSortFilterProxyModel): + + def __init__(self, *args, **kwargs): + super(ProjectListSortFilterProxy, self).__init__(*args, **kwargs) + self._enable_filter = True + + def filterAcceptsRow(self, source_row, source_parent): + if not self._enable_filter: + return True + + index = self.sourceModel().index(source_row, 0, source_parent) + is_active = bool(index.data(self.filterRole())) + is_selected = bool(index.data(ProjectListModel.selected_role)) + + return is_active or is_selected + + def is_filter_enabled(self): + return self._enable_filter + + def set_filter_enabled(self, value): + self._enable_filter = value + self.invalidateFilter() + + class ProjectListWidget(QtWidgets.QWidget): default = "< Default >" project_changed = QtCore.Signal() - def __init__(self, parent): + def __init__(self, parent, only_active=False): self._parent = parent self.current_project = None @@ -625,8 +655,17 @@ class ProjectListWidget(QtWidgets.QWidget): self.setObjectName("ProjectListWidget") label_widget = QtWidgets.QLabel("Projects") + project_list = ProjectListView(self) - project_list.setModel(QtGui.QStandardItemModel()) + project_model = ProjectListModel() + project_proxy = ProjectListSortFilterProxy() + + project_proxy.setFilterRole(ProjectListModel.filter_role) + project_proxy.setSortRole(ProjectListModel.sort_role) + project_proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + + project_proxy.setSourceModel(project_model) + project_list.setModel(project_proxy) # Do not allow editing project_list.setEditTriggers( @@ -640,11 +679,27 @@ class ProjectListWidget(QtWidgets.QWidget): layout.addWidget(label_widget, 0) layout.addWidget(project_list, 1) + if only_active: + inactive_chk = None + else: + inactive_chk = QtWidgets.QCheckBox(" Show Inactive Projects ") + inactive_chk.setChecked(not project_proxy.is_filter_enabled()) + + layout.addSpacing(5) + layout.addWidget(inactive_chk, 0) + layout.addSpacing(5) + + inactive_chk.stateChanged.connect(self.on_inactive_vis_changed) + project_list.left_mouse_released_at.connect(self.on_item_clicked) self.project_list = project_list + self.project_proxy = project_proxy + self.project_model = project_model + self.inactive_chk = inactive_chk self.dbcon = None + self._only_active = only_active def on_item_clicked(self, new_index): new_project_name = new_index.data(QtCore.Qt.DisplayRole) @@ -679,6 +734,14 @@ class ProjectListWidget(QtWidgets.QWidget): else: self.select_project(self.current_project) + def on_inactive_vis_changed(self): + if self.inactive_chk is None: + # should not happen. + return + + enable_filter = not self.inactive_chk.isChecked() + self.project_proxy.set_filter_enabled(enable_filter) + def validate_context_change(self): return not self._parent.entity.has_unsaved_changes @@ -691,12 +754,18 @@ class ProjectListWidget(QtWidgets.QWidget): self.select_project(self.default) def select_project(self, project_name): - model = self.project_list.model() + model = self.project_model + proxy = self.project_proxy + found_items = model.findItems(project_name) if not found_items: found_items = model.findItems(self.default) index = model.indexFromItem(found_items[0]) + model.setData(index, True, ProjectListModel.selected_role) + + index = proxy.mapFromSource(index) + self.project_list.selectionModel().clear() self.project_list.selectionModel().setCurrentIndex( index, QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent @@ -708,11 +777,9 @@ class ProjectListWidget(QtWidgets.QWidget): selected_project = index.data(QtCore.Qt.DisplayRole) break - model = self.project_list.model() + model = self.project_model model.clear() - items = [self.default] - mongo_url = os.environ["OPENPYPE_MONGO"] # Force uninstall of whole avalon connection if url does not match @@ -730,17 +797,37 @@ class ProjectListWidget(QtWidgets.QWidget): self.dbcon = None self.current_project = None + items = [(self.default, True)] + if self.dbcon: - database = self.dbcon.database - for project_name in database.collection_names(): - project_doc = database[project_name].find_one( - {"type": "project"}, - {"name": 1} + + for doc in self.dbcon.projects( + projection={"name": 1, "data.active": 1}, + only_active=self._only_active + ): + items.append( + (doc["name"], doc.get("data", {}).get("active", True)) ) - if project_doc: - items.append(project_doc["name"]) - for item in items: - model.appendRow(QtGui.QStandardItem(item)) + + for project_name, is_active in items: + + row = QtGui.QStandardItem(project_name) + row.setData(is_active, ProjectListModel.filter_role) + row.setData(False, ProjectListModel.selected_role) + + if is_active: + row.setData(project_name, ProjectListModel.sort_role) + + else: + row.setData("~" + project_name, ProjectListModel.sort_role) + + font = row.font() + font.setItalic(True) + row.setFont(font) + + model.appendRow(row) + + self.project_proxy.sort(0) self.select_project(selected_project) diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index c39d71b055..eb22883c11 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -273,8 +273,11 @@ class AssetWidget(QtWidgets.QWidget): def _set_projects(self): project_names = list() - for project in self.dbcon.projects(): - project_name = project.get("name") + + for doc in self.dbcon.projects(projection={"name": 1}, + only_active=True): + + project_name = doc.get("name") if project_name: project_names.append(project_name) @@ -299,7 +302,9 @@ class AssetWidget(QtWidgets.QWidget): def on_project_change(self): projects = list() - for project in self.dbcon.projects(): + + for project in self.dbcon.projects(projection={"name": 1}, + only_active=True): projects.append(project['name']) project_name = self.combo_projects.currentText() if project_name in projects: