From 0c8a517d075fddd3eff73e68a124a167231977ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 15 Nov 2021 19:06:16 +0100 Subject: [PATCH] separated switch dialog from window file --- .../tools/sceneinventory/switch_dialog.py | 989 ++++++++++++++++ openpype/tools/sceneinventory/widgets.py | 51 + openpype/tools/sceneinventory/window.py | 1021 +---------------- 3 files changed, 1041 insertions(+), 1020 deletions(-) create mode 100644 openpype/tools/sceneinventory/switch_dialog.py create mode 100644 openpype/tools/sceneinventory/widgets.py diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py new file mode 100644 index 0000000000..37659b2370 --- /dev/null +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -0,0 +1,989 @@ +import collections +from Qt import QtWidgets, QtCore + +from avalon import io, api, style +from avalon.vendor import qtawesome + +from .widgets import SearchComboBox + + +class ValidationState: + def __init__(self): + self.asset_ok = True + self.subset_ok = True + self.repre_ok = True + + @property + def all_ok(self): + return ( + self.asset_ok + and self.subset_ok + and self.repre_ok + ) + + +class SwitchAssetDialog(QtWidgets.QDialog): + """Widget to support asset switching""" + + MIN_WIDTH = 550 + + switched = QtCore.Signal() + + def __init__(self, parent=None, items=None): + super(SwitchAssetDialog, self).__init__(parent) + + self.setWindowTitle("Switch selected items ...") + + # Force and keep focus dialog + self.setModal(True) + + assets_combox = SearchComboBox(self) + subsets_combox = SearchComboBox(self) + repres_combobox = SearchComboBox(self) + + assets_combox.set_placeholder("") + subsets_combox.set_placeholder("") + repres_combobox.set_placeholder("") + + asset_label = QtWidgets.QLabel(self) + subset_label = QtWidgets.QLabel(self) + repre_label = QtWidgets.QLabel(self) + + current_asset_btn = QtWidgets.QPushButton("Use current asset") + + accept_icon = qtawesome.icon("fa.check", color="white") + accept_btn = QtWidgets.QPushButton(self) + accept_btn.setIcon(accept_icon) + + main_layout = QtWidgets.QGridLayout(self) + # Asset column + main_layout.addWidget(current_asset_btn, 0, 0) + main_layout.addWidget(assets_combox, 1, 0) + main_layout.addWidget(asset_label, 2, 0) + # Subset column + main_layout.addWidget(subsets_combox, 1, 1) + main_layout.addWidget(subset_label, 2, 1) + # Representation column + main_layout.addWidget(repres_combobox, 1, 2) + main_layout.addWidget(repre_label, 2, 2) + # Btn column + main_layout.addWidget(accept_btn, 1, 3) + + assets_combox.currentIndexChanged.connect( + self._combobox_value_changed + ) + subsets_combox.currentIndexChanged.connect( + self._combobox_value_changed + ) + repres_combobox.currentIndexChanged.connect( + self._combobox_value_changed + ) + accept_btn.clicked.connect(self._on_accept) + current_asset_btn.clicked.connect(self._on_current_asset) + + self._current_asset_btn = current_asset_btn + + self._assets_box = assets_combox + self._subsets_box = subsets_combox + self._representations_box = repres_combobox + + self._asset_label = asset_label + self._subset_label = subset_label + self._repre_label = repre_label + + self._accept_btn = accept_btn + + self._init_asset_name = None + self._init_subset_name = None + self._init_repre_name = None + + self._fill_check = False + + self._items = items + self._prepare_content_data() + self.refresh(True) + + self.setMinimumWidth(self.MIN_WIDTH) + + # Set default focus to accept button so you don't directly type in + # first asset field, this also allows to see the placeholder value. + accept_btn.setFocus() + + def _prepare_content_data(self): + repre_ids = [ + io.ObjectId(item["representation"]) + for item in self._items + ] + repres = list(io.find({ + "type": {"$in": ["representation", "archived_representation"]}, + "_id": {"$in": repre_ids} + })) + repres_by_id = {repre["_id"]: repre for repre in repres} + + # stash context values, works only for single representation + if len(repres) == 1: + self._init_asset_name = repres[0]["context"]["asset"] + self._init_subset_name = repres[0]["context"]["subset"] + self._init_repre_name = repres[0]["context"]["representation"] + + content_repres = {} + archived_repres = [] + missing_repres = [] + version_ids = [] + for repre_id in repre_ids: + if repre_id not in repres_by_id: + missing_repres.append(repre_id) + elif repres_by_id[repre_id]["type"] == "archived_representation": + repre = repres_by_id[repre_id] + archived_repres.append(repre) + version_ids.append(repre["parent"]) + else: + repre = repres_by_id[repre_id] + content_repres[repre_id] = repres_by_id[repre_id] + version_ids.append(repre["parent"]) + + versions = io.find({ + "type": {"$in": ["version", "hero_version"]}, + "_id": {"$in": list(set(version_ids))} + }) + content_versions = {} + hero_version_ids = set() + for version in versions: + content_versions[version["_id"]] = version + if version["type"] == "hero_version": + hero_version_ids.add(version["_id"]) + + missing_versions = [] + subset_ids = [] + for version_id in version_ids: + if version_id not in content_versions: + missing_versions.append(version_id) + else: + subset_ids.append(content_versions[version_id]["parent"]) + + subsets = io.find({ + "type": {"$in": ["subset", "archived_subset"]}, + "_id": {"$in": subset_ids} + }) + subsets_by_id = {sub["_id"]: sub for sub in subsets} + + asset_ids = [] + archived_subsets = [] + missing_subsets = [] + content_subsets = {} + for subset_id in subset_ids: + if subset_id not in subsets_by_id: + missing_subsets.append(subset_id) + elif subsets_by_id[subset_id]["type"] == "archived_subset": + subset = subsets_by_id[subset_id] + asset_ids.append(subset["parent"]) + archived_subsets.append(subset) + else: + subset = subsets_by_id[subset_id] + asset_ids.append(subset["parent"]) + content_subsets[subset_id] = subset + + assets = io.find({ + "type": {"$in": ["asset", "archived_asset"]}, + "_id": {"$in": list(asset_ids)} + }) + assets_by_id = {asset["_id"]: asset for asset in assets} + + missing_assets = [] + archived_assets = [] + content_assets = {} + for asset_id in asset_ids: + if asset_id not in assets_by_id: + missing_assets.append(asset_id) + elif assets_by_id[asset_id]["type"] == "archived_asset": + archived_assets.append(assets_by_id[asset_id]) + else: + content_assets[asset_id] = assets_by_id[asset_id] + + self.content_assets = content_assets + self.content_subsets = content_subsets + self.content_versions = content_versions + self.content_repres = content_repres + + self.hero_version_ids = hero_version_ids + + self.missing_assets = missing_assets + self.missing_versions = missing_versions + self.missing_subsets = missing_subsets + self.missing_repres = missing_repres + self.missing_docs = ( + bool(missing_assets) + or bool(missing_versions) + or bool(missing_subsets) + or bool(missing_repres) + ) + + self.archived_assets = archived_assets + self.archived_subsets = archived_subsets + self.archived_repres = archived_repres + + def _combobox_value_changed(self, *args, **kwargs): + self.refresh() + + def refresh(self, init_refresh=False): + """Build the need comboboxes with content""" + if not self._fill_check and not init_refresh: + return + + self._fill_check = False + + if init_refresh: + asset_values = self._get_asset_box_values() + self._fill_combobox(asset_values, "asset") + + validation_state = ValidationState() + + # Set other comboboxes to empty if any document is missing or any asset + # of loaded representations is archived. + self._is_asset_ok(validation_state) + if validation_state.asset_ok: + subset_values = self._get_subset_box_values() + self._fill_combobox(subset_values, "subset") + self._is_subset_ok(validation_state) + + if validation_state.asset_ok and validation_state.subset_ok: + repre_values = sorted(self._representations_box_values()) + self._fill_combobox(repre_values, "repre") + self._is_repre_ok(validation_state) + + # Fill comboboxes with values + self.set_labels() + self.apply_validations(validation_state) + + if init_refresh: # pre select context if possible + self._assets_box.set_valid_value(self._init_asset_name) + self._subsets_box.set_valid_value(self._init_subset_name) + self._representations_box.set_valid_value(self._init_repre_name) + + self._fill_check = True + + def _get_loaders(self, representations): + if not representations: + return list() + + available_loaders = filter( + lambda l: not (hasattr(l, "is_utility") and l.is_utility), + api.discover(api.Loader) + ) + + loaders = set() + + for representation in representations: + for loader in api.loaders_from_representation( + available_loaders, + representation + ): + loaders.add(loader) + + return loaders + + def _fill_combobox(self, values, combobox_type): + if combobox_type == "asset": + combobox_widget = self._assets_box + elif combobox_type == "subset": + combobox_widget = self._subsets_box + elif combobox_type == "repre": + combobox_widget = self._representations_box + else: + return + selected_value = combobox_widget.get_valid_value() + + # Fill combobox + if values is not None: + combobox_widget.populate(list(sorted(values))) + if selected_value and selected_value in values: + index = None + for idx in range(combobox_widget.count()): + if selected_value == str(combobox_widget.itemText(idx)): + index = idx + break + if index is not None: + combobox_widget.setCurrentIndex(index) + + def set_labels(self): + asset_label = self._assets_box.get_valid_value() + subset_label = self._subsets_box.get_valid_value() + repre_label = self._representations_box.get_valid_value() + + default = "*No changes" + self._asset_label.setText(asset_label or default) + self._subset_label.setText(subset_label or default) + self._repre_label.setText(repre_label or default) + + def apply_validations(self, validation_state): + error_msg = "*Please select" + error_sheet = "border: 1px solid red;" + success_sheet = "border: 1px solid green;" + + asset_sheet = None + subset_sheet = None + repre_sheet = None + accept_sheet = None + if validation_state.asset_ok is False: + asset_sheet = error_sheet + self._asset_label.setText(error_msg) + elif validation_state.subset_ok is False: + subset_sheet = error_sheet + self._subset_label.setText(error_msg) + elif validation_state.repre_ok is False: + repre_sheet = error_sheet + self._repre_label.setText(error_msg) + + if validation_state.all_ok: + accept_sheet = success_sheet + + self._assets_box.setStyleSheet(asset_sheet or "") + self._subsets_box.setStyleSheet(subset_sheet or "") + self._representations_box.setStyleSheet(repre_sheet or "") + + self._accept_btn.setEnabled(validation_state.all_ok) + self._accept_btn.setStyleSheet(accept_sheet or "") + + def _get_asset_box_values(self): + asset_docs = io.find( + {"type": "asset"}, + {"_id": 1, "name": 1} + ) + asset_names_by_id = { + asset_doc["_id"]: asset_doc["name"] + for asset_doc in asset_docs + } + subsets = io.find( + { + "type": "subset", + "parent": {"$in": list(asset_names_by_id.keys())} + }, + { + "parent": 1 + } + ) + + filtered_assets = [] + for subset in subsets: + asset_name = asset_names_by_id[subset["parent"]] + if asset_name not in filtered_assets: + filtered_assets.append(asset_name) + return sorted(filtered_assets) + + def _get_subset_box_values(self): + selected_asset = self._assets_box.get_valid_value() + if selected_asset: + asset_doc = io.find_one({"type": "asset", "name": selected_asset}) + asset_ids = [asset_doc["_id"]] + else: + asset_ids = list(self.content_assets.keys()) + + subsets = io.find( + { + "type": "subset", + "parent": {"$in": asset_ids} + }, + { + "parent": 1, + "name": 1 + } + ) + + subset_names_by_parent_id = collections.defaultdict(set) + for subset in subsets: + subset_names_by_parent_id[subset["parent"]].add(subset["name"]) + + possible_subsets = None + for subset_names in subset_names_by_parent_id.values(): + if possible_subsets is None: + possible_subsets = subset_names + else: + possible_subsets = (possible_subsets & subset_names) + + if not possible_subsets: + break + + return list(possible_subsets or list()) + + def _representations_box_values(self): + # NOTE hero versions are not used because it is expected that + # hero version has same representations as latests + selected_asset = self._assets_box.currentText() + selected_subset = self._subsets_box.currentText() + + # If nothing is selected + # [ ] [ ] [?] + if not selected_asset and not selected_subset: + # Find all representations of selection's subsets + possible_repres = list(io.find( + { + "type": "representation", + "parent": {"$in": list(self.content_versions.keys())} + }, + { + "parent": 1, + "name": 1 + } + )) + + possible_repres_by_parent = collections.defaultdict(set) + for repre in possible_repres: + possible_repres_by_parent[repre["parent"]].add(repre["name"]) + + output_repres = None + for repre_names in possible_repres_by_parent.values(): + if output_repres is None: + output_repres = repre_names + else: + output_repres = (output_repres & repre_names) + + if not output_repres: + break + + return list(output_repres or list()) + + # [x] [x] [?] + if selected_asset and selected_subset: + asset_doc = io.find_one( + {"type": "asset", "name": selected_asset}, + {"_id": 1} + ) + subset_doc = io.find_one( + { + "type": "subset", + "name": selected_subset, + "parent": asset_doc["_id"] + }, + {"_id": 1} + ) + subset_id = subset_doc["_id"] + last_versions_by_subset_id = self.find_last_versions([subset_id]) + version_doc = last_versions_by_subset_id.get(subset_id) + repre_docs = io.find( + { + "type": "representation", + "parent": version_doc["_id"] + }, + { + "name": 1 + } + ) + return [ + repre_doc["name"] + for repre_doc in repre_docs + ] + + # [x] [ ] [?] + # If asset only is selected + if selected_asset: + asset_doc = io.find_one( + {"type": "asset", "name": selected_asset}, + {"_id": 1} + ) + if not asset_doc: + return list() + + # Filter subsets by subset names from content + subset_names = set() + for subset_doc in self.content_subsets.values(): + subset_names.add(subset_doc["name"]) + subset_docs = io.find( + { + "type": "subset", + "parent": asset_doc["_id"], + "name": {"$in": list(subset_names)} + }, + {"_id": 1} + ) + subset_ids = [ + subset_doc["_id"] + for subset_doc in subset_docs + ] + if not subset_ids: + return list() + + last_versions_by_subset_id = self.find_last_versions(subset_ids) + subset_id_by_version_id = {} + for subset_id, last_version in last_versions_by_subset_id.items(): + version_id = last_version["_id"] + subset_id_by_version_id[version_id] = subset_id + + if not subset_id_by_version_id: + return list() + + repre_docs = list(io.find( + { + "type": "representation", + "parent": {"$in": list(subset_id_by_version_id.keys())} + }, + { + "name": 1, + "parent": 1 + } + )) + if not repre_docs: + return list() + + repre_names_by_parent = collections.defaultdict(set) + for repre_doc in repre_docs: + repre_names_by_parent[repre_doc["parent"]].add( + repre_doc["name"] + ) + + available_repres = None + for repre_names in repre_names_by_parent.values(): + if available_repres is None: + available_repres = repre_names + continue + + available_repres = available_repres.intersection(repre_names) + + return list(available_repres) + + # [ ] [x] [?] + subset_docs = list(io.find( + { + "type": "subset", + "parent": {"$in": list(self.content_assets.keys())}, + "name": selected_subset + }, + {"_id": 1, "parent": 1} + )) + if not subset_docs: + return list() + + subset_docs_by_id = { + subset_doc["_id"]: subset_doc + for subset_doc in subset_docs + } + last_versions_by_subset_id = self.find_last_versions( + subset_docs_by_id.keys() + ) + + subset_id_by_version_id = {} + for subset_id, last_version in last_versions_by_subset_id.items(): + version_id = last_version["_id"] + subset_id_by_version_id[version_id] = subset_id + + if not subset_id_by_version_id: + return list() + + repre_docs = list(io.find( + { + "type": "representation", + "parent": {"$in": list(subset_id_by_version_id.keys())} + }, + { + "name": 1, + "parent": 1 + } + )) + if not repre_docs: + return list() + + repre_names_by_asset_id = {} + for repre_doc in repre_docs: + subset_id = subset_id_by_version_id[repre_doc["parent"]] + asset_id = subset_docs_by_id[subset_id]["parent"] + if asset_id not in repre_names_by_asset_id: + repre_names_by_asset_id[asset_id] = set() + repre_names_by_asset_id[asset_id].add(repre_doc["name"]) + + available_repres = None + for repre_names in repre_names_by_asset_id.values(): + if available_repres is None: + available_repres = repre_names + continue + + available_repres = available_repres.intersection(repre_names) + + return list(available_repres) + + def _is_asset_ok(self, validation_state): + selected_asset = self._assets_box.get_valid_value() + if ( + selected_asset is None + and (self.missing_docs or self.archived_assets) + ): + validation_state.asset_ok = False + + def _is_subset_ok(self, validation_state): + selected_asset = self._assets_box.get_valid_value() + selected_subset = self._subsets_box.get_valid_value() + + # [?] [x] [?] + # If subset is selected then must be ok + if selected_subset is not None: + return + + # [ ] [ ] [?] + if selected_asset is None: + # If there were archived subsets and asset is not selected + if self.archived_subsets: + validation_state.subset_ok = False + return + + # [x] [ ] [?] + asset_doc = io.find_one( + {"type": "asset", "name": selected_asset}, + {"_id": 1} + ) + subset_docs = io.find( + {"type": "subset", "parent": asset_doc["_id"]}, + {"name": 1} + ) + subset_names = set( + subset_doc["name"] + for subset_doc in subset_docs + ) + + for subset_doc in self.content_subsets.values(): + if subset_doc["name"] not in subset_names: + validation_state.subset_ok = False + break + + def find_last_versions(self, subset_ids): + _pipeline = [ + # Find all versions of those subsets + {"$match": { + "type": "version", + "parent": {"$in": list(subset_ids)} + }}, + # Sorting versions all together + {"$sort": {"name": 1}}, + # Group them by "parent", but only take the last + {"$group": { + "_id": "$parent", + "_version_id": {"$last": "$_id"}, + "type": {"$last": "$type"} + }} + ] + last_versions_by_subset_id = dict() + for doc in io.aggregate(_pipeline): + doc["parent"] = doc["_id"] + doc["_id"] = doc.pop("_version_id") + last_versions_by_subset_id[doc["parent"]] = doc + return last_versions_by_subset_id + + def _is_repre_ok(self, validation_state): + selected_asset = self._assets_box.get_valid_value() + selected_subset = self._subsets_box.get_valid_value() + selected_repre = self._representations_box.get_valid_value() + + # [?] [?] [x] + # If subset is selected then must be ok + if selected_repre is not None: + return + + # [ ] [ ] [ ] + if selected_asset is None and selected_subset is None: + if ( + self.archived_repres + or self.missing_versions + or self.missing_repres + ): + validation_state.repre_ok = False + return + + # [x] [x] [ ] + if selected_asset is not None and selected_subset is not None: + asset_doc = io.find_one( + {"type": "asset", "name": selected_asset}, + {"_id": 1} + ) + subset_doc = io.find_one( + { + "type": "subset", + "parent": asset_doc["_id"], + "name": selected_subset + }, + {"_id": 1} + ) + last_versions_by_subset_id = self.find_last_versions( + [subset_doc["_id"]] + ) + last_version = last_versions_by_subset_id.get(subset_doc["_id"]) + if not last_version: + validation_state.repre_ok = False + return + + repre_docs = io.find( + { + "type": "representation", + "parent": last_version["_id"] + }, + {"name": 1} + ) + + repre_names = set( + repre_doc["name"] + for repre_doc in repre_docs + ) + for repre_doc in self.content_repres.values(): + if repre_doc["name"] not in repre_names: + validation_state.repre_ok = False + break + return + + # [x] [ ] [ ] + if selected_asset is not None: + asset_doc = io.find_one( + {"type": "asset", "name": selected_asset}, + {"_id": 1} + ) + subset_docs = list(io.find( + { + "type": "subset", + "parent": asset_doc["_id"] + }, + {"_id": 1, "name": 1} + )) + + subset_name_by_id = {} + subset_ids = set() + for subset_doc in subset_docs: + subset_id = subset_doc["_id"] + subset_ids.add(subset_id) + subset_name_by_id[subset_id] = subset_doc["name"] + + last_versions_by_subset_id = self.find_last_versions(subset_ids) + + subset_id_by_version_id = {} + for subset_id, last_version in last_versions_by_subset_id.items(): + version_id = last_version["_id"] + subset_id_by_version_id[version_id] = subset_id + + repre_docs = io.find( + { + "type": "representation", + "parent": {"$in": list(subset_id_by_version_id.keys())} + }, + { + "name": 1, + "parent": 1 + } + ) + repres_by_subset_name = {} + for repre_doc in repre_docs: + subset_id = subset_id_by_version_id[repre_doc["parent"]] + subset_name = subset_name_by_id[subset_id] + if subset_name not in repres_by_subset_name: + repres_by_subset_name[subset_name] = set() + repres_by_subset_name[subset_name].add(repre_doc["name"]) + + for repre_doc in self.content_repres.values(): + version_doc = self.content_versions[repre_doc["parent"]] + subset_doc = self.content_subsets[version_doc["parent"]] + repre_names = ( + repres_by_subset_name.get(subset_doc["name"]) or [] + ) + if repre_doc["name"] not in repre_names: + validation_state.repre_ok = False + break + return + + # [ ] [x] [ ] + # Subset documents + subset_docs = io.find( + { + "type": "subset", + "parent": {"$in": list(self.content_assets.keys())}, + "name": selected_subset + }, + {"_id": 1, "name": 1, "parent": 1} + ) + + subset_docs_by_id = {} + for subset_doc in subset_docs: + subset_docs_by_id[subset_doc["_id"]] = subset_doc + + last_versions_by_subset_id = self.find_last_versions( + subset_docs_by_id.keys() + ) + subset_id_by_version_id = {} + for subset_id, last_version in last_versions_by_subset_id.items(): + version_id = last_version["_id"] + subset_id_by_version_id[version_id] = subset_id + + repre_docs = io.find( + { + "type": "representation", + "parent": {"$in": list(subset_id_by_version_id.keys())} + }, + { + "name": 1, + "parent": 1 + } + ) + repres_by_asset_id = {} + for repre_doc in repre_docs: + subset_id = subset_id_by_version_id[repre_doc["parent"]] + asset_id = subset_docs_by_id[subset_id]["parent"] + if asset_id not in repres_by_asset_id: + repres_by_asset_id[asset_id] = set() + repres_by_asset_id[asset_id].add(repre_doc["name"]) + + for repre_doc in self.content_repres.values(): + version_doc = self.content_versions[repre_doc["parent"]] + subset_doc = self.content_subsets[version_doc["parent"]] + asset_id = subset_doc["parent"] + repre_names = ( + repres_by_asset_id.get(asset_id) or [] + ) + if repre_doc["name"] not in repre_names: + validation_state.repre_ok = False + break + + def _on_current_asset(self): + # Set initial asset as current. + asset_name = io.Session["AVALON_ASSET"] + index = self._assets_box.findText( + asset_name, QtCore.Qt.MatchFixedString + ) + if index >= 0: + print("Setting asset to {}".format(asset_name)) + self._assets_box.setCurrentIndex(index) + + def _on_accept(self): + # Use None when not a valid value or when placeholder value + selected_asset = self._assets_box.get_valid_value() + selected_subset = self._subsets_box.get_valid_value() + selected_representation = self._representations_box.get_valid_value() + + if selected_asset: + asset_doc = io.find_one({"type": "asset", "name": selected_asset}) + asset_docs_by_id = {asset_doc["_id"]: asset_doc} + else: + asset_docs_by_id = self.content_assets + + asset_docs_by_name = { + asset_doc["name"]: asset_doc + for asset_doc in asset_docs_by_id.values() + } + + asset_ids = list(asset_docs_by_id.keys()) + + subset_query = { + "type": "subset", + "parent": {"$in": asset_ids} + } + if selected_subset: + subset_query["name"] = selected_subset + + subset_docs = list(io.find(subset_query)) + subset_ids = [] + subset_docs_by_parent_and_name = collections.defaultdict(dict) + for subset in subset_docs: + subset_ids.append(subset["_id"]) + parent_id = subset["parent"] + name = subset["name"] + subset_docs_by_parent_and_name[parent_id][name] = subset + + # versions + version_docs = list(io.find({ + "type": "version", + "parent": {"$in": subset_ids} + }, sort=[("name", -1)])) + + hero_version_docs = list(io.find({ + "type": "hero_version", + "parent": {"$in": subset_ids} + })) + + version_ids = list() + + version_docs_by_parent_id = {} + for version_doc in version_docs: + parent_id = version_doc["parent"] + if parent_id not in version_docs_by_parent_id: + version_ids.append(version_doc["_id"]) + version_docs_by_parent_id[parent_id] = version_doc + + hero_version_docs_by_parent_id = {} + for hero_version_doc in hero_version_docs: + version_ids.append(hero_version_doc["_id"]) + parent_id = hero_version_doc["parent"] + hero_version_docs_by_parent_id[parent_id] = hero_version_doc + + repre_docs = io.find({ + "type": "representation", + "parent": {"$in": version_ids} + }) + repre_docs_by_parent_id_by_name = collections.defaultdict(dict) + for repre_doc in repre_docs: + parent_id = repre_doc["parent"] + name = repre_doc["name"] + repre_docs_by_parent_id_by_name[parent_id][name] = repre_doc + + for container in self._items: + container_repre_id = io.ObjectId(container["representation"]) + container_repre = self.content_repres[container_repre_id] + container_repre_name = container_repre["name"] + + container_version_id = container_repre["parent"] + container_version = self.content_versions[container_version_id] + + container_subset_id = container_version["parent"] + container_subset = self.content_subsets[container_subset_id] + container_subset_name = container_subset["name"] + + container_asset_id = container_subset["parent"] + container_asset = self.content_assets[container_asset_id] + container_asset_name = container_asset["name"] + + if selected_asset: + asset_doc = asset_docs_by_name[selected_asset] + else: + asset_doc = asset_docs_by_name[container_asset_name] + + subsets_by_name = subset_docs_by_parent_and_name[asset_doc["_id"]] + if selected_subset: + subset_doc = subsets_by_name[selected_subset] + else: + subset_doc = subsets_by_name[container_subset_name] + + repre_doc = None + subset_id = subset_doc["_id"] + if container_version["type"] == "hero_version": + hero_version = hero_version_docs_by_parent_id.get( + subset_id + ) + if hero_version: + _repres = repre_docs_by_parent_id_by_name.get( + hero_version["_id"] + ) + if selected_representation: + repre_doc = _repres.get(selected_representation) + else: + repre_doc = _repres.get(container_repre_name) + + if not repre_doc: + version_doc = version_docs_by_parent_id[subset_id] + version_id = version_doc["_id"] + repres_by_name = repre_docs_by_parent_id_by_name[version_id] + if selected_representation: + repre_doc = repres_by_name[selected_representation] + else: + repre_doc = repres_by_name[container_repre_name] + + try: + api.switch(container, repre_doc) + except Exception: + log.warning( + ( + "Couldn't switch asset." + "See traceback for more information." + ), + exc_info=True + ) + dialog = QtWidgets.QMessageBox() + dialog.setStyleSheet(style.load_stylesheet()) + dialog.setWindowTitle("Switch asset failed") + msg = "Switch asset failed. "\ + "Search console log for more details" + dialog.setText(msg) + dialog.exec_() + + self.switched.emit() + + self.close() diff --git a/openpype/tools/sceneinventory/widgets.py b/openpype/tools/sceneinventory/widgets.py new file mode 100644 index 0000000000..6bb74d2d1b --- /dev/null +++ b/openpype/tools/sceneinventory/widgets.py @@ -0,0 +1,51 @@ +from Qt import QtWidgets, QtCore + + +class SearchComboBox(QtWidgets.QComboBox): + """Searchable ComboBox with empty placeholder value as first value""" + + def __init__(self, parent=None): + super(SearchComboBox, self).__init__(parent) + + self.setEditable(True) + self.setInsertPolicy(self.NoInsert) + + # Apply completer settings + completer = self.completer() + completer.setCompletionMode(completer.PopupCompletion) + completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) + + # Force style sheet on popup menu + # It won't take the parent stylesheet for some reason + # todo: better fix for completer popup stylesheet + # if module.window: + # popup = completer.popup() + # popup.setStyleSheet(module.window.styleSheet()) + + def set_placeholder(self, placeholder): + self.lineEdit().setPlaceholderText(placeholder) + + def populate(self, items): + self.clear() + self.addItems([""]) # ensure first item is placeholder + self.addItems(items) + + def get_valid_value(self): + """Return the current text if it's a valid value else None + + Note: The empty placeholder value is valid and returns as "" + + """ + + text = self.currentText() + lookup = set(self.itemText(i) for i in range(self.count())) + if text not in lookup: + return None + + return text or None + + def set_valid_value(self, value): + """Try to locate 'value' and pre-select it in dropdown.""" + index = self.findText(value) + if index > -1: + self.setCurrentIndex(index) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 93c1debe3d..1bd96ef85e 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -14,6 +14,7 @@ from ..delegates import VersionDelegate from .proxy import FilterProxyModel from .model import InventoryModel +from .switch_dialog import SwitchAssetDialog from openpype.modules import ModulesManager @@ -779,1026 +780,6 @@ class View(QtWidgets.QTreeView): dialog.exec_() -class SearchComboBox(QtWidgets.QComboBox): - """Searchable ComboBox with empty placeholder value as first value""" - - def __init__(self, parent=None, placeholder=""): - super(SearchComboBox, self).__init__(parent) - - self.setEditable(True) - self.setInsertPolicy(self.NoInsert) - self.lineEdit().setPlaceholderText(placeholder) - - # Apply completer settings - completer = self.completer() - completer.setCompletionMode(completer.PopupCompletion) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - - # Force style sheet on popup menu - # It won't take the parent stylesheet for some reason - # todo: better fix for completer popup stylesheet - if module.window: - popup = completer.popup() - popup.setStyleSheet(module.window.styleSheet()) - - def populate(self, items): - self.clear() - self.addItems([""]) # ensure first item is placeholder - self.addItems(items) - - def get_valid_value(self): - """Return the current text if it's a valid value else None - - Note: The empty placeholder value is valid and returns as "" - - """ - - text = self.currentText() - lookup = set(self.itemText(i) for i in range(self.count())) - if text not in lookup: - return None - - return text or None - - def set_valid_value(self, value): - """Try to locate 'value' and pre-select it in dropdown.""" - index = self.findText(value) - if index > -1: - self.setCurrentIndex(index) - - -class ValidationState: - def __init__(self): - self.asset_ok = True - self.subset_ok = True - self.repre_ok = True - - @property - def all_ok(self): - return ( - self.asset_ok - and self.subset_ok - and self.repre_ok - ) - - -class SwitchAssetDialog(QtWidgets.QDialog): - """Widget to support asset switching""" - - MIN_WIDTH = 550 - - fill_check = False - switched = QtCore.Signal() - - def __init__(self, parent=None, items=None): - QtWidgets.QDialog.__init__(self, parent) - - self.setWindowTitle("Switch selected items ...") - - # Force and keep focus dialog - self.setModal(True) - - self._assets_box = SearchComboBox(placeholder="") - self._subsets_box = SearchComboBox(placeholder="") - self._representations_box = SearchComboBox( - placeholder="" - ) - - self._asset_label = QtWidgets.QLabel("") - self._subset_label = QtWidgets.QLabel("") - self._repre_label = QtWidgets.QLabel("") - - self.current_asset_btn = QtWidgets.QPushButton("Use current asset") - - main_layout = QtWidgets.QGridLayout(self) - - accept_icon = qtawesome.icon("fa.check", color="white") - accept_btn = QtWidgets.QPushButton() - accept_btn.setIcon(accept_icon) - accept_btn.setFixedWidth(24) - accept_btn.setFixedHeight(24) - - # Asset column - main_layout.addWidget(self.current_asset_btn, 0, 0) - main_layout.addWidget(self._assets_box, 1, 0) - main_layout.addWidget(self._asset_label, 2, 0) - # Subset column - main_layout.addWidget(self._subsets_box, 1, 1) - main_layout.addWidget(self._subset_label, 2, 1) - # Representation column - main_layout.addWidget(self._representations_box, 1, 2) - main_layout.addWidget(self._repre_label, 2, 2) - # Btn column - main_layout.addWidget(accept_btn, 1, 3) - - self._accept_btn = accept_btn - - self._assets_box.currentIndexChanged.connect( - self._combobox_value_changed - ) - self._subsets_box.currentIndexChanged.connect( - self._combobox_value_changed - ) - self._representations_box.currentIndexChanged.connect( - self._combobox_value_changed - ) - self._accept_btn.clicked.connect(self._on_accept) - self.current_asset_btn.clicked.connect(self._on_current_asset) - - self._init_asset_name = None - self._init_subset_name = None - self._init_repre_name = None - - self._items = items - self._prepare_content_data() - self.refresh(True) - - self.setMinimumWidth(self.MIN_WIDTH) - - # Set default focus to accept button so you don't directly type in - # first asset field, this also allows to see the placeholder value. - accept_btn.setFocus() - - def _prepare_content_data(self): - repre_ids = [ - io.ObjectId(item["representation"]) - for item in self._items - ] - repres = list(io.find({ - "type": {"$in": ["representation", "archived_representation"]}, - "_id": {"$in": repre_ids} - })) - repres_by_id = {repre["_id"]: repre for repre in repres} - - # stash context values, works only for single representation - if len(repres) == 1: - self._init_asset_name = repres[0]["context"]["asset"] - self._init_subset_name = repres[0]["context"]["subset"] - self._init_repre_name = repres[0]["context"]["representation"] - - content_repres = {} - archived_repres = [] - missing_repres = [] - version_ids = [] - for repre_id in repre_ids: - if repre_id not in repres_by_id: - missing_repres.append(repre_id) - elif repres_by_id[repre_id]["type"] == "archived_representation": - repre = repres_by_id[repre_id] - archived_repres.append(repre) - version_ids.append(repre["parent"]) - else: - repre = repres_by_id[repre_id] - content_repres[repre_id] = repres_by_id[repre_id] - version_ids.append(repre["parent"]) - - versions = io.find({ - "type": {"$in": ["version", "hero_version"]}, - "_id": {"$in": list(set(version_ids))} - }) - content_versions = {} - hero_version_ids = set() - for version in versions: - content_versions[version["_id"]] = version - if version["type"] == "hero_version": - hero_version_ids.add(version["_id"]) - - missing_versions = [] - subset_ids = [] - for version_id in version_ids: - if version_id not in content_versions: - missing_versions.append(version_id) - else: - subset_ids.append(content_versions[version_id]["parent"]) - - subsets = io.find({ - "type": {"$in": ["subset", "archived_subset"]}, - "_id": {"$in": subset_ids} - }) - subsets_by_id = {sub["_id"]: sub for sub in subsets} - - asset_ids = [] - archived_subsets = [] - missing_subsets = [] - content_subsets = {} - for subset_id in subset_ids: - if subset_id not in subsets_by_id: - missing_subsets.append(subset_id) - elif subsets_by_id[subset_id]["type"] == "archived_subset": - subset = subsets_by_id[subset_id] - asset_ids.append(subset["parent"]) - archived_subsets.append(subset) - else: - subset = subsets_by_id[subset_id] - asset_ids.append(subset["parent"]) - content_subsets[subset_id] = subset - - assets = io.find({ - "type": {"$in": ["asset", "archived_asset"]}, - "_id": {"$in": list(asset_ids)} - }) - assets_by_id = {asset["_id"]: asset for asset in assets} - - missing_assets = [] - archived_assets = [] - content_assets = {} - for asset_id in asset_ids: - if asset_id not in assets_by_id: - missing_assets.append(asset_id) - elif assets_by_id[asset_id]["type"] == "archived_asset": - archived_assets.append(assets_by_id[asset_id]) - else: - content_assets[asset_id] = assets_by_id[asset_id] - - self.content_assets = content_assets - self.content_subsets = content_subsets - self.content_versions = content_versions - self.content_repres = content_repres - - self.hero_version_ids = hero_version_ids - - self.missing_assets = missing_assets - self.missing_versions = missing_versions - self.missing_subsets = missing_subsets - self.missing_repres = missing_repres - self.missing_docs = ( - bool(missing_assets) - or bool(missing_versions) - or bool(missing_subsets) - or bool(missing_repres) - ) - - self.archived_assets = archived_assets - self.archived_subsets = archived_subsets - self.archived_repres = archived_repres - - def _combobox_value_changed(self, *args, **kwargs): - self.refresh() - - def refresh(self, init_refresh=False): - """Build the need comboboxes with content""" - if not self.fill_check and not init_refresh: - return - - self.fill_check = False - - if init_refresh: - asset_values = self._get_asset_box_values() - self._fill_combobox(asset_values, "asset") - - validation_state = ValidationState() - - # Set other comboboxes to empty if any document is missing or any asset - # of loaded representations is archived. - self._is_asset_ok(validation_state) - if validation_state.asset_ok: - subset_values = self._get_subset_box_values() - self._fill_combobox(subset_values, "subset") - self._is_subset_ok(validation_state) - - if validation_state.asset_ok and validation_state.subset_ok: - repre_values = sorted(self._representations_box_values()) - self._fill_combobox(repre_values, "repre") - self._is_repre_ok(validation_state) - - # Fill comboboxes with values - self.set_labels() - self.apply_validations(validation_state) - - if init_refresh: # pre select context if possible - self._assets_box.set_valid_value(self._init_asset_name) - self._subsets_box.set_valid_value(self._init_subset_name) - self._representations_box.set_valid_value(self._init_repre_name) - - self.fill_check = True - - def _get_loaders(self, representations): - if not representations: - return list() - - available_loaders = filter( - lambda l: not (hasattr(l, "is_utility") and l.is_utility), - api.discover(api.Loader) - ) - - loaders = set() - - for representation in representations: - for loader in api.loaders_from_representation( - available_loaders, - representation - ): - loaders.add(loader) - - return loaders - - def _fill_combobox(self, values, combobox_type): - if combobox_type == "asset": - combobox_widget = self._assets_box - elif combobox_type == "subset": - combobox_widget = self._subsets_box - elif combobox_type == "repre": - combobox_widget = self._representations_box - else: - return - selected_value = combobox_widget.get_valid_value() - - # Fill combobox - if values is not None: - combobox_widget.populate(list(sorted(values))) - if selected_value and selected_value in values: - index = None - for idx in range(combobox_widget.count()): - if selected_value == str(combobox_widget.itemText(idx)): - index = idx - break - if index is not None: - combobox_widget.setCurrentIndex(index) - - def set_labels(self): - asset_label = self._assets_box.get_valid_value() - subset_label = self._subsets_box.get_valid_value() - repre_label = self._representations_box.get_valid_value() - - default = "*No changes" - self._asset_label.setText(asset_label or default) - self._subset_label.setText(subset_label or default) - self._repre_label.setText(repre_label or default) - - def apply_validations(self, validation_state): - error_msg = "*Please select" - error_sheet = "border: 1px solid red;" - success_sheet = "border: 1px solid green;" - - asset_sheet = None - subset_sheet = None - repre_sheet = None - accept_sheet = None - if validation_state.asset_ok is False: - asset_sheet = error_sheet - self._asset_label.setText(error_msg) - elif validation_state.subset_ok is False: - subset_sheet = error_sheet - self._subset_label.setText(error_msg) - elif validation_state.repre_ok is False: - repre_sheet = error_sheet - self._repre_label.setText(error_msg) - - if validation_state.all_ok: - accept_sheet = success_sheet - - self._assets_box.setStyleSheet(asset_sheet or "") - self._subsets_box.setStyleSheet(subset_sheet or "") - self._representations_box.setStyleSheet(repre_sheet or "") - - self._accept_btn.setEnabled(validation_state.all_ok) - self._accept_btn.setStyleSheet(accept_sheet or "") - - def _get_asset_box_values(self): - asset_docs = io.find( - {"type": "asset"}, - {"_id": 1, "name": 1} - ) - asset_names_by_id = { - asset_doc["_id"]: asset_doc["name"] - for asset_doc in asset_docs - } - subsets = io.find( - { - "type": "subset", - "parent": {"$in": list(asset_names_by_id.keys())} - }, - { - "parent": 1 - } - ) - - filtered_assets = [] - for subset in subsets: - asset_name = asset_names_by_id[subset["parent"]] - if asset_name not in filtered_assets: - filtered_assets.append(asset_name) - return sorted(filtered_assets) - - def _get_subset_box_values(self): - selected_asset = self._assets_box.get_valid_value() - if selected_asset: - asset_doc = io.find_one({"type": "asset", "name": selected_asset}) - asset_ids = [asset_doc["_id"]] - else: - asset_ids = list(self.content_assets.keys()) - - subsets = io.find( - { - "type": "subset", - "parent": {"$in": asset_ids} - }, - { - "parent": 1, - "name": 1 - } - ) - - subset_names_by_parent_id = collections.defaultdict(set) - for subset in subsets: - subset_names_by_parent_id[subset["parent"]].add(subset["name"]) - - possible_subsets = None - for subset_names in subset_names_by_parent_id.values(): - if possible_subsets is None: - possible_subsets = subset_names - else: - possible_subsets = (possible_subsets & subset_names) - - if not possible_subsets: - break - - return list(possible_subsets or list()) - - def _representations_box_values(self): - # NOTE hero versions are not used because it is expected that - # hero version has same representations as latests - selected_asset = self._assets_box.currentText() - selected_subset = self._subsets_box.currentText() - - # If nothing is selected - # [ ] [ ] [?] - if not selected_asset and not selected_subset: - # Find all representations of selection's subsets - possible_repres = list(io.find( - { - "type": "representation", - "parent": {"$in": list(self.content_versions.keys())} - }, - { - "parent": 1, - "name": 1 - } - )) - - possible_repres_by_parent = collections.defaultdict(set) - for repre in possible_repres: - possible_repres_by_parent[repre["parent"]].add(repre["name"]) - - output_repres = None - for repre_names in possible_repres_by_parent.values(): - if output_repres is None: - output_repres = repre_names - else: - output_repres = (output_repres & repre_names) - - if not output_repres: - break - - return list(output_repres or list()) - - # [x] [x] [?] - if selected_asset and selected_subset: - asset_doc = io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} - ) - subset_doc = io.find_one( - { - "type": "subset", - "name": selected_subset, - "parent": asset_doc["_id"] - }, - {"_id": 1} - ) - subset_id = subset_doc["_id"] - last_versions_by_subset_id = self.find_last_versions([subset_id]) - version_doc = last_versions_by_subset_id.get(subset_id) - repre_docs = io.find( - { - "type": "representation", - "parent": version_doc["_id"] - }, - { - "name": 1 - } - ) - return [ - repre_doc["name"] - for repre_doc in repre_docs - ] - - # [x] [ ] [?] - # If asset only is selected - if selected_asset: - asset_doc = io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} - ) - if not asset_doc: - return list() - - # Filter subsets by subset names from content - subset_names = set() - for subset_doc in self.content_subsets.values(): - subset_names.add(subset_doc["name"]) - subset_docs = io.find( - { - "type": "subset", - "parent": asset_doc["_id"], - "name": {"$in": list(subset_names)} - }, - {"_id": 1} - ) - subset_ids = [ - subset_doc["_id"] - for subset_doc in subset_docs - ] - if not subset_ids: - return list() - - last_versions_by_subset_id = self.find_last_versions(subset_ids) - subset_id_by_version_id = {} - for subset_id, last_version in last_versions_by_subset_id.items(): - version_id = last_version["_id"] - subset_id_by_version_id[version_id] = subset_id - - if not subset_id_by_version_id: - return list() - - repre_docs = list(io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } - )) - if not repre_docs: - return list() - - repre_names_by_parent = collections.defaultdict(set) - for repre_doc in repre_docs: - repre_names_by_parent[repre_doc["parent"]].add( - repre_doc["name"] - ) - - available_repres = None - for repre_names in repre_names_by_parent.values(): - if available_repres is None: - available_repres = repre_names - continue - - available_repres = available_repres.intersection(repre_names) - - return list(available_repres) - - # [ ] [x] [?] - subset_docs = list(io.find( - { - "type": "subset", - "parent": {"$in": list(self.content_assets.keys())}, - "name": selected_subset - }, - {"_id": 1, "parent": 1} - )) - if not subset_docs: - return list() - - subset_docs_by_id = { - subset_doc["_id"]: subset_doc - for subset_doc in subset_docs - } - last_versions_by_subset_id = self.find_last_versions( - subset_docs_by_id.keys() - ) - - subset_id_by_version_id = {} - for subset_id, last_version in last_versions_by_subset_id.items(): - version_id = last_version["_id"] - subset_id_by_version_id[version_id] = subset_id - - if not subset_id_by_version_id: - return list() - - repre_docs = list(io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } - )) - if not repre_docs: - return list() - - repre_names_by_asset_id = {} - for repre_doc in repre_docs: - subset_id = subset_id_by_version_id[repre_doc["parent"]] - asset_id = subset_docs_by_id[subset_id]["parent"] - if asset_id not in repre_names_by_asset_id: - repre_names_by_asset_id[asset_id] = set() - repre_names_by_asset_id[asset_id].add(repre_doc["name"]) - - available_repres = None - for repre_names in repre_names_by_asset_id.values(): - if available_repres is None: - available_repres = repre_names - continue - - available_repres = available_repres.intersection(repre_names) - - return list(available_repres) - - def _is_asset_ok(self, validation_state): - selected_asset = self._assets_box.get_valid_value() - if ( - selected_asset is None - and (self.missing_docs or self.archived_assets) - ): - validation_state.asset_ok = False - - def _is_subset_ok(self, validation_state): - selected_asset = self._assets_box.get_valid_value() - selected_subset = self._subsets_box.get_valid_value() - - # [?] [x] [?] - # If subset is selected then must be ok - if selected_subset is not None: - return - - # [ ] [ ] [?] - if selected_asset is None: - # If there were archived subsets and asset is not selected - if self.archived_subsets: - validation_state.subset_ok = False - return - - # [x] [ ] [?] - asset_doc = io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} - ) - subset_docs = io.find( - {"type": "subset", "parent": asset_doc["_id"]}, - {"name": 1} - ) - subset_names = set( - subset_doc["name"] - for subset_doc in subset_docs - ) - - for subset_doc in self.content_subsets.values(): - if subset_doc["name"] not in subset_names: - validation_state.subset_ok = False - break - - def find_last_versions(self, subset_ids): - _pipeline = [ - # Find all versions of those subsets - {"$match": { - "type": "version", - "parent": {"$in": list(subset_ids)} - }}, - # Sorting versions all together - {"$sort": {"name": 1}}, - # Group them by "parent", but only take the last - {"$group": { - "_id": "$parent", - "_version_id": {"$last": "$_id"}, - "type": {"$last": "$type"} - }} - ] - last_versions_by_subset_id = dict() - for doc in io.aggregate(_pipeline): - doc["parent"] = doc["_id"] - doc["_id"] = doc.pop("_version_id") - last_versions_by_subset_id[doc["parent"]] = doc - return last_versions_by_subset_id - - def _is_repre_ok(self, validation_state): - selected_asset = self._assets_box.get_valid_value() - selected_subset = self._subsets_box.get_valid_value() - selected_repre = self._representations_box.get_valid_value() - - # [?] [?] [x] - # If subset is selected then must be ok - if selected_repre is not None: - return - - # [ ] [ ] [ ] - if selected_asset is None and selected_subset is None: - if ( - self.archived_repres - or self.missing_versions - or self.missing_repres - ): - validation_state.repre_ok = False - return - - # [x] [x] [ ] - if selected_asset is not None and selected_subset is not None: - asset_doc = io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} - ) - subset_doc = io.find_one( - { - "type": "subset", - "parent": asset_doc["_id"], - "name": selected_subset - }, - {"_id": 1} - ) - last_versions_by_subset_id = self.find_last_versions( - [subset_doc["_id"]] - ) - last_version = last_versions_by_subset_id.get(subset_doc["_id"]) - if not last_version: - validation_state.repre_ok = False - return - - repre_docs = io.find( - { - "type": "representation", - "parent": last_version["_id"] - }, - {"name": 1} - ) - - repre_names = set( - repre_doc["name"] - for repre_doc in repre_docs - ) - for repre_doc in self.content_repres.values(): - if repre_doc["name"] not in repre_names: - validation_state.repre_ok = False - break - return - - # [x] [ ] [ ] - if selected_asset is not None: - asset_doc = io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} - ) - subset_docs = list(io.find( - { - "type": "subset", - "parent": asset_doc["_id"] - }, - {"_id": 1, "name": 1} - )) - - subset_name_by_id = {} - subset_ids = set() - for subset_doc in subset_docs: - subset_id = subset_doc["_id"] - subset_ids.add(subset_id) - subset_name_by_id[subset_id] = subset_doc["name"] - - last_versions_by_subset_id = self.find_last_versions(subset_ids) - - subset_id_by_version_id = {} - for subset_id, last_version in last_versions_by_subset_id.items(): - version_id = last_version["_id"] - subset_id_by_version_id[version_id] = subset_id - - repre_docs = io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } - ) - repres_by_subset_name = {} - for repre_doc in repre_docs: - subset_id = subset_id_by_version_id[repre_doc["parent"]] - subset_name = subset_name_by_id[subset_id] - if subset_name not in repres_by_subset_name: - repres_by_subset_name[subset_name] = set() - repres_by_subset_name[subset_name].add(repre_doc["name"]) - - for repre_doc in self.content_repres.values(): - version_doc = self.content_versions[repre_doc["parent"]] - subset_doc = self.content_subsets[version_doc["parent"]] - repre_names = ( - repres_by_subset_name.get(subset_doc["name"]) or [] - ) - if repre_doc["name"] not in repre_names: - validation_state.repre_ok = False - break - return - - # [ ] [x] [ ] - # Subset documents - subset_docs = io.find( - { - "type": "subset", - "parent": {"$in": list(self.content_assets.keys())}, - "name": selected_subset - }, - {"_id": 1, "name": 1, "parent": 1} - ) - - subset_docs_by_id = {} - for subset_doc in subset_docs: - subset_docs_by_id[subset_doc["_id"]] = subset_doc - - last_versions_by_subset_id = self.find_last_versions( - subset_docs_by_id.keys() - ) - subset_id_by_version_id = {} - for subset_id, last_version in last_versions_by_subset_id.items(): - version_id = last_version["_id"] - subset_id_by_version_id[version_id] = subset_id - - repre_docs = io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } - ) - repres_by_asset_id = {} - for repre_doc in repre_docs: - subset_id = subset_id_by_version_id[repre_doc["parent"]] - asset_id = subset_docs_by_id[subset_id]["parent"] - if asset_id not in repres_by_asset_id: - repres_by_asset_id[asset_id] = set() - repres_by_asset_id[asset_id].add(repre_doc["name"]) - - for repre_doc in self.content_repres.values(): - version_doc = self.content_versions[repre_doc["parent"]] - subset_doc = self.content_subsets[version_doc["parent"]] - asset_id = subset_doc["parent"] - repre_names = ( - repres_by_asset_id.get(asset_id) or [] - ) - if repre_doc["name"] not in repre_names: - validation_state.repre_ok = False - break - - def _on_current_asset(self): - # Set initial asset as current. - asset_name = api.Session["AVALON_ASSET"] - index = self._assets_box.findText( - asset_name, QtCore.Qt.MatchFixedString - ) - if index >= 0: - print("Setting asset to {}".format(asset_name)) - self._assets_box.setCurrentIndex(index) - - def _on_accept(self): - # Use None when not a valid value or when placeholder value - selected_asset = self._assets_box.get_valid_value() - selected_subset = self._subsets_box.get_valid_value() - selected_representation = self._representations_box.get_valid_value() - - if selected_asset: - asset_doc = io.find_one({"type": "asset", "name": selected_asset}) - asset_docs_by_id = {asset_doc["_id"]: asset_doc} - else: - asset_docs_by_id = self.content_assets - - asset_docs_by_name = { - asset_doc["name"]: asset_doc - for asset_doc in asset_docs_by_id.values() - } - - asset_ids = list(asset_docs_by_id.keys()) - - subset_query = { - "type": "subset", - "parent": {"$in": asset_ids} - } - if selected_subset: - subset_query["name"] = selected_subset - - subset_docs = list(io.find(subset_query)) - subset_ids = [] - subset_docs_by_parent_and_name = collections.defaultdict(dict) - for subset in subset_docs: - subset_ids.append(subset["_id"]) - parent_id = subset["parent"] - name = subset["name"] - subset_docs_by_parent_and_name[parent_id][name] = subset - - # versions - version_docs = list(io.find({ - "type": "version", - "parent": {"$in": subset_ids} - }, sort=[("name", -1)])) - - hero_version_docs = list(io.find({ - "type": "hero_version", - "parent": {"$in": subset_ids} - })) - - version_ids = list() - - version_docs_by_parent_id = {} - for version_doc in version_docs: - parent_id = version_doc["parent"] - if parent_id not in version_docs_by_parent_id: - version_ids.append(version_doc["_id"]) - version_docs_by_parent_id[parent_id] = version_doc - - hero_version_docs_by_parent_id = {} - for hero_version_doc in hero_version_docs: - version_ids.append(hero_version_doc["_id"]) - parent_id = hero_version_doc["parent"] - hero_version_docs_by_parent_id[parent_id] = hero_version_doc - - repre_docs = io.find({ - "type": "representation", - "parent": {"$in": version_ids} - }) - repre_docs_by_parent_id_by_name = collections.defaultdict(dict) - for repre_doc in repre_docs: - parent_id = repre_doc["parent"] - name = repre_doc["name"] - repre_docs_by_parent_id_by_name[parent_id][name] = repre_doc - - for container in self._items: - container_repre_id = io.ObjectId(container["representation"]) - container_repre = self.content_repres[container_repre_id] - container_repre_name = container_repre["name"] - - container_version_id = container_repre["parent"] - container_version = self.content_versions[container_version_id] - - container_subset_id = container_version["parent"] - container_subset = self.content_subsets[container_subset_id] - container_subset_name = container_subset["name"] - - container_asset_id = container_subset["parent"] - container_asset = self.content_assets[container_asset_id] - container_asset_name = container_asset["name"] - - if selected_asset: - asset_doc = asset_docs_by_name[selected_asset] - else: - asset_doc = asset_docs_by_name[container_asset_name] - - subsets_by_name = subset_docs_by_parent_and_name[asset_doc["_id"]] - if selected_subset: - subset_doc = subsets_by_name[selected_subset] - else: - subset_doc = subsets_by_name[container_subset_name] - - repre_doc = None - subset_id = subset_doc["_id"] - if container_version["type"] == "hero_version": - hero_version = hero_version_docs_by_parent_id.get( - subset_id - ) - if hero_version: - _repres = repre_docs_by_parent_id_by_name.get( - hero_version["_id"] - ) - if selected_representation: - repre_doc = _repres.get(selected_representation) - else: - repre_doc = _repres.get(container_repre_name) - - if not repre_doc: - version_doc = version_docs_by_parent_id[subset_id] - version_id = version_doc["_id"] - repres_by_name = repre_docs_by_parent_id_by_name[version_id] - if selected_representation: - repre_doc = repres_by_name[selected_representation] - else: - repre_doc = repres_by_name[container_repre_name] - - try: - api.switch(container, repre_doc) - except Exception: - log.warning( - ( - "Couldn't switch asset." - "See traceback for more information." - ), - exc_info=True - ) - dialog = QtWidgets.QMessageBox() - dialog.setStyleSheet(style.load_stylesheet()) - dialog.setWindowTitle("Switch asset failed") - msg = "Switch asset failed. "\ - "Search console log for more details" - dialog.setText(msg) - dialog.exec_() - - self.switched.emit() - - self.close() - - class SceneInventoryWindow(QtWidgets.QDialog): """Scene Inventory window"""