From 26fd516700ab6c47383a72f186615eb59b2441b2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 21 Feb 2019 16:38:45 +0100 Subject: [PATCH] Working GUI with comboboxes and create function loading info from presets --- pype/tools/assetcreator/app.py | 389 ++++++++++++++++++++++++++++++--- 1 file changed, 356 insertions(+), 33 deletions(-) diff --git a/pype/tools/assetcreator/app.py b/pype/tools/assetcreator/app.py index 9e4f50bbdb..c47133f986 100644 --- a/pype/tools/assetcreator/app.py +++ b/pype/tools/assetcreator/app.py @@ -1,9 +1,13 @@ +import os import sys +import json +import ftrack_api_old as ftrack_api +from pype import lib as pypelib from avalon.vendor.Qt import QtWidgets, QtCore from avalon import io, api, style from avalon.tools import lib as parentlib -from . import widget +from . import widget, model module = sys.modules[__name__] module.window = None @@ -24,50 +28,87 @@ class Window(QtWidgets.QDialog): # Validators self.valid_parent = False - # assets - assets_widgets = QtWidgets.QWidget() - assets_widgets.setContentsMargins(0, 0, 0, 0) - assets_layout = QtWidgets.QVBoxLayout(assets_widgets) + self.session = None + + # assets widget + assets_widget = QtWidgets.QWidget() + assets_widget.setContentsMargins(0, 0, 0, 0) + assets_layout = QtWidgets.QVBoxLayout(assets_widget) assets = widget.AssetWidget() assets.view.setSelectionMode(assets.view.ExtendedSelection) assets_layout.addWidget(assets) - # info - widget_name = QtWidgets.QWidget() - widget_name.setContentsMargins(0, 0, 0, 0) - layout_name = QtWidgets.QHBoxLayout(widget_name) - label_name = QtWidgets.QLabel("Name:") - input_name = QtWidgets.QLineEdit() - input_name.setPlaceholderText("") - layout_name.addWidget(label_name) - layout_name.addWidget(input_name) - + # Outlink + label_outlink = QtWidgets.QLabel("Outlink:") + input_outlink = QtWidgets.QLineEdit() + input_outlink.setReadOnly(True) + input_outlink.setStyleSheet("background-color: #333333;") # greyed out + checkbox_outlink = QtWidgets.QCheckBox("Use outlink") # Parent - widget_parent = QtWidgets.QWidget() - widget_parent.setContentsMargins(0, 0, 0, 0) - layout_parent = QtWidgets.QHBoxLayout(widget_parent) label_parent = QtWidgets.QLabel("Parent:") input_parent = QtWidgets.QLineEdit() input_parent.setReadOnly(True) input_parent.setStyleSheet("background-color: #333333;") # greyed out - layout_parent.addWidget(label_parent) - layout_parent.addWidget(input_parent) + # Name + label_name = QtWidgets.QLabel("Name:") + input_name = QtWidgets.QLineEdit() + input_name.setPlaceholderText("") + + # Asset Build + label_assetbuild = QtWidgets.QLabel("Asset Build:") + combo_assetbuilt = QtWidgets.QComboBox() + + # Task template + label_task_template = QtWidgets.QLabel("Task template:") + combo_task_template = QtWidgets.QComboBox() + + # Info widget info_widget = QtWidgets.QWidget() - info_widget.setContentsMargins(0, 0, 0, 0) + info_widget.setContentsMargins(10, 10, 10, 10) info_layout = QtWidgets.QVBoxLayout(info_widget) - info_layout.addWidget(widget_parent) - info_layout.addWidget(widget_name) + # Inputs widget + inputs_widget = QtWidgets.QWidget() + inputs_widget.setContentsMargins(0, 0, 0, 0) + + inputs_layout = QtWidgets.QFormLayout(inputs_widget) + inputs_layout.addRow(label_outlink, input_outlink) + inputs_layout.addRow(None, checkbox_outlink) + inputs_layout.addRow(label_parent, input_parent) + inputs_layout.addRow(label_name, input_name) + inputs_layout.addRow(label_assetbuild, combo_assetbuilt) + inputs_layout.addRow(label_task_template, combo_task_template) + + # Add button + btns_widget = QtWidgets.QWidget() + btns_widget.setContentsMargins(0, 0, 0, 0) + btn_layout = QtWidgets.QVBoxLayout(btns_widget) + btn_create_asset = QtWidgets.QPushButton("Create asset") + btn_create_asset.setToolTip( + "Creates all neccessary components for asset" + ) + btn_layout.addWidget(btn_create_asset) + + task_view = QtWidgets.QTreeView() + task_view.setIndentation(0) + task_model = model.TasksTemplateModel() + task_view.setModel(task_model) + + info_layout.addWidget(inputs_widget) + info_layout.addWidget(task_view) + info_layout.addWidget(btns_widget) + + # Body body = QtWidgets.QSplitter() body.setContentsMargins(0, 0, 0, 0) body.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) body.setOrientation(QtCore.Qt.Horizontal) - body.addWidget(assets_widgets) + body.addWidget(assets_widget) body.addWidget(info_widget) body.setStretchFactor(0, 100) - body.setStretchFactor(1, 200) + body.setStretchFactor(1, 150) # statusbar message = QtWidgets.QLabel() @@ -87,27 +128,273 @@ class Window(QtWidgets.QDialog): "message": message, }, "model": { - "assets": assets + "assets": assets, + "tasks": task_model }, "inputs": { + "outlink": input_outlink, + "outlink_cb": checkbox_outlink, "parent": input_parent, - "name": input_name + "name": input_name, + "assetbuild": combo_assetbuilt, + "tasktemplate": combo_task_template }, "buttons": { - + "create_asset": btn_create_asset } } # signals + btn_create_asset.clicked.connect(self.create_asset) assets.selection_changed.connect(self.on_asset_changed) + checkbox_outlink.toggled.connect(self.on_outlink_checkbox_change) + combo_task_template.currentTextChanged.connect( + self.on_task_template_changed + ) # on start - self.on_asset_changed() - self.resize(800, 500) + self.on_start() + + self.resize(600, 500) self.echo("Connected to project: {0}".format(project_name)) - def refresh(self): + def on_start(self): + # Load config + preset_path = pypelib.get_presets_path() + config_file_items = [ + preset_path, 'tools', 'asset_creator', 'config.json' + ] + config_file = os.path.sep.join(config_file_items) + with open(config_file) as data_file: + self.config_data = json.load(data_file) + + # set outlink + input_outlink = self.data['inputs']['outlink'] + checkbox_outlink = self.data['inputs']['outlink_cb'] + outlink_text = io.Session.get('AVALON_ASSET', '') + checkbox_outlink.setChecked(True) + if outlink_text == '': + outlink_text = '< No context >' + checkbox_outlink.setChecked(False) + checkbox_outlink.hide() + input_outlink.setText(outlink_text) + + # load asset build types + self.load_assetbuild_types() + + # Load task templates + self.load_task_templates() self.data["model"]["assets"].refresh() + self.on_asset_changed() + + def create_asset(self): + # Init validation + name_input = self.data['inputs']['name'] + name = name_input.text() + test_name = name.replace(' ', '') + error_message = None + message = QtWidgets.QMessageBox(self) + message.setWindowTitle("Some errors has occured") + message.setIcon(QtWidgets.QMessageBox.Critical) + # TODO: show error messages on any error + if self.valid_parent is not True and test_name == '': + error_message = "Name is not set and Parent is not selected" + elif self.valid_parent is not True: + error_message = "Parent is not selected" + elif test_name == '': + error_message = "Name is not set" + if error_message is not None: + message.setText(error_message) + message.show() + return + + test_name_exists = io.find({ + 'type': 'asset', + 'name': name + }) + existing_assets = [x for x in test_name_exists] + if len(existing_assets) > 0: + message.setText("Entered Asset name is occupied") + message.show() + return + + # Get ftrack session + if self.session is None: + session = ftrack_api.Session() + self.session = session + else: + session = self.session + + # Get Ftrack project entity + project_name = io.Session['AVALON_PROJECT'] + project_query = 'Project where full_name is "{}"'.format(project_name) + try: + ft_project = session.query(project_query).one() + except Exception: + message.setText("Ftrack project was not found") + message.show() + return + + # Get Ftrack entity of parent + ft_asset = None + assets_model = self.data["model"]["assets"] + selected = assets_model.get_selected_assets() + asset = io.find_one({"_id": selected[0], "type": "asset"}) + asset_id = asset.get('data', {}).get('ftrackId', None) + asset_entity_type = asset.get('data', {}).get('entityType', None) + asset_query = '{} where id is "{}"' + if asset_id is not None and asset_entity_type is not None: + try: + ft_asset = session.query(asset_query.format( + asset_entity_type, asset_id) + ).one() + except Exception: + ft_asset = None + + if ft_asset is None: + ft_asset = self.get_ftrack_asset(asset, ft_project) + + if ft_asset is None: + message.setText("Parent's Ftrack entity was not found") + message.show() + return + + asset_build_combo = self.data['inputs']['assetbuild'] + asset_type_name = asset_build_combo.currentText() + asset_type_query = 'Type where name is "{}"'.format(asset_type_name) + try: + asset_type = session.query(asset_type_query).one() + except Exception: + message.setText("Selected Asset Build type does not exists") + message.show() + return + + for children in ft_asset['children']: + if children['name'] == name: + message.setText("Entered Asset name is occupied") + message.show() + return + + task_template_combo = self.data['inputs']['tasktemplate'] + task_template = task_template_combo.currentText() + tasks = self.config_data['task_templates'].get(task_template, []) + + available_task_types = [] + task_types = ft_project['project_schema']['_task_type_schema'] + for task_type in task_types['types']: + available_task_types.append(task_type['name']) + + not_possible_tasks = [] + for task in tasks: + if task not in available_task_types: + not_possible_tasks.append(task) + + if len(not_possible_tasks) != 0: + message.setText(( + "These Task types weren't found" + " in Ftrack project schema:\n{}").format( + ', '.join(not_possible_tasks)) + ) + message.show() + return + + # Create asset build + asset_build_data = { + 'name': name, + 'project_id': ft_project['id'], + 'parent_id': ft_asset['id'], + 'type': asset_type + } + + new_entity = session.create('AssetBuild', asset_build_data) + session.commit() + + task_data = { + 'project_id': ft_project['id'], + 'parent_id': new_entity['id'] + } + + for task in tasks: + type = session.query('Type where name is "{}"'.format(task)).one() + + task_data['type_id'] = type['id'] + task_data['name'] = task + session.create('Task', task_data) + session.commit() + + outlink_cb = self.data['inputs']['outlink_cb'] + if outlink_cb.isChecked() is True: + outlink_input = self.data['inputs']['outlink'] + outlink_name = outlink_input.text() + outlink_asset = io.find_one({ + 'type': 'asset', + 'name': outlink_name + }) + outlink_ft_id = outlink_asset.get('data', {}).get('ftrackId', None) + outlink_entity_type = outlink_asset.get( + 'data', {} + ).get('entityType', None) + if outlink_ft_id is not None and outlink_entity_type is not None: + try: + outlink_entity = session.query(asset_query.format()).one() + except Exception: + outlink_entity = None + + if outlink_entity is None: + outlink_entity = self.get_ftrack_asset( + outlink_asset, ft_project + ) + + if outlink_entity is None: + message.setText("Outlink's Ftrack entity was not found") + message.show() + return + + link_data = { + 'from_id': new_entity['id'], + 'to_id': outlink_entity['id'] + } + session.create('TypedContextLink', link_data) + session.commit() + + message.setWindowTitle("Asset Created") + message.setText("Asset Created successfully") + message.setIcon(QtWidgets.QMessageBox.Information) + message.show() + + def get_ftrack_asset(self, asset, ft_project): + parenthood = [] + parenthood.append(asset['name']) + parenthood.extend(self.get_avalon_parent(asset)) + + output_entity = None + ft_entity = ft_project + index = len(parenthood) - 1 + while True: + name = parenthood[index] + found = False + for children in ft_entity['children']: + if children['name'] == name: + ft_entity = children + found = True + break + if found is False: + return None + if index == 0: + output_entity = ft_entity + break + index -= 1 + + return output_entity + + def get_avalon_parent(self, entity): + parent_id = entity['data']['visualParent'] + parents = [] + if parent_id is not None: + parent = io.find_one({'_id': parent_id}) + parents.append(parent['name']) + parents.extend(self.get_avalon_parent(parent)) + return parents def echo(self, message): widget = self.data["label"]["message"] @@ -117,6 +404,40 @@ class Window(QtWidgets.QDialog): print(message) + def load_task_templates(self): + templates = self.config_data.get('task_templates', {}) + all_names = [] + for name, statuses in templates.items(): + all_names.append(name) + + tt_combobox = self.data['inputs']['tasktemplate'] + tt_combobox.clear() + tt_combobox.addItems(all_names) + + def load_assetbuild_types(self): + types = self.config_data.get('assetbuild_types', []) + + ab_combobox = self.data['inputs']['assetbuild'] + ab_combobox.clear() + ab_combobox.addItems(types) + + def on_outlink_checkbox_change(self): + checkbox_outlink = self.data['inputs']['outlink_cb'] + outlink_input = self.data['inputs']['outlink'] + if checkbox_outlink.isChecked() is True: + outlink_text = io.Session['AVALON_ASSET'] + else: + outlink_text = '< Outlinks won\'t be set >' + + outlink_input.setText(outlink_text) + + def on_task_template_changed(self): + combobox = self.data['inputs']['tasktemplate'] + task_model = self.data['model']['tasks'] + name = combobox.currentText() + tasks = self.config_data['task_templates'].get(name, []) + task_model.set_tasks(tasks) + def on_asset_changed(self): """Callback on asset selection changed @@ -160,7 +481,6 @@ def show(root=None, debug=False, parent=None): window = Window(parent) window.setStyleSheet(style.load_stylesheet()) window.show() - window.refresh() module.window = window @@ -169,12 +489,15 @@ def cli(args): import argparse parser = argparse.ArgumentParser() parser.add_argument("project") + parser.add_argument("asset") args = parser.parse_args(args) project = args.project - + asset = args.asset io.install() api.Session["AVALON_PROJECT"] = project + if asset is not None: + api.Session["AVALON_ASSET"] = asset show()