mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
feature/OP-7692_Folder_Batch_publishing_tool
This commit is contained in:
parent
7bf7780dd6
commit
abd609fb3c
8 changed files with 1199 additions and 0 deletions
0
client/ayon_core/hosts/batchpublisher/__init__.py
Normal file
0
client/ayon_core/hosts/batchpublisher/__init__.py
Normal file
97
client/ayon_core/hosts/batchpublisher/addon.py
Normal file
97
client/ayon_core/hosts/batchpublisher/addon.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import click
|
||||
|
||||
# from openpype.lib import get_openpype_execute_args
|
||||
# from openpype.lib.execute import run_detached_process
|
||||
from openpype.modules import OpenPypeModule, ITrayAction, IHostAddon
|
||||
|
||||
|
||||
class BatchPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
|
||||
label = "Batch Publisher"
|
||||
name = "batchpublisher"
|
||||
host_name = "batchpublisher"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
self.enabled = True
|
||||
# UI which must not be created at this time
|
||||
self._dialog = None
|
||||
|
||||
def tray_init(self):
|
||||
return
|
||||
|
||||
def on_action_trigger(self):
|
||||
self.show_dialog()
|
||||
# self.run_batchpublisher()
|
||||
|
||||
def connect_with_modules(self, enabled_modules):
|
||||
"""Collect publish paths from other modules."""
|
||||
return
|
||||
|
||||
# def run_batchpublisher(self):
|
||||
# name = "traypublisher"
|
||||
# args = get_openpype_execute_args(
|
||||
# "module", name, "launch"
|
||||
# # "module", self.name, "launch"
|
||||
# )
|
||||
# print("ARGS" , args)
|
||||
# run_detached_process(args)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
|
||||
def _create_dialog(self):
|
||||
# # Don't recreate dialog if already exists
|
||||
# if self._dialog is not None:
|
||||
# return
|
||||
|
||||
from importlib import reload
|
||||
|
||||
import openpype.hosts.batchpublisher.controller
|
||||
import openpype.hosts.batchpublisher.ui.batch_publisher_model
|
||||
import openpype.hosts.batchpublisher.ui.batch_publisher_delegate
|
||||
import openpype.hosts.batchpublisher.ui.batch_publisher_view
|
||||
import openpype.hosts.batchpublisher.ui.window
|
||||
|
||||
# TODO: These lines are only for testing current branch
|
||||
reload(openpype.hosts.batchpublisher.controller)
|
||||
reload(openpype.hosts.batchpublisher.ui.batch_publisher_model)
|
||||
reload(
|
||||
openpype.hosts.batchpublisher.ui.batch_publisher_delegate)
|
||||
reload(openpype.hosts.batchpublisher.ui.batch_publisher_view)
|
||||
reload(openpype.hosts.batchpublisher.ui.window)
|
||||
|
||||
import openpype.hosts.batchpublisher.deadline
|
||||
import openpype.hosts.batchpublisher.publish
|
||||
import openpype.hosts.batchpublisher.utils
|
||||
|
||||
reload(openpype.hosts.batchpublisher.deadline)
|
||||
reload(openpype.hosts.batchpublisher.publish)
|
||||
reload(openpype.hosts.batchpublisher.utils)
|
||||
|
||||
# from openpype.hosts.batchpublisher.ui.window \
|
||||
# import BatchPublisherWindow
|
||||
self._dialog = openpype.hosts.batchpublisher.ui.window. \
|
||||
BatchPublisherWindow()
|
||||
|
||||
def show_dialog(self):
|
||||
"""Show dialog with connected modules.
|
||||
This can be called from anywhere but can also crash in headless mode.
|
||||
There is no way to prevent addon to do invalid operations if he's
|
||||
not handling them.
|
||||
"""
|
||||
# Make sure dialog is created
|
||||
self._create_dialog()
|
||||
# Show dialog
|
||||
self._dialog.show()
|
||||
|
||||
|
||||
@click.group(BatchPublishAddon.name, help="BatchPublisher related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
def launch():
|
||||
"""Launch BatchPublisher tool UI."""
|
||||
print("LAUNCHING BATCH PUBLISHER")
|
||||
from openpype.hosts.batchpublisher.ui import window
|
||||
window.main()
|
||||
379
client/ayon_core/hosts/batchpublisher/controller.py
Normal file
379
client/ayon_core/hosts/batchpublisher/controller.py
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
import collections
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
|
||||
# from openpype.settings import get_project_settings
|
||||
from openpype.client.entities import (
|
||||
get_projects,
|
||||
get_assets,
|
||||
)
|
||||
from openpype.hosts.batchpublisher import publish
|
||||
|
||||
|
||||
# List that contains dictionary including glob statement to check for match.
|
||||
# If filepath matches then it becomes product type.
|
||||
# TODO: add to OpenPype settings so other studios can change
|
||||
GLOB_SEARCH_TO_PRODUCT_INFO_MAP = [
|
||||
{
|
||||
"glob": "*/fbx/*.fbx",
|
||||
"is_sequence": False,
|
||||
"product_type": "model",
|
||||
"representation_name": "fbx"
|
||||
},
|
||||
{
|
||||
"glob": "*/ma/*.ma",
|
||||
"is_sequence": False,
|
||||
"product_type": "model",
|
||||
"representation_name": "maya"
|
||||
},
|
||||
]
|
||||
|
||||
# Dictionary that maps the extension name to the representation name.
|
||||
# We only use this as fallback if
|
||||
# GLOB_SEARCH_TO_PRODUCT_INFO_MAP doesn't match any glob statement.
|
||||
# TODO: add to OpenPype settings so other studios can change
|
||||
EXT_TO_REP_NAME_MAP = {
|
||||
".nk": "nuke",
|
||||
".ma": "maya",
|
||||
".mb": "maya",
|
||||
".hip": "houdini",
|
||||
".sfx": "silhouette",
|
||||
".mocha": "mocha",
|
||||
".psd": "photoshop"
|
||||
}
|
||||
|
||||
# Dictionary that maps the product type (family) to file extensions
|
||||
# We only use this as fallback if
|
||||
# GLOB_SEARCH_TO_PRODUCT_INFO_MAP doesn't match any glob statement.
|
||||
# TODO: add to OpenPype settings so other studios can change
|
||||
PRODUCT_TYPE_TO_EXT_MAP = {
|
||||
"render": {".exr", ".dpx", ".tif", ".tiff", ".jpg", ".jpeg"},
|
||||
"pointcache": {".abc"},
|
||||
"camera": {".abc", ".fbx"},
|
||||
"reference": {".mov", ".mp4", ".mxf", ".avi", ".wmv"},
|
||||
"workfile": {".nk", ".ma", ".mb", ".hip", ".sfx", ".mocha", ".psd"},
|
||||
"distortion": {".nk", ".exr"},
|
||||
"color_grade": {".ccc", ".cc"},
|
||||
}
|
||||
|
||||
|
||||
class ProductItem(object):
|
||||
def __init__(
|
||||
self,
|
||||
filepath,
|
||||
product_type,
|
||||
representation_name,
|
||||
product_name=None,
|
||||
version=None,
|
||||
comment=None,
|
||||
enabled=True,
|
||||
folder_path=None,
|
||||
task_name=None,
|
||||
frame_start=None,
|
||||
frame_end=None):
|
||||
self.enabled = enabled
|
||||
self.filepath = filepath
|
||||
self.product_type = product_type
|
||||
self.product_name = product_name
|
||||
self.representation_name = representation_name
|
||||
self.version = version
|
||||
self.folder_path = folder_path
|
||||
self.comment = comment
|
||||
self.task_name = task_name
|
||||
self.frame_start = frame_start
|
||||
self.frame_end = frame_end
|
||||
|
||||
self.derive_product_name()
|
||||
|
||||
def derive_product_name(self):
|
||||
filename = os.path.basename(self.filepath)
|
||||
filename_no_ext, extension = os.path.splitext(filename)
|
||||
# Exclude possible frame in product name
|
||||
product_name = filename_no_ext.split(".")[0]
|
||||
# Add the product type as prefix to product name
|
||||
if product_name.startswith("_"):
|
||||
product_name = self.product_type + product_name
|
||||
else:
|
||||
product_name = self.product_type + "_" + product_name
|
||||
# Try to extract version number from filename
|
||||
self.version = None
|
||||
results = re.findall("_v[0-9]*", self.filepath)
|
||||
if results:
|
||||
self.version = int(results[0].replace("_v", ""))
|
||||
# Remove version from product name
|
||||
self.product_name = re.sub("_v[0-9]*", "", product_name)
|
||||
return self.product_name
|
||||
|
||||
@property
|
||||
def defined(self):
|
||||
return all([
|
||||
self.filepath,
|
||||
self.folder_path,
|
||||
self.task_name,
|
||||
self.product_type,
|
||||
self.product_name,
|
||||
self.representation_name])
|
||||
|
||||
|
||||
class HierarchyItem:
|
||||
def __init__(self, folder_name, folder_path, folder_id, parent_id):
|
||||
self.folder_name = folder_name
|
||||
self.folder_path = folder_path
|
||||
self.folder_id = folder_id
|
||||
self.parent_id = parent_id
|
||||
|
||||
|
||||
class BatchPublisherController(object):
|
||||
|
||||
def __init__(self):
|
||||
self._selected_project_name = None
|
||||
self._project_names = None
|
||||
self._asset_docs_by_project = {}
|
||||
self._asset_docs_by_path = {}
|
||||
|
||||
def get_project_names(self):
|
||||
if self._project_names is None:
|
||||
projects = get_projects(fields={"name"})
|
||||
project_names = []
|
||||
for project in projects:
|
||||
project_names.append(project["name"])
|
||||
self._project_names = project_names
|
||||
return self._project_names
|
||||
|
||||
def get_selected_project_name(self):
|
||||
return self._selected_project_name
|
||||
|
||||
def set_selected_project_name(self, project_name):
|
||||
self._selected_project_name = project_name
|
||||
|
||||
def _get_asset_docs(self):
|
||||
"""
|
||||
Returns:
|
||||
dict[str, dict]: Dictionary of asset documents by path.
|
||||
"""
|
||||
|
||||
project_name = self._selected_project_name
|
||||
if not project_name:
|
||||
return {}
|
||||
|
||||
asset_docs = self._asset_docs_by_project.get(project_name)
|
||||
if asset_docs is None:
|
||||
asset_docs = list(get_assets(
|
||||
project_name,
|
||||
fields={
|
||||
"name",
|
||||
# "data.visualParent",
|
||||
"data.parents",
|
||||
"data.tasks",
|
||||
}
|
||||
))
|
||||
asset_docs_by_path = self._prepare_assets_by_path(asset_docs)
|
||||
self._asset_docs_by_project[project_name] = asset_docs_by_path
|
||||
return self._asset_docs_by_project[project_name]
|
||||
|
||||
def get_hierarchy_items(self):
|
||||
"""
|
||||
Returns:
|
||||
list[HierarchyItem]: List of hierarchy items.
|
||||
"""
|
||||
|
||||
asset_docs = self._get_asset_docs()
|
||||
if not asset_docs:
|
||||
return []
|
||||
|
||||
output = []
|
||||
for folder_path, asset_doc in asset_docs.items():
|
||||
folder_name = asset_doc["name"]
|
||||
folder_id = asset_doc["_id"]
|
||||
parent_id = asset_doc["data"]["visualParent"]
|
||||
hierarchy_item = HierarchyItem(
|
||||
folder_name, folder_path, folder_id, parent_id)
|
||||
output.append(hierarchy_item)
|
||||
return output
|
||||
|
||||
def get_task_names(self, folder_path):
|
||||
asset_docs_by_path = self._get_asset_docs()
|
||||
if not asset_docs_by_path:
|
||||
return []
|
||||
asset_doc = asset_docs_by_path.get(folder_path)
|
||||
if not asset_doc:
|
||||
return []
|
||||
return list(asset_doc["data"]["tasks"].keys())
|
||||
|
||||
def _prepare_assets_by_path(self, asset_docs):
|
||||
output = {}
|
||||
for asset_doc in asset_docs:
|
||||
parents = list(asset_doc["data"]["parents"])
|
||||
parents.append(asset_doc["name"])
|
||||
folder_path = "/" + "/".join(parents)
|
||||
output[folder_path] = asset_doc
|
||||
return output
|
||||
|
||||
def get_product_items(self, directory):
|
||||
"""
|
||||
Returns:
|
||||
list[ProductItem]: List of ingest files for the given directory
|
||||
"""
|
||||
product_items = collections.OrderedDict()
|
||||
if not directory or not os.path.exists(directory):
|
||||
return product_items
|
||||
# project_name = self._selected_project_name
|
||||
# project_settings = get_project_settings(project_name)
|
||||
# file_mappings = project_settings["batchpublisher"].get(
|
||||
# "file_mappings", [])
|
||||
file_mappings = GLOB_SEARCH_TO_PRODUCT_INFO_MAP
|
||||
for file_mapping in file_mappings:
|
||||
product_type = file_mapping["product_type"]
|
||||
glob_full_path = directory + "/" + file_mapping["glob"]
|
||||
representation_name = file_mapping.get("representation_name")
|
||||
files = glob.glob(glob_full_path, recursive=False)
|
||||
for filepath in files:
|
||||
filename = os.path.basename(filepath)
|
||||
frame_start = None
|
||||
frame_end = None
|
||||
if filename.count(".") >= 2:
|
||||
# Lets add the star in place of the frame number
|
||||
filepath_parts = filepath.split(".")
|
||||
filepath_parts[-2] = "#" * len(filepath_parts[-2])
|
||||
# Replace the file path with the version with star in it
|
||||
_filepath = ".".join(filepath_parts)
|
||||
frames = self._get_frames_for_filepath(_filepath)
|
||||
if frames:
|
||||
filepath = _filepath
|
||||
frame_start = frames[0]
|
||||
frame_end = frames[-1]
|
||||
# Do not add ingest file path, if it's already been added
|
||||
if filepath in product_items:
|
||||
continue
|
||||
_filename_no_ext, extension = os.path.splitext(filename)
|
||||
# Create representation name from extension
|
||||
representation_name = representation_name or \
|
||||
EXT_TO_REP_NAME_MAP.get(extension)
|
||||
if not representation_name:
|
||||
representation_name = extension.lstrip(".")
|
||||
product_item = ProductItem(
|
||||
filepath,
|
||||
product_type,
|
||||
representation_name,
|
||||
frame_start=frame_start,
|
||||
frame_end=frame_end)
|
||||
product_items[filepath] = product_item
|
||||
|
||||
# Walk the entire directory structure again
|
||||
# and look for product items to add.
|
||||
# This time we are looking for product types
|
||||
# by PRODUCT_TYPE_TO_EXT_MAP
|
||||
for root, _dirs, filenames in os.walk(directory, topdown=False):
|
||||
for filename in filenames:
|
||||
filepath = os.path.join(root, filename)
|
||||
filename = os.path.basename(filepath)
|
||||
# Get frame infomration (if any)
|
||||
frame_start = None
|
||||
frame_end = None
|
||||
if filename.count(".") >= 2:
|
||||
# Lets add the star in place of the frame number
|
||||
filepath_parts = filepath.split(".")
|
||||
filepath_parts[-2] = "*"
|
||||
# Replace the file path with the version with star in it
|
||||
_filepath = ".".join(filepath_parts)
|
||||
frames = self._get_frames_for_filepath(_filepath)
|
||||
if frames:
|
||||
filepath = _filepath
|
||||
frame_start = frames[0]
|
||||
frame_end = frames[-1]
|
||||
# Do not add ingest file path, if it's already been added
|
||||
if filepath in product_items:
|
||||
continue
|
||||
_filename_no_ext, extension = os.path.splitext(filename)
|
||||
product_type = None
|
||||
for _product_type, extensions in \
|
||||
PRODUCT_TYPE_TO_EXT_MAP.items():
|
||||
if extension in extensions:
|
||||
product_type = _product_type
|
||||
break
|
||||
if not product_type:
|
||||
continue
|
||||
# Create representation name from extension
|
||||
representation_name = EXT_TO_REP_NAME_MAP.get(extension)
|
||||
if not representation_name:
|
||||
representation_name = extension.lstrip(".")
|
||||
product_item = ProductItem(
|
||||
filepath,
|
||||
product_type,
|
||||
representation_name,
|
||||
frame_start=frame_start,
|
||||
frame_end=frame_end)
|
||||
product_items[filepath] = product_item
|
||||
|
||||
return list(product_items.values())
|
||||
|
||||
def publish_product_items(self, product_items):
|
||||
"""
|
||||
Args:
|
||||
product_items (list[ProductItem]): List of ingest files to publish.
|
||||
"""
|
||||
|
||||
for product_item in product_items:
|
||||
if product_item.enabled and product_item.defined:
|
||||
self._publish_product_item(product_item)
|
||||
|
||||
def _publish_product_item(self, product_item):
|
||||
msg = f"""
|
||||
Publishing (ingesting): {product_item.filepath}
|
||||
As Folder (Asset): {product_item.folder_path}
|
||||
Task: {product_item.task_name}
|
||||
Product Type (Family): {product_item.product_type}
|
||||
Product Name (Subset): {product_item.product_name}
|
||||
Representation: {product_item.representation_name}
|
||||
Version: {product_item.version}
|
||||
Comment: {product_item.comment}
|
||||
Frame start: {product_item.frame_start}
|
||||
Frame end: {product_item.frame_end}
|
||||
Project: {self._selected_project_name}
|
||||
"""
|
||||
print(msg)
|
||||
publish_data = dict()
|
||||
publish_data["version"] = product_item.version
|
||||
publish_data["comment"] = product_item.comment
|
||||
expected_representations = dict()
|
||||
expected_representations[product_item.representation_name] = \
|
||||
product_item.filepath
|
||||
publish.publish_version_pyblish(
|
||||
self._selected_project_name,
|
||||
product_item.folder_path,
|
||||
product_item.task_name,
|
||||
product_item.product_type,
|
||||
product_item.product_name,
|
||||
expected_representations,
|
||||
publish_data,
|
||||
frame_start=product_item.frame_start,
|
||||
frame_end=product_item.frame_end)
|
||||
# publish.publish_version(
|
||||
# self._selected_project_name,
|
||||
# product_item.folder_path,
|
||||
# product_item.task_name,
|
||||
# product_item.product_type,
|
||||
# product_item.product_name,
|
||||
# expected_representations,
|
||||
# publish_data)
|
||||
# publish.publish_version(
|
||||
# project_name,
|
||||
# asset_name,
|
||||
# task_name,
|
||||
# family_name,
|
||||
# subset_name,
|
||||
# expected_representations,
|
||||
# publish_data,
|
||||
|
||||
def _get_frames_for_filepath(self, filepath):
|
||||
# Collect all the frames found within the paths of glob search string
|
||||
frames = list()
|
||||
for _filepath in glob.glob(filepath):
|
||||
filepath_parts = _filepath.split(".")
|
||||
try:
|
||||
frame = int(filepath_parts[-2])
|
||||
except Exception:
|
||||
continue
|
||||
frames.append(frame)
|
||||
return sorted(frames)
|
||||
0
client/ayon_core/hosts/batchpublisher/ui/__init__.py
Normal file
0
client/ayon_core/hosts/batchpublisher/ui/__init__.py
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import collections
|
||||
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .batch_publisher_model import BatchPublisherModel
|
||||
|
||||
FOLDER_PATH_ROLE = QtCore.Qt.UserRole + 1
|
||||
|
||||
|
||||
class BatchPublisherTableDelegate(QtWidgets.QStyledItemDelegate):
|
||||
|
||||
def __init__(self, controller, parent=None):
|
||||
super(BatchPublisherTableDelegate, self).__init__(parent)
|
||||
self._controller = controller
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
model = index.model()
|
||||
ingest_file = model.get_product_items()[index.row()]
|
||||
|
||||
if index.column() == BatchPublisherModel.COLUMN_OF_FOLDER:
|
||||
editor = None
|
||||
view = parent.parent()
|
||||
# NOTE: Project name has been disabled to change from this dialog
|
||||
accepted, _project_name, folder_path, task_name = \
|
||||
self._on_choose_context(ingest_file.folder_path)
|
||||
if accepted and folder_path:
|
||||
for _index in view.selectedIndexes():
|
||||
model.setData(
|
||||
model.index(_index.row(), model.COLUMN_OF_FOLDER),
|
||||
folder_path,
|
||||
QtCore.Qt.EditRole)
|
||||
if task_name:
|
||||
model.setData(
|
||||
model.index(_index.row(), model.COLUMN_OF_TASK),
|
||||
task_name,
|
||||
QtCore.Qt.EditRole)
|
||||
# # clear the folder
|
||||
# model.setData(index, None, QtCore.Qt.EditRole)
|
||||
# # clear the task
|
||||
# model.setData(
|
||||
# model.index(index.row(), BatchPublisherModel.COLUMN_OF_TASK),
|
||||
# None,
|
||||
# QtCore.Qt.EditRole)
|
||||
# treeview = QtWidgets.QTreeView()
|
||||
# treeview.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
|
||||
# treeview.setSelectionBehavior(QtWidgets.QTreeView.SelectRows)
|
||||
# treeview.setSelectionMode(QtWidgets.QTreeView.SingleSelection)
|
||||
# treeview.setItemsExpandable(True)
|
||||
# treeview.header().setVisible(False)
|
||||
# treeview.setMinimumHeight(250)
|
||||
# editor = ComboBox(parent)
|
||||
# editor.setInsertPolicy(QtWidgets.QComboBox.NoInsert)
|
||||
# editor.setView(treeview)
|
||||
# model = QtGui.QStandardItemModel()
|
||||
# editor.setModel(model)
|
||||
# self._fill_model_with_hierarchy(model)
|
||||
# editor.view().expandAll()
|
||||
# # editor.showPopup()
|
||||
# # editor = QtWidgets.QLineEdit(parent)
|
||||
# # completer = QtWidgets.QCompleter(self._folder_names, self)
|
||||
# # completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
# # editor.setCompleter(completer)
|
||||
return editor
|
||||
|
||||
elif index.column() == BatchPublisherModel.COLUMN_OF_TASK:
|
||||
task_names = self._controller.get_task_names(
|
||||
ingest_file.folder_path)
|
||||
# editor = QtWidgets.QLineEdit(parent)
|
||||
# completer = QtWidgets.QCompleter(
|
||||
# task_names,
|
||||
# self)
|
||||
# completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
# editor.setCompleter(completer)
|
||||
editor = QtWidgets.QComboBox(parent)
|
||||
editor.addItems(task_names)
|
||||
return editor
|
||||
|
||||
elif index.column() == BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE:
|
||||
from openpype.plugins.publish import integrate
|
||||
product_types = sorted(integrate.IntegrateAsset.families)
|
||||
editor = QtWidgets.QComboBox(parent)
|
||||
editor.addItems(product_types)
|
||||
return editor
|
||||
# return QtWidgets.QStyledItemDelegate.createEditor(
|
||||
# self,
|
||||
# parent,
|
||||
# option,
|
||||
# index)
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
# if index.column() == BatchPublisherModel.COLUMN_OF_FOLDER:
|
||||
# editor.blockSignals(True)
|
||||
# # value = index.data(QtCore.Qt.DisplayRole)
|
||||
# # editor.setText(value)
|
||||
# # Lets return the QComboxBox back to unselected state
|
||||
# editor.setRootModelIndex(QtCore.QModelIndex())
|
||||
# editor.setCurrentIndex(-1)
|
||||
# editor.blockSignals(False)
|
||||
if index.column() == BatchPublisherModel.COLUMN_OF_TASK:
|
||||
editor.blockSignals(True)
|
||||
value = index.data(QtCore.Qt.DisplayRole)
|
||||
row = editor.findText(value)
|
||||
editor.setCurrentIndex(row)
|
||||
editor.blockSignals(False)
|
||||
elif index.column() == BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE:
|
||||
editor.blockSignals(True)
|
||||
value = index.data(QtCore.Qt.DisplayRole)
|
||||
row = editor.findText(value)
|
||||
editor.setCurrentIndex(row)
|
||||
editor.blockSignals(False)
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
model = index.model()
|
||||
# if index.column() == BatchPublisherModel.COLUMN_OF_FOLDER:
|
||||
# # value = editor.text()
|
||||
# value = editor.model().data(
|
||||
# editor.view().currentIndex(),
|
||||
# FOLDER_PATH_ROLE)
|
||||
# model.setData(index, value, QtCore.Qt.EditRole)
|
||||
if index.column() == BatchPublisherModel.COLUMN_OF_TASK:
|
||||
value = editor.currentText()
|
||||
model.setData(index, value, QtCore.Qt.EditRole)
|
||||
elif index.column() == BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE:
|
||||
value = editor.currentText()
|
||||
model.setData(index, value, QtCore.Qt.EditRole)
|
||||
|
||||
def _fill_model_with_hierarchy(self, model):
|
||||
hierarchy_items = self._controller.get_hierarchy_items()
|
||||
hierarchy_items_by_parent_id = collections.defaultdict(list)
|
||||
for hierarchy_item in hierarchy_items:
|
||||
hierarchy_items_by_parent_id[hierarchy_item.parent_id].append(
|
||||
hierarchy_item
|
||||
)
|
||||
|
||||
root_item = model.invisibleRootItem()
|
||||
|
||||
hierarchy_queue = collections.deque()
|
||||
hierarchy_queue.append((root_item, None))
|
||||
|
||||
while hierarchy_queue:
|
||||
(parent_item, parent_id) = hierarchy_queue.popleft()
|
||||
new_rows = []
|
||||
for hierarchy_item in hierarchy_items_by_parent_id[parent_id]:
|
||||
new_row = QtGui.QStandardItem(hierarchy_item.folder_name)
|
||||
new_row.setData(hierarchy_item.folder_path, FOLDER_PATH_ROLE)
|
||||
new_row.setData(
|
||||
hierarchy_item.folder_path, QtCore.Qt.ToolTipRole)
|
||||
# new_row.setFlags(
|
||||
# QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
new_rows.append(new_row)
|
||||
hierarchy_queue.append((new_row, hierarchy_item.folder_id))
|
||||
|
||||
if new_rows:
|
||||
parent_item.appendRows(new_rows)
|
||||
|
||||
def _on_choose_context(self, folder_path):
|
||||
from openpype.tools.context_dialog import ContextDialog
|
||||
project_name = self._controller.get_selected_project_name()
|
||||
dialog = ContextDialog()
|
||||
dialog._project_combobox.hide()
|
||||
dialog.set_context(
|
||||
project_name=project_name)
|
||||
accepted = dialog.exec_()
|
||||
if accepted:
|
||||
context = dialog.get_context()
|
||||
project = context["project"]
|
||||
asset = context["asset"]
|
||||
# AYON version of dialog stores the folder path
|
||||
folder_path = context.get("folder_path")
|
||||
if folder_path:
|
||||
# Folder path returned by ContextDialog is missing slash
|
||||
folder_path = "/" + folder_path
|
||||
folder_path = folder_path or asset
|
||||
task_name = context["task"]
|
||||
return accepted, project, folder_path, task_name
|
||||
return accepted, None, None, None
|
||||
|
||||
|
||||
class ComboBox(QtWidgets.QComboBox):
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
# This is to prevent pressing "a" button with folder cell
|
||||
# selected and the "assets" is selected in QComboBox.
|
||||
# A default behaviour coming from QComboBox, when key is pressed
|
||||
# it selects first matching item in QComboBox root model index.
|
||||
# We don't want to select the "assets", since its not a full path
|
||||
# of folder.
|
||||
if event.type() == QtCore.QEvent.KeyPress:
|
||||
return
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
from qtpy import QtCore, QtGui
|
||||
|
||||
from openpype.plugins.publish import integrate
|
||||
|
||||
|
||||
class BatchPublisherModel(QtCore.QAbstractTableModel):
|
||||
HEADER_LABELS = [
|
||||
str(),
|
||||
"Filepath",
|
||||
"Folder",
|
||||
"Task",
|
||||
"Product Type",
|
||||
"Product Name",
|
||||
"Representation",
|
||||
"Version",
|
||||
"Comment"]
|
||||
COLUMN_OF_ENABLED = 0
|
||||
COLUMN_OF_FILEPATH = 1
|
||||
COLUMN_OF_FOLDER = 2
|
||||
COLUMN_OF_TASK = 3
|
||||
COLUMN_OF_PRODUCT_TYPE = 4
|
||||
COLUMN_OF_PRODUCT_NAME = 5
|
||||
COLUMN_OF_REPRESENTATION = 6
|
||||
COLUMN_OF_VERSION = 7
|
||||
COLUMN_OF_COMMENT = 8
|
||||
|
||||
def __init__(self, controller):
|
||||
super(BatchPublisherModel, self).__init__()
|
||||
|
||||
self._controller = controller
|
||||
self._product_items = []
|
||||
|
||||
def set_current_directory(self, directory):
|
||||
self._populate_from_directory(directory)
|
||||
|
||||
def get_product_items(self):
|
||||
return list(self._product_items)
|
||||
|
||||
def rowCount(self, parent=None):
|
||||
if parent is None:
|
||||
parent = QtCore.QModelIndex()
|
||||
return len(self._product_items)
|
||||
|
||||
def columnCount(self, parent=None):
|
||||
if parent is None:
|
||||
parent = QtCore.QModelIndex()
|
||||
return len(BatchPublisherModel.HEADER_LABELS)
|
||||
|
||||
def headerData(self, section, orientation, role=None):
|
||||
if role is None:
|
||||
role = QtCore.Qt.DisplayRole
|
||||
|
||||
if role != QtCore.Qt.DisplayRole:
|
||||
return None
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
return BatchPublisherModel.HEADER_LABELS[section]
|
||||
|
||||
def setData(self, index, value, role=None):
|
||||
column = index.column()
|
||||
row = index.row()
|
||||
product_item = self._product_items[row]
|
||||
if role == QtCore.Qt.EditRole:
|
||||
if column == BatchPublisherModel.COLUMN_OF_FILEPATH:
|
||||
product_item.filepath = value
|
||||
elif column == BatchPublisherModel.COLUMN_OF_FOLDER:
|
||||
# Check folder path is valid in available docs.
|
||||
# Folder path might also be reset to None.
|
||||
asset_docs_by_path = self._controller._get_asset_docs()
|
||||
if value is None or value in asset_docs_by_path:
|
||||
# Update folder path
|
||||
product_item.folder_path = value
|
||||
# Update task name
|
||||
product_item.task_name = None
|
||||
task_names = self._controller.get_task_names(value)
|
||||
if not product_item.task_name and task_names:
|
||||
product_item.task_name = task_names[0]
|
||||
elif column == BatchPublisherModel.COLUMN_OF_TASK:
|
||||
# Check task is valid in availble task names.
|
||||
# Task name might also be reset to None.
|
||||
if value is None or value in self._controller.get_task_names(
|
||||
product_item.folder_path):
|
||||
product_item.task_name = value
|
||||
elif column == BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE:
|
||||
# Check family is valid in available families
|
||||
# Product type might also be reset to None.
|
||||
if value is None or value in integrate.IntegrateAsset.families:
|
||||
product_item.product_type = value
|
||||
# Update the product name based on product type
|
||||
product_item.derive_product_name()
|
||||
roles = [QtCore.Qt.DisplayRole]
|
||||
self.dataChanged.emit(
|
||||
self.index(
|
||||
row, BatchPublisherModel.COLUMN_OF_ENABLED),
|
||||
self.index(
|
||||
row, BatchPublisherModel.COLUMN_OF_COMMENT),
|
||||
roles)
|
||||
elif column == BatchPublisherModel.COLUMN_OF_PRODUCT_NAME:
|
||||
product_item.product_name = value
|
||||
elif column == BatchPublisherModel.COLUMN_OF_REPRESENTATION:
|
||||
product_item.representation_name = value
|
||||
elif column == BatchPublisherModel.COLUMN_OF_VERSION:
|
||||
try:
|
||||
product_item.version = int(value)
|
||||
except Exception:
|
||||
product_item.version = None
|
||||
elif column == BatchPublisherModel.COLUMN_OF_COMMENT:
|
||||
product_item.comment = value
|
||||
return True
|
||||
elif role == QtCore.Qt.CheckStateRole:
|
||||
if column == BatchPublisherModel.COLUMN_OF_ENABLED:
|
||||
enabled = True if value == QtCore.Qt.Checked else False
|
||||
product_item.enabled = enabled
|
||||
roles = [QtCore.Qt.ForegroundRole]
|
||||
self.dataChanged.emit(
|
||||
self.index(row, column),
|
||||
self.index(row, BatchPublisherModel.COLUMN_OF_VERSION),
|
||||
roles)
|
||||
return True
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
column = index.column()
|
||||
row = index.row()
|
||||
product_item = self._product_items[row]
|
||||
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
|
||||
if column == BatchPublisherModel.COLUMN_OF_FILEPATH:
|
||||
return product_item.filepath
|
||||
elif column == BatchPublisherModel.COLUMN_OF_FOLDER:
|
||||
return product_item.folder_path
|
||||
elif column == BatchPublisherModel.COLUMN_OF_TASK:
|
||||
return product_item.task_name
|
||||
elif column == BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE:
|
||||
return product_item.product_type
|
||||
elif column == BatchPublisherModel.COLUMN_OF_PRODUCT_NAME:
|
||||
return product_item.product_name
|
||||
elif column == BatchPublisherModel.COLUMN_OF_REPRESENTATION:
|
||||
return product_item.representation_name
|
||||
elif column == BatchPublisherModel.COLUMN_OF_VERSION:
|
||||
return str(product_item.version or "")
|
||||
elif column == BatchPublisherModel.COLUMN_OF_COMMENT:
|
||||
return product_item.comment
|
||||
elif role == QtCore.Qt.ForegroundRole:
|
||||
if product_item.defined and product_item.enabled:
|
||||
return QtGui.QColor(240, 240, 240)
|
||||
else:
|
||||
return QtGui.QColor(120, 120, 120)
|
||||
# elif role == QtCore.Qt.BackgroundRole:
|
||||
# return QtGui.QColor(QtCore.Qt.white)
|
||||
# elif role == QtCore.Qt.TextAlignmentRole:
|
||||
# return QtCore.Qt.AlignRight
|
||||
elif role == QtCore.Qt.ToolTipRole:
|
||||
project_name = self._controller.get_selected_project_name()
|
||||
task_names = self._controller.get_task_names(
|
||||
product_item.folder_path)
|
||||
tooltip = f"""
|
||||
Enabled: <b>{product_item.enabled}</b>
|
||||
<br>Filepath: <b>{product_item.filepath}</b>
|
||||
<br>Folder (Asset): <b>{product_item.folder_path}</b>
|
||||
<br>Task: <b>{product_item.task_name}</b>
|
||||
<br>Product Type (Family): <b>{product_item.product_type}</b>
|
||||
<br>Product Name (Subset): <b>{product_item.product_name}</b>
|
||||
<br>Representation: <b>{product_item.representation_name}</b>
|
||||
<br>Version: <b>{product_item.version}</b>
|
||||
<br>Comment: <b>{product_item.comment}</b>
|
||||
<br>Frame start: <b>{product_item.frame_start}</b>
|
||||
<br>Frame end: <b>{product_item.frame_end}</b>
|
||||
<br>Defined: <b>{product_item.defined}</b>
|
||||
<br>Task Names: <b>{task_names}</b>
|
||||
<br>Project: <b>{project_name}</b>
|
||||
"""
|
||||
return tooltip
|
||||
|
||||
elif role == QtCore.Qt.CheckStateRole:
|
||||
if column == BatchPublisherModel.COLUMN_OF_ENABLED:
|
||||
return QtCore.Qt.Checked if product_item.enabled \
|
||||
else QtCore.Qt.Unchecked
|
||||
elif role == QtCore.Qt.FontRole:
|
||||
# if column in [
|
||||
# BatchPublisherModel.COLUMN_OF_FILEPATH,
|
||||
# BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE,
|
||||
# BatchPublisherModel.COLUMN_OF_PRODUCT_NAME]:
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(9)
|
||||
return font
|
||||
|
||||
# return None
|
||||
|
||||
def flags(self, index):
|
||||
flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
|
||||
if index.column() == BatchPublisherModel.COLUMN_OF_ENABLED:
|
||||
flags |= QtCore.Qt.ItemIsUserCheckable
|
||||
elif index.column() > BatchPublisherModel.COLUMN_OF_FILEPATH:
|
||||
flags |= QtCore.Qt.ItemIsEditable
|
||||
return flags
|
||||
|
||||
def _populate_from_directory(self, directory):
|
||||
print("model set_current_directory", directory)
|
||||
self.beginResetModel()
|
||||
self._product_items = self._controller.get_product_items(
|
||||
directory
|
||||
)
|
||||
self.endResetModel()
|
||||
|
||||
def _change_project(self, project_name):
|
||||
"""Clear the existing picked folder names, since project changed"""
|
||||
for row in range(self.rowCount()):
|
||||
product_item = self._product_items[row]
|
||||
product_item.folder_path = None
|
||||
product_item.task_name = None
|
||||
roles = [QtCore.Qt.DisplayRole]
|
||||
self.dataChanged.emit(
|
||||
self.index(row, self.COLUMN_OF_ENABLED),
|
||||
self.index(row, self.COLUMN_OF_COMMENT),
|
||||
roles)
|
||||
140
client/ayon_core/hosts/batchpublisher/ui/batch_publisher_view.py
Normal file
140
client/ayon_core/hosts/batchpublisher/ui/batch_publisher_view.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import functools
|
||||
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .batch_publisher_model import BatchPublisherModel
|
||||
|
||||
|
||||
class BatchPublisherTableView(QtWidgets.QTableView):
|
||||
|
||||
def __init__(self, controller, parent=None):
|
||||
super(BatchPublisherTableView, self).__init__(parent)
|
||||
|
||||
model = BatchPublisherModel(controller)
|
||||
self.setModel(model)
|
||||
|
||||
# self.setEditTriggers(self.NoEditTriggers)
|
||||
self.setSelectionMode(self.ExtendedSelection)
|
||||
# self.setSelectionBehavior(self.SelectRows)
|
||||
|
||||
self.setColumnWidth(model.COLUMN_OF_ENABLED, 22)
|
||||
self.setColumnWidth(model.COLUMN_OF_FILEPATH, 700)
|
||||
self.setColumnWidth(model.COLUMN_OF_FOLDER, 200)
|
||||
self.setColumnWidth(model.COLUMN_OF_TASK, 90)
|
||||
self.setColumnWidth(model.COLUMN_OF_PRODUCT_TYPE, 140)
|
||||
self.setColumnWidth(model.COLUMN_OF_PRODUCT_NAME, 275)
|
||||
self.setColumnWidth(model.COLUMN_OF_REPRESENTATION, 120)
|
||||
self.setColumnWidth(model.COLUMN_OF_VERSION, 70)
|
||||
self.setColumnWidth(model.COLUMN_OF_COMMENT, 120)
|
||||
|
||||
self.setTextElideMode(QtCore.Qt.ElideNone)
|
||||
self.setWordWrap(False)
|
||||
|
||||
header = self.horizontalHeader()
|
||||
header.setSectionResizeMode(
|
||||
BatchPublisherModel.COLUMN_OF_FILEPATH,
|
||||
header.Stretch)
|
||||
self.verticalHeader().hide()
|
||||
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self._open_menu)
|
||||
|
||||
self._model = model
|
||||
self._controller = controller
|
||||
|
||||
def set_current_directory(self, directory):
|
||||
print("view set_current_directory", directory)
|
||||
self._model.set_current_directory(directory)
|
||||
|
||||
def get_product_items(self):
|
||||
return self._model.get_product_items()
|
||||
|
||||
def commitData(self, editor):
|
||||
super(BatchPublisherTableView, self).commitData(editor)
|
||||
current_index = self.currentIndex()
|
||||
model = self.currentIndex().model()
|
||||
|
||||
# Apply edit role to every other row of selection
|
||||
value = model.data(current_index, QtCore.Qt.EditRole)
|
||||
for qmodelindex in self.selectedIndexes():
|
||||
# row = qmodelindex.row()
|
||||
# product_item = model.product_items[row]
|
||||
model.setData(qmodelindex, value, role=QtCore.Qt.EditRole)
|
||||
|
||||
# When changing folder we need to propagate
|
||||
# the chosen task value to every other row
|
||||
if current_index.column() == BatchPublisherModel.COLUMN_OF_FOLDER:
|
||||
qmodelindex_task = model.index(
|
||||
current_index.row(),
|
||||
BatchPublisherModel.COLUMN_OF_TASK)
|
||||
value_task = model.data(
|
||||
qmodelindex_task,
|
||||
QtCore.Qt.DisplayRole)
|
||||
for qmodelindex in self.selectedIndexes():
|
||||
qmodelindex_task = model.index(
|
||||
qmodelindex.row(),
|
||||
BatchPublisherModel.COLUMN_OF_TASK)
|
||||
model.setData(
|
||||
qmodelindex_task,
|
||||
value_task,
|
||||
QtCore.Qt.EditRole)
|
||||
|
||||
def _open_menu(self, pos):
|
||||
index = self.indexAt(pos)
|
||||
if not index.isValid():
|
||||
return
|
||||
product_items = self._model.get_product_items()
|
||||
product_item = product_items[index.row()]
|
||||
enabled = not product_item.enabled
|
||||
|
||||
menu = QtWidgets.QMenu()
|
||||
|
||||
action_copy = QtWidgets.QAction()
|
||||
action_copy.setText("Copy selected text")
|
||||
action_copy.triggered.connect(
|
||||
functools.partial(self.__copy_selected_text, pos))
|
||||
menu.addAction(action_copy)
|
||||
|
||||
action_paste = QtWidgets.QAction()
|
||||
action_paste.setText("Paste text into selected cells")
|
||||
action_paste.triggered.connect(
|
||||
functools.partial(self.__paste_selected_text, pos))
|
||||
menu.addAction(action_paste)
|
||||
|
||||
action_toggle_enabled = QtWidgets.QAction()
|
||||
action_toggle_enabled.setText("Toggle enabled")
|
||||
action_toggle_enabled.triggered.connect(
|
||||
functools.partial(self.__toggle_selected_enabled, enabled))
|
||||
menu.addAction(action_toggle_enabled)
|
||||
|
||||
menu.exec_(self.viewport().mapToGlobal(pos))
|
||||
|
||||
def __toggle_selected_enabled(self, enabled):
|
||||
product_items = self._model.get_product_items()
|
||||
for _index in self.selectedIndexes():
|
||||
product_item = product_items[_index.row()]
|
||||
product_item.enabled = enabled
|
||||
roles = [QtCore.Qt.DisplayRole]
|
||||
self._model.dataChanged.emit(
|
||||
self._model.index(_index.row(), self._model.COLUMN_OF_ENABLED),
|
||||
self._model.index(_index.row(), self._model.COLUMN_OF_COMMENT),
|
||||
roles)
|
||||
|
||||
def __copy_selected_text(self, pos):
|
||||
index = self.indexAt(pos)
|
||||
if not index.isValid():
|
||||
return
|
||||
value = self._model.data(index)
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
clipboard.setText(value, QtGui.QClipboard.Clipboard)
|
||||
|
||||
def __paste_selected_text(self, pos):
|
||||
index = self.indexAt(pos)
|
||||
if not index.isValid():
|
||||
return
|
||||
value = QtWidgets.QApplication.clipboard().text()
|
||||
column = index.column()
|
||||
for index in self.selectedIndexes():
|
||||
if column == self._model.COLUMN_OF_FILEPATH:
|
||||
continue
|
||||
self._model.setData(index, value, QtCore.Qt.EditRole)
|
||||
181
client/ayon_core/hosts/batchpublisher/ui/window.py
Normal file
181
client/ayon_core/hosts/batchpublisher/ui/window.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
from openpype import style
|
||||
|
||||
from qtpy import QtWidgets
|
||||
|
||||
from openpype.hosts.batchpublisher import controller
|
||||
from .batch_publisher_model import BatchPublisherModel
|
||||
from .batch_publisher_delegate import BatchPublisherTableDelegate
|
||||
from .batch_publisher_view import BatchPublisherTableView
|
||||
|
||||
|
||||
class BatchPublisherWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(BatchPublisherWindow, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("AYON Batch Publisher")
|
||||
self.resize(1750, 900)
|
||||
|
||||
main_widget = QtWidgets.QWidget(self)
|
||||
self.setCentralWidget(main_widget)
|
||||
|
||||
# --- Top inputs (project, directory) ---
|
||||
top_inputs_widget = QtWidgets.QWidget(self)
|
||||
|
||||
self._project_combobox = QtWidgets.QComboBox(top_inputs_widget)
|
||||
self._project_combobox.setSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding,
|
||||
QtWidgets.QSizePolicy.Fixed)
|
||||
|
||||
dir_inputs_widget = QtWidgets.QWidget(top_inputs_widget)
|
||||
dir_input = QtWidgets.QLineEdit(dir_inputs_widget)
|
||||
dir_browse_btn = QtWidgets.QPushButton("Browse", dir_inputs_widget)
|
||||
|
||||
dir_inputs_layout = QtWidgets.QHBoxLayout(dir_inputs_widget)
|
||||
dir_inputs_layout.setContentsMargins(0, 0, 0, 0)
|
||||
dir_inputs_layout.addWidget(dir_input, 1)
|
||||
dir_inputs_layout.addWidget(dir_browse_btn, 0)
|
||||
|
||||
top_inputs_layout = QtWidgets.QFormLayout(top_inputs_widget)
|
||||
top_inputs_layout.setContentsMargins(0, 0, 0, 0)
|
||||
top_inputs_layout.addRow("Choose project", self._project_combobox)
|
||||
# pushbutton_change_project = QtWidgets.QPushButton("Change project")
|
||||
# top_inputs_layout.addRow(pushbutton_change_project)
|
||||
top_inputs_layout.addRow("Directory to ingest", dir_inputs_widget)
|
||||
|
||||
self._controller = controller.BatchPublisherController()
|
||||
|
||||
# --- Main view ---
|
||||
table_view = BatchPublisherTableView(self._controller, main_widget)
|
||||
|
||||
# --- Footer ---
|
||||
footer_widget = QtWidgets.QWidget(main_widget)
|
||||
|
||||
publish_btn = QtWidgets.QPushButton("Publish", footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_layout.addStretch(1)
|
||||
footer_layout.addWidget(publish_btn, 0)
|
||||
|
||||
# --- Main layout ---
|
||||
main_layout = QtWidgets.QVBoxLayout(main_widget)
|
||||
main_layout.setContentsMargins(12, 12, 12, 12)
|
||||
main_layout.setSpacing(12)
|
||||
main_layout.addWidget(top_inputs_widget, 0)
|
||||
main_layout.addWidget(table_view, 1)
|
||||
main_layout.addWidget(footer_widget, 0)
|
||||
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
self._project_combobox.currentIndexChanged.connect(
|
||||
self._on_project_changed)
|
||||
# pushbutton_change_project.clicked.connect(self._on_project_changed)
|
||||
dir_browse_btn.clicked.connect(self._on_browse_button_clicked)
|
||||
publish_btn.clicked.connect(self._on_publish_button_clicked)
|
||||
|
||||
editors_delegate = BatchPublisherTableDelegate(self._controller)
|
||||
table_view.setItemDelegateForColumn(
|
||||
BatchPublisherModel.COLUMN_OF_FOLDER,
|
||||
editors_delegate)
|
||||
table_view.setItemDelegateForColumn(
|
||||
BatchPublisherModel.COLUMN_OF_TASK,
|
||||
editors_delegate)
|
||||
table_view.setItemDelegateForColumn(
|
||||
BatchPublisherModel.COLUMN_OF_PRODUCT_TYPE,
|
||||
editors_delegate)
|
||||
dir_input.textChanged.connect(self._on_dir_change)
|
||||
|
||||
# self._project_combobox = project_combobox
|
||||
self._dir_input = dir_input
|
||||
self._table_view = table_view
|
||||
self._editors_delegate = editors_delegate
|
||||
self._pushbutton_publish = publish_btn
|
||||
|
||||
self._first_show = True
|
||||
|
||||
def showEvent(self, event):
|
||||
super(BatchPublisherWindow, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
self._on_first_show()
|
||||
|
||||
def _on_first_show(self):
|
||||
project_names = sorted(self._controller.get_project_names())
|
||||
for project_name in project_names:
|
||||
self._project_combobox.addItem(project_name)
|
||||
|
||||
def _on_project_changed(self):
|
||||
project_name = str(self._project_combobox.currentText())
|
||||
self._controller.set_selected_project_name(project_name)
|
||||
self._table_view._model._change_project(project_name)
|
||||
|
||||
def _on_browse_button_clicked(self):
|
||||
directory = self._dir_input.text()
|
||||
directory = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
dir=directory)
|
||||
if not directory:
|
||||
return
|
||||
# Lets insure text changes even if the directory picked
|
||||
# is the same as before
|
||||
self._dir_input.blockSignals(True)
|
||||
self._dir_input.setText(directory)
|
||||
self._dir_input.blockSignals(False)
|
||||
self._dir_input.textChanged.emit(directory)
|
||||
|
||||
def _on_dir_change(self, directory):
|
||||
print("_on_dir_changed")
|
||||
self._table_view.set_current_directory(directory)
|
||||
|
||||
def _on_publish_button_clicked(self):
|
||||
product_items = self._table_view.get_product_items()
|
||||
publish_count = 0
|
||||
enabled_count = 0
|
||||
defined_count = 0
|
||||
for product_item in product_items:
|
||||
if product_item.enabled and product_item.defined:
|
||||
publish_count += 1
|
||||
if product_item.enabled:
|
||||
enabled_count += 1
|
||||
if product_item.defined:
|
||||
defined_count += 1
|
||||
|
||||
if publish_count == 0:
|
||||
msg = "You must provide asset, task, family, "
|
||||
msg += "subset etc and they must be enabled"
|
||||
QtWidgets.QMessageBox.warning(
|
||||
None,
|
||||
"No enabled and defined ingest items!",
|
||||
msg)
|
||||
return
|
||||
elif publish_count > 0:
|
||||
msg = "Are you sure you want to publish "
|
||||
msg += "{} products".format(publish_count)
|
||||
result = QtWidgets.QMessageBox.question(
|
||||
None,
|
||||
"Okay to publish?",
|
||||
msg,
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
if result == QtWidgets.QMessageBox.No:
|
||||
print("User cancelled publishing")
|
||||
return
|
||||
elif enabled_count == 0:
|
||||
QtWidgets.QMessageBox.warning(
|
||||
None,
|
||||
"Nothing enabled for publish!",
|
||||
"There is no items enabled for publish")
|
||||
return
|
||||
elif defined_count == 0:
|
||||
QtWidgets.QMessageBox.warning(
|
||||
None,
|
||||
"No defined ingest items!",
|
||||
"You must provide asset, task, family, subset etc")
|
||||
return
|
||||
|
||||
self._controller.publish_product_items(product_items)
|
||||
|
||||
|
||||
def main():
|
||||
batch_publisher = BatchPublisherWindow()
|
||||
batch_publisher.show()
|
||||
Loading…
Add table
Add a link
Reference in a new issue