diff --git a/Prism/Plugins/Apps/3dsMax/Integration/PrismMenu.ms b/Prism/Plugins/Apps/3dsMax/Integration/PrismMenu.ms index 8b8a9282..8290c22c 100644 --- a/Prism/Plugins/Apps/3dsMax/Integration/PrismMenu.ms +++ b/Prism/Plugins/Apps/3dsMax/Integration/PrismMenu.ms @@ -1,24 +1,45 @@ -if menuMan.findMenu "Prism" != undefined then -( -menuMan.unRegisterMenu (menuMan.findMenu "Prism") -) -( -local mainMenuBar = menuMan.getMainMenuBar() -local subMenu = menuMan.createMenu "Prism" -local psaveItem = menuMan.createActionItem "PrismSave" "Prism" -subMenu.addItem psaveItem -1 -local pcommentsaveItem = menuMan.createActionItem "PrismCommentsave" "Prism" -subMenu.addItem pcommentsaveItem -1 -local browserItem = menuMan.createActionItem "OpenProjectBrowser" "Prism" -subMenu.addItem browserItem -1 -local pmanagerItem = menuMan.createActionItem "PrismStateManager" "Prism" -subMenu.addItem pmanagerItem -1 -local settingsItem = menuMan.createActionItem "PrismSettings" "Prism" -subMenu.addItem settingsItem -1 -local subMenuItem = menuMan.createSubMenuItem "Prism" subMenu -local subMenuIndex = mainMenuBar.numItems() -mainMenuBar.addItem subMenuItem subMenuIndex -menuMan.updateMenuBar() -menuMan.saveMenuFile (menuMan.getMenuFile()) ---menuMan.unRegisterMenu (menuMan.findMenu "Prism") +version = getFileVersion "$max/3dsmax.exe" +versionData = filterString version "," +if versionData[1] as Integer >= 27 then ( + function prismMenuCallback = + ( + local menuMgr = callbacks.notificationParam() + local mainMenuBar = menuMgr.mainMenuBar + local helpMenuId = "cee8f758-2199-411b-81e7-d3ff4a80d143" + local newSubMenu = mainMenuBar.CreateSubMenu "F8FFB827-741C-4A81-8C89-BBF856DCF56D" "Prism" beforeId:helpMenuId + newSubMenu.CreateAction "bb767ab4-b239-414f-80dd-d55b0a991ce1" 647394 "PrismSave`Prism" + newSubMenu.CreateAction "bb767ab4-b239-414f-80dd-d55b0a991ce2" 647394 "PrismCommentsave`Prism" + newSubMenu.CreateAction "bb767ab4-b239-414f-80dd-d55b0a991ce3" 647394 "OpenProjectBrowser`Prism" + newSubMenu.CreateAction "bb767ab4-b239-414f-80dd-d55b0a991ce4" 647394 "PrismStateManager`Prism" + newSubMenu.CreateAction "bb767ab4-b239-414f-80dd-d55b0a991ce5" 647394 "PrismSettings`Prism" + ) + callbacks.removeScripts id:#prismMenu + callbacks.addScript #cuiRegisterMenus prismMenuCallback id:#prismMenu + mng = maxops.GetICuiMenuMgr() + mng.LoadConfiguration("") +) else ( + if menuMan.findMenu "Prism" != undefined then + ( + menuMan.unRegisterMenu (menuMan.findMenu "Prism") + ) + ( + local mainMenuBar = menuMan.getMainMenuBar() + local subMenu = menuMan.createMenu "Prism" + local psaveItem = menuMan.createActionItem "PrismSave" "Prism" + subMenu.addItem psaveItem -1 + local pcommentsaveItem = menuMan.createActionItem "PrismCommentsave" "Prism" + subMenu.addItem pcommentsaveItem -1 + local browserItem = menuMan.createActionItem "OpenProjectBrowser" "Prism" + subMenu.addItem browserItem -1 + local pmanagerItem = menuMan.createActionItem "PrismStateManager" "Prism" + subMenu.addItem pmanagerItem -1 + local settingsItem = menuMan.createActionItem "PrismSettings" "Prism" + subMenu.addItem settingsItem -1 + local subMenuItem = menuMan.createSubMenuItem "Prism" subMenu + local subMenuIndex = mainMenuBar.numItems() + mainMenuBar.addItem subMenuItem subMenuIndex + menuMan.updateMenuBar() + menuMan.saveMenuFile (menuMan.getMenuFile()) + --menuMan.unRegisterMenu (menuMan.findMenu "Prism") + ) ) \ No newline at end of file diff --git a/Prism/Plugins/Apps/3dsMax/README.md b/Prism/Plugins/Apps/3dsMax/README.md new file mode 100644 index 00000000..85d63a5c --- /dev/null +++ b/Prism/Plugins/Apps/3dsMax/README.md @@ -0,0 +1 @@ +# PrismPlugin_3dsMax diff --git a/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Integration.py b/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Integration.py index e9faa6a7..9b618edc 100644 --- a/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Integration.py +++ b/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Integration.py @@ -58,7 +58,7 @@ def __init__(self, core, plugin): if platform.system() == "Windows": self.examplePath = ( - os.environ["localappdata"] + "\\Autodesk\\3dsMax\\2024 - 64bit" + os.environ["localappdata"] + "\\Autodesk\\3dsMax\\2025 - 64bit" ) @err_catcher(name=__name__) diff --git a/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Variables.py b/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Variables.py index a9398f5a..7fb87913 100644 --- a/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Variables.py +++ b/Prism/Plugins/Apps/3dsMax/Scripts/Prism_3dsMax_Variables.py @@ -37,7 +37,7 @@ class Prism_3dsMax_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.3" + self.version = "v2.0.5" self.pluginName = "3dsMax" self.pluginType = "App" self.appShortName = "Max" diff --git a/Prism/Plugins/Apps/Blender/Integration/PrismInit.py b/Prism/Plugins/Apps/Blender/Integration/PrismInit.py index 62350f08..ad700ac4 100644 --- a/Prism/Plugins/Apps/Blender/Integration/PrismInit.py +++ b/Prism/Plugins/Apps/Blender/Integration/PrismInit.py @@ -55,9 +55,9 @@ sys.path.insert(0, os.path.join(prismRoot, "Scripts")) import PrismCore -from PySide2.QtCore import * -from PySide2.QtGui import * -from PySide2.QtWidgets import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * from bpy.app.handlers import persistent diff --git a/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Functions.py b/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Functions.py index f51628ec..6536a3e0 100644 --- a/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Functions.py +++ b/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Functions.py @@ -273,7 +273,8 @@ def openScene(self, origin, filepath, force=False): if bpy.app.version < (4, 0, 0): bpy.ops.wm.open_mainfile(ctx, "INVOKE_DEFAULT", filepath=filepath, display_file_selector=False) else: - bpy.ops.wm.open_mainfile(filepath=filepath, display_file_selector=False) + with bpy.context.temp_override(**ctx): + bpy.ops.wm.open_mainfile("INVOKE_DEFAULT", filepath=filepath, display_file_selector=False) except Exception as e: if "File written by newer Blender binary" in str(e): msg = "Warning occurred while opening file:\n\n%s" % str(e) @@ -564,6 +565,31 @@ def exportObj(self, outputName, origin, startFrame, endFrame, expNodes): outputName = foutputName return outputName + @err_catcher(name=__name__) + def exportSelectionToObj(self, outputName): + with bpy.context.temp_override(**self.getOverrideContext()): + bpy.ops.wm.obj_export( + filepath=outputName, + export_selected_objects=True, + export_colors=True, + ) + + return True + + @err_catcher(name=__name__) + def exportSelectionToFbx(self, outputName): + with bpy.context.temp_override(**self.getOverrideContext()): + bpy.ops.export_scene.fbx( + filepath=outputName, + use_selection=True, + bake_anim=False, + colors_type="LINEAR", + apply_unit_scale=False, + global_scale=0.01, + ) + + return True + @err_catcher(name=__name__) def exportFBX(self, outputName, origin, startFrame, endFrame, expNodes): useAnim = startFrame != endFrame @@ -754,6 +780,51 @@ def getOverrideContext(self, origin=None, context=None, dftContext=True): return ctx + @err_catcher(name=__name__) + def registerOperator(self, name, label, code): + def execute(self, context): + exec(code) + return {"FINISHED"} + + opClass = type( + "Prism_" + name, + (bpy.types.Operator,), + { + "bl_idname": "object.prism_%s" % name, + "bl_label": label, + "execute": execute + }, + ) + + bpy.utils.register_class(opClass) + + @err_catcher(name=__name__) + def addMenuToMainMenuBar(self, name, label, options): + for option in options: + self.registerOperator(option["name"], option["label"], option["code"]) + + def draw(self, context): + layout = self.layout + + for option in options: + row = layout.row() + row.operator("object.prism_%s" % option["name"]) + + menuClass = type( + "TOPBAR_MT_" + name, + (bpy.types.Menu,), + { + "bl_label": label, + "draw": draw, + }, + ) + + def draw(self, context): + self.layout.menu("TOPBAR_MT_" + name) + + bpy.utils.register_class(menuClass) + bpy.types.TOPBAR_MT_editor_menus.append(draw) + @err_catcher(name=__name__) def sm_export_preExecute(self, origin, startFrame, endFrame): warnings = [] @@ -995,58 +1066,63 @@ def sm_render_preSubmit(self, origin, rSettings): if len(list(i.links)) > 0: connections.append([i.links[0], idx]) - m.base_path = os.path.dirname(rSettings["outputName"]) + extensions = { + "PNG": ".png", + "JPEG": ".jpg", + "JPEG2000": "jpg", + "TARGA": ".tga", + "TARGA_RAW": ".tga", + "OPEN_EXR_MULTILAYER": ".exr", + "OPEN_EXR": ".exr", + "TIFF": ".tif", + } + nodeExt = extensions[m.format.file_format] + if m.format.file_format == "OPEN_EXR_MULTILAYER": + m.base_path = rSettings["outputName"] + newOutputPath = rSettings["outputName"] + if connections: + usePasses = True + else: + m.base_path = os.path.dirname(rSettings["outputName"]) + for i, idx in connections: + passName = i.from_socket.name - for i, idx in connections: - passName = i.from_socket.name + if passName == "Image": + passName = "beauty" - if passName == "Image": - passName = "beauty" + if i.from_node.type == "R_LAYERS": + if len(rlayerNodes) > 1: + passName = "%s_%s" % (i.from_node.layer, passName) - if i.from_node.type == "R_LAYERS": - if len(rlayerNodes) > 1: - passName = "%s_%s" % (i.from_node.layer, passName) + else: + if hasattr(i.from_node, "label") and i.from_node.label != "": + passName = i.from_node.label - else: - if hasattr(i.from_node, "label") and i.from_node.label != "": - passName = i.from_node.label - - extensions = { - "PNG": ".png", - "JPEG": ".jpg", - "JPEG2000": "jpg", - "TARGA": ".tga", - "TARGA_RAW": ".tga", - "OPEN_EXR_MULTILAYER": ".exr", - "OPEN_EXR": ".exr", - "TIFF": ".tif", - } - nodeExt = extensions[m.format.file_format] - curSlot = m.file_slots[idx] - if curSlot.use_node_format: - ext = nodeExt - else: - ext = extensions[curSlot.format.file_format] - - curSlot.path = "../%s/%s" % ( - passName, - os.path.splitext(os.path.basename(rSettings["outputName"]))[ - 0 - ].replace("beauty", passName) - + ext, - ) - newOutputPath = os.path.abspath( - os.path.join( - rSettings["outputName"], - "../..", + curSlot = m.file_slots[idx] + if curSlot.use_node_format: + ext = nodeExt + else: + ext = extensions[curSlot.format.file_format] + + curSlot.path = "../%s/%s" % ( passName, os.path.splitext(os.path.basename(rSettings["outputName"]))[ 0 ].replace("beauty", passName) + ext, ) - ) - usePasses = True + newOutputPath = os.path.abspath( + os.path.join( + rSettings["outputName"], + "../..", + passName, + os.path.splitext(os.path.basename(rSettings["outputName"]))[ + 0 + ].replace("beauty", passName) + + ext, + ) + ) + usePasses = True if usePasses: rSettings["outputName"] = newOutputPath @@ -1259,10 +1335,10 @@ def sm_render_preExecute(self, origin): return warnings @err_catcher(name=__name__) - def sm_render_fixOutputPath(self, origin, outputName, singleFrame=False): - if not singleFrame: + def sm_render_fixOutputPath(self, origin, outputName, singleFrame=False, state=None): + if (not singleFrame) or self.useNodeAOVs() or (state and not state.gb_submit.isHidden() and state.gb_submit.isChecked()): outputName = ( - os.path.splitext(outputName)[0] + os.path.splitext(outputName)[0].rstrip("#") + "." + "#"*self.core.framePadding + os.path.splitext(outputName)[1] ) @@ -1343,7 +1419,7 @@ def importFBX(self, importPath, origin): bpy.ops.import_scene.fbx(filepath=importPath) @err_catcher(name=__name__) - def importObj(self, importPath, origin): + def importObj(self, importPath, origin=None): if bpy.app.version < (4, 0, 0): bpy.ops.import_scene.obj(self.getOverrideContext(origin), filepath=importPath) else: @@ -1465,6 +1541,18 @@ def getObject(self, node): ): return obj + @err_catcher(name=__name__) + def isolateSelection(self): + if bpy.app.version < (4, 0, 0): + bpy.ops.view3d.localview(self.getOverrideContext(context="VIEW_3D")) + else: + with bpy.context.temp_override(**self.getOverrideContext(context="VIEW_3D")): + print(bpy.context.space_data.local_view) + if bpy.context.space_data.local_view: + bpy.ops.view3d.localview() + + bpy.ops.view3d.localview() + @err_catcher(name=__name__) def sm_import_disableObjectTracking(self, origin): stateGroup = [x for x in self.getGroups() if x.name == origin.setName] @@ -1514,18 +1602,27 @@ def sm_playblast_startup(self, origin): origin.b_resPresets.setMinimumWidth(30 * self.core.uiScaleFactor) origin.b_resPresets.setMinimumHeight(0) origin.b_resPresets.setMaximumHeight(500 * self.core.uiScaleFactor) + origin.cb_formats.addItem(".mp4 (with audio)") @err_catcher(name=__name__) def prePlayblast(self, **kwargs): + outputName = origOutputName = kwargs["outputpath"] + tmpOutputName = os.path.splitext(kwargs["outputpath"])[0].rstrip("#") + tmpOutputName = tmpOutputName.strip(".") + selFmt = kwargs["state"].cb_formats.currentText() + if selFmt == ".mp4 (with audio)": + outputName = tmpOutputName + ".mp4" + renderAnim = kwargs["startframe"] != kwargs["endframe"] if not renderAnim: outputName = ( - os.path.splitext(kwargs["outputpath"])[0] + os.path.splitext(outputName)[0] + "." + ("%0" + str(self.core.framePadding) + "d") % kwargs["startframe"] - + os.path.splitext(kwargs["outputpath"])[1] + + os.path.splitext(outputName)[1] ) + if outputName != origOutputName: return {"outputName": outputName} @err_catcher(name=__name__) @@ -1560,7 +1657,13 @@ def sm_playblast_createPlayblast(self, origin, jobFrames, outputName): bpy.context.scene.render.resolution_percentage = 100 bpy.context.scene.render.filepath = os.path.normpath(outputName) - bpy.context.scene.render.image_settings.file_format = "JPEG" + base, ext = os.path.splitext(outputName) + if ext == ".jpg": + bpy.context.scene.render.image_settings.file_format = "JPEG" + if ext == ".mp4": + bpy.context.scene.render.image_settings.file_format = "FFMPEG" + bpy.context.scene.render.ffmpeg.format = "MPEG4" + bpy.context.scene.render.ffmpeg.audio_codec = "MP3" if bpy.app.version < (4, 0, 0): bpy.ops.render.opengl( diff --git a/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Variables.py b/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Variables.py index 4fb22103..75d02083 100644 --- a/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Variables.py +++ b/Prism/Plugins/Apps/Blender/Scripts/Prism_Blender_Variables.py @@ -37,7 +37,7 @@ class Prism_Blender_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.4" + self.version = "v2.0.10" self.pluginName = "Blender" self.pluginType = "App" self.appShortName = "Bld" diff --git a/Prism/Plugins/Apps/Blender/Scripts/widget_import_scenedata.py b/Prism/Plugins/Apps/Blender/Scripts/widget_import_scenedata.py index 3034f9bf..bb38fad2 100644 --- a/Prism/Plugins/Apps/Blender/Scripts/widget_import_scenedata.py +++ b/Prism/Plugins/Apps/Blender/Scripts/widget_import_scenedata.py @@ -36,17 +36,9 @@ import bpy -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * - - psVersion = 2 -except: - from PySide.QtCore import * - from PySide.QtGui import * - - psVersion = 1 +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * from PrismUtils.Decorators import err_catcher as err_catcher diff --git a/Prism/Plugins/Apps/Houdini/Integration/otls/prism_Filecache.hda b/Prism/Plugins/Apps/Houdini/Integration/otls/prism_Filecache.hda index 29d396b6..9b5dc4d5 100644 Binary files a/Prism/Plugins/Apps/Houdini/Integration/otls/prism_Filecache.hda and b/Prism/Plugins/Apps/Houdini/Integration/otls/prism_Filecache.hda differ diff --git a/Prism/Plugins/Apps/Houdini/Integration/python3.11libs/PrismInit.py b/Prism/Plugins/Apps/Houdini/Integration/python3.11libs/PrismInit.py new file mode 100644 index 00000000..33fbc343 --- /dev/null +++ b/Prism/Plugins/Apps/Houdini/Integration/python3.11libs/PrismInit.py @@ -0,0 +1,37 @@ +import os +import sys + + +def prismInit(prismArgs=[]): + root = os.getenv("PRISM_ROOT", "") + if not root: + if not os.getenv("PRISM_STANDALONE_KARMA", ""): + from PySide2 import QtWidgets + QtWidgets.QMessageBox.warning(None, "Prism", "The environment variable \"PRISM_ROOT\" is not defined. Try to setup the Prism Houdini integration again from the DCC apps tab in the Prism User Settings.") + + return + + scriptPath = os.path.join(root, "Scripts") + if scriptPath not in sys.path: + sys.path.append(scriptPath) + + if "hython" in os.path.basename(sys.executable).lower() and "noUI" not in prismArgs: + prismArgs.append("noUI") + + import PrismCore + + pcore = PrismCore.PrismCore(app="Houdini", prismArgs=prismArgs) + return pcore + + +def createPrismCore(): + if os.getenv("PRISM_ENABLED") == "0": + return + + try: + import PySide2 + except: + return + + global pcore + pcore = prismInit() diff --git a/Prism/Plugins/Apps/Houdini/Integration/python3.11libs/pythonrc.py b/Prism/Plugins/Apps/Houdini/Integration/python3.11libs/pythonrc.py new file mode 100644 index 00000000..0b4f9cdd --- /dev/null +++ b/Prism/Plugins/Apps/Houdini/Integration/python3.11libs/pythonrc.py @@ -0,0 +1,8 @@ +# >>>PrismStart +try: + import PrismInit + + PrismInit.createPrismCore() +except Exception as e: + print(str(e)) +# << 1: + buttons.insert(1, "Yes to all") + + result = self.core.popupQuestion(msg, buttons=buttons, title="Delete State", default="No") if result in ["Yes", "Yes to all"]: try: @@ -1481,8 +1563,10 @@ def getCamNodes(self, origin, cur=False): sceneCams = [] for node in hou.node("/").allSubChildren(): if ( - node.type().name() == "cam" and node.name() != "ipr_camera" - ) or node.type().name() == "vrcam": + (node.type().name() == "cam" and node.name() != "ipr_camera") + or node.type().name() == "vrcam" + or node.type().name() == "lopimportcam" + ): sceneCams.append(node) if cur: diff --git a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Integration.py b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Integration.py index 620c366d..2f606d88 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Integration.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Integration.py @@ -130,13 +130,17 @@ def getPreferencesBasePath(self): def addIntegration(self, installPath): try: + confirmed = False if not os.path.exists(installPath): - msg = "Invalid Houdini path: %s.\n\nThe path has to be the Houdini preferences folder, which usually looks like this: (with your Houdini version):\n\n%s" % (installPath, self.examplePath) - self.core.popup(msg) - return False + msg = "Houdini path doesn't exist:\n\n%s\n\nThe path has to be the Houdini preferences folder, which usually looks like this: (with your Houdini version):\n\n%s" % (installPath, self.examplePath) + result = self.core.popupQuestion(msg, buttons=["Continue", "Cancel"], icon=QMessageBox.Warning, default="Continue") + if result != "Continue": + return False + + confirmed = True houPath = os.path.join(installPath, "bin") - if os.path.exists(houPath): + if os.path.exists(houPath) and not confirmed: msg = "The selected folder seems to be the Houdini installation folder. The Prism integration has to be installed to your Houdini preferences folder. Are you sure you want to continue?\n\n%s" % installPath result = self.core.popupQuestion(msg, icon=QMessageBox.Warning, default="No") if result == "No": diff --git a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Node_Filecache.py b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Node_Filecache.py index 9014e732..0a502e27 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Node_Filecache.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Node_Filecache.py @@ -52,7 +52,6 @@ def __init__(self, plugin): self.core = self.plugin.core self.initState = None self.executeBackground = False - self.nodeExecuted = False self.stateType = "Export" self.listType = "Export" @@ -61,10 +60,12 @@ def getTypeName(self): return "prism::Filecache" @err_catcher(name=__name__) - def getFormats(self): + def getFormats(self, kwargs=None): blacklisted = [".hda", "ShotCam", "other", ".rs"] appFormats = self.core.appPlugin.outputFormats nodeFormats = [f for f in appFormats if f not in blacklisted] + if kwargs and kwargs["node"].parm("showUsdSettings").eval(): + nodeFormats = [n for n in nodeFormats if "usd" in n] tokens = [] for f in nodeFormats: @@ -226,18 +227,36 @@ def showInStateManagerFromNode(self, kwargs): self.plugin.showInStateManagerFromNode(kwargs) @err_catcher(name=__name__) - def openInExplorerFromNode(self, kwargs): + def openInFromNode(self, kwargs): state = self.getStateFromNode(kwargs) if not state: return folderpath = state.ui.l_pathLast.text() - if not os.path.exists(os.path.dirname(folderpath)): - impPath = self.getImportPath() - if os.path.exists(os.path.dirname(impPath)): - folderpath = impPath - self.core.openFolder(folderpath) + parent = self.core.messageParent + menu = QMenu(parent) + + act_open = QAction("Open in Product Browser", parent) + act_open.triggered.connect(lambda: self.openInProductBrowser(folderpath)) + menu.addAction(act_open) + + act_open = QAction("Open in explorer", parent) + act_open.triggered.connect(lambda: self.core.openFolder(folderpath)) + menu.addAction(act_open) + + act_copy = QAction("Copy", parent) + act_copy.triggered.connect(lambda: self.core.copyToClipboard(folderpath, file=True)) + menu.addAction(act_copy) + + menu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def openInProductBrowser(self, path): + self.core.projectBrowser() + self.core.pb.showTab("Products") + data = self.core.paths.getCachePathData(path) + self.core.pb.productBrowser.navigateToProduct(data["product"], entity=data) @err_catcher(name=__name__) def refreshNodeUi(self, node, state, forceCook=False): @@ -283,7 +302,8 @@ def refreshNodeUi(self, node, state, forceCook=False): else: parent3 = None - lopChild = bool(parent3 and node.parm("showLopFetch") and self.core.getPlugin("USD")) + usdPlug = self.core.getPlugin("USD") + lopChild = bool(parent3 and node.parm("showLopFetch") and usdPlug) node.parm("showLopFetch").set(lopChild) @err_catcher(name=__name__) @@ -307,6 +327,10 @@ def refreshContextFromEntity(self, node, entity, needsToCook=False): def getRenderNode(self, node): if node.parm("format").evalAsString() == ".abc": ropName = "write_alembic" + elif node.parm("format").evalAsString() == ".fbx": + ropName = "write_fbx" + elif node.parm("format").evalAsString() in [".usda", ".usdc"]: + ropName = "write_usd" else: ropName = "write_geo" @@ -316,30 +340,57 @@ def getRenderNode(self, node): @err_catcher(name=__name__) def executeNode(self, node): rop = self.getRenderNode(node) + self.updateLatestVersion(node) + if node.parm("useWedging").eval(): + if node.parm("nextVersionWrite").eval(): + node.parm("nextVersionWrite").set(False) + enableNext = True + else: + enableNext = False + + import nodegraphtopui + topnet = node.node("wedging") + node.parm("useWedging").set(0) + node.wedgeInProgress = True + nodegraphtopui.dirtyAll(topnet, False) + topnet.cookOutputWorkItems(block=True) + node.parm("useWedging").set(1) + node.wedgeInProgress = False + node.nodeExecuted = True + if enableNext: + node.parm("nextVersionWrite").set(True) - if self.executeBackground: - parmName = "executebackground" else: - parmName = "execute" + if self.executeBackground: + parmName = "executebackground" + else: + parmName = "execute" - rop.parm(parmName).pressButton() - QCoreApplication.processEvents() - self.updateLatestVersion(node) - node.node("switch_abc").cook(force=True) + rop.parm(parmName).pressButton() + QCoreApplication.processEvents() + + node.node("switch_format").cook(force=True) if ( not self.executeBackground and node.parm("showSuccessPopup").eval() - and self.nodeExecuted + and getattr(node, "nodeExecuted", False) and not rop.errors() + and not getattr(node, "wedgeInProgress", False) ): self.core.popup( "Finished caching successfully.", severity="info", modal=False ) + if not getattr(node, "wedgeInProgress", False): + self.updateLatestVersion(node) + if self.executeBackground: return "background" else: - return True + if node.parm("useWedging").eval(): + return "wedges" + else: + return True @err_catcher(name=__name__) def executePressed(self, kwargs, background=False): @@ -360,7 +411,11 @@ def executePressed(self, kwargs, background=False): ) state.ui.gb_submit.setChecked(False) - self.nodeExecuted = True + if getattr(state.ui.node, "wedgeInProgress", False): + saveScene = False + sanityChecks = False + + state.ui.node.nodeExecuted = True self.executeBackground = background sm.publish( executeState=True, @@ -373,7 +428,10 @@ def executePressed(self, kwargs, background=False): versionWarning=False, ) self.executeBackground = False - self.nodeExecuted = False + state.ui.node.nodeExecuted = False + if not kwargs["node"].parm("autorefresh").eval() and kwargs["node"].parm("latestVersionRead").eval(): + self.refreshImportPath(kwargs) + self.reload(kwargs) @err_catcher(name=__name__) @@ -402,6 +460,29 @@ def nextChanged(self, kwargs): @err_catcher(name=__name__) def latestChanged(self, kwargs): self.updateLatestVersion(kwargs["node"]) + if not kwargs["node"].parm("autorefresh").eval(): + self.refreshImportPath(kwargs) + + @err_catcher(name=__name__) + def masterChanged(self, kwargs): + self.updateLatestVersion(kwargs["node"]) + if not kwargs["node"].parm("autorefresh").eval(): + self.refreshImportPath(kwargs) + + @err_catcher(name=__name__) + def readVersionChanged(self, kwargs): + if not kwargs["node"].parm("autorefresh").eval(): + self.refreshImportPath(kwargs) + + @err_catcher(name=__name__) + def useWedgeChanged(self, kwargs): + if not kwargs["node"].parm("autorefresh").eval(): + self.refreshImportPath(kwargs) + + @err_catcher(name=__name__) + def wedgeChanged(self, kwargs): + if not kwargs["node"].parm("autorefresh").eval(): + self.refreshImportPath(kwargs) @err_catcher(name=__name__) def getReadVersionFromNode(self, node): @@ -479,7 +560,7 @@ def getParentFolder(self, create=True, node=None): stateData = { "statename": "Filecaches", "listtype": "Export", - "stateenabled": "PySide2.QtCore.Qt.CheckState.Checked", + "stateenabled": 2, "stateexpanded": True, } state = sm.createState("Folder", stateData=stateData) @@ -579,6 +660,9 @@ def versionSelected(self, path, mode, kwargs): self.plugin.setNodeParm(kwargs["node"], "readWedge", True, clear=True) self.plugin.setNodeParm(kwargs["node"], "readWedgeNum", int(data["wedge"]), clear=True) + if not kwargs["node"].parm("autorefresh").eval(): + self.refreshImportPath(kwargs) + return version @err_catcher(name=__name__) @@ -666,7 +750,7 @@ def getProductName(self, node): return node.parm("task").unexpandedString() @err_catcher(name=__name__) - def getImportPath(self): + def getImportPath(self, expand=True): if hou.hipFile.isLoadingHipFile(): return "" @@ -688,14 +772,17 @@ def getImportPath(self): wedge = None if version == "latest": - path = self.core.products.getLatestVersionpathFromProduct(product, entity=entity, wedge=wedge) + includeMaster = node.parm("includeMaster").eval() + path = self.core.products.getLatestVersionpathFromProduct(product, includeMaster=includeMaster, entity=entity, wedge=wedge) else: path = self.core.products.getVersionpathFromProductVersion(product, version, entity=entity, wedge=wedge) if path: path = path.replace("\\", "/") path = self.core.appPlugin.detectCacheSequence(path) - path = hou.text.expandString(path) + path = self.core.appPlugin.getPathRelativeToProject(path) if self.core.appPlugin.getUseRelativePath() else path + if expand: + path = hou.text.expandString(path) else: path = "" @@ -718,9 +805,22 @@ def getProductNames(self): names = [name for name in names for _ in range(2)] return names + @err_catcher(name=__name__) + def autoRefreshToggled(self, kwargs): + auto = kwargs["node"].parm("autorefresh").eval() + if auto: + kwargs["node"].parm("importPath").setExpression("hou.phm().getImportPath()", language=hou.exprLanguage.Python) + else: + self.refreshImportPath(kwargs) + + @err_catcher(name=__name__) + def refreshImportPath(self, kwargs): + path = self.getImportPath(expand=False) + self.plugin.setNodeParm(kwargs["node"], "importPath", path, clear=True) + @err_catcher(name=__name__) def reload(self, kwargs): - isAbc = kwargs["node"].parm("switch_abc/input").eval() + isAbc = kwargs["node"].parm("switch_format/input").eval() if isAbc: kwargs["node"].parm("read_alembic/reload").pressButton() else: diff --git a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Arnold.py b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Arnold.py index 345c5d2d..0dbd9da1 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Arnold.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Arnold.py @@ -361,4 +361,5 @@ def getAssOutputPath(origin, renderOutputPath): os.path.dirname(renderOutputPath), "_ass", os.path.basename(renderOutputPath) ) jobOutputFile = os.path.splitext(jobOutputFile)[0] + ".ass" + jobOutputFile = jobOutputFile.replace("\\", "/") return jobOutputFile diff --git a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Mantra.py b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Mantra.py index ff53125a..7d21cfc4 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Mantra.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_Mantra.py @@ -198,8 +198,8 @@ def executeAOVs(origin, outputName): return [origin.state.text(0) + ": error - Publish canceled"] deepPath = os.path.splitext(parmPath)[0] - if deepPath.endswith(".$F4"): - deepPath = deepPath[:-4] + "_deep" + ".$F4" + if deepPath.endswith((".$F" + str(origin.core.framePadding))): + deepPath = deepPath[:-origin.core.framePadding] + "_deep" + (".$F" + str(origin.core.framePadding)) else: deepPath += "_deep" diff --git a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_OpenGL.py b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_OpenGL.py new file mode 100644 index 00000000..fe3d9177 --- /dev/null +++ b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Renderer_OpenGL.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +# +#################################################### +# +# PRISM - Pipeline for animation and VFX projects +# +# www.prism-pipeline.com +# +# contact: contact@prism-pipeline.com +# +#################################################### +# +# +# Copyright (C) 2016-2023 Richard Frangenberg +# Copyright (C) 2023 Prism Software GmbH +# +# Licensed under GNU LGPL-3.0-or-later +# +# This file is part of Prism. +# +# Prism is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Prism is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Prism. If not, see . + + +import os + +import hou + +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * + + +label = "OpenGL" +ropNames = ["opengl"] + + +def isActive(): + return True + + +def getCam(node): + return hou.node(node.parm("camera").eval()) + + +def getFormatFromNode(node): + ext = os.path.splitext(node.parm("picture").eval())[1] + return ext + + +def createROP(origin): + origin.node = origin.core.appPlugin.createRop("opengl") + + +def setAOVData(origin, node, aovNum, item): + pass + + +def getDefaultPasses(origin): + pass + + +def addAOV(origin, aovData): + pass + + +def refreshAOVs(origin): + origin.gb_passes.setVisible(False) + return + + +def deleteAOV(origin, row): + pass + + +def aovDbClick(origin, event): + pass + + +def setCam(origin, node, val): + return origin.core.appPlugin.setNodeParm(node, "camera", val=val) + + +def executeAOVs(origin, outputName): + parmPath = origin.core.appPlugin.getPathRelativeToProject(outputName) if origin.core.appPlugin.getUseRelativePath() else outputName + if not origin.core.appPlugin.setNodeParm(origin.node, "picture", val=parmPath): + return [origin.state.text(0) + ": error - Publish canceled"] + + return True + + +def setResolution(origin): + if not origin.core.appPlugin.setNodeParm( + origin.node, "tres", val=True + ): + return [origin.state.text(0) + ": error - Publish canceled"] + if not origin.core.appPlugin.setNodeParm( + origin.node, "res1", val=origin.sp_resWidth.value() + ): + return [origin.state.text(0) + ": error - Publish canceled"] + if not origin.core.appPlugin.setNodeParm( + origin.node, "res2", val=origin.sp_resHeight.value() + ): + return [origin.state.text(0) + ": error - Publish canceled"] + + return True + + +def executeRender(origin): + origin.node.parm("execute").pressButton() + return True + + +def postExecute(origin): + return True diff --git a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Variables.py b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Variables.py index 4f80931f..d54b876e 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Variables.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/Prism_Houdini_Variables.py @@ -37,7 +37,7 @@ class Prism_Houdini_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.4" + self.version = "v2.0.10" self.pluginName = "Houdini" self.pluginType = "App" self.appShortName = "Hou" @@ -49,6 +49,8 @@ def __init__(self, core, plugin): ".bgeo", ".vdb", ".abc", + ".usda", + ".usdc", ".fbx", ".obj", "ShotCam", diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py index 59589436..7463c3a2 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py @@ -1,10 +1,10 @@ fname = "hou_ImageRender" -fname = "hou_ImportFile" -fname = "hou_Export" -fname = "hou_InstallHDA" -fname = "hou_SaveHDA" -fname = "hou_Playblast" -fname = "hou_Dependency" +# fname = "hou_ImportFile" +# fname = "hou_Export" +# fname = "hou_InstallHDA" +# fname = "hou_SaveHDA" +# fname = "hou_Playblast" +# fname = "hou_Dependency" code = """ with open("%s" + "_ui.py", "r+") as f: diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export.ui b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export.ui index cf683b05..eeb3f328 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export.ui +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export.ui @@ -196,12 +196,6 @@ - - - 55 - 16777215 - - 99999 @@ -212,12 +206,6 @@ - - - 55 - 16777215 - - 99999 diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export_ui.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export_ui.py index 05511ab8..9999393d 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export_ui.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Export_ui.py @@ -120,7 +120,6 @@ def setupUi(self, wg_Export): self.sp_rangeEnd = QSpinBox(self.w_frameRangeValues) self.sp_rangeEnd.setObjectName(u"sp_rangeEnd") - self.sp_rangeEnd.setMaximumSize(QSize(55, 16777215)) self.sp_rangeEnd.setMaximum(99999) self.sp_rangeEnd.setValue(1100) @@ -128,7 +127,6 @@ def setupUi(self, wg_Export): self.sp_rangeStart = QSpinBox(self.w_frameRangeValues) self.sp_rangeStart.setObjectName(u"sp_rangeStart") - self.sp_rangeStart.setMaximumSize(QSize(55, 16777215)) self.sp_rangeStart.setMaximum(99999) self.sp_rangeStart.setValue(1001) diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender.ui b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender.ui index e3a79c5c..3042a5b3 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender.ui +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender.ui @@ -196,12 +196,6 @@ - - - 55 - 16777215 - - 99999 @@ -212,12 +206,6 @@ - - - 55 - 16777215 - - 99999 diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender_ui.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender_ui.py index 4947138b..38ddeb68 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender_ui.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_ImageRender_ui.py @@ -119,7 +119,6 @@ def setupUi(self, wg_ImageRender): self.sp_rangeEnd = QSpinBox(self.w_frameRangeValues) self.sp_rangeEnd.setObjectName(u"sp_rangeEnd") - self.sp_rangeEnd.setMaximumSize(QSize(55, 16777215)) self.sp_rangeEnd.setMaximum(99999) self.sp_rangeEnd.setValue(1100) @@ -127,7 +126,6 @@ def setupUi(self, wg_ImageRender): self.sp_rangeStart = QSpinBox(self.w_frameRangeValues) self.sp_rangeStart.setObjectName(u"sp_rangeStart") - self.sp_rangeStart.setMaximumSize(QSize(55, 16777215)) self.sp_rangeStart.setMaximum(99999) self.sp_rangeStart.setValue(1001) diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast.ui b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast.ui index 7569ef43..5d9de285 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast.ui +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast.ui @@ -189,12 +189,6 @@ - - - 55 - 16777215 - - 99999 @@ -205,12 +199,6 @@ - - - 55 - 16777215 - - 99999 diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast_ui.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast_ui.py index f68ff211..728eac93 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast_ui.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/StateUserInterfaces/hou_Playblast_ui.py @@ -118,7 +118,6 @@ def setupUi(self, wg_Playblast): self.sp_rangeEnd = QSpinBox(self.f_frameRange_2) self.sp_rangeEnd.setObjectName(u"sp_rangeEnd") - self.sp_rangeEnd.setMaximumSize(QSize(55, 16777215)) self.sp_rangeEnd.setMaximum(99999) self.sp_rangeEnd.setValue(1100) @@ -126,7 +125,6 @@ def setupUi(self, wg_Playblast): self.sp_rangeStart = QSpinBox(self.f_frameRange_2) self.sp_rangeStart.setObjectName(u"sp_rangeStart") - self.sp_rangeStart.setMaximumSize(QSize(55, 16777215)) self.sp_rangeStart.setMaximum(99999) self.sp_rangeStart.setValue(1001) diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Dependency.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Dependency.py index 5732676c..e81d850c 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Dependency.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Dependency.py @@ -36,13 +36,9 @@ import time import traceback -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * import hou @@ -101,14 +97,10 @@ def loadData(self, data): if "dependencies" in data: self.dependencies = eval(data["dependencies"]) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.core.callback("onStateSettingsLoaded", self, data) self.updateUi() @@ -245,5 +237,5 @@ def getStateProps(self): "dependencyType": self.cb_depType.currentText(), "frameoffset": self.sp_offset.value(), "dependencies": str(self.dependencies), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Export.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Export.py index 313631ef..0308b718 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Export.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Export.py @@ -39,13 +39,9 @@ import platform import logging -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * import hou @@ -141,7 +137,7 @@ def setup(self, state, core, stateManager, node=None, stateData=None): elif self.node.type().name() in ["rop_fbx"]: idx = self.cb_outType.findText(".fbx") elif self.node.type().name() in ["pixar::usdrop", "usd"]: - idx = self.cb_outType.findText(".usd") + idx = self.cb_outType.findText(".usdc") elif self.node.type().name() in ["Redshift_Proxy_Output"]: idx = self.cb_outType.findText(".rs") @@ -221,9 +217,6 @@ def loadData(self, data): idx = self.cb_outPath.findText(data["curoutputpath"]) if idx != -1: self.cb_outPath.setCurrentIndex(idx) - if "outputtypes" in data: - self.cb_outType.clear() - self.cb_outType.addItems(eval(data["outputtypes"])) if "curoutputtype" in data: idx = self.cb_outType.findText(data["curoutputtype"]) if idx != -1: @@ -268,14 +261,10 @@ def loadData(self, data): self.l_pathLast.setText(lePath) self.l_pathLast.setToolTip(lePath) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.nameChanged(self.e_name.text()) self.core.callback("onStateSettingsLoaded", self, data) @@ -358,7 +347,7 @@ def createNode(self, nodePath=None): ropType = "alembic" elif self.getOutputType() == ".fbx": ropType = "rop_fbx" - elif self.getOutputType() == ".usd": + elif self.getOutputType() in [".usd", ".usda", ".usdc"]: ropType = "usd" elif self.getOutputType() == ".rs": ropType = "Redshift_Proxy_Output" @@ -403,8 +392,8 @@ def connectEvents(self): self.cb_rangeType.activated.connect(self.rangeTypeChanged) self.sp_rangeStart.editingFinished.connect(self.startChanged) self.sp_rangeEnd.editingFinished.connect(self.endChanged) - self.cb_outPath.activated[str].connect(self.onLocationChanged) - self.cb_outType.activated[str].connect(self.typeChanged) + self.cb_outPath.activated.connect(self.onLocationChanged) + self.cb_outType.activated.connect(lambda x: self.typeChanged()) self.chb_useTake.stateChanged.connect(self.useTakeChanged) self.cb_take.activated.connect(self.stateManager.saveStatesToScene) self.chb_master.stateChanged.connect(self.onUpdateMasterChanged) @@ -562,6 +551,14 @@ def setTaskname(self, taskname): @err_catcher(name=__name__) def getTaskname(self, expanded=False): + if self.isPrismFilecacheNode(self.node) and self.node.parm("showUsdSettings").eval(): + usdPlug = self.core.getPlugin("USD") + if not usdPlug: + self.core.popup("USD plugin is not loaded.") + return + + return usdPlug.api.usdExport.getTaskname(self, expanded) + if self.getOutputType() == "ShotCam": taskName = "_ShotCam" else: @@ -625,7 +622,8 @@ def setUpdateMasterVersion(self, master): self.chb_master.setChecked(master) @err_catcher(name=__name__) - def onLocationChanged(self, location): + def onLocationChanged(self, idx): + location = self.cb_outPath.currentText() if self.isPrismFilecacheNode(self.node): self.core.appPlugin.filecache.setLocationOnNode(self.node, location) @@ -781,8 +779,10 @@ def getFrameRange(self, rangeType): return startFrame, endFrame @err_catcher(name=__name__) - def typeChanged(self, idx, createMissing=True): + def typeChanged(self, idx=None, createMissing=True): self.isNodeValid() + if idx is None: + idx = self.cb_outType.currentText() if idx == ".abc": self.f_cam.setVisible(False) @@ -816,7 +816,7 @@ def typeChanged(self, idx, createMissing=True): not in ["rop_fbx", "wedge", "prism::Filecache::1.0"] ) and createMissing: self.createNode() - elif idx == ".usd": + elif idx in [".usd", ".usdc", ".usda"]: self.f_cam.setVisible(False) self.w_sCamShot.setVisible(False) self.f_taskName.setVisible(True) @@ -1216,16 +1216,15 @@ def preExecuteState(self): @err_catcher(name=__name__) def getCurrentWedgeIndex(self): - if self.isPrismFilecacheNode(self.node): - if self.node.parm("wedge").eval(): - wedge = self.node.parm("wedgeNum").evalAsString() - else: - wedge = None - + # wedge = hou.text.expandString("`@wedgeindex`") or None + import pdg + workItem = pdg.workItem() + if workItem and workItem.attrib("wedgeindex") and workItem.state == pdg.workItemState.Cooking: + wdgIdx = str(workItem.attrib("wedgeindex").value()) else: - wedge = hou.text.expandString("`@wedgeindex`") or None + wdgIdx = None - return wedge + return wdgIdx @err_catcher(name=__name__) def isContextSourceCooked(self): @@ -1328,6 +1327,14 @@ def getOutputEntity(self, forceCook=False): @err_catcher(name=__name__) def getOutputName(self, useVersion="next"): + if self.isPrismFilecacheNode(self.node) and self.node.parm("showUsdSettings").eval(): + usdPlug = self.core.getPlugin("USD") + if not usdPlug: + self.core.popup("USD plugin is not loaded.") + return + + return usdPlug.api.usdExport.getOutputName(self, useVersion) + entity = self.getOutputEntity() location = self.cb_outPath.currentText() version = useVersion if useVersion != "next" else None @@ -1353,11 +1360,10 @@ def getOutputName(self, useVersion="next"): if self.core.appPlugin.filecache.isSingleFrame(self.node): rangeType = "Single Frame" - framePadding = "$F4" if rangeType != "Single Frame" else "" + framePadding = ("$F" + str(self.core.framePadding)) if rangeType != "Single Frame" else "" extension = self.getOutputType() wedge = self.getCurrentWedgeIndex() - outputPathData = self.core.products.generateProductPath( entity=entity, task=task, @@ -1595,8 +1601,8 @@ def executeState(self, parent, useVersion="next"): % outLength ] - if self.getOutputType() in [".abc", ".fbx", ".usd"]: - outputName = outputName.replace(".$F4", "") + if self.getOutputType() in [".abc", ".fbx", ".usd", ".usda", ".usdc"]: + outputName = outputName.replace((".$F" + str(self.core.framePadding)), "") api = self.core.appPlugin.getApiFromNode(self.node) isStart = ropNode.parm("f1").eval() == startFrame @@ -1660,7 +1666,8 @@ def executeState(self, parent, useVersion="next"): expandedOutputPath = hou.text.expandString(outputPath) expandedOutputName = hou.text.expandString(outputName) - if not os.path.exists(expandedOutputPath): + isWedging = self.isPrismFilecacheNode(self.node) and self.node.parm("useWedging").eval() + if not isWedging and not os.path.exists(expandedOutputPath): os.makedirs(expandedOutputPath) kwargs = { @@ -1703,11 +1710,13 @@ def executeState(self, parent, useVersion="next"): details["version"] = hVersion details["sourceScene"] = fileName details["product"] = self.getTaskname(expanded=True) + details["comment"] = self.stateManager.publishComment if startFrame != endFrame: details["fps"] = self.core.getFPS() - self.core.saveVersionInfo(filepath=infoPath, details=details) + if not isWedging: + self.core.saveVersionInfo(filepath=infoPath, details=details) outputNames = [outputName] @@ -1740,7 +1749,8 @@ def executeState(self, parent, useVersion="next"): if self.node.parm("saveScene").eval(): hou.hipFile.save() else: - hou.hipFile.save() + if self.stateManager.actionSaveDuringPub.isChecked(): + hou.hipFile.save() if not self.gb_submit.isHidden() and self.gb_submit.isChecked(): wasLocked = False @@ -1760,18 +1770,19 @@ def executeState(self, parent, useVersion="next"): else: try: result = self.executeNode() - if result in [True, "background"]: + if result in [True, "background", "wedges"]: if result == "background": updateMaster = False if ( - result == "background" + result in ["background", "wedges"] or len(os.listdir(os.path.dirname(expandedOutputName))) > 1 ): result = "Result=Success" else: result = "unknown error (files do not exist)" + logger.debug("files do not exist. Expected path: %s" % expandedOutputName) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() @@ -1787,6 +1798,10 @@ def executeState(self, parent, useVersion="next"): + " - unknown error (view console for more information)" ] + if self.isPrismFilecacheNode(self.node) and self.node.parm("showUsdSettings").eval(): + usdPlug = self.core.getPlugin("USD") + usdPlug.api.usdExport.postExport(self, expandedOutputName) + if updateMaster: self.handleMasterVersion(expandedOutputName) @@ -1853,14 +1868,14 @@ def handleMasterVersion(self, outputName): if not self.isUsingMasterVersion(): return + isWedging = self.isPrismFilecacheNode(self.node) and self.node.parm("useWedging").eval() + if isWedging: + return + self.core.products.updateMasterVersion(outputName) @err_catcher(name=__name__) def getStateProps(self): - outputTypes = [] - for i in range(self.cb_outType.count()): - outputTypes.append(str(self.cb_outType.itemText(i))) - try: curNode = self.node.path() self.node.setUserData("PrismPath", curNode) @@ -1883,7 +1898,6 @@ def getStateProps(self): "take": self.cb_take.currentText(), "updateMasterVersion": self.chb_master.isChecked(), "curoutputpath": self.cb_outPath.currentText(), - "outputtypes": str(outputTypes), "curoutputtype": self.getOutputType(), "connectednode": curNode, "submitrender": str(self.gb_submit.isChecked()), @@ -1899,7 +1913,7 @@ def getStateProps(self): "currentcam": str(curCam), "currentscamshot": self.cb_sCamShot.currentText(), "lastexportpath": self.l_pathLast.text().replace("\\", "/"), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } self.core.callback("onStateGetSettings", self, stateProps) return stateProps diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImageRender.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImageRender.py index b351e28f..a83b0a1d 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImageRender.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImageRender.py @@ -151,7 +151,7 @@ def setup( if not hasattr(self, "curRenderer"): create = (stateData is None) and QApplication.keyboardModifiers() != Qt.ControlModifier - self.rendererChanged(self.cb_renderer.currentText(), create=create) + self.rendererChanged(create=create) if hasattr(self, "node") and self.node is not None: self.sp_rangeStart.setValue(self.node.parm("f1").eval()) @@ -332,14 +332,10 @@ def loadData(self, data): self.l_pathLast.setText(lePath) self.l_pathLast.setToolTip(lePath) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.nameChanged(self.e_name.text()) self.core.callback("onStateSettingsLoaded", self, data) @@ -384,10 +380,10 @@ def connectEvents(self): self.chb_useTake.stateChanged.connect(self.useTakeChanged) self.cb_take.activated.connect(self.stateManager.saveStatesToScene) self.cb_master.activated.connect(self.stateManager.saveStatesToScene) - self.cb_outPath.activated[str].connect(self.stateManager.saveStatesToScene) + self.cb_outPath.activated.connect(self.stateManager.saveStatesToScene) self.chb_format.stateChanged.connect(self.useFormatChanged) self.cb_format.activated.connect(self.onFormatChanged) - self.cb_renderer.currentIndexChanged[str].connect(self.rendererChanged) + self.cb_renderer.currentIndexChanged.connect(self.rendererChanged) self.chb_separateAovs.stateChanged.connect(self.stateManager.saveStatesToScene) self.gb_submit.toggled.connect(self.rjToggled) self.cb_manager.activated.connect(self.managerChanged) @@ -585,7 +581,8 @@ def setCam(self, index): self.stateManager.saveStatesToScene() @err_catcher(name=__name__) - def rendererChanged(self, renderer, create=True): + def rendererChanged(self, idx=None, create=True): + renderer = self.cb_renderer.currentText() if hasattr(self, "curRenderer"): getattr(self.curRenderer, "deactivated", lambda x: None)(self) @@ -1042,7 +1039,7 @@ def connectNode(self, node=None): self.setTaskname("$OS") self.cb_renderer.setCurrentIndex(self.cb_renderer.findText(i.label)) - self.rendererChanged(self.cb_renderer.currentText()) + self.rendererChanged() self.setFormat(self.getFormatFromNode()) result = True @@ -1296,7 +1293,7 @@ def getOutputName(self, useVersion="next"): extension = extension.split(" ")[0] entity = self.getOutputEntity() framePadding = ( - "$F4" if self.cb_rangeType.currentText() != "Single Frame" else "" + ("$F" + str(self.core.framePadding)) if self.cb_rangeType.currentText() != "Single Frame" else "" ) location = self.cb_outPath.currentText() @@ -1427,7 +1424,9 @@ def executeState(self, parent, useVersion="next"): self.state.text(0) + ": error - take '%s' doesn't exist." % pTake ] - hou.hipFile.save() + if self.stateManager.actionSaveDuringPub.isChecked(): + hou.hipFile.save() + if self.core.getConfig("globals", "backupScenesOnPublish", config="project"): self.core.entities.backupScenefile(os.path.dirname(outputName), bufferMinutes=0) @@ -1715,7 +1714,7 @@ def getStateProps(self): "dlgpupt": self.sp_dlGPUpt.value(), "dlgpudevices": self.le_dlGPUdevices.text(), "lastexportpath": self.l_pathLast.text().replace("\\", "/"), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } self.core.callback("onStateGetSettings", self, stateProps) return stateProps diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImportFile.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImportFile.py index f9437760..32a8a801 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImportFile.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_ImportFile.py @@ -35,13 +35,9 @@ import os import logging -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * import hou @@ -75,9 +71,8 @@ def setup( self.stateNameTemplate = self.core.getConfig( "globals", "defaultImportStateName", - dft=stateNameTemplate, configPath=self.core.prismIni, - ) + ) or stateNameTemplate self.e_name.setText(self.stateNameTemplate) self.l_name.setVisible(False) self.e_name.setVisible(False) @@ -110,15 +105,12 @@ def setup( and not createEmptyState and not self.stateManager.standalone ): - import ProductBrowser - - ts = ProductBrowser.ProductBrowser(core=core, importState=self) - self.core.parentWindow(ts) - if self.core.uiScaleFactor != 1: - self.core.scaleUI(self.state, sFactor=0.5) - ts.exec_() - - importPath = ts.productPath + importPaths = self.requestImportPaths() + if importPaths: + importPath = importPaths[-1] + if len(importPaths) > 1: + for importPath in importPaths[:-1]: + stateManager.importFile(importPath) if importPath: self.setImportPath(importPath) @@ -169,6 +161,24 @@ def loadData(self, data): self.core.callback("onStateSettingsLoaded", self, data) + @err_catcher(name=__name__) + def requestImportPaths(self): + result = self.core.callback("requestImportPath", self) + for res in result: + if isinstance(res, dict) and res.get("importPaths") is not None: + return res["importPaths"] + + import ProductBrowser + + ts = ProductBrowser.ProductBrowser(core=self.core, importState=self) + self.core.parentWindow(ts) + if self.core.uiScaleFactor != 1: + self.core.scaleUI(self.state, sFactor=0.5) + ts.exec_() + + importPaths = [ts.productPath] + return importPaths + @err_catcher(name=__name__) def findNode(self, path): for node in hou.node("/").allSubChildren(): diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_InstallHDA.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_InstallHDA.py index 3c24fc80..c8173440 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_InstallHDA.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_InstallHDA.py @@ -34,13 +34,9 @@ import os -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * import hou import hou_ImportFile diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Playblast.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Playblast.py index 40bbf42d..9d8c1a74 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Playblast.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_Playblast.py @@ -40,13 +40,9 @@ import glob import logging -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * import hou @@ -177,14 +173,10 @@ def loadData(self, data): self.l_pathLast.setText(lePath) self.l_pathLast.setToolTip(lePath) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.core.callback("onStateSettingsLoaded", self, data) @@ -202,7 +194,7 @@ def connectEvents(self): self.sp_resHeight.editingFinished.connect(self.stateManager.saveStatesToScene) self.b_resPresets.clicked.connect(self.showResPresets) self.cb_master.activated.connect(self.stateManager.saveStatesToScene) - self.cb_location.activated[str].connect(self.stateManager.saveStatesToScene) + self.cb_location.activated.connect(self.stateManager.saveStatesToScene) self.cb_formats.activated.connect(self.stateManager.saveStatesToScene) self.b_pathLast.clicked.connect(self.showLastPathMenu) @@ -598,7 +590,7 @@ def getOutputName(self, useVersion="next", extension=None): entity = self.getOutputEntity() comment = self.stateManager.publishComment framePadding = ( - "$F4" if self.cb_rangeType.currentText() != "Single Frame" else "" + ("$F" + str(self.core.framePadding)) if self.cb_rangeType.currentText() != "Single Frame" else "" ) if "type" not in entity: @@ -699,7 +691,8 @@ def executeState(self, parent, useVersion="next"): self.updateLastPath(outputName) self.stateManager.saveStatesToScene() - hou.hipFile.save() + if self.stateManager.actionSaveDuringPub.isChecked(): + hou.hipFile.save() kwargs = { "state": self, @@ -987,7 +980,7 @@ def getStateProps(self): "location": self.cb_location.currentText(), "outputformat": str(self.cb_formats.currentText()), "lastexportpath": self.l_pathLast.text().replace("\\", "/"), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } self.core.callback("onStateGetSettings", self, stateProps) return stateProps diff --git a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_SaveHDA.py b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_SaveHDA.py index 0687ccf8..e01907c2 100644 --- a/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_SaveHDA.py +++ b/Prism/Plugins/Apps/Houdini/Scripts/StateManagerNodes/hou_SaveHDA.py @@ -36,13 +36,9 @@ import time import platform -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * import hou import hou_Export @@ -129,7 +125,10 @@ def loadData(self, data): self.l_pathLast.setText(lePath) self.l_pathLast.setToolTip(lePath) if "stateenabled" in data: - self.state.setCheckState(0, Qt.CheckState(data["stateenabled"])) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.nameChanged(self.e_name.text()) self.core.callback("onStateSettingsLoaded", self, data) @@ -139,7 +138,7 @@ def connectEvents(self): self.e_name.textChanged.connect(self.nameChanged) self.e_name.editingFinished.connect(self.stateManager.saveStatesToScene) self.b_changeTask.clicked.connect(self.changeTask) - self.cb_outPath.activated[str].connect(self.stateManager.saveStatesToScene) + self.cb_outPath.activated.connect(self.stateManager.saveStatesToScene) self.chb_projectHDA.stateChanged.connect( lambda x: self.w_outPath.setEnabled(not x) ) @@ -428,7 +427,8 @@ def executeState(self, parent, useVersion="next"): self.l_pathLast.setToolTip(outputName) self.stateManager.saveStatesToScene() - hou.hipFile.save() + if self.stateManager.actionSaveDuringPub.isChecked(): + hou.hipFile.save() version = int(hVersion[1:]) if hVersion else None result = self.exportHDA(ropNode, outputName, version) @@ -522,7 +522,7 @@ def getStateProps(self): "externalReferences": self.chb_externalReferences.isChecked(), "blackboxhda": self.chb_blackboxHDA.isChecked(), "lastexportpath": self.l_pathLast.text(), - "stateenabled": int(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } return stateProps diff --git a/Prism/Plugins/Apps/Maya/Integration/Prism.mod b/Prism/Plugins/Apps/Maya/Integration/Prism.mod new file mode 100644 index 00000000..e9c4724d --- /dev/null +++ b/Prism/Plugins/Apps/Maya/Integration/Prism.mod @@ -0,0 +1,3 @@ ++ Prism 1.0.0 PLUGINROOT/Integration +PRISM_ROOT=PRISMROOT +MAYA_SHELF_PATH+:=shelves \ No newline at end of file diff --git a/Prism/Plugins/Apps/Maya/Integration/PrismInit.py b/Prism/Plugins/Apps/Maya/Integration/scripts/PrismInit.py similarity index 73% rename from Prism/Plugins/Apps/Maya/Integration/PrismInit.py rename to Prism/Plugins/Apps/Maya/Integration/scripts/PrismInit.py index ae02ce83..e8821365 100644 --- a/Prism/Plugins/Apps/Maya/Integration/PrismInit.py +++ b/Prism/Plugins/Apps/Maya/Integration/scripts/PrismInit.py @@ -3,16 +3,17 @@ def prismInit(prismArgs=[]): - if "PRISM_ROOT" in os.environ: - prismRoot = os.environ["PRISM_ROOT"] - if not prismRoot: - return - else: - prismRoot = PRISMROOT + prismRoot = os.getenv("PRISM_ROOT") + if not prismRoot: + raise Exception("PRISM_ROOT env var is not defined. Please reinstall the Prism integration") import maya.cmds as cmds if cmds.about(batch=True): - from PySide2 import QtWidgets + try: + from PySide6 import QtWidgets + except: + from PySide2 import QtWidgets + qapp = QtWidgets.QApplication.instance() if not isinstance(qapp, QtWidgets.QApplication): print("Cannot create Prism instance because no QApplication exists. To load Prism you can create a QApplication before initilizing mayapy like this: import sys;from PySide2 import QtWidgets;QtWidgets.QApplication(sys.argv)") diff --git a/Prism/Plugins/Apps/Maya/Integration/userSetup.py b/Prism/Plugins/Apps/Maya/Integration/scripts/userSetup.py similarity index 60% rename from Prism/Plugins/Apps/Maya/Integration/userSetup.py rename to Prism/Plugins/Apps/Maya/Integration/scripts/userSetup.py index 7cbf9c27..11174352 100644 --- a/Prism/Plugins/Apps/Maya/Integration/userSetup.py +++ b/Prism/Plugins/Apps/Maya/Integration/scripts/userSetup.py @@ -5,8 +5,11 @@ if omya.MGlobal.mayaState() != omya.MGlobal.kBatch: if "pcore" in locals() and pcore: - import PySide2 - PySide2.QtWidgets.QMessageBox.warning(None, "Prism Warning", "Prism is loaded multiple times. This can cause unexpected errors. Please clean all Prism related content from the userSetup.py in your Maya user preferences.\n\nYou can add a new Prism integration through the Prism Settings dialog.") + try: + from PySide6 import QtWidgets + except: + from PySide2 import QtWidgets + QtWidgets.QMessageBox.warning(None, "Prism Warning", "Prism is loaded multiple times. This can cause unexpected errors. Please clean all Prism related content from the userSetup.py in your Maya user preferences.\n\nYou can add a new Prism integration through the Prism Settings dialog.") elif sys.version[0] == "2": import PySide2 PySide2.QtWidgets.QMessageBox.warning(None, "Prism Warning", "Prism supports only Python 3 versions of Maya.\nPython 3 is the default in Maya 2022 and later.") diff --git a/Prism/Plugins/Apps/Maya/Integration/shelf_Prism.mel b/Prism/Plugins/Apps/Maya/Integration/shelf_Prism.mel deleted file mode 100644 index b0b238fc..00000000 --- a/Prism/Plugins/Apps/Maya/Integration/shelf_Prism.mel +++ /dev/null @@ -1,285 +0,0 @@ -global proc shelf_Prism () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Saves the current file to a new version" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_save" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismSave.png" - -image1 "prismSave.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.saveScene()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - -mi "Save with comment" ( "pcore.saveWithComment()" ) - -mip 0 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Opens the Project Browser" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_browser" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismBrowser.png" - -image1 "prismBrowser.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.projectBrowser()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Import" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_import" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismImport.png" - -image1 "prismImport.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedImport()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Export" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_export" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismExport.png" - -image1 "prismExport.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedExport()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -doubleClickCommand "pcore.getPlugin(\"Maya\").onShelfClickedExport(doubleclick=True)" - -commandRepeatable 1 - -flat 1 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Playblast" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_playblast" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismPlayblast.png" - -image1 "prismPlayblast.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedPlayblast()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -doubleClickCommand "pcore.getPlugin(\"Maya\").onShelfClickedPlayblast(doubleclick=True)" - -commandRepeatable 1 - -flat 1 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Render" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_render" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismRender.png" - -image1 "prismRender.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedRender()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -doubleClickCommand "pcore.getPlugin(\"Maya\").onShelfClickedRender(doubleclick=True)" - -commandRepeatable 1 - -flat 1 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Opens the State Manager" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_manager" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismStates.png" - -image1 "prismStates.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.stateManager()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Opens the Prism settings" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "prism_settings" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "prismSettings.png" - -image1 "prismSettings.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "try:\n\tpcore.prismSettings()\nexcept:\n\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -} diff --git a/Prism/Plugins/Apps/Maya/Integration/shelves/shelf_Prism.mel b/Prism/Plugins/Apps/Maya/Integration/shelves/shelf_Prism.mel new file mode 100644 index 00000000..7403eca3 --- /dev/null +++ b/Prism/Plugins/Apps/Maya/Integration/shelves/shelf_Prism.mel @@ -0,0 +1,287 @@ +global proc shelf_Prism () { + global string $gBuffStr; + global string $gBuffStr0; + global string $gBuffStr1; + + + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Saves the current file to a new version" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_save" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismSave.png" + -image1 "prismSave.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.saveScene()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -commandRepeatable 1 + -flat 1 + -mi "Save with comment" ( "pcore.saveWithComment()" ) + -mip 0 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Opens the Project Browser" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_browser" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismBrowser.png" + -image1 "prismBrowser.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.projectBrowser()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -commandRepeatable 1 + -flat 1 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Import" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_import" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismImport.png" + -image1 "prismImport.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedImport()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -commandRepeatable 1 + -flat 1 + -mi "Import Connected Assets..." ( "pcore.getPlugin(\"Maya\").onShelfClickedImportConnectedAssets()" ) + -mip 0 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Export" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_export" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismExport.png" + -image1 "prismExport.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedExport()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -doubleClickCommand "pcore.getPlugin(\"Maya\").onShelfClickedExport(doubleclick=True)" + -commandRepeatable 1 + -flat 1 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Playblast" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_playblast" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismPlayblast.png" + -image1 "prismPlayblast.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedPlayblast()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -doubleClickCommand "pcore.getPlugin(\"Maya\").onShelfClickedPlayblast(doubleclick=True)" + -commandRepeatable 1 + -flat 1 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Render" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_render" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismRender.png" + -image1 "prismRender.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.getPlugin(\"Maya\").onShelfClickedRender()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -doubleClickCommand "pcore.getPlugin(\"Maya\").onShelfClickedRender(doubleclick=True)" + -commandRepeatable 1 + -flat 1 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Opens the State Manager" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_manager" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismStates.png" + -image1 "prismStates.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.stateManager()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -commandRepeatable 1 + -flat 1 + ; + shelfButton + -enableCommandRepeat 1 + -flexibleWidthType 3 + -flexibleWidthValue 32 + -enable 1 + -width 35 + -height 34 + -manage 1 + -visible 1 + -preventOverride 0 + -annotation "Opens the Prism settings" + -enableBackground 0 + -backgroundColor 0 0 0 + -highlightColor 0.321569 0.521569 0.65098 + -align "center" + -label "prism_settings" + -labelOffset 0 + -rotation 0 + -flipX 0 + -flipY 0 + -useAlpha 1 + -font "plainLabelFont" + -overlayLabelColor 0.8 0.8 0.8 + -overlayLabelBackColor 0 0 0 0.5 + -image "prismSettings.png" + -image1 "prismSettings.png" + -style "iconOnly" + -marginWidth 0 + -marginHeight 1 + -command "try:\n\tpcore.prismSettings()\nexcept:\n\ttry:\n\t\tfrom PySide6.QtWidgets import *\n\texcept:\n\t\tfrom PySide2.QtWidgets import *\n\tmsg = QMessageBox(QMessageBox.Warning, \"Prism Warning\", \"Failed to load Prism.\")\n\tmsg.addButton(\"Details\", QMessageBox.YesRole)\n\tmsg.addButton(\"Close\", QMessageBox.RejectRole)\n\tmsg.exec_()\n\tbutton = msg.clickedButton()\n\tif button and button.text() == \"Details\":\n\t\ttry:\n\t\t\timport PrismInit\n\t\t\tprismInitLoaded = True\n\t\texcept Exception as e:\n\t\t\tprismInitError = e\n\t\t\tprismInitLoaded = False\n\n\t\ttry:\n\t\t\tpcoreTest = PrismInit.prismInit(prismArgs=[\"noUI\"])\n\t\t\tprismLoaded = bool(pcoreTest)\n\t\texcept Exception as e:\n\t\t\tprismError = e\n\t\t\tprismLoaded = False\n\n\t\tif not prismInitLoaded:\n\t\t\timport pkgutil\n\t\t\tpkg = pkgutil.get_loader(\"PrismInit\")\n\t\t\tif not pkg:\n\t\t\t\tsolution = \"No PrismInit.py found. Add the Prism integration to Maya in the standalone Prism User Settings window.\"\n\t\t\telse:\n\t\t\t\tsolution = \"Failed to load this file:\\n\\n%s\\n\\nError:\\n\\n%s\" % (pkg.path, userSetupError)\n\t\telif not prismLoaded:\n\t\t\tsolution = \"Prism encountered an error. Please contact the support.\\n\\n%s\" % (prismError)\n\t\telse:\n\t\t\tsolution = \"\"\"No problem with Prism could be detected.\n\nMake sure that the userSetup.py file in your Maya user preferences doesn't contain errors.\nYou can check the Maya Output Window for errors and remove all non Prism code from the userSetup.py file.\n\nIf the problems persists please contact the support.\"\"\"\n\n\t\tQMessageBox.information(None, \"Prism\", solution)" + -sourceType "python" + -commandRepeatable 1 + -flat 1 + ; + +} diff --git a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Functions.py b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Functions.py index c424a205..4557ef44 100644 --- a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Functions.py +++ b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Functions.py @@ -100,9 +100,12 @@ def startup(self, origin): except: return False + if not topLevelShelf: + return False + if ( cmds.shelfTabLayout(topLevelShelf, query=True, tabLabelIndex=True) - == None + is None ): return False @@ -247,6 +250,49 @@ def onShelfClickedImport(self): return state + @err_catcher(name=__name__) + def onShelfClickedImportConnectedAssets(self, doubleclick=False): + sm = self.core.getStateManager() + if not sm: + return + + filepath = self.core.getCurrentFileName() + entity = self.core.getScenefileData(filepath) + if not entity or entity.get("type") != "shot": + msg = "Importing connected assets is possible in shot scenefiles only." + self.core.popup(msg) + return + + productsToImport = [] + entities = self.core.entities.getConnectedEntities(entity) + if not entities: + result = self.core.popupQuestion("No assets are connected to the current shot.", buttons=["Connect Assets...", "Close"], icon=QMessageBox.Information) + if result == "Connect Assets...": + self.core.entities.connectEntityDlg(entities=[entity]) + + return + + tags = ["usd", "assembly"] + for centity in entities: + products = self.core.products.getProductsByTags(centity, tags) + productsToImport += products + + if not productsToImport: + msg = "No products to import.\n(checking for tags: \"%s\")" % "\", \"".join(tags) + self.core.popup(msg) + return + + for product in productsToImport: + if "asset_path" not in product: + continue + + productPath = self.core.products.getLatestVersionpathFromProduct(product["product"], entity=product) + if not productPath: + continue + + sm.importFile(productPath) + logger.debug("added product to shot: %s - %s" % (self.core.entities.getShotName(entity), productPath)) + @err_catcher(name=__name__) def onShelfClickedExport(self, doubleclick=False): sm = self.core.getStateManager() @@ -354,6 +400,10 @@ def onShelfClickedRender(self, doubleclick=False): state.ui.gb_previous.setVisible(False) self.dlg_render.show() + @err_catcher(name=__name__) + def getSetPrefix(self): + return self.core.getConfig("maya", "setPrefix", config="project") or "" + @err_catcher(name=__name__) def getDftStateParent(self, create=True): sm = self.core.getStateManager() @@ -373,7 +423,7 @@ def getDftStateParent(self, create=True): stateData = { "statename": "Default States", "listtype": "Export", - "stateenabled": "PySide2.QtCore.Qt.CheckState.Checked", + "stateenabled": 2, "stateexpanded": False, } state = sm.createState("Folder", stateData=stateData) @@ -646,6 +696,47 @@ def appendEnvFile(self, envVar="MAYA_MODULE_PATH"): % modPath, ) + @err_catcher(name=__name__) + def importImages(self, filepath=None, mediaBrowser=None, parent=None): + if mediaBrowser: + sourceData = mediaBrowser.compGetImportSource() + if not sourceData: + return + + filepath = sourceData[0][0] + firstFrame = sourceData[0][1] + lastFrame = sourceData[0][2] + parent = parent or mediaBrowser + + fString = "Please select an import option:" + buttons = ["Camera Backplate", "Dome Light", "Cancel"] + result = self.core.popupQuestion(fString, buttons=buttons, icon=QMessageBox.NoIcon, parent=parent) + + if result == "Camera Backplate": + self.importBackplate(filepath) + elif result == "Dome Light": + self.importDomeLightTexture(filepath) + else: + return + + @err_catcher(name=__name__) + def importBackplate(self, mediaPath): + camera = cmds.camera() + imagePlane = cmds.imagePlane(camera=camera[1]) + cmds.setAttr(imagePlane[1] + ".imageName", mediaPath, type="string") + if "#" in os.path.basename(mediaPath): + cmds.setAttr(imagePlane[1] + ".useFrameExtension", 1) + + cmds.lookThru(camera) + + @err_catcher(name=__name__) + def importDomeLightTexture(self, mediaPath): + import mtoa.utils as mutils + lightShape, light = mutils.createLocator("aiSkyDomeLight", asLight=True) + filenode = cmds.shadingNode("file", asTexture=True, isColorManaged=True) + cmds.setAttr("%s.fileTextureName" % filenode, mediaPath, type="string") + cmds.connectAttr("%s.outColor" % filenode, "%s.color" % lightShape, force=True) + @err_catcher(name=__name__) def sm_export_addObjects(self, origin, objects=None): if objects: @@ -655,17 +746,20 @@ def sm_export_addObjects(self, origin, objects=None): if not setName: setName = origin.setTaskname("Export") - setName = self.validate(origin.getTaskname()) + setName = self.getSetPrefix() + setName + valid = self.isNodeValid(origin, setName) + if not valid: + setName = cmds.sets(name=setName) + taskName = setName.split(self.getSetPrefix(), 1)[-1] if self.getSetPrefix() else setName + if taskName != origin.getTaskname(): + origin.setTaskname(taskName) + for i in cmds.ls(selection=True, long=True): if i not in origin.nodes: try: cmds.sets(i, include=setName) except Exception as e: - QMessageBox.warning( - self.core.messageParent, - "Warning", - "Cannot add object:\n\n%s" % str(e), - ) + self.core.popup("Cannot add object:\n\n%s" % str(e)) else: origin.nodes.append(i) @@ -676,6 +770,10 @@ def getNodeName(self, origin, node): else: return "invalid" + @err_catcher(name=__name__) + def getSelectedNodes(self): + return cmds.ls(selection=True) + @err_catcher(name=__name__) def selectNodes(self, origin): if origin.lw_objects.selectedItems() != []: @@ -736,6 +834,17 @@ def sm_export_startup(self, origin): if hasattr(origin, "gb_submit"): origin.gb_submit.setVisible(True) + origin.w_fbxSettings = QWidget() + origin.lo_fbxSettings = QHBoxLayout() + origin.lo_fbxSettings.setContentsMargins(9, 0, 9, 0) + origin.w_fbxSettings.setLayout(origin.lo_fbxSettings) + origin.l_fbxSettings = QLabel("Settings:") + spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Expanding) + origin.b_fbxSettings = QPushButton("Edit FBX Settings...") + origin.lo_fbxSettings.addWidget(origin.l_fbxSettings) + origin.lo_fbxSettings.addSpacerItem(spacer) + origin.lo_fbxSettings.addWidget(origin.b_fbxSettings) + origin.w_exportNamespaces = QWidget() origin.lo_exportNamespaces = QHBoxLayout() origin.lo_exportNamespaces.setContentsMargins(9, 0, 9, 0) @@ -797,12 +906,14 @@ def sm_export_startup(self, origin): origin.lo_deleteDisplayLayers.addSpacerItem(spacer) origin.lo_deleteDisplayLayers.addWidget(origin.chb_deleteDisplayLayers) - origin.gb_export.layout().insertWidget(10, origin.w_exportNamespaces) - origin.gb_export.layout().insertWidget(11, origin.w_importReferences) - origin.gb_export.layout().insertWidget(12, origin.w_preserveReferences) - origin.gb_export.layout().insertWidget(13, origin.w_deleteUnknownNodes) - origin.gb_export.layout().insertWidget(14, origin.w_deleteDisplayLayers) + origin.gb_export.layout().insertWidget(10, origin.w_fbxSettings) + origin.gb_export.layout().insertWidget(11, origin.w_exportNamespaces) + origin.gb_export.layout().insertWidget(12, origin.w_importReferences) + origin.gb_export.layout().insertWidget(13, origin.w_preserveReferences) + origin.gb_export.layout().insertWidget(14, origin.w_deleteUnknownNodes) + origin.gb_export.layout().insertWidget(15, origin.w_deleteDisplayLayers) + origin.b_fbxSettings.clicked.connect(self.editFbxSettings) origin.chb_exportNamespaces.stateChanged.connect( origin.stateManager.saveStatesToScene ) @@ -822,6 +933,11 @@ def sm_export_startup(self, origin): origin.stateManager.saveStatesToScene ) + @err_catcher(name=__name__) + def editFbxSettings(self): + cmd = "FBXUICallBack -1 editExportPresetInNewWindow fbx;" + mel.eval(cmd) + @err_catcher(name=__name__) def validate(self, string): vstr = self.core.validateStr(string, denyChars=["-"]) @@ -835,45 +951,57 @@ def mergeSets(self, fromSet, toSet): cmds.delete(fromSet) @err_catcher(name=__name__) - def sm_export_setTaskText(self, origin, prevTaskName, newTaskName): + def sm_export_setTaskText(self, origin, prevTaskName, newTaskName, create=True): prev = self.validate(prevTaskName) if prevTaskName else "" - if self.isNodeValid(origin, prev) and "objectSet" in cmds.nodeType( - prev, inherited=True + prevSet = self.getSetPrefix() + prev + newSetName = self.getSetPrefix() + newTaskName + if self.isNodeValid(origin, prevSet) and "objectSet" in cmds.nodeType( + prevSet, inherited=True ): - if self.isNodeValid(origin, newTaskName) and "objectSet" in cmds.nodeType( - newTaskName, inherited=True - ): - msg = "A selection set with the name \"%s\" does already exist." % newTaskName - result = self.core.popupQuestion(msg, buttons=["Merge sets", "Use unique name", "Cancel"], icon=QMessageBox.Warning) - if result == "Merge sets": - self.mergeSets(prev, newTaskName) - return newTaskName - elif result == "Cancel": - return prev + if create: + if self.isNodeValid(origin, newSetName) and "objectSet" in cmds.nodeType( + newSetName, inherited=True + ): + import traceback + traceback.print_stack() + msg = "A selection set with the name \"%s\" does already exist." % newSetName + result = self.core.popupQuestion(msg, buttons=["Merge sets", "Use unique name", "Cancel"], icon=QMessageBox.Warning) + if result == "Merge sets": + self.mergeSets(prevSet, newSetName) + return newTaskName + elif result == "Cancel": + return prev - try: - setName = cmds.rename(prev, newTaskName) - except Exception as e: - self.core.popup("Failed to rename set: %s" % e) - setName = prev - else: - if self.isNodeValid(origin, newTaskName) and "objectSet" in cmds.nodeType( - newTaskName, inherited=True - ) and origin.stateManager.loading: + try: + setName = cmds.rename(prevSet, newSetName) + setName = setName.split(self.getSetPrefix(), 1)[-1] if self.getSetPrefix() else setName + except Exception as e: + self.core.popup("Failed to rename set: %s" % e) + setName = prev + else: + cmds.delete(prevSet) + setName = None + elif create: + valid = self.isNodeValid(origin, newSetName) + isSet = "objectSet" in cmds.nodeType(newSetName, inherited=True) if valid else False + if valid and isSet and origin.stateManager.loading: setName = newTaskName else: - setName = cmds.sets(name=newTaskName) + setName = cmds.sets(name=newSetName) + setName = setName.split(self.getSetPrefix(), 1)[-1] if self.getSetPrefix() else setName + else: + setName = None return setName @err_catcher(name=__name__) def sm_export_removeSetItem(self, origin, node): - setName = self.validate(origin.getTaskname()) + setName = self.getSetPrefix() + self.validate(origin.getTaskname()) cmds.sets(node, remove=setName) @err_catcher(name=__name__) def sm_export_clearSet(self, origin): - setName = origin.getTaskname() + setName = self.getSetPrefix() + origin.getTaskname() if self.isNodeValid(origin, setName): cmds.sets(clear=setName) @@ -884,6 +1012,7 @@ def sm_export_updateObjects(self, origin): if not setName: setName = origin.setTaskname("Export") + setName = self.getSetPrefix() + setName try: # the nodes in the set need to be selected to get their long dag path cmds.select(setName) @@ -893,7 +1022,8 @@ def sm_export_updateObjects(self, origin): except: newSetName = cmds.sets(name=setName) if newSetName != setName: - origin.setTaskname(newSetName) + newTaskName = newSetName.split(self.getSetPrefix(), 1)[-1] if self.getSetPrefix() else newSetName + origin.setTaskname(newTaskName) origin.nodes = cmds.ls(selection=True, long=True) try: @@ -933,7 +1063,7 @@ def sm_export_exportAppObjects( ): cmds.select(clear=True) if nodes is None: - setName = self.validate(origin.getTaskname()) + setName = self.getSetPrefix() + self.validate(origin.getTaskname()) if not self.isNodeValid(origin, setName): return 'Canceled: The selection set "%s" is invalid.' % setName @@ -949,36 +1079,13 @@ def sm_export_exportAppObjects( expType = origin.getOutputType() if expType == ".obj": - cmds.loadPlugin("objExport", quiet=True) - objNodes = [ - x - for x in origin.nodes - if cmds.listRelatives(x, shapes=True) is not None - ] - cmds.select(objNodes) - for i in range(startFrame, endFrame + 1): - cmds.currentTime(i, edit=True) - foutputName = outputName.replace("####", format(i, "04")) - if origin.chb_wholeScene.isChecked(): - cmds.file( - foutputName, - force=True, - exportAll=True, - type="OBJexport", - options="groups=1;ptgroups=1;materials=1;smoothing=1;normals=1", - ) - else: - if cmds.ls(selection=True) == []: - return "Canceled: No valid objects are specified for .obj export. No output will be created." - else: - cmds.file( - foutputName, - force=True, - exportSelected=True, - type="OBJexport", - options="groups=1;ptgroups=1;materials=1;smoothing=1;normals=1", - ) - outputName = foutputName + self.exportAsObj( + outputName, + objects=origin.nodes, + wholeScene=origin.chb_wholeScene.isChecked(), + startFrame=startFrame, + endFrame=endFrame + ) elif expType == ".fbx": origRange = self.getFrameRange() self.setFrameRange(None, startFrame, endFrame) @@ -1191,6 +1298,46 @@ def sm_export_exportAppObjects( return outputName + @err_catcher(name=__name__) + def exportAsObj(self, outputPath, objects=None, wholeScene=False, startFrame=None, endFrame=None): + cmds.loadPlugin("objExport", quiet=True) + if objects: + cmds.select(clear=True) + objNodes = [ + x + for x in objects + if cmds.listRelatives(x, shapes=True) is not None + ] + cmds.select(objNodes) + + if startFrame is None: + startFrame = endFrame = int(self.getCurrentFrame()) + + for i in range(startFrame, endFrame + 1): + cmds.currentTime(i, edit=True) + foutputName = outputPath.replace("####", format(i, "04")) + if wholeScene: + cmds.file( + foutputName, + force=True, + exportAll=True, + type="OBJexport", + options="groups=1;ptgroups=1;materials=1;smoothing=1;normals=1", + ) + else: + if cmds.ls(selection=True) == []: + return "Canceled: No valid objects are specified for .obj export. No output will be created." + else: + cmds.file( + foutputName, + force=True, + exportSelected=True, + type="OBJexport", + options="groups=1;ptgroups=1;materials=1;smoothing=1;normals=1", + ) + + return foutputName + @err_catcher(name=__name__) def deleteOutOfRangeKeys(self): startframe = cmds.playbackOptions(q=True, minTime=True) @@ -1292,7 +1439,7 @@ def exportAlembic(self, outputName, startFrame, endFrame, nodes=None, wholeScene @err_catcher(name=__name__) def sm_export_preDelete(self, origin): - setName = self.validate(origin.getTaskname()) + setName = self.getSetPrefix() + self.validate(origin.getTaskname()) try: cmds.delete(setName) except: @@ -1306,6 +1453,7 @@ def sm_export_unColorObjList(self, origin): @err_catcher(name=__name__) def sm_export_typeChanged(self, origin, idx): + origin.w_fbxSettings.setVisible(idx == ".fbx") origin.w_exportNamespaces.setVisible(idx == ".abc") exportScene = idx in [".ma", ".mb"] origin.w_importReferences.setVisible(exportScene) @@ -1837,11 +1985,11 @@ def sm_render_preSubmit(self, origin, rSettings): outputPrefix = outputPrefix[3:] cmds.setAttr( - "rmanGlobals.imageFileFormat", os.path.basename(outputPrefix) + "..", type="string" + "rmanGlobals.imageFileFormat", os.path.basename(outputPrefix).replace("beauty", "") + "..", type="string" ) cmds.setAttr( - "rmanGlobals.imageOutputDir", os.path.dirname(rSettings["outputName"]).replace("\\", "/"), type="string" + "rmanGlobals.imageOutputDir", os.path.dirname(rSettings["outputName"]).replace("beauty", "").replace("\\", "/"), type="string" ) cmds.setAttr( "rmanGlobals.ribOutputDir", os.path.dirname(rSettings["outputName"]).replace("\\", "/"), type="string" @@ -2100,7 +2248,6 @@ def sm_render_getDeadlineParams(self, origin, dlParams, homeDir): dlParams["pluginInfos"]["OutputFilePrefix"] = os.path.splitext( os.path.basename(dlParams["jobInfos"]["OutputFilename0"]) )[0] - dlParams["pluginInfos"]["Renderer"] = self.getCurrentRenderer(origin) import maya.app.renderSetup.model.renderSetup as renderSetup @@ -2155,6 +2302,12 @@ def sm_render_getDeadlineParams(self, origin, dlParams, homeDir): self.core.saveScene() else: dlParams["pluginInfos"]["Renderer"] = self.getCurrentRenderer(origin) + if dlParams["pluginInfos"]["Renderer"] == "renderman": + dlParams["pluginInfos"]["Renderer"] = "renderman22" + dlParams["pluginInfos"]["OutputFilePrefix"] += ".." + dlParams["pluginInfos"]["OutputFilePrefix"] = dlParams["pluginInfos"]["OutputFilePrefix"].replace("beauty", "") + dlParams["pluginInfos"]["OutputFilePath"] = dlParams["pluginInfos"]["OutputFilePath"].replace("beauty", "") + if hasattr(origin, "curCam") and origin.curCam != "Current View": dlParams["pluginInfos"]["Camera"] = self.core.appPlugin.getCamName( origin, origin.curCam @@ -2163,18 +2316,23 @@ def sm_render_getDeadlineParams(self, origin, dlParams, homeDir): @err_catcher(name=__name__) def getDeadlineScript(self, stateType): script = """ -from PySide2.QtCore import * -from PySide2.QtGui import * -from PySide2.QtWidgets import * +try: + from PySide6.QtCore import * + from PySide6.QtGui import * + from PySide6.QtWidgets import * +except: + from PySide2.QtCore import * + from PySide2.QtGui import * + from PySide2.QtWidgets import * + qapp = QApplication.instance() if not qapp: qapp = QApplication(sys.argv) -from PySide2 import QtWidgets import shiboken2 -shiboken2.delete(QtWidgets.QApplication.instance()) -QtWidgets.QApplication.instance() -QtWidgets.QApplication([]) +shiboken2.delete(QApplication.instance()) +QApplication.instance() +QApplication([]) import PrismInit pcore = PrismInit.prismInit(prismArgs=["noUI"]) @@ -2323,7 +2481,7 @@ def sm_render_preExecute(self, origin): return warnings @err_catcher(name=__name__) - def sm_render_fixOutputPath(self, origin, outputName, singleFrame=False): + def sm_render_fixOutputPath(self, origin, outputName, singleFrame=False, state=None): curRender = self.getCurrentRenderer(origin) if curRender == "vray": @@ -2429,7 +2587,8 @@ def connectRefNode(self, origin): origin.chb_trackObjects.setChecked(True) origin.nodes = [refNode] setName = os.path.splitext(os.path.basename(scenePath))[0] - origin.setName = cmds.sets(name="Import_%s_" % setName) + name = self.getSetPrefix() + "Import_%s_" % setName + origin.setName = cmds.sets(name=name) for i in origin.nodes: cmds.sets(i, include=origin.setName) @@ -2794,7 +2953,6 @@ def sm_import_importToApp(self, origin, doImport, update, impFileName, settings= newObjs.append(obj) objs = newObjs - print(objs) cmds.AbcImport( impFileName, mode="import", @@ -2913,7 +3071,8 @@ def sm_import_importToApp(self, origin, doImport, update, impFileName, settings= cmds.delete(origin.setName) if len(origin.nodes) > 0: - origin.setName = cmds.sets(name="Import_%s_" % fileName[0]) + name = self.getSetPrefix() + "Import_%s_" % fileName[0] + origin.setName = cmds.sets(name=name) for node in origin.nodes: cmds.sets(node, include=origin.setName) result = len(importedNodes) > 0 @@ -2924,9 +3083,14 @@ def sm_import_importToApp(self, origin, doImport, update, impFileName, settings= return rDict @err_catcher(name=__name__) - def basicImport(self, filepath, kwargs): - del kwargs["importFunction"] - del kwargs["settings"] + def basicImport(self, filepath, kwargs=None): + kwargs = kwargs or {} + if "importFunction" in kwargs: + del kwargs["importFunction"] + + if "settings" in kwargs: + del kwargs["settings"] + try: importedNodes = cmds.file(filepath, **kwargs) except Exception as e: @@ -3048,6 +3212,7 @@ def sm_playblast_startup(self, origin): origin.chb_useRecommendedSettings.stateChanged.connect( origin.stateManager.saveStatesToScene ) + origin.cb_formats.addItem(".png") origin.cb_formats.addItem(".mp4 (with audio)") if platform.system() == "Windows": origin.cb_formats.addItem(".avi (with audio)") @@ -3090,6 +3255,9 @@ def prePlayblast(self, **kwargs): + "#" * self.core.framePadding + os.path.splitext(kwargs["outputpath"])[1] ) + + if selFmt == ".png": + outputName = os.path.splitext(kwargs["outputpath"])[0] + ".png" if outputName and outputName != kwargs["outputpath"]: return {"outputName": outputName} @@ -3192,6 +3360,9 @@ def sm_playblast_createPlayblast(self, origin, jobFrames, outputName): soundNode, ) + if selFmt == ".png": + cmdString += ", compression=\"png\"" + if origin.chb_resOverride.isChecked(): cmdString += ", width=%s, height=%s" % ( origin.sp_resWidth.value(), diff --git a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Integration.py b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Integration.py index 06ba7f99..04f93eb5 100644 --- a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Integration.py +++ b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Integration.py @@ -55,14 +55,14 @@ def __init__(self, core, plugin): self.plugin = plugin if platform.system() == "Windows": - self.examplePath = self.core.getWindowsDocumentsPath() + "\\maya\\2024" + self.examplePath = self.core.getWindowsDocumentsPath() + "\\maya\\2025" elif platform.system() == "Linux": userName = ( os.environ["SUDO_USER"] if "SUDO_USER" in os.environ else os.environ["USER"] ) - self.examplePath = os.path.join("/home", userName, "maya", "2024") + self.examplePath = os.path.join("/home", userName, "maya", "2025") elif platform.system() == "Darwin": userName = ( os.environ["SUDO_USER"] @@ -70,7 +70,7 @@ def __init__(self, core, plugin): else os.environ["USER"] ) self.examplePath = ( - "/Users/%s/Library/Preferences/Autodesk/maya/2024" % userName + "/Users/%s/Library/Preferences/Autodesk/maya/2025" % userName ) @err_catcher(name=__name__) @@ -125,17 +125,13 @@ def getMayaPath(self): def addIntegration(self, installPath): try: - if not os.path.exists( - os.path.join(installPath, "scripts") - ) or not os.path.exists(os.path.join(installPath, "prefs", "shelves")): - QMessageBox.warning( - self.core.messageParent, - "Prism Installation", - "Invalid Maya path:\n\n%s\n\nThe path has to be the Maya preferences folder, which usually looks like this: (with your username and Maya version):\n\n%s" - % (installPath, self.examplePath), - QMessageBox.Ok, - ) - return False + scriptPath = os.path.join(installPath, "scripts") + shelfPath = os.path.join(installPath, "prefs", "shelves") + if not os.path.exists(scriptPath) or not os.path.exists(shelfPath): + msg = "Maya path appears invalid:\n\n%s\n\nThe expected paths don't exist:\n%s\n%s\n\nThe Maya path has to be the Maya preferences folder, which usually looks like this: (with your username and Maya version):\n\n%s" % (installPath, scriptPath, shelfPath, self.examplePath) + result = self.core.popupQuestion(msg, buttons=["Continue", "Cancel"], icon=QMessageBox.Warning, default="Continue") + if result != "Continue": + return False integrationBase = os.path.join( os.path.dirname(os.path.dirname(__file__)), "Integration" @@ -143,85 +139,39 @@ def addIntegration(self, installPath): addedFiles = [] integrationFiles = {} - integrationFiles["userSetup.py"] = os.path.join( - integrationBase, "userSetup.py" - ) - integrationFiles["PrismInit.py"] = os.path.join( - integrationBase, "PrismInit.py" + integrationFiles["Prism.mod"] = os.path.join( + integrationBase, "Prism.mod" ) integrationFiles["shelf_Prism.mel"] = os.path.join( - integrationBase, "shelf_Prism.mel" + integrationBase, "shelves", "shelf_Prism.mel" ) self.core.callback( name="preIntegrationAdded", args=[self, integrationFiles] ) - origSetupFile = integrationFiles["userSetup.py"] - with open(origSetupFile, "r") as mFile: - setupString = mFile.read() - - prismSetup = os.path.join(installPath, "scripts", "userSetup.py") - self.core.integration.removeIntegrationData(filepath=prismSetup) - - with open(prismSetup, "a") as setupfile: - setupfile.write(setupString) + origModFile = integrationFiles["Prism.mod"] + modpath = os.path.join(installPath, "modules", "Prism.mod") + if os.path.exists(modpath): + os.remove(modpath) - addedFiles.append(prismSetup) + if not os.path.exists(os.path.dirname(modpath)): + os.makedirs(os.path.dirname(modpath)) - initpath = os.path.join(installPath, "scripts", "PrismInit.py") + shutil.copy2(origModFile, modpath) + addedFiles.append(modpath) - if os.path.exists(initpath): - os.remove(initpath) - - if os.path.exists(initpath + "c"): - os.remove(initpath + "c") - - origInitFile = integrationFiles["PrismInit.py"] - shutil.copy2(origInitFile, initpath) - addedFiles.append(initpath) - - with open(initpath, "r") as init: + with open(modpath, "r") as init: initStr = init.read() - with open(initpath, "w") as init: + with open(modpath, "w") as init: initStr = initStr.replace( - "PRISMROOT", '"%s"' % self.core.prismRoot.replace("\\", "/") + "PRISMROOT", self.core.prismRoot.replace("\\", "/") ) - init.write(initStr) - - shelfpath = os.path.join(installPath, "prefs", "shelves", "shelf_Prism.mel") - - if os.path.exists(shelfpath): - os.remove(shelfpath) - - origShelfFile = integrationFiles["shelf_Prism.mel"] - shutil.copy2(origShelfFile, shelfpath) - addedFiles.append(shelfpath) - - icons = [ - "prismSave.png", - "prismSaveComment.png", - "prismBrowser.png", - "prismStates.png", - "prismSettings.png", - "prismImport.png", - "prismExport.png", - "prismPlayblast.png", - "prismRender.png", - ] - - for i in icons: - iconPath = os.path.join( - integrationBase, "icons", i + initStr = initStr.replace( + "PLUGINROOT", os.path.dirname(self.pluginPath).replace("\\", "/") ) - tPath = os.path.join(installPath, "prefs", "icons", i) - - if os.path.exists(tPath): - os.remove(tPath) - - shutil.copy2(iconPath, tPath) - addedFiles.append(tPath) + init.write(initStr) if platform.system() in ["Linux", "Darwin"]: for i in addedFiles: @@ -243,11 +193,12 @@ def addIntegration(self, installPath): def removeIntegration(self, installPath): try: + modPath = os.path.join(installPath, "modules", "Prism.mod") initPy = os.path.join(installPath, "scripts", "PrismInit.py") initPyc = os.path.join(installPath, "scripts", "PrismInit.pyc") shelfpath = os.path.join(installPath, "prefs", "shelves", "shelf_Prism.mel") - for i in [initPy, initPyc, shelfpath]: + for i in [modPath, initPy, initPyc, shelfpath]: if os.path.exists(i): os.remove(i) @@ -275,6 +226,7 @@ def updateInstallerUI(self, userFolders, pItem): os.path.join(userFolders["Documents"], "maya", "2022"), os.path.join(userFolders["Documents"], "maya", "2023"), os.path.join(userFolders["Documents"], "maya", "2024"), + os.path.join(userFolders["Documents"], "maya", "2025"), ] elif platform.system() == "Linux": userName = ( @@ -286,6 +238,7 @@ def updateInstallerUI(self, userFolders, pItem): os.path.join("/home", userName, "maya", "2022"), os.path.join("/home", userName, "maya", "2023"), os.path.join("/home", userName, "maya", "2024"), + os.path.join("/home", userName, "maya", "2025"), ] elif platform.system() == "Darwin": userName = ( @@ -297,6 +250,7 @@ def updateInstallerUI(self, userFolders, pItem): "/Users/%s/Library/Preferences/Autodesk/maya/2022" % userName, "/Users/%s/Library/Preferences/Autodesk/maya/2023" % userName, "/Users/%s/Library/Preferences/Autodesk/maya/2024" % userName, + "/Users/%s/Library/Preferences/Autodesk/maya/2025" % userName, ] mayaItem = QTreeWidgetItem(["Maya"]) diff --git a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Variables.py b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Variables.py index ff9c6e1f..1975229d 100644 --- a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Variables.py +++ b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_Variables.py @@ -37,7 +37,7 @@ class Prism_Maya_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.4" + self.version = "v2.0.9" self.pluginName = "Maya" self.pluginType = "App" self.appShortName = "Maya" diff --git a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_externalAccess_Functions.py b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_externalAccess_Functions.py index 5c13122a..9375cd30 100644 --- a/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_externalAccess_Functions.py +++ b/Prism/Plugins/Apps/Maya/Scripts/Prism_Maya_externalAccess_Functions.py @@ -58,6 +58,15 @@ def __init__(self, core, plugin): plugin=self.plugin, ) self.core.registerCallback("getPresetScenes", self.getPresetScenes, plugin=self.plugin) + self.core.registerCallback( + "preProjectSettingsLoad", self.preProjectSettingsLoad, plugin=self.plugin + ) + self.core.registerCallback( + "preProjectSettingsSave", self.preProjectSettingsSave, plugin=self.plugin + ) + self.core.registerCallback( + "projectSettings_loadUI", self.projectSettings_loadUI, plugin=self.plugin + ) @err_catcher(name=__name__) def userSettings_loadUI(self, origin, tab): @@ -199,3 +208,39 @@ def getPresetScenes(self, presetScenes): presetDir = os.path.join(self.pluginDirectory, "Presets") scenes = self.core.entities.getPresetScenesFromFolder(presetDir) presetScenes += scenes + + @err_catcher(name=__name__) + def preProjectSettingsLoad(self, origin, settings): + if settings and "maya" in settings: + if "setPrefix" in settings["maya"]: + origin.e_mayaSetPrefix.setText(settings["maya"]["setPrefix"]) + + @err_catcher(name=__name__) + def preProjectSettingsSave(self, origin, settings): + if "maya" not in settings: + settings["maya"] = {} + + prefix = origin.e_mayaSetPrefix.text() + settings["maya"]["setPrefix"] = prefix + + @err_catcher(name=__name__) + def projectSettings_loadUI(self, origin): + self.addUiToProjectSettings(origin) + + @err_catcher(name=__name__) + def addUiToProjectSettings(self, projectSettings): + projectSettings.w_maya = QGroupBox("Maya") + lo_maya = QGridLayout() + projectSettings.w_maya.setLayout(lo_maya) + + ttip = "Prefix for all selection sets created by Prism in Maya." + l_relative = QLabel("Selection Set Prefix:") + l_relative.setToolTip(ttip) + projectSettings.e_mayaSetPrefix = QLineEdit() + projectSettings.e_mayaSetPrefix.setToolTip(ttip) + + lo_maya.addWidget(l_relative, 0, 0) + sp_stretch = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred) + lo_maya.addItem(sp_stretch, 0, 1) + lo_maya.addWidget(projectSettings.e_mayaSetPrefix, 0, 2) + projectSettings.w_prjSettings.layout().addWidget(projectSettings.w_maya) diff --git a/Prism/Plugins/Apps/Nuke/Integration/init.py b/Prism/Plugins/Apps/Nuke/Integration/init.py index 3e146e90..ab92f47c 100644 --- a/Prism/Plugins/Apps/Nuke/Integration/init.py +++ b/Prism/Plugins/Apps/Nuke/Integration/init.py @@ -38,7 +38,9 @@ if type(qapp) == QCoreApplication: if os.getenv("PRISM_NUKE_TERMINAL_FILES"): import importlib - files = os.getenv("PRISM_NUKE_TERMINAL_FILES").split(os.pathsep) + tfiles = os.getenv("PRISM_NUKE_TERMINAL_FILES") + # if mixing Windows and Linux workstations, you might need to do some path remapping here + files = tfiles.split(os.pathsep) for file in files: sys.path.append(os.path.dirname(file)) mod = importlib.import_module(os.path.splitext(os.path.basename(file))[0]) diff --git a/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Functions.py b/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Functions.py index 1494b83c..c84906d5 100644 --- a/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Functions.py +++ b/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Functions.py @@ -51,10 +51,21 @@ from PySide2.QtGui import * from PySide2.QtWidgets import * + try: from PrismUtils.Decorators import err_catcher as err_catcher except: - err_catcher = lambda name: lambda func, *args, **kwargs: func(*args, **kwargs) + # err_catcher = lambda name: lambda func, *args, **kwargs: func(*args, **kwargs) + from functools import wraps + def err_catcher(name): + return lambda x, y=name, z=False: err_handler(x, name=y, plugin=z) + + def err_handler(func, name="", plugin=False): + @wraps(func) + def func_wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return func_wrapper logger = logging.getLogger(__name__) @@ -79,6 +90,9 @@ def __init__(self, core, plugin): self.core.registerCallback( "onStateManagerOpen", self.onStateManagerOpen, plugin=self.plugin ) + self.core.registerCallback( + "productSelectorContextMenuRequested", self.productSelectorContextMenuRequested, plugin=self.plugin + ) @err_catcher(name=__name__) def startup(self, origin): @@ -120,13 +134,13 @@ def addPluginPaths(self): def addMenus(self): nuke.menu("Nuke").addCommand("Prism/Save", self.saveScene, "Ctrl+s") nuke.menu("Nuke").addCommand("Prism/Save Version", self.core.saveScene, "Alt+Shift+s") - nuke.menu("Nuke").addCommand("Prism/Save Comment", self.core.saveWithComment, "Ctrl+Shift+S") - nuke.menu("Nuke").addCommand("Prism/Project Browser", self.core.projectBrowser) - nuke.menu("Nuke").addCommand("Prism/Settings", self.core.prismSettings) + nuke.menu("Nuke").addCommand("Prism/Save Comment...", self.core.saveWithComment, "Ctrl+Shift+S") + nuke.menu("Nuke").addCommand("Prism/Project Browser...", self.core.projectBrowser) + nuke.menu("Nuke").addCommand("Prism/Settings...", self.core.prismSettings) nuke.menu("Nuke").addCommand( "Prism/Update selected read nodes", self.updateNukeNodes ) - nuke.menu("Nuke").addCommand("Prism/Export Nodes", self.onExportTriggered) + nuke.menu("Nuke").addCommand("Prism/Export Nodes...", self.onExportTriggered) toolbar = nuke.toolbar("Nodes") iconPath = os.path.join( @@ -177,7 +191,7 @@ def dropHandler(self, mimeType, text): @err_catcher(name=__name__) def expandEnvVarsInFilepath(self, path): if not self.core.getConfig("nuke", "useRelativePaths", dft=False, config="user"): - return + return path expanded_path = os.path.expandvars(path) return expanded_path @@ -256,8 +270,20 @@ def getResolution(self): return resFormat @err_catcher(name=__name__) - def setResolution(self, width=None, height=None): - return nuke.knob("root.format", "%s %s" % (width, height)) + def setResolution(self, width=None, height=None, pixelAspect=None): + if pixelAspect: + return nuke.knob("root.format", "%s %s 0 0 %s %s %s" % (width, height, width, height, pixelAspect)) + else: + return nuke.knob("root.format", "%s %s" % (width, height)) + + @err_catcher(name=__name__) + def getPixelAspectRatio(self): + return nuke.root().pixelAspect() + + @err_catcher(name=__name__) + def setPixelAspectRatio(self, pixelAspect): + res = self.getResolution() + self.setResolution(res[0], res[1], pixelAspect) @err_catcher(name=__name__) def updateNukeNodes(self): @@ -268,9 +294,13 @@ def updateNukeNodes(self): continue curPath = i.knob("file").value() + curPath = os.path.expandvars(curPath) version = self.core.mediaProducts.getLatestVersionFromFilepath(curPath) if version and version["path"] not in curPath: filepattern = self.core.mediaProducts.getFilePatternFromVersion(version) + if self.core.getConfig("nuke", "useRelativePaths", dft=False, config="user"): + filepattern = os.path.normpath(filepattern).replace(os.path.normpath(self.core.projectPath), "%PRISM_JOB%") + filepattern = filepattern.replace("\\", "/") i.knob("file").setValue(filepattern) updatedNodes.append(i) @@ -352,6 +382,13 @@ def readNode_onCreateReadClicked(self, node): def getIdentifierFromNode(self, node): return node.knob("identifier").evaluate() + @err_catcher(name=__name__) + def sm_render_fixOutputPath(self, origin, outputName, singleFrame=False, state=None): + if self.core.getConfig("nuke", "useRelativePaths", dft=False, config="user"): + outputName = os.path.normpath(outputName).replace(os.path.normpath(self.core.projectPath), "%PRISM_JOB%") + + return outputName + @err_catcher(name=__name__) def getOutputPath(self, node, group, render=False, updateValues=True): if not nuke.env.get("gui"): @@ -522,22 +559,23 @@ def openScene(self, origin, filepath, force=False): return True @err_catcher(name=__name__) - def importImages(self, origin): - if origin.origin.getCurrentAOV(): - fString = "Please select an import option:" - buttons = ["Current AOV", "All AOVs", "Layout all AOVs"] - result = self.core.popupQuestion(fString, buttons=buttons, icon=QMessageBox.NoIcon) - else: - result = "Current AOV" - - if result == "Current AOV": - self.nukeImportSource(origin) - elif result == "All AOVs": - self.nukeImportPasses(origin) - elif result == "Layout all AOVs": - self.nukeLayout(origin) - else: - return + def importImages(self, filepath=None, mediaBrowser=None, parent=None): + if mediaBrowser: + if mediaBrowser.origin.getCurrentAOV(): + fString = "Please select an import option:" + buttons = ["Current AOV", "All AOVs", "Layout all AOVs"] + result = self.core.popupQuestion(fString, buttons=buttons, icon=QMessageBox.NoIcon) + else: + result = "Current AOV" + + if result == "Current AOV": + self.nukeImportSource(mediaBrowser) + elif result == "All AOVs": + self.nukeImportPasses(mediaBrowser) + elif result == "Layout all AOVs": + self.nukeLayout(mediaBrowser) + else: + return @err_catcher(name=__name__) def nukeImportSource(self, origin): @@ -547,6 +585,8 @@ def nukeImportSource(self, origin): filePath = i[0] firstFrame = i[1] lastFrame = i[2] + if self.core.getConfig("nuke", "useRelativePaths", dft=False, config="user"): + filePath = os.path.normpath(filePath).replace(os.path.normpath(self.core.projectPath), "%PRISM_JOB%").replace("\\", "/") node = nuke.createNode( "Read", @@ -566,6 +606,8 @@ def nukeImportPasses(self, origin): filePath = i[0] firstFrame = i[1] lastFrame = i[2] + if self.core.getConfig("nuke", "useRelativePaths", dft=False, config="user"): + filePath = os.path.normpath(filePath).replace(os.path.normpath(self.core.projectPath), "%PRISM_JOB%").replace("\\", "/") node = nuke.createNode( "Read", @@ -1113,6 +1155,8 @@ def writeGizmoCreated(self): cmd = "try:\n\tpcore.getPlugin(\"Nuke\").updateNodeUI(\"writePrism\", nuke.toNode(nuke.thisNode().fullName().rsplit(\".\", 1)[0]))\nexcept:\n\tpass" nuke.thisNode().node("WritePrismBase").knob("knobChanged").setValue(cmd) + kwargs = {"origin": self, "node": nuke.thisNode()} + self.core.callback("onWriteGizmoCreated", **kwargs) @err_catcher(name=__name__) def updateNodeUI(self, nodeType, node): @@ -1147,7 +1191,16 @@ def sm_render_getDeadlineParams(self, origin, dlParams, homeDir): "PRISM_NUKE_TERMINAL_FILES", os.path.abspath(__file__) ) - + self.core.getPlugin("Deadline").addEnvironmentItem( + dlParams["jobInfos"], + "PRISM_NUKE_USE_RELATIVE_PATHS", + self.core.getConfig("nuke", "useRelativePaths", dft=False, config="user") + ) + self.core.getPlugin("Deadline").addEnvironmentItem( + dlParams["jobInfos"], + "PRISM_JOB", + os.getenv("PRISM_JOB", "") + ) dlParams["pluginInfos"]["Version"] = self.getAppVersion(origin).split("v")[0] dlParams["pluginInfos"]["OutputFilePath"] = os.path.split( dlParams["jobInfos"]["OutputFilename0"] @@ -1186,6 +1239,7 @@ def openFarmSubmitter(self, node, group): state.ui.setFormat(fmt) state.ui.node = node + state.ui.group = group self.submitter = Farm_Submitter(self, state) self.submitter.show() @@ -1194,7 +1248,8 @@ def openInClicked(self, node, group): path = group.knob("prevFileName").value() if path == "None": return - + + path = self.expandEnvVarsInFilepath(path) menu = QMenu() act_play = QAction("Play") @@ -1348,12 +1403,50 @@ def sm_import_importToApp(self, origin, doImport, update, impFileName): ext = fileName[1].lower() if ext in self.plugin.sceneFormats: result = nuke.nodePaste(impFileName) + elif ext in [".obj", ".fbx", ".abc"]: + path = impFileName.replace("\\", "/") + node_read = nuke.nodes.ReadGeo2(file=path) + node_scene = nuke.nodes.Scene(xpos=node_read.xpos() + 10, ypos=node_read.ypos()+200) + node_render = nuke.nodes.ScanlineRender(xpos=node_read.xpos(), ypos=node_read.ypos()+400) + node_render.connectInput(1, node_scene) + node_scene.connectInput(0, node_read) + result = [node_read] else: self.core.popup("Format is not supported.") return {"result": False, "doImport": doImport} return {"result": result, "doImport": doImport} + @err_catcher(name=__name__) + def importCamera(self, data, filepath): + path = filepath.replace("\\", "/") + node_read = nuke.nodes.Camera3(read_from_file_link=True, file_link=path) + node_scene = nuke.nodes.Scene(xpos=node_read.xpos()+300, ypos=node_read.ypos()) + node_render = nuke.nodes.ScanlineRender(xpos=node_scene.xpos()-10, ypos=node_read.ypos()+200) + node_render.connectInput(0, node_read) + node_render.connectInput(1, node_scene) + node_scene.connectInput(0, node_read) + + @err_catcher(name=__name__) + def productSelectorContextMenuRequested(self, origin, widget, pos, menu): + if widget == origin.tw_versions: + row = widget.rowAt(pos.y()) + if row != -1: + pathC = widget.model().columnCount() - 1 + path = widget.model().index(row, pathC).data() + ext = os.path.splitext(path)[1] + if ext in [".fbx", ".abc"]: + item = origin.tw_identifier.currentItem() + data = item.data(0, Qt.UserRole) + action = QAction("Import Camera", origin) + action.triggered.connect(lambda: self.importCamera(data, filepath=path)) + for idx, act in enumerate(menu.actions()): + if act.text() == "Import": + menu.insertAction(menu.actions()[idx+1], action) + break + else: + menu.insertAction(0, action) + if nuke.env.get("gui") and "fnFlipbookRenderer" in globals(): class PrismRenderedFlipbook(fnFlipbookRenderer.SynchronousRenderedFlipbook): @@ -1412,7 +1505,6 @@ def setupUi(self): self.setLayout(self.lo_main) self.lo_main.addWidget(self.state.ui) self.state.ui.f_name.setVisible(False) - self.state.ui.w_master.setVisible(False) self.state.ui.w_format.setVisible(False) self.state.ui.f_taskname.setVisible(False) self.state.ui.f_resolution.setVisible(False) @@ -1437,6 +1529,8 @@ def submit(self): self.state.ui.gb_submit.setChecked(True) sm = self.core.getStateManager() + comment = self.state.ui.group.knob("comment").value() + sm.e_comment.setText(comment) result = sm.publish(successPopup=False, executeState=True, states=[self.state]) sm.deleteState(self.state) if result: @@ -1546,6 +1640,7 @@ def submit(self): class Prism_NoQt(object): def __init__(self): self.addPluginPaths() + nuke.addFilenameFilter(self.expandEnvVarsInFilepath) def addPluginPaths(self): gdir = os.path.join( @@ -1554,6 +1649,14 @@ def addPluginPaths(self): gdir = gdir.replace("\\", "/") nuke.pluginAddPath(gdir) + @err_catcher(name=__name__) + def expandEnvVarsInFilepath(self, path): + if os.getenv("PRISM_NUKE_USE_RELATIVE_PATHS", "1") == "0": + return path + + expanded_path = os.path.expandvars(path) + return expanded_path + class VersionDlg(QDialog): diff --git a/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Variables.py b/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Variables.py index 3c240995..d3801f73 100644 --- a/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Variables.py +++ b/Prism/Plugins/Apps/Nuke/Scripts/Prism_Nuke_Variables.py @@ -37,7 +37,7 @@ class Prism_Nuke_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.4" + self.version = "v2.0.10" self.pluginName = "Nuke" self.pluginType = "App" self.appShortName = "Nuke" diff --git a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 1 Tools.jsx b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 1 Tools.jsx index fb899e9e..407a11b2 100644 --- a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 1 Tools.jsx +++ b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 1 Tools.jsx @@ -1 +1 @@ -app.system('start "" "PRISMLIBS/Python39/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" Tools') \ No newline at end of file +app.system('start "" "PRISMLIBS/Python311/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" Tools') \ No newline at end of file diff --git a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 2 Save version.jsx b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 2 Save version.jsx index ad63d443..1efb2211 100644 --- a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 2 Save version.jsx +++ b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 2 Save version.jsx @@ -1 +1 @@ -app.system('start "" "PRISMLIBS/Python39/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" SaveVersion') \ No newline at end of file +app.system('start "" "PRISMLIBS/Python311/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" SaveVersion') \ No newline at end of file diff --git a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 3 Save Extended.jsx b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 3 Save Extended.jsx index 04c25598..6c04e872 100644 --- a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 3 Save Extended.jsx +++ b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 3 Save Extended.jsx @@ -1 +1 @@ -app.system('start "" "PRISMLIBS/Python39/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" SaveComment') \ No newline at end of file +app.system('start "" "PRISMLIBS/Python311/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" SaveComment') \ No newline at end of file diff --git a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 4 Export.jsx b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 4 Export.jsx index 380548a0..6f6810f8 100644 --- a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 4 Export.jsx +++ b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 4 Export.jsx @@ -1 +1 @@ -app.system('start "" "PRISMLIBS/Python39/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" Export') \ No newline at end of file +app.system('start "" "PRISMLIBS/Python311/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" Export') \ No newline at end of file diff --git a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 5 Project Browser.jsx b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 5 Project Browser.jsx index 3d9a6406..926dc141 100644 --- a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 5 Project Browser.jsx +++ b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 5 Project Browser.jsx @@ -1 +1 @@ -app.system('start "" "PRISMLIBS/Python39/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" ProjectBrowser') \ No newline at end of file +app.system('start "" "PRISMLIBS/Python311/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" ProjectBrowser') \ No newline at end of file diff --git a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 6 Settings.jsx b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 6 Settings.jsx index 6ab0fe4c..322738bf 100644 --- a/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 6 Settings.jsx +++ b/Prism/Plugins/Apps/Photoshop/Integration/Windows/Prism - 6 Settings.jsx @@ -1 +1 @@ -app.system('start "" "PRISMLIBS/Python39/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" Settings') \ No newline at end of file +app.system('start "" "PRISMLIBS/Python311/Prism.exe" "PLUGINROOT/Scripts/Prism_Photoshop_MenuTools.py" "PRISMROOT" Settings') \ No newline at end of file diff --git a/Prism/Plugins/Apps/Photoshop/Scripts/Prism_Photoshop_Variables.py b/Prism/Plugins/Apps/Photoshop/Scripts/Prism_Photoshop_Variables.py index ccf82167..3580ff66 100644 --- a/Prism/Plugins/Apps/Photoshop/Scripts/Prism_Photoshop_Variables.py +++ b/Prism/Plugins/Apps/Photoshop/Scripts/Prism_Photoshop_Variables.py @@ -37,7 +37,7 @@ class Prism_Photoshop_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.0" + self.version = "v2.0.5" self.pluginName = "Photoshop" self.pluginType = "App" self.appShortName = "Photoshop" diff --git a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py index 1695ae91..cbfddd23 100644 --- a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py +++ b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py @@ -35,13 +35,9 @@ import os import sys -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * from PrismUtils.Decorators import err_catcher as err_catcher diff --git a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Integration.py b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Integration.py index e1c8cada..6638f858 100644 --- a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Integration.py +++ b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Integration.py @@ -37,13 +37,9 @@ import platform import shutil -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * from PrismUtils.Decorators import err_catcher_plugin as err_catcher diff --git a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py index fd97e0d2..f7085b08 100644 --- a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py +++ b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py @@ -37,7 +37,7 @@ class Prism_PluginEmpty_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.0.beta2" + self.version = "v2.0.0" self.pluginName = "PluginEmpty" self.pluginType = "App" self.appShortName = "PluginEmpty" @@ -48,7 +48,7 @@ def __init__(self, core, plugin): self.outputFormats = [".abc", ".obj", ".fbx", "ShotCam"] self.appColor = [255, 255, 255] self.renderPasses = [] - self.platforms = ["Windows", "Linux", "Darwin"] + self.platforms = ["Windows"] self.pluginDirectory = os.path.abspath( os.path.dirname(os.path.dirname(__file__)) ) diff --git a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_externalAccess_Functions.py b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_externalAccess_Functions.py index 3c83cf61..fb34f58e 100644 --- a/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_externalAccess_Functions.py +++ b/Prism/Plugins/Apps/PluginEmpty/Scripts/Prism_PluginEmpty_externalAccess_Functions.py @@ -35,13 +35,9 @@ import os import platform -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * from PrismUtils.Decorators import err_catcher_plugin as err_catcher diff --git a/Prism/Plugins/Apps/PureRef/Scripts/Prism_PureRef_Variables.py b/Prism/Plugins/Apps/PureRef/Scripts/Prism_PureRef_Variables.py index 4d2738a8..c24bea30 100644 --- a/Prism/Plugins/Apps/PureRef/Scripts/Prism_PureRef_Variables.py +++ b/Prism/Plugins/Apps/PureRef/Scripts/Prism_PureRef_Variables.py @@ -37,7 +37,7 @@ class Prism_PureRef_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.0" + self.version = "v2.0.10" self.pluginName = "PureRef" self.pluginType = "App" self.appShortName = "PureRef" @@ -47,7 +47,7 @@ def __init__(self, core, plugin): self.appSpecificFormats = self.sceneFormats self.appColor = [200, 200, 200] self.renderPasses = [] - self.platforms = ["Windows"] + self.platforms = ["Windows", "Linux"] self.pluginDirectory = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) self.hasIntegration = False self.appIcon = os.path.join(self.pluginDirectory, "Resources", "PureRef.ico") diff --git a/Prism/Plugins/Apps/Standalone/Scripts/Prism_Standalone_Functions.py b/Prism/Plugins/Apps/Standalone/Scripts/Prism_Standalone_Functions.py index 7e88399c..db317a3a 100644 --- a/Prism/Plugins/Apps/Standalone/Scripts/Prism_Standalone_Functions.py +++ b/Prism/Plugins/Apps/Standalone/Scripts/Prism_Standalone_Functions.py @@ -117,7 +117,7 @@ def saveScene(self, origin, filepath, details={}, underscore=True): return @err_catcher(name=__name__) - def createWinStartMenu(self, origin): + def createWinStartMenu(self, origin, allUsers=False): if os.environ.get("prism_skip_root_install"): logger.warning( "skipped creating Prism startmenu because of missing permissions." @@ -125,15 +125,24 @@ def createWinStartMenu(self, origin): return if platform.system() == "Windows": - startMenuPath = os.path.join( - os.environ["AppData"], "Microsoft", "Windows", "Start Menu", "Programs" - ) + if allUsers: + startMenuPath = os.path.join( + os.environ["PROGRAMDATA"], "Microsoft", "Windows", "Start Menu", "Programs" + ) + else: + startMenuPath = os.path.join( + os.environ["AppData"], "Microsoft", "Windows", "Start Menu", "Programs" + ) + trayStartup = os.path.join(startMenuPath, "Startup", "Prism.lnk") trayStartupOld = os.path.join(startMenuPath, "Startup", "Prism Tray.lnk") trayStartupOld2 = os.path.join(startMenuPath, "Startup", "PrismTray.lnk") prismStartMenu = os.path.join(startMenuPath, "Prism") trayStartMenu = os.path.join(prismStartMenu, "Prism.lnk") - desktopIcon = os.path.join(os.environ["USERPROFILE"], "Desktop", "Prism.lnk") + if allUsers: + desktopIcon = os.path.join(os.environ["PUBLIC"], "Desktop", "Prism.lnk") + else: + desktopIcon = os.path.join(os.environ["USERPROFILE"], "Desktop", "Prism.lnk") if os.path.exists(prismStartMenu): try: @@ -185,10 +194,16 @@ def createWinStartMenu(self, origin): return True @err_catcher(name=__name__) - def addWindowsStartMenuEntry(self, name, executable, script, args=None): - startMenuPath = os.path.join( - os.environ["AppData"], "Microsoft", "Windows", "Start Menu", "Programs" - ) + def addWindowsStartMenuEntry(self, name, executable, script, args=None, allUsers=False): + if allUsers: + startMenuPath = os.path.join( + os.environ["PROGRAMDATA"], "Microsoft", "Windows", "Start Menu", "Programs" + ) + else: + startMenuPath = os.path.join( + os.environ["AppData"], "Microsoft", "Windows", "Start Menu", "Programs" + ) + scPath = os.path.join(startMenuPath, "Prism", name + ".lnk") if not os.path.isabs(executable): diff --git a/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Functions.py b/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Functions.py index 608ad29f..6b05b8dc 100644 --- a/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Functions.py +++ b/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Functions.py @@ -1457,14 +1457,19 @@ def handleMaster(self, origin, masterType, jobId, jobOutputFile, jobName): import PrismCore pcore = PrismCore.create(prismArgs=["noUI", "loadProject"]) path = r\"%s\" -""" % (self.core.prismRoot, jobOutputFile) +""" % (self.core.prismRoot, os.path.expandvars(jobOutputFile)) if masterType == "media": + if self.core.appPlugin.appType == "2d": + mediaType = "2drenders" + else: + mediaType = "3drenders" + masterAction = origin.cb_master.currentText() if masterAction == "Set as master": - code += "pcore.mediaProducts.updateMasterVersion(path)" + code += "pcore.mediaProducts.updateMasterVersion(path, mediaType=\"%s\")" % mediaType elif masterAction == "Add to master": - code += "pcore.mediaProducts.addToMasterVersion(path)" + code += "pcore.mediaProducts.addToMasterVersion(path, mediaType=\"%s\")" % mediaType elif masterType == "product": code += "pcore.products.updateMasterVersion(path)" @@ -1579,7 +1584,7 @@ def submitSceneDescription3Delight(self, origin, jobId, jobOutputFile, jobOutput cleanupScript = None if cleanupScript: - arguments = ["\"%s\"" % args[0]] + arguments = [args[0]] depId = self.getJobIdFromSubmitResult(result) if depId: cleanupDep = [depId] @@ -1768,7 +1773,7 @@ def getVrayOutputPath(self, origin, jobOutputFile): def submitPythonJob( self, code="", - version="3.9", + version="3.11", jobName=None, jobOutput=None, jobPool="None", @@ -2242,7 +2247,7 @@ def submitMantraJob( if cleanupScript: jobName = jobName.rsplit("_", 1)[0] - arguments = ["\"%s\"" % args[0]] + arguments = [args[0]] depId = self.getJobIdFromSubmitResult(result) if depId: cleanupDep = [depId] @@ -2408,7 +2413,7 @@ def submitRedshiftJob( if cleanupScript: jobName = jobName.rsplit("_", 1)[0] - arguments = ["\"%s\"" % args[0]] + arguments = [args[0]] depId = self.getJobIdFromSubmitResult(result) if depId: cleanupDep = [depId] @@ -2569,7 +2574,7 @@ def submitArnoldJob( if cleanupScript: jobName = jobName.rsplit("_", 1)[0] - arguments = ["\"%s\"" % args[0]] + arguments = [args[0]] depId = self.getJobIdFromSubmitResult(result) if depId: cleanupDep = [depId] @@ -2731,7 +2736,7 @@ def submitVrayJob( if cleanupScript: jobName = jobName.rsplit("_", 1)[0] - arguments = ["\"%s\"" % args[0]] + arguments = [args[0]] depId = self.getJobIdFromSubmitResult(result) if depId: cleanupDep = [depId] diff --git a/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Variables.py b/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Variables.py index c110cc75..4e399bad 100644 --- a/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Variables.py +++ b/Prism/Plugins/Custom/Deadline/Scripts/Prism_Deadline_Variables.py @@ -34,8 +34,8 @@ class Prism_Deadline_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.4" + self.version = "v2.0.10" self.pluginName = "Deadline" self.pluginType = "Custom" self.canOutputLocal = True - self.platforms = ["Windows"] + self.platforms = ["Windows", "Linux"] diff --git a/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py b/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py index 34514a93..26a986bd 100644 --- a/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py +++ b/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Functions.py @@ -32,13 +32,9 @@ # along with Prism. If not, see . -try: - from PySide2.QtCore import * - from PySide2.QtGui import * - from PySide2.QtWidgets import * -except: - from PySide.QtCore import * - from PySide.QtGui import * +from qtpy.QtCore import * +from qtpy.QtGui import * +from qtpy.QtWidgets import * from PrismUtils.Decorators import err_catcher_plugin as err_catcher diff --git a/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py b/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py index 07264fec..4d23a207 100644 --- a/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py +++ b/Prism/Plugins/Custom/PluginEmpty/Scripts/Prism_PluginEmpty_Variables.py @@ -37,8 +37,8 @@ class Prism_PluginEmpty_Variables(object): def __init__(self, core, plugin): - self.version = "v2.0.0.beta13" + self.version = "v2.0.0" self.pluginName = "PluginEmpty" self.pluginType = "Custom" - self.platforms = ["Windows", "Linux", "Darwin"] + self.platforms = ["Windows"] self.pluginDirectory = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) diff --git a/Prism/Scripts/PrismCore.py b/Prism/Scripts/PrismCore.py index f9240e21..e01892c8 100644 --- a/Prism/Scripts/PrismCore.py +++ b/Prism/Scripts/PrismCore.py @@ -56,15 +56,14 @@ # check if python 2 or python 3 is used if sys.version[0] == "3": pVersion = 3 - if sys.version[2] == "7": - pyLibs = "Python37" - elif sys.version[2] == "9": - pyLibs = "Python39" - else: + if sys.version_info.minor == 11: + pyLibs = "Python311" + elif sys.version_info.minor == 10: pyLibs = "Python310" -else: - pVersion = 2 - pyLibs = "Python27" + elif sys.version_info.minor == 9: + pyLibs = "Python39" + elif sys.version_info.minor == 7: + pyLibs = "Python37" prismRoot = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) prismLibs = os.getenv("PRISM_LIBS") @@ -94,9 +93,9 @@ sys.path.append(py3LibPath) if platform.system() == "Windows": - sys.path.insert(0, os.path.join(py3LibPath, "win32")) - sys.path.insert(0, os.path.join(py3LibPath, "win32", "lib")) - pywinpath = os.path.join(prismLibs, "PythonLibs", pyLibs, "pywin32_system32") + sys.path.insert(0, os.path.join(pyLibPath, "win32")) + sys.path.insert(0, os.path.join(pyLibPath, "win32", "lib")) + pywinpath = os.path.join(pyLibPath, "pywin32_system32") sys.path.insert(0, pywinpath) os.environ["PATH"] = pywinpath + os.pathsep + os.environ["PATH"] if hasattr(os, "add_dll_directory") and os.path.exists(pywinpath): @@ -107,10 +106,16 @@ from qtpy.QtGui import * from qtpy.QtWidgets import * from qtpy import API_NAME - try: - import shiboken2 - except: - pass + if API_NAME == "PySide6": + try: + import shiboken6 + except: + pass + else: + try: + import shiboken2 + except: + pass except: if pVersion == 3: psLibs = "Python3" @@ -121,10 +126,16 @@ from qtpy.QtGui import * from qtpy.QtWidgets import * from qtpy import API_NAME - try: - import shiboken2 - except: - pass + if API_NAME == "PySide6": + try: + import shiboken6 + except: + pass + else: + try: + import shiboken2 + except: + pass from PrismUtils.Decorators import err_catcher from PrismUtils import ( @@ -168,7 +179,7 @@ def __init__(self, app="Standalone", prismArgs=[], splashScreen=None): try: # set some general variables - self.version = "v2.0.4" + self.version = "v2.0.10" self.requiredLibraries = "v2.0.0" self.core = self self.preferredExtension = os.getenv("PRISM_CONFIG_EXTENSION", ".json") @@ -177,23 +188,9 @@ def __init__(self, app="Standalone", prismArgs=[], splashScreen=None): self.prismRoot = prismRoot.replace("\\", "/") self.prismLibs = prismLibs.replace("\\", "/") - self.pythonVersion = "Python39" + self.pythonVersion = "Python" + os.getenv("PRISM_PYTHON_VERSION", "3.11").replace(".", "") self.userini = self.getUserPrefConfigPath() - - self.pluginPathApp = os.path.abspath( - os.path.join(__file__, os.pardir, os.pardir, "Plugins", "Apps") - ) - self.pluginPathCustom = os.path.abspath( - os.path.join(__file__, os.pardir, os.pardir, "Plugins", "Custom") - ) - self.pluginDirs = [ - self.pluginPathApp, - self.pluginPathCustom, - ] - for path in self.pluginDirs: - sys.path.append(path) - prjScriptPath = os.path.abspath( os.path.join(__file__, os.pardir, "ProjectScripts") ) @@ -288,11 +285,30 @@ def __init__(self, app="Standalone", prismArgs=[], splashScreen=None): oldSheet = os.path.join(self.prismRoot, "Scripts", "UserInterfacesPrism", "stylesheets", "qdarkstyle") self.registerStyleSheet(oldSheet) + + self.pluginPathApp = os.path.abspath( + os.path.join(__file__, os.pardir, os.pardir, "Plugins", "Apps") + ) + self.pluginPathCustom = os.path.abspath( + os.path.join(__file__, os.pardir, os.pardir, "Plugins", "Custom") + ) + self.pluginDirs = [ + self.pluginPathApp, + self.pluginPathCustom, + ] + if os.getenv("PRISM_LOAD_PLUGINS_FROM_DFT_PATH", "1") == "1": + self.pluginDirs.append(self.plugins.getDefaultPluginPath()) + + for path in self.pluginDirs: + sys.path.append(path) + self.users.ensureUser() self.getUIscale() self.initializePlugins(app) atexit.register(self.onExit) - QApplication.instance().aboutToQuit.connect(self.onExit) + qapp = QApplication.instance() + if qapp: + qapp.aboutToQuit.connect(self.onExit) if sys.argv and sys.argv[-1] == "setupStartMenu": if self.splashScreen: @@ -354,7 +370,11 @@ def getPrismDataDir(self): if os.getenv("PRISM_DATA_DIR"): return os.getenv("PRISM_DATA_DIR") - path = os.path.join(os.environ["PROGRAMDATA"], "Prism2") + if platform.system() == "Windows": + path = os.path.join(os.environ["PROGRAMDATA"], "Prism2") + elif platform.system() == "Linux": + path = "/var/lib/Prism2" + return path @err_catcher(name=__name__) @@ -523,7 +543,7 @@ def checkAutoSave(self): return self.autosave_msg = QMessageBox() - self.autosave_msg.setWindowTitle("Autosave") + self.autosave_msg.setWindowTitle("Prism Autosave") self.autosave_msg.setText("Autosave is disabled. Would you like to save now?") self.autosave_msg.addButton("Save", QMessageBox.YesRole) button = self.autosave_msg.addButton("Save new version", QMessageBox.YesRole) @@ -809,26 +829,6 @@ def getQScreenGeo(self): @err_catcher(name=__name__) def getUIscale(self): sFactor = 1 - highdpi = self.getConfig("globals", "highdpi") - if highdpi: - from qtpy import QtCore - qtVers = [int(n) for n in QtCore.__version__.split(".")] - - if qtVers[0] >= 5 and qtVers[1] >= 6: - screen = self.getQScreenGeo() - if screen: - screenWidth, screenHeight = ( - screen.width(), - screen.height(), - ) - wFactor = screenWidth / 960.0 - hFactor = screenHeight / 540.0 - if abs(wFactor - 1) < abs(hFactor - 1): - sFactor = wFactor - else: - sFactor = hFactor - - # sFactor = QApplication.screens()[0].logicalDotsPerInch() / 96 self.uiScaleFactor = sFactor return self.uiScaleFactor @@ -928,7 +928,7 @@ def showAbout(self): self.popup(astr, title="About", severity="info") @err_catcher(name=__name__) - def sendFeedbackDlg(self): + def sendFeedbackDlg(self, state=None): fbDlg = PrismWidgets.EnterText() fbDlg.setModal(True) self.parentWindow(fbDlg) @@ -955,7 +955,7 @@ def sendFeedbackDlg(self): fbDlg.layout().insertLayout(fbDlg.layout().count() - 1, fbDlg.lo_screenGrab) fbDlg.layout().insertItem(fbDlg.layout().count() - 1, fbDlg.sp_main) - size = QSize(fbDlg.size().width(), fbDlg.size().height() * 0.7) + size = QSize(fbDlg.size().width(), int(fbDlg.size().height() * 0.7)) fbDlg.b_addScreenGrab.clicked.connect(lambda: self.attachScreenGrab(fbDlg, size=size)) fbDlg.b_removeScreenGrab.clicked.connect(lambda: self.removeScreenGrab(fbDlg)) fbDlg.b_removeScreenGrab.setVisible(False) @@ -1006,7 +1006,7 @@ def attachScreenGrab(self, dlg, size=None): @err_catcher(name=__name__) def removeScreenGrab(self, dlg): dlg.screenGrab = None - dlg.l_screenGrab.setPixmap(None) + dlg.l_screenGrab.clear() dlg.b_addScreenGrab.setVisible(True) dlg.b_removeScreenGrab.setVisible(False) dlg.resize(dlg.origSize) @@ -1020,6 +1020,8 @@ def openWebsite(self, location): url = "https://prism-pipeline.com/docs/latest" elif location == "downloads": url = "https://prism-pipeline.com/downloads/" + elif location == "discord": + url = "https://prism-pipeline.com/discord/" else: url = location @@ -1027,9 +1029,22 @@ def openWebsite(self, location): webbrowser.open(url) + @err_catcher(name=__name__) + def getCheckStateValue(self, checkState): + if hasattr(checkState, "value"): + return checkState.value + else: + return int(checkState) + @err_catcher(name=__name__) def isObjectValid(self, obj): - if "shiboken2" in globals(): + if "shiboken6" in globals(): + if not obj or not shiboken6.isValid(obj): + return False + else: + return True + + elif "shiboken2" in globals(): if not obj or not shiboken2.isValid(obj): return False else: @@ -1041,7 +1056,10 @@ def getStateManager(self, create=True): if not sm: sm = getattr(self, "stateManagerInCreation", None) - if "shiboken2" in globals(): + if "shiboken6" in globals(): + if sm and not shiboken6.isValid(sm): + sm = None + elif "shiboken2" in globals(): if sm and not shiboken2.isValid(sm): sm = None @@ -1317,6 +1335,7 @@ def getConfig( config=None, dft=None, location=None, + allowCache=True, ): return self.configs.getConfig( cat=cat, @@ -1325,6 +1344,7 @@ def getConfig( config=config, dft=dft, location=location, + allowCache=allowCache, ) @err_catcher(name=__name__) @@ -1393,6 +1413,12 @@ def resolveFrameExpression(self, expression): else: step = 1 + if cData[0].strip().startswith("^"): + mode = "substract" + cData[0] = cData[0].strip().strip("^") + else: + mode = "add" + se = [x for x in cData[0].split("-") if x] if len(se) == 2: try: @@ -1406,11 +1432,14 @@ def resolveFrameExpression(self, expression): frame = int(se[0]) except: continue - if frame not in rframes: + if frame not in rframes and mode == "add": rframes.append(frame) if len(rframes) > 10000: return rframes + elif frame in rframes and mode == "substract": + rframes.remove(frame) + continue else: continue @@ -1422,10 +1451,12 @@ def resolveFrameExpression(self, expression): end += 1 for frame in range(start, end, step): - if frame not in rframes: + if frame not in rframes and mode == "add": rframes.append(frame) if len(rframes) > 10000: return rframes + elif frame in rframes and mode == "substract": + rframes.remove(frame) return rframes @@ -1570,6 +1601,7 @@ def detectFileSequence(self, path): pathDir = os.path.dirname(path) regName = "" seqFiles = [] + siblings = [] path = path.replace("$F4", "1001") for root, folders, files in os.walk(pathDir): @@ -1816,7 +1848,7 @@ def saveScene( return False if versionUp: - fnameData = self.getScenefileData(curfile) + fnameData = self.getScenefileData(curfile, getEntityFromPath=True) if "department" not in fnameData: title = "Could not save the file" msg = "Couldn't get the required data from the current scenefile. Did you save it using Prism?\nUse the Project Browser to save your current scenefile with the correct name." @@ -1860,8 +1892,20 @@ def saveScene( detailData = self.getScenefileData(curfile) if detailData.get("type") == "asset": key = "assetScenefiles" + if "sequence" in detailData: + del detailData["sequence"] + + if "shot" in detailData: + del detailData["shot"] + elif detailData.get("type") == "shot": key = "shotScenefiles" + if "asset" in detailData: + del detailData["asset"] + + if "asset_path" in detailData: + del detailData["asset_path"] + else: key = None @@ -1878,6 +1922,10 @@ def saveScene( del detailData["user"] if "username" in detailData: del detailData["username"] + if "description" in detailData: + del detailData["description"] + if "locations" in detailData: + del detailData["locations"] else: detailData = {} @@ -1947,7 +1995,7 @@ def getVersioninfoPath(self, scenepath): return filepath @err_catcher(name=__name__) - def saveSceneInfo(self, filepath, details=None, preview=None, clean=True): + def saveSceneInfo(self, filepath, details=None, preview=None, clean=True, replace=False): details = details or {} if "username" not in details: details["username"] = self.username @@ -1961,8 +2009,11 @@ def saveSceneInfo(self, filepath, details=None, preview=None, clean=True): details["dependencies"] = deps["dependencies"] details["externalFiles"] = deps["externalFiles"] - sData = self.getScenefileData(filepath) - sData.update(details) + if replace: + sData = details + else: + sData = self.getScenefileData(filepath) + sData.update(details) if clean: keys = ["filename", "extension", "path", "paths", "task_path"] @@ -1971,7 +2022,7 @@ def saveSceneInfo(self, filepath, details=None, preview=None, clean=True): del sData[key] infoPath = self.getVersioninfoPath(filepath) - self.setConfig(configPath=infoPath, data=sData) + self.setConfig(configPath=infoPath, data=sData, updateNestedData=not replace) if preview: self.core.entities.setScenePreview(filepath, preview) @@ -2175,11 +2226,15 @@ def getFileModificationDate(self, path, validate=False, ignoreError=True, asStri return cdate @err_catcher(name=__name__) - def getFormattedDate(self, stamp): + def getFormattedDate(self, stamp=None, datetimeInst=None): if self.isStr(stamp): return "" - cdate = datetime.fromtimestamp(stamp) + if datetimeInst: + cdate = datetimeInst + else: + cdate = datetime.fromtimestamp(stamp) + cdate = cdate.replace(microsecond=0) fmt = "%d.%m.%y, %H:%M:%S" if os.getenv("PRISM_DATE_FORMAT"): @@ -2609,7 +2664,7 @@ def createShortcut(self, link, target, args="", ignoreError=False): proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) - result = proc.communicate()[0] + result = proc.communicate() except Exception as e: result = str(e) finally: @@ -2642,10 +2697,16 @@ def createSymlink(self, link, target): logger.warning("not implemented") @err_catcher(name=__name__) - def setTrayStartupWindows(self, enabled): - startMenuPath = os.path.join( - os.environ["AppData"], "Microsoft", "Windows", "Start Menu", "Programs" - ) + def setTrayStartupWindows(self, enabled, allUsers=False): + if allUsers: + startMenuPath = os.path.join( + os.environ["PROGRAMDATA"], "Microsoft", "Windows", "Start Menu", "Programs" + ) + else: + startMenuPath = os.path.join( + os.environ["AppData"], "Microsoft", "Windows", "Start Menu", "Programs" + ) + trayStartup = os.path.join(startMenuPath, "Startup", "Prism.lnk") if os.path.exists(trayStartup): try: @@ -2670,19 +2731,29 @@ def setTrayStartupWindows(self, enabled): @err_catcher(name=__name__) def getTempFilepath(self, filename=None, ext=".jpg", filenamebase=None): - filenamebase = filenamebase or "prism" - path = os.path.join(os.environ["temp"], "Prism", filenamebase + "_") + if platform.system() == "Windows": + base = os.environ["temp"] + else: + base = "/tmp" + + path = os.path.join(base, "Prism") + if not os.path.exists(path): + os.makedirs(path) if filename: + if filenamebase: + filename = filenamebase + filename + filepath = os.path.join(path, filename) else: + path += "/" + if filenamebase: + path += filenamebase + "_" + file = tempfile.NamedTemporaryFile(prefix=path, suffix=ext) filepath = file.name file.close() - if not os.path.exists(os.path.dirname(filepath)): - os.makedirs(os.path.dirname(filepath)) - return filepath @property diff --git a/Prism/Scripts/PrismInstaller.py b/Prism/Scripts/PrismInstaller.py index 6fd1d344..0a83e8ad 100644 --- a/Prism/Scripts/PrismInstaller.py +++ b/Prism/Scripts/PrismInstaller.py @@ -162,8 +162,11 @@ def setupUi(self): self.chb_startmenu.setChecked(True) self.chb_integrations = QCheckBox("DCC integrations") self.chb_integrations.setChecked(True) + self.chb_uninstaller = QCheckBox("Create Uninstaller") + self.chb_uninstaller.setChecked(False) self.lo_options.addWidget(self.chb_startmenu) self.lo_options.addWidget(self.chb_integrations) + self.lo_options.addWidget(self.chb_uninstaller) self.w_footer = QWidget() self.lo_footer = QHBoxLayout() @@ -202,6 +205,8 @@ def entered(self): QApplication.processEvents() if self.parent.w_pageStart.chb_startmenu.isChecked(): self.parent.core.setupStartMenu(quiet=True) + + if self.parent.w_pageStart.chb_uninstaller.isChecked(): self.l_header.setText("Creating uninstaller. Please wait...") QApplication.processEvents() self.parent.core.setupUninstaller(quiet=True) diff --git a/Prism/Scripts/PrismSettings.py b/Prism/Scripts/PrismSettings.py index d43ad58e..3d89788e 100644 --- a/Prism/Scripts/PrismSettings.py +++ b/Prism/Scripts/PrismSettings.py @@ -295,6 +295,7 @@ def loadUI(self): "badd": b_addInteg, "bremove": b_removeInteg, "lexample": l_examplePath, + "tab": tab, } getattr( @@ -311,7 +312,7 @@ def loadUI(self): self.tab_dccApps.layout().addStretch() - headerLabels = ["Loaded", "Auto load", "Name", "Type", "Version", "Location"] + headerLabels = ["Loaded", "Auto Load", "Name", "Type", "Version", "Location"] self.tw_plugins.setColumnCount(len(headerLabels)) self.tw_plugins.setHorizontalHeaderLabels(headerLabels) self.tw_plugins.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) @@ -677,6 +678,8 @@ def rclPluginList(self, pos=None): act_unload = rcmenu.addAction( "Unload", lambda: self.loadPlugins(selected=True, unload=True) ) + act_autoload = rcmenu.addAction("Auto Load") + act_autoload.setCheckable(True) act_open = rcmenu.addAction("Open in explorer", self.openPluginFolder) if len(selPlugs) == 0: @@ -687,9 +690,24 @@ def rclPluginList(self, pos=None): else: act_reload.setEnabled(False) act_unload.setEnabled(False) + + if self.tw_plugins.cellWidget(selPlugs[0], 1).isChecked(): + act_autoload.setChecked(True) + else: + act_autoload.setChecked(False) + elif len(selPlugs) > 1: act_open.setEnabled(False) + isAutoLoad = False + for selPlug in selPlugs: + if self.tw_plugins.cellWidget(selPlug, 1).isChecked(): + isAutoLoad = True + break + + act_autoload.setChecked(isAutoLoad) + + act_autoload.toggled.connect(lambda x: self.setAutoLoadPlugins(selected=True, state=x)) rcmenu.exec_(QCursor.pos()) @err_catcher(name=__name__) @@ -831,7 +849,6 @@ def saveSettings(self, changeProject=True, configPath=None, export=False): cData["globals"]["useMediaThumbnails"] = self.chb_mediaThumbnails.isChecked() cData["globals"]["autosave"] = self.chb_autosave.isChecked() cData["globals"]["capture_viewport"] = self.chb_captureViewport.isChecked() - cData["globals"]["highdpi"] = self.chb_highDPI.isChecked() cData["globals"]["send_error_reports"] = self.chb_errorReports.isChecked() cData["globals"]["debug_mode"] = self.chb_debug.isChecked() cData["globals"]["standalone_stylesheet"] = self.cb_styleSheet.currentData().get("name", "") @@ -899,7 +916,7 @@ def loadSettings(self, configPath=None): if platform.system() == "Windows": trayStartupPath = os.path.join( - os.getenv("APPDATA"), + os.getenv("PROGRAMDATA"), "Microsoft", "Windows", "Start Menu", @@ -907,6 +924,16 @@ def loadSettings(self, configPath=None): "Startup", "Prism.lnk", ) + if not os.path.exists(trayStartupPath): + trayStartupPath = os.path.join( + os.getenv("APPDATA"), + "Microsoft", + "Windows", + "Start Menu", + "Programs", + "Startup", + "Prism.lnk", + ) elif platform.system() == "Linux": trayStartupPath = "/etc/xdg/autostart/PrismTray.desktop" elif platform.system() == "Darwin": @@ -961,9 +988,6 @@ def loadSettings(self, configPath=None): if "capture_viewport" in gblData: self.chb_captureViewport.setChecked(gblData["capture_viewport"]) - if "highdpi" in gblData: - self.chb_highDPI.setChecked(gblData["highdpi"]) - if "send_error_reports" in gblData: self.chb_errorReports.setChecked(gblData["send_error_reports"]) @@ -1220,13 +1244,33 @@ def loadPlugins(self, plugins=None, selected=False, unload=False): self.core.ps.activateWindow() self.core.ps.raise_() + @err_catcher(name=__name__) + def setAutoLoadPlugins(self, plugins=None, selected=False, state=False): + if plugins is None and selected: + plugins = [] + for i in self.tw_plugins.selectedItems(): + if i.column() != 0: + continue + + pluginPath = i.data(Qt.UserRole) + if pluginPath: + name = self.tw_plugins.item(i.row(), 2).text() + plugins.append({"path": pluginPath, "name": name, "row": i.row()}) + + if not plugins: + return + + for plugin in plugins: + self.tw_plugins.cellWidget(plugin["row"], 1).setChecked(state) + self.core.plugins.setAutoLoadPlugin(plugin["name"], state) + @err_catcher(name=__name__) def managePluginsDlg(self, state=None): self.dlg_managePluginPaths = ManagePluginPaths(self) self.dlg_managePluginPaths.show() @err_catcher(name=__name__) - def loadExternalPlugin(self): + def loadExternalPlugin(self, state=None): startPath = getattr( self, "externalPluginStartPath", None ) or self.core.plugins.getPluginPath(location="root") @@ -1237,11 +1281,10 @@ def loadExternalPlugin(self): if not selectedPath: return - result = self.core.plugins.loadPlugin(selectedPath, activate=True) + result = self.core.plugins.loadPlugin(selectedPath, activate=True, showWarnings=True) selectedParent = os.path.dirname(selectedPath) if not result: self.externalPluginStartPath = selectedParent - self.core.popup("Couldn't load plugin") return self.core.plugins.addToPluginConfig(selectedPath) @@ -1324,7 +1367,7 @@ def createPlugin(self, pluginName, pluginType, location, path=""): return self.core.plugins.loadPlugin(pluginPath) - if pluginType == "Custom": + if pluginType in ["Custom", "Single File"]: self.core.plugins.addToPluginConfig(pluginPath) ps = self.core.ps # keep the Settings Window in memory to avoid crash @@ -1476,7 +1519,7 @@ def setupUi(self): self.lo_type = QHBoxLayout() self.l_type = QLabel("Type:") self.cb_type = QComboBox() - self.cb_type.addItems(["App", "Custom"]) + self.cb_type.addItems(["App", "Custom", "Single File"]) self.cb_type.setCurrentIndex(1) self.lo_type.addWidget(self.l_type) self.lo_type.addWidget(self.cb_type) @@ -1522,14 +1565,14 @@ def connectEvents(self): self.e_path.textChanged.connect(lambda x: self.validate(self.e_path, x)) self.cb_type.activated.connect(lambda x: self.refreshPath()) self.cb_location.activated.connect(lambda x: self.refreshPath()) - self.cb_location.activated[str].connect( - lambda x: self.l_path.setVisible(x != "Custom") + self.cb_location.activated.connect( + lambda x: self.l_path.setVisible(self.cb_location.currentText() != "Custom") ) - self.cb_location.activated[str].connect( - lambda x: self.e_path.setVisible(x == "Custom") + self.cb_location.activated.connect( + lambda x: self.e_path.setVisible(self.cb_location.currentText() == "Custom") ) - self.cb_location.activated[str].connect( - lambda x: self.b_browse.setVisible(x == "Custom") + self.cb_location.activated.connect( + lambda x: self.b_browse.setVisible(self.cb_location.currentText() == "Custom") ) self.b_browse.clicked.connect(self.browse) self.b_browse.customContextMenuRequested.connect( @@ -1935,12 +1978,13 @@ def addPluginPath(self): self.core.prismSettings(restart=True, reload_module=False) QApplication.processEvents() - self.core.ps.externalPluginStartPath = selectedParent - self.core.ps.navigate({"tab": "Plugins", "settingsType": "User"}) - self.core.ps.activateWindow() - self.core.ps.raise_() - self.core.ps.w_user.managePluginsDlg() - self.close() + if self.core.ps: + self.core.ps.externalPluginStartPath = selectedParent + self.core.ps.navigate({"tab": "Plugins", "settingsType": "User"}) + self.core.ps.activateWindow() + self.core.ps.raise_() + self.core.ps.w_user.managePluginsDlg() + self.close() @err_catcher(name=__name__) def removePluginPaths(self): diff --git a/Prism/Scripts/PrismTray.py b/Prism/Scripts/PrismTray.py index 5c5ee4e4..8305dc9f 100644 --- a/Prism/Scripts/PrismTray.py +++ b/Prism/Scripts/PrismTray.py @@ -307,13 +307,15 @@ def restartTray(self): pythonPath = self.core.getPythonPath(executable="Prism") filepath = os.path.join(self.core.prismRoot, "Scripts", "PrismTray.py") - cmd = """start "" "%s" "%s" showSplash""" % (pythonPath, filepath) + cmd = """start "" "%s" "%s" showSplash ignore_pid=%s""" % (pythonPath, filepath, os.getpid()) subprocess.Popen(cmd, cwd=self.core.prismRoot, shell=True) sys.exit(0) def exitTray(self): - self.listenerThread.shutDown() - qApp.quit() + if hasattr(self, "listenerThread"): + self.listenerThread.shutDown() + + QApplication.instance().quit() class ListenerThread(QThread): @@ -383,10 +385,16 @@ def send(self, data): def isAlreadyRunning(): if platform.system() == "Windows": coreProc = [] + ignoredPids = [os.getpid()] + for arg in sys.argv: + if arg.startswith("ignore_pid="): + pid = int(arg.split("=")[-1]) + ignoredPids.append(pid) + for proc in psutil.process_iter(): try: if ( - proc.pid != os.getpid() + proc.pid not in ignoredPids and os.path.basename(proc.exe()) == "Prism.exe" and proc.username() == psutil.Process(os.getpid()).username() ): @@ -398,18 +406,145 @@ def isAlreadyRunning(): return False -if __name__ == "__main__": - qApp = QApplication(sys.argv) - qApp.setQuitOnLastWindowClosed(False) - if not QSystemTrayIcon.isSystemTrayAvailable(): - QMessageBox.critical( - None, - "PrismTray", - "Could not launch PrismTray. Tray icons are not supported on this OS.", - ) - sys.exit(1) +def findPrismProcesses(): + procs = [] + exes = [ + "Prism.exe", + ] + try: + import psutil + except Exception as e: + pass + else: + ignoredPids = [os.getpid()] + for arg in sys.argv: + if arg.startswith("ignore_pid="): + pid = int(arg.split("=")[-1]) + ignoredPids.append(pid) + + for proc in psutil.process_iter(): + try: + if proc.pid in ignoredPids: + continue + + try: + if os.path.basename(proc.exe()) in exes: + procs.append("%s (%s)" % (proc.exe(), proc.pid)) + except: + continue + except: + pass + + return procs + + +def showDetailPopup(msgTxt, parent): + procUserTxt = findPrismProcesses() + if procUserTxt: + msgTxt += "\n\nThe following Prism processes are already running:\n\n" + msgTxt += "\n".join(procUserTxt) + + title = "Details" + icon = QMessageBox.Information + buttons = ["Stop processes", "Close"] + default = buttons[0] + escapeButton = "Close" + + msg = QMessageBox( + icon, + title, + msgTxt, + parent=parent, + ) + + for button in buttons: + if button in ["Close", "Cancel", "Ignore"]: + role = QMessageBox.RejectRole + else: + role = QMessageBox.YesRole + + b = msg.addButton(button, role) + if default == button: + msg.setDefaultButton(b) + + if escapeButton == button: + msg.setEscapeButton(b) + + msg.exec_() + result = msg.clickedButton().text() + if result == "Stop processes": + closePrismProcesses() + parent._result = "Stop running process" + parent.close() + elif result == "Close": + pass + + return result + + +def popupQuestion(text, buttons): + text = str(text) + title = "Prism" + icon = QMessageBox.Warning + default = buttons[0] + escapeButton = "Close" + + msg = QMessageBox( + icon, + title, + text, + ) + for button in buttons: + if button in ["Close", "Cancel", "Ignore"]: + role = QMessageBox.RejectRole + else: + role = QMessageBox.YesRole + + b = msg.addButton(button, role) + if default == button: + msg.setDefaultButton(b) + + if button == "Details...": + b.clicked.disconnect() + b.clicked.connect(lambda: showDetailPopup(text, msg)) + + if escapeButton == button: + msg.setEscapeButton(b) + + msg.exec_() + result = msg.clickedButton().text() + if hasattr(msg, "_result"): + result = msg._result + + return result + +def closePrismProcesses(): + try: + import psutil + except Exception as e: + pass + else: + PROCNAMES = ["Prism.exe"] + for proc in psutil.process_iter(): + if proc.name() in PROCNAMES: + p = psutil.Process(proc.pid) + if proc.pid == os.getpid(): + continue + + try: + if "SYSTEM" not in p.username(): + try: + proc.kill() + except Exception as e: + logger.warning("error while killing process: %s" % str(e)) + except Exception as e: + logger.warning("failed to kill process: %s" % str(e)) + + +def launch(): if isAlreadyRunning(): + qApp = QApplication(sys.argv) senderThread = SenderThread() senderThread.start() idx = 0 @@ -427,8 +562,28 @@ def isAlreadyRunning(): senderThread.shutDown() else: senderThread.quit() - QMessageBox.warning(None, "Prism", "Prism is already running.") - + qApp = QApplication.instance() + wIcon = QIcon( + os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "UserInterfacesPrism", + "p_tray.png", + ) + ) + qApp.setWindowIcon(wIcon) + + from UserInterfacesPrism.stylesheets import blue_moon + qApp.setStyleSheet(blue_moon.load_stylesheet(pyside=True)) + + result = popupQuestion("Prism is already running.", buttons=["Stop running process", "Details...", "Close"]) + if result == "Stop running process": + closePrismProcesses() + return launch() + elif result == "Close": + pass + elif result == "Ignore": + return + sys.exit() else: args = ["loadProject", "tray"] @@ -438,5 +593,19 @@ def isAlreadyRunning(): args.append("noSplash") pc = PrismCore.create(prismArgs=args) + qApp = QApplication.instance() + qApp.setQuitOnLastWindowClosed(False) + if not QSystemTrayIcon.isSystemTrayAvailable(): + QMessageBox.critical( + None, + "PrismTray", + "Could not launch PrismTray. Tray icons are not supported on this OS.", + ) + sys.exit(1) + pc.startTray() sys.exit(qApp.exec_()) + + +if __name__ == "__main__": + launch() diff --git a/Prism/Scripts/PrismUtils/ConfigManager.py b/Prism/Scripts/PrismUtils/ConfigManager.py index b5f53015..08bdd547 100644 --- a/Prism/Scripts/PrismUtils/ConfigManager.py +++ b/Prism/Scripts/PrismUtils/ConfigManager.py @@ -162,6 +162,8 @@ def clearCache(self, path=None): else: self.cachedConfigs = {} + self.core.callback("postClearConfigCache", args=[path]) + @err_catcher(name=__name__) def getCacheTime(self, path): if path: @@ -280,6 +282,7 @@ def getConfig( config=None, dft=None, location=None, + allowCache=True, ): if not configPath and config: configPath = self.getConfigPath(config, location=location) @@ -289,7 +292,7 @@ def getConfig( if configPath: configPath = os.path.normpath(configPath) - if configPath in self.cachedConfigs: + if configPath in self.cachedConfigs and allowCache: configData = self.cachedConfigs[configPath]["data"] if isinstance(configData, collections.Mapping): configData = configData.copy() @@ -331,11 +334,12 @@ def getConfig( if configData is None: return dft - mdate = self.core.getFileModificationDate(configPath, asString=False) - self.cachedConfigs[configPath] = { - "modtime": mdate, - "data": configData, - } + if allowCache: + mdate = self.core.getFileModificationDate(configPath, asString=False) + self.cachedConfigs[configPath] = { + "modtime": mdate, + "data": configData, + } # logger.debug("adding cache: %s ---- %s" % (configPath, configData)) diff --git a/Prism/Scripts/PrismUtils/MediaManager.py b/Prism/Scripts/PrismUtils/MediaManager.py index ae0bd8eb..f5f7ee67 100644 --- a/Prism/Scripts/PrismUtils/MediaManager.py +++ b/Prism/Scripts/PrismUtils/MediaManager.py @@ -93,15 +93,18 @@ def __init__(self, core): ".mp4", ".mov", ".avi", + ".m4v", ] - self.videoFormats = [".mp4", ".mov", ".avi"] + self.videoFormats = [".mp4", ".mov", ".avi", ".m4v"] self.getImageIO() @err_catcher(name=__name__) def filterValidMediaFiles(self, filepaths): validFiles = [] for mediaFile in sorted(filepaths): - if os.path.splitext(mediaFile)[1] in self.supportedFormats: + if os.path.splitext(mediaFile)[1].lower() in self.supportedFormats: + validFiles.append(mediaFile) + elif os.path.basename(mediaFile) == "REDIRECT.txt": validFiles.append(mediaFile) return validFiles @@ -109,9 +112,9 @@ def filterValidMediaFiles(self, filepaths): @err_catcher(name=__name__) def detectSequence(self, filepaths): seq = [] - base = re.sub("\d+", "", filepaths[0]) + base = re.sub(r"\d+", "", filepaths[0]) for filepath in sorted(filepaths): - if re.sub("\d+", "", filepath) == base: + if re.sub(r"\d+", "", filepath) == base: seq.append(filepath) return seq @@ -120,6 +123,7 @@ def detectSequence(self, filepaths): def getSequenceFromFilename(self, filename): seq = filename baseName, extension = os.path.splitext(os.path.basename(filename)) + extension = extension.lower() if len(baseName) >= self.core.framePadding: endStr = baseName[-self.core.framePadding:] if pVersion == 2: @@ -179,12 +183,14 @@ def detectSequences(self, files, getFirstFile=False, sequencePattern=True): psources = [] for file in files: baseName, extension = os.path.splitext(file) + extension = extension.lower() if extension in self.core.media.supportedFormats: filename = self.getFilenameWithoutFrameNumber(file) psources.append(os.path.splitext(filename)) for file in sorted(files): baseName, extension = os.path.splitext(file) + extension = extension.lower() if extension in self.core.media.supportedFormats: if getFirstFile: return [file] @@ -278,7 +284,7 @@ def getVideoReader(self, filepath): reader = "Error - empty file: %s" % filepath else: imageio = self.getImageIO() - filepath = str(filepath) # unicode causes errors in Python 2 + filepath = str(filepath).lower() # unicode causes errors in Python 2 try: reader = imageio.get_reader(filepath, "ffmpeg") except Exception as e: @@ -309,13 +315,7 @@ def getOIIO(self): oiio = None try: if platform.system() == "Windows": - if pVersion == 2: - if self.core.appPlugin.pluginName == "Nuke": - from oiio22_msvc1426 import OpenImageIO as oiio - else: - from oiio1618 import OpenImageIO as oiio - else: - from oiio_2_4 import OpenImageIO as oiio + from oiio_2_4 import OpenImageIO as oiio elif platform.system() in ["Linux", "Darwin"]: import OpenImageIO as oiio @@ -337,6 +337,14 @@ def getImageIO(self): import imageio_ffmpeg except: logger.debug("failed to load imageio: %s" % traceback.format_exc()) + else: + try: + imageio.config.known_plugins["FFMPEG"].legacy_args["extensions"] += " .m4v" + for ext in imageio.config.extension_list: + if ext.extension == ".m4v": + ext.priority.insert(0, "FFMPEG") + except: + pass self._imageio = imageio @@ -401,9 +409,9 @@ def checkOddResolution(self, path, popup=False): @err_catcher(name=__name__) def convertMedia(self, inputpath, startNum, outputpath, settings=None): inputpath = inputpath.replace("\\", "/") - inputExt = os.path.splitext(inputpath)[1] - outputExt = os.path.splitext(outputpath)[1] - videoInput = inputExt in [".mp4", ".mov"] + inputExt = os.path.splitext(inputpath)[1].lower() + outputExt = os.path.splitext(outputpath)[1].lower() + videoInput = inputExt in [".mp4", ".mov", ".m4v"] startNum = str(startNum) if startNum is not None else None ffmpegPath = self.getFFmpeg(validate=True) @@ -553,6 +561,7 @@ def getThumbnailPath(self, path): @err_catcher(name=__name__) def getUseThumbnailForFile(self, filepath): _, ext = os.path.splitext(filepath) + ext = ext.lower() useThumb = ext in [".exr", ".dpx", ".hdr"] or ext in self.videoFormats return useThumb @@ -606,14 +615,23 @@ def getPixmapFromExrPath(self, path, width=None, height=None, channel=None, allo chend = chbegin + 3 break - pixels = imgInput.read_image(subimage=subimage, miplevel=0, chbegin=chbegin, chend=chend) + try: + pixels = imgInput.read_image(subimage=subimage, miplevel=0, chbegin=chbegin, chend=chend) + except Exception as e: + logger.warning("failed to read image: %s - %s" % (path, e)) + return + + if pixels is None: + logger.warning("failed to read image (no pixels): %s" % (path)) + return + rgbImgSrc = oiio.ImageBuf( oiio.ImageSpec(imgInput.spec().full_width, imgInput.spec().full_height, 3, oiio.UINT16) ) imgInput.close() if "numpy" in globals(): - rgbImgSrc.set_pixels(oiio.ROI.All, numpy.array(pixels)) + rgbImgSrc.set_pixels(imgInput.spec().roi, numpy.array(pixels)) else: for h in range(height): for w in range(width): @@ -673,7 +691,7 @@ def getPixmapFromExrPath(self, path, width=None, height=None, channel=None, allo @err_catcher(name=__name__) def getPixmapFromPath(self, path, width=None, height=None, colorAdjust=False): if path: - _, ext = os.path.splitext(path) + ext = os.path.splitext(path)[1].lower() if ext in self.core.media.videoFormats: return self.getPixmapFromVideoPath(path) elif ext in [".exr", ".dpx", ".hdr"]: @@ -785,6 +803,9 @@ def getPixmapFromClipboard(self): @err_catcher(name=__name__) def scalePixmap(self, pixmap, width, height, keepRatio=True, fitIntoBounds=True, crop=False, fillBackground=False): + if not pixmap: + return pixmap + if keepRatio: if fitIntoBounds: mode = Qt.KeepAspectRatio @@ -805,7 +826,11 @@ def scalePixmap(self, pixmap, width, height, keepRatio=True, fitIntoBounds=True, if fitIntoBounds: if fillBackground: new_pixmap = QPixmap(width, height) - new_pixmap.fill(Qt.black) + if fillBackground is True: + new_pixmap.fill(Qt.black) + else: + new_pixmap.fill(fillBackground) + painter = QPainter(new_pixmap) painter.drawPixmap((width-pixmap.width())/2, (height-pixmap.height())/2, pixmap) painter.end() @@ -862,6 +887,7 @@ def getMediaResolution(self, path, videoReader=None): pwidth = None pheight = None base, ext = os.path.splitext(path) + ext = ext.lower() if ext in [ ".jpg", @@ -887,7 +913,7 @@ def getMediaResolution(self, path, videoReader=None): imgSpecs = buf.spec() pwidth = imgSpecs.full_width pheight = imgSpecs.full_height - elif ext in [".mp4", ".mov", ".avi"]: + elif ext in [".mp4", ".mov", ".avi", ".m4v"]: if videoReader is None: videoReader = self.getVideoReader(path) diff --git a/Prism/Scripts/PrismUtils/MediaProducts.py b/Prism/Scripts/PrismUtils/MediaProducts.py index 350dc2cc..70b25463 100644 --- a/Prism/Scripts/PrismUtils/MediaProducts.py +++ b/Prism/Scripts/PrismUtils/MediaProducts.py @@ -57,7 +57,7 @@ def __init__(self, core): self.core = core @err_catcher(name=__name__) - def createExternalMedia(self, filepath, entity, identifier, version, action="copy"): + def createExternalMedia(self, filepath, entity, identifier, version, action="copy", location="global"): if entity["type"] == "asset": key = "renderFilesAssets" elif entity["type"] == "shot": @@ -74,6 +74,9 @@ def createExternalMedia(self, filepath, entity, identifier, version, action="cop if "comment" not in context: context["comment"] = "" + basePath = self.core.paths.getRenderProductBasePaths()[location] + context["project_path"] = basePath + path = self.core.projects.getResolvedProjectStructurePath(key, context=context) folderpath = os.path.dirname(path) @@ -127,6 +130,18 @@ def getExternalPathFromVersion(self, version): return curLoc + @err_catcher(name=__name__) + def getDisplayNameForIdentifier(self, identifier, mediaType): + display = identifier + if mediaType == "2drenders": + display += " (2d)" + elif mediaType == "playblasts": + display += " (playblast)" + elif mediaType == "externalMedia": + display += " (external)" + + return display + @err_catcher(name=__name__) def getIdentifiersByType(self, entity, locations=None): locationData = self.core.paths.getRenderProductBasePaths() @@ -369,6 +384,9 @@ def getFilesFromContext(self, context): if context.get("locations"): locations = self.core.paths.getRenderProductBasePaths() for loc in context["locations"]: + if loc not in locations: + continue + ctx = context.copy() ctx["project_path"] = locations[loc] template = self.core.projects.getResolvedProjectStructurePath( @@ -388,16 +406,45 @@ def getFilesFromContext(self, context): logger.warning("folder doesn't exist: %s" % folder) continue - if context.get("source"): + if context.get("redirect"): + base, ext = os.path.splitext(context["redirect"]) + if ext: + globPath = context["redirect"].replace("#", "?") + files = glob.glob(globPath) + else: + if context.get("source"): + globPath = os.path.join(context["redirect"], context["source"].replace("#", "?")) + files = glob.glob(globPath) + else: + for rdroot, rdfolders, rdfiles in os.walk(context["redirect"]): + break + + files = [os.path.join(rdroot, rdf) for rdf in rdfiles] + + elif context.get("source"): globPath = os.path.join(folder, context["source"].replace("#", "?")) files = glob.glob(globPath) - else: for root, folders, files in os.walk(folder): break for file in files: - filepaths.append(os.path.join(folder, file)) + filepath = os.path.join(folder, file) + if file == "REDIRECT.txt": + with open(filepath, "r") as rfile: + rpath = rfile.read() + base, ext = os.path.splitext(rpath) + if ext: + filepaths.append(rpath) + else: + for rdroot, rdfolders, rdfiles in os.walk(rpath): + break + + filepaths += [os.path.join(rdroot, rdf) for rdf in rdfiles] + + context["redirect"] = rpath + else: + filepaths.append(filepath) return filepaths @@ -530,7 +577,7 @@ def getLatestVersionFromIdentifier(self, identifier, includeMaster=True): @err_catcher(name=__name__) def getLatestVersionFromFilepath(self, filepath, includeMaster=True): data = self.getDataFromFilepath(filepath) - if not data: + if not data or len(data.keys()) <= 1: return versions = self.getVersionsFromIdentifier(data) @@ -559,7 +606,9 @@ def generateMediaProductPath( ignoreEmpty=False, ignoreFolder=False, user=None, - additionalContext=None + additionalContext=None, + state=None, + filenameTemplate=None, ): framePadding = framePadding or "" comment = comment or "" @@ -602,8 +651,8 @@ def generateMediaProductPath( key, context=context ) outputPath = getattr( - self.core.appPlugin, "sm_render_fixOutputPath", lambda x, y, singleFrame: y - )(self, outputPath, singleFrame=singleFrame) + self.core.appPlugin, "sm_render_fixOutputPath", lambda x, y, singleFrame, state: y + )(self, outputPath, singleFrame=singleFrame, state=state) if returnDetails: context["path"] = outputPath return context @@ -622,6 +671,7 @@ def generatePlayblastPath( location="global", returnDetails=False, user=None, + filenameTemplate=None, ): versionUser = user or self.core.user basePath = self.core.paths.getRenderProductBasePaths()[location] @@ -674,6 +724,9 @@ def getHighestMediaVersion(self, context, getExisting=False, ignoreEmpty=False, locations = self.core.paths.getRenderProductBasePaths() validData = [] + if "version" in context: + del context["version"] + for loc in locations: ctx = context.copy() ctx["project_path"] = locations[loc] @@ -758,6 +811,17 @@ def getDataFromFilepath(self, path): template = self.core.projects.getResolvedProjectStructurePath(key) data = self.core.projects.extractKeysFromPath(path, template, context={"entityType": entityType}) + if not data: + if entityType == "asset": + key = "playblastFilesAssets" + elif entityType == "shot": + key = "playblastFilesShots" + + template = self.core.projects.getResolvedProjectStructurePath(key) + data = self.core.projects.extractKeysFromPath(path, template, context={"entityType": entityType}) + if data: + data["mediaType"] = "playblasts" + data["type"] = entityType if "asset_path" in data: data["asset"] = os.path.basename(data["asset_path"]) @@ -775,7 +839,6 @@ def getVersionFromPlayblastFilepath(self, path): template = self.core.projects.getResolvedProjectStructurePath(key) data = self.core.projects.extractKeysFromPath(path, template, context={"entityType": entityType}) - if "version" not in data: return @@ -1013,8 +1076,8 @@ def updateMasterVersion(self, path=None, context=None, isFilepath=True, add=Fals return masterPath @err_catcher(name=__name__) - def getMasterVersionNumber(self, masterPath): - versionData = self.core.paths.getRenderProductData(masterPath, validateModTime=True) + def getMasterVersionNumber(self, masterPath, allowCache=True): + versionData = self.core.paths.getRenderProductData(masterPath, validateModTime=True, allowCache=allowCache) if "versionpaths" in versionData: context = versionData.copy() for path in versionData["versionpaths"]: @@ -1023,6 +1086,12 @@ def getMasterVersionNumber(self, masterPath): ) if vName: return vName + else: + if "sourceVersion" in versionData: + return versionData["sourceVersion"] + + if "version" in versionData: + return versionData["version"] @err_catcher(name=__name__) def getMasterVersionLabel(self, path): @@ -1174,7 +1243,11 @@ def getUseMaster(self): ) @err_catcher(name=__name__) - def createIdentifier(self, entity, identifier, identifierType="3drenders"): + def getLinkedToTasks(self): + return self.core.getConfig("globals", "productTasks", config="project") + + @err_catcher(name=__name__) + def createIdentifier(self, entity, identifier, identifierType="3drenders", location="global"): context = entity.copy() context["identifier"] = identifier if "task" not in context: @@ -1183,6 +1256,8 @@ def createIdentifier(self, entity, identifier, identifierType="3drenders"): if "user" not in context: context["user"] = self.core.user + basePath = self.core.paths.getRenderProductBasePaths()[location] + context["project_path"] = basePath path = self.core.projects.getResolvedProjectStructurePath(identifierType, context) if not os.path.exists(path): @@ -1204,7 +1279,7 @@ def createIdentifier(self, entity, identifier, identifierType="3drenders"): return path @err_catcher(name=__name__) - def createVersion(self, entity, identifier, version, identifierType="3drenders"): + def createVersion(self, entity, identifier, version, identifierType="3drenders", location="global"): context = entity.copy() context["identifier"] = identifier context["mediaType"] = identifierType @@ -1215,6 +1290,8 @@ def createVersion(self, entity, identifier, version, identifierType="3drenders") if "user" not in context: context["user"] = self.core.user + basePath = self.core.paths.getRenderProductBasePaths()[location] + context["project_path"] = basePath if context.get("mediaType") == "playblasts": key = "playblastVersions" else: @@ -1273,7 +1350,7 @@ def createAov(self, entity, identifier, version, aov, identifierType="3drenders" return path @err_catcher(name=__name__) - def ingestMedia(self, files, entity, identifier, version, aov, mediaType="3drenders"): + def ingestMedia(self, files, entity, identifier, version, aov, mediaType="3drenders", filenameTemplate=None, location="global"): if not files: return @@ -1283,7 +1360,9 @@ def ingestMedia(self, files, entity, identifier, version, aov, mediaType="3drend "version": version, "aov": aov, "user": self.core.user, - "mediaType": mediaType + "mediaType": mediaType, + "filenameTemplate": filenameTemplate, + "location": location, } baseTxt = "Copying file - please wait..\n\n" @@ -1362,6 +1441,7 @@ def ingestMedia(self, files, entity, identifier, version, aov, mediaType="3drend details["version"] = kwargs["version"] details["comment"] = kwargs.get("comment", "") details["extension"] = kwargs["extension"] + details["mediaType"] = kwargs["mediaType"] infoPath = self.getMediaVersionInfoPathFromFilepath(targetPath, mediaType=mediaType) self.core.saveVersionInfo(filepath=os.path.dirname(infoPath), details=details) diff --git a/Prism/Scripts/PrismUtils/PathManager.py b/Prism/Scripts/PrismUtils/PathManager.py index 7934e7c1..49a89f14 100644 --- a/Prism/Scripts/PrismUtils/PathManager.py +++ b/Prism/Scripts/PrismUtils/PathManager.py @@ -58,7 +58,7 @@ def getCompositingOut( render, location="global", comment="", - ignoreEmpty=True, + ignoreEmpty=False, node=None, ): fileName = self.core.getCurrentFileName() @@ -95,15 +95,16 @@ def getCompositingOut( outputPath = "FileNotInPipeline" if render and outputPath != "FileNotInPipeline": - if not os.path.exists(os.path.dirname(outputPath)): + expandedOutputpath = os.path.expandvars(outputPath) + if not os.path.exists(os.path.dirname(expandedOutputpath)): try: - os.makedirs(os.path.dirname(outputPath)) + os.makedirs(os.path.dirname(expandedOutputpath)) except: self.core.popup("Could not create output folder") details = outputData.copy() details["sourceScene"] = self.core.getCurrentFileName() - filepath = os.path.dirname(outputPath) + filepath = os.path.dirname(expandedOutputpath) self.core.saveVersionInfo( filepath=filepath, details=details, @@ -242,6 +243,12 @@ def generateScenePath( context["asset"] = os.path.basename(entity["asset_path"]) context["asset_path"] = entity["asset_path"] + if "sequence" in context: + del context["sequence"] + + if "shot" in context: + del context["shot"] + scenePath = self.core.projects.getResolvedProjectStructurePath( "assetScenefiles", context=context ) @@ -249,6 +256,12 @@ def generateScenePath( elif entity["type"] == "shot": context["sequence"] = entity["sequence"] context["shot"] = entity["shot"] + if "asset" in context: + del context["asset"] + + if "asset_path" in context: + del context["asset_path"] + scenePath = self.core.projects.getResolvedProjectStructurePath( "shotScenefiles", context=context ) @@ -260,7 +273,7 @@ def generateScenePath( return scenePath @err_catcher(name=__name__) - def getCachePathData(self, cachePath, addPathData=True, validateModTime=False): + def getCachePathData(self, cachePath, addPathData=True, validateModTime=False, allowCache=True): if not cachePath: return {} @@ -277,7 +290,7 @@ def getCachePathData(self, cachePath, addPathData=True, validateModTime=False): if cacheDate and cacheDate != mdate: self.core.configs.clearCache(path=cacheConfig) - cacheData = self.core.getConfig(configPath=cacheConfig) or {} + cacheData = self.core.getConfig(configPath=cacheConfig, allowCache=allowCache) or {} cacheData = cacheData.copy() if addPathData: if os.path.splitext(cachePath)[1]: @@ -315,7 +328,7 @@ def getMediaProductData(self, productPath, isFilepath=True, addPathData=True, me return self.getRenderProductData(productPath, isFilepath=isFilepath, addPathData=addPathData, mediaType=mediaType, validateModTime=validateModTime) @err_catcher(name=__name__) - def getRenderProductData(self, productPath, isFilepath=True, addPathData=True, mediaType="3drenders", validateModTime=False, isVersionFolder=False): + def getRenderProductData(self, productPath, isFilepath=True, addPathData=True, mediaType="3drenders", validateModTime=False, isVersionFolder=False, allowCache=True): productPath = os.path.normpath(productPath) if os.path.splitext(productPath)[1]: productConfig = self.core.mediaProducts.getMediaVersionInfoPathFromFilepath(productPath, mediaType=mediaType) @@ -330,7 +343,7 @@ def getRenderProductData(self, productPath, isFilepath=True, addPathData=True, m if cacheDate and cacheDate != mdate: self.core.configs.clearCache(path=productConfig) - productData = self.core.getConfig(configPath=productConfig) or {} + productData = self.core.getConfig(configPath=productConfig, allowCache=allowCache) or {} if addPathData: if isVersionFolder: pathData = self.core.mediaProducts.getMediaDataFromVersionFolder(productPath, mediaType=mediaType) @@ -620,12 +633,21 @@ def splitext(self, path): return os.path.splitext(path) @err_catcher(name=__name__) - def getEntityTypeFromPath(self, path): + def getEntityTypeFromPath(self, path, projectPath=None): globalPath = self.core.convertPath(path, "global") globalPath = os.path.normpath(globalPath) globalPath = os.path.splitdrive(globalPath)[1] - assetPath = os.path.splitdrive(self.core.assetPath)[1] - sequencePath = os.path.splitdrive(self.core.sequencePath)[1] + assetPath = self.core.assetPath + if projectPath: + assetPath = assetPath.replace(os.path.normpath(self.core.projectPath), projectPath) + + assetPath = os.path.splitdrive(assetPath)[1] + + sequencePath = self.core.sequencePath + if projectPath: + sequencePath = sequencePath.replace(os.path.normpath(self.core.projectPath), projectPath) + + sequencePath = os.path.splitdrive(sequencePath)[1] if globalPath.startswith(assetPath): return "asset" elif globalPath.startswith(sequencePath): diff --git a/Prism/Scripts/PrismUtils/PluginManager.py b/Prism/Scripts/PrismUtils/PluginManager.py index fee7e816..76b4720d 100644 --- a/Prism/Scripts/PrismUtils/PluginManager.py +++ b/Prism/Scripts/PrismUtils/PluginManager.py @@ -186,11 +186,16 @@ def getPluginPath(self, location="root", pluginType="", path="", pluginName=""): dirName = "Apps" elif pluginType == "Custom": dirName = "Custom" + else: + dirName = "" - pluginPath = os.path.join(pluginPath, dirName) + if dirName: + pluginPath = os.path.join(pluginPath, dirName) if pluginName: pluginPath = os.path.join(pluginPath, pluginName) + if pluginType == "Single File": + pluginPath += ".py" return pluginPath.replace("\\", "/") @@ -267,7 +272,7 @@ def loadAppPlugin(self, pluginName, pluginPath=None, startup=False): if ( not getattr(self.core, "messageParent", None) - and QApplication.instance() is not None + and ((self.core.appPlugin.pluginName != "Houdini" or sys.version_info.minor != 11) and QApplication.instance() is not None) ): for arg in self.core.prismArgs: if isinstance(arg, dict) and "messageParent" in arg: @@ -288,6 +293,7 @@ def loadAppPlugin(self, pluginName, pluginPath=None, startup=False): if not startup: self.core.appPlugin.startup(self.core) + self.core.callback("pluginLoaded", args=[self.core.appPlugin]) logger.debug("loaded app plugin %s" % pluginName) return self.core.appPlugin @@ -438,16 +444,33 @@ def activatePlugin(self, path): return self.loadPlugin(path) @err_catcher(name=__name__) - def loadPlugin(self, path=None, name=None, force=True, activate=None): + def loadPlugin(self, path=None, name=None, force=True, activate=None, showWarnings=False): if not path: if name: - path = self.searchPluginPath(name) - if not path: - logger.debug("couldn't find plugin: %s" % name) + pluginPaths = self.searchPluginPaths(name) + if not pluginPaths: + msg = "couldn't find plugin: %s" % name + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return + for pluginPath in pluginPaths: + result = self.loadPlugin(path=pluginPath, name=name, force=force, activate=activate) + if result: + return result + + return + if not path: - logger.debug('invalid pluginpath: "%s"' % path) + msg = "invalid pluginpath: \"%s\"" % path + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if os.path.normpath(path).startswith(os.path.normpath(self.core.prismRoot)): @@ -498,7 +521,12 @@ def loadPlugin(self, path=None, name=None, force=True, activate=None): if force: self.unloadPlugin(pluginName) else: - logger.warning('plugin is already loaded: "%s"' % pluginName) + msg = "plugin is already loaded: \"%s\"" % pluginName + if showWarnings: + self.core.popup(msg) + else: + logger.warning(msg) + return # logger.debug(pluginName) @@ -508,17 +536,24 @@ def loadPlugin(self, path=None, name=None, force=True, activate=None): if pluginName in notAutoLoadedPlugins and not force: if not os.path.exists(path): - logger.debug("pluginpath doesn't exist: %s" % path) + msg = "pluginpath doesn't exist: %s" % path + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if activate: return self.activatePlugin(path) self.core.unloadedPlugins[pluginName] = UnloadedPlugin(self.core, pluginName, path=pluginPath, location=location) - logger.debug( - "skipped loading plugin %s - autoload of this plugin is disabled in the preferences" - % pluginName - ) + msg = "skipped loading plugin %s - autoload of this plugin is disabled in the preferences" % pluginName + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if self.core.appPlugin and (pluginName == self.core.appPlugin.pluginName): @@ -529,9 +564,12 @@ def loadPlugin(self, path=None, name=None, force=True, activate=None): or os.path.exists(initPath.replace("_init", "_init_unloaded")) ): # self.core.unloadedPlugins[pluginName] = UnloadedPlugin(self.core, pluginName, path=pluginPath, location=location) - logger.warning( - "skipped loading plugin %s - folder doesn't contain a valid plugin (no init script) - check your plugin configuration. %s " % (pluginName, path) - ) + msg = "skipped loading plugin %s - folder doesn't contain a valid plugin (no init script) - check your plugin configuration. %s " % (pluginName, path) + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if os.path.dirname(initPath) not in sys.path: @@ -539,23 +577,30 @@ def loadPlugin(self, path=None, name=None, force=True, activate=None): try: if path.endswith(".py"): + if not os.path.exists(path): + msg = "pluginpath doesn't exist: %s" % path + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + + return + plugModule = __import__(pluginName) if hasattr(plugModule, "name"): pluginName = plugModule.name if pluginName in notAutoLoadedPlugins and not force: - if not os.path.exists(path): - logger.debug("pluginpath doesn't exist: %s" % path) - return - if activate: return self.activatePlugin(path) self.core.unloadedPlugins[pluginName] = UnloadedPlugin(self.core, pluginName, path=pluginPath, location=location) - logger.debug( - "skipped loading plugin %s - autoload of this plugin is disabled in the preferences" - % pluginName - ) + msg = "skipped loading plugin %s - autoload of this plugin is disabled in the preferences" % pluginName + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if hasattr(plugModule, "classname"): @@ -597,10 +642,12 @@ def loadPlugin(self, path=None, name=None, force=True, activate=None): return if hasattr(pPlug, "platforms") and platform.system() not in pPlug.platforms: - logger.debug( - "skipped loading plugin %s - plugin doesn't support this OS" - % pPlug.pluginName - ) + msg = "skipped loading plugin %s - plugin doesn't support this OS" % pPlug.pluginName + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if pluginName in self.core.unloadedPlugins: @@ -614,7 +661,12 @@ def loadPlugin(self, path=None, name=None, force=True, activate=None): else: if not getattr(pPlug, "isActive", lambda: True)(): self.core.unloadedPlugins[pPlug.pluginName] = pPlug - logger.debug('plugin "%s" is inactive' % pPlug.pluginName) + msg = "plugin \"%s\" is inactive" % pPlug.pluginName + if showWarnings: + self.core.popup(msg) + else: + logger.debug(msg) + return if not hasattr(pPlug, "pluginType") or pPlug.pluginType in ["Custom"]: @@ -1001,38 +1053,65 @@ def getRenderfarmPlugin(self, name): @err_catcher(name=__name__) def createPlugin(self, pluginName, pluginType, location="root", path=""): - presetPath = self.getPluginPath("root", pluginType) - presetPath = os.path.join(presetPath, "PluginEmpty") - - if not os.path.exists(presetPath): - msg = ( - "Canceled plugin creation: Empty preset doesn't exist:\n\n%s" - % self.core.fixPath(presetPath) - ) - self.core.popup(msg) - return - targetPath = self.getPluginPath(location, pluginType, path, pluginName) - if os.path.exists(targetPath): msg = "Canceled plugin creation: Plugin already exists:\n\n%s" % targetPath self.core.popup(msg) return - try: - shutil.copytree(presetPath, targetPath) - except PermissionError: - msg = "Failed to copy files to: \"%s\"\n\nMake sure you have the required permissions and try again." % targetPath - self.core.popup(msg) - return + if pluginType == "Single File": + script = """name = "PLUGINNAME" +classname = "PLUGINNAME" + + +import os +from qtpy.QtWidgets import * - self.core.replaceFolderContent(targetPath, "PluginEmpty", pluginName) - scriptPath = os.path.join(targetPath, "Scripts") - if not os.path.exists(scriptPath): - scriptPath = targetPath +class PLUGINNAME: + def __init__(self, core): + self.core = core + self.version = "v1.0.0" + + self.core.registerCallback("postInitialize", self.postInitialize, plugin=self) + + def postInitialize(self): + # do stuff after Prism launched + pass +""" + script = script.replace("PLUGINNAME", pluginName) + if not os.path.exists(os.path.dirname(targetPath)): + os.makedirs(os.path.dirname(targetPath)) + + with open(targetPath, "w") as f: + f.write(script) + + self.core.openFolder(targetPath) + else: + presetPath = self.getPluginPath("root", pluginType) + presetPath = os.path.join(presetPath, "PluginEmpty") + if not os.path.exists(presetPath): + msg = ( + "Canceled plugin creation: Empty preset doesn't exist:\n\n%s" + % self.core.fixPath(presetPath) + ) + self.core.popup(msg) + return + + try: + shutil.copytree(presetPath, targetPath) + except PermissionError: + msg = "Failed to copy files to: \"%s\"\n\nMake sure you have the required permissions and try again." % targetPath + self.core.popup(msg) + return + + self.core.replaceFolderContent(targetPath, "PluginEmpty", pluginName) + scriptPath = os.path.join(targetPath, "Scripts") + if not os.path.exists(scriptPath): + scriptPath = targetPath + + self.core.openFolder(scriptPath) - self.core.openFolder(scriptPath) return targetPath @err_catcher(name=__name__) @@ -1127,6 +1206,15 @@ def canPluginBeFound(self, pluginPath): @err_catcher(name=__name__) def searchPluginPath(self, pluginName): + paths = self.searchPluginPaths(pluginName) + if paths: + return paths[0] + else: + return False + + @err_catcher(name=__name__) + def searchPluginPaths(self, pluginName): + paths = [] userPluginConfig = self.core.getConfig(config="PluginPaths") or {} if "plugins" in userPluginConfig: for path in userPluginConfig["plugins"]: @@ -1134,7 +1222,7 @@ def searchPluginPath(self, pluginName): continue if pluginName == os.path.basename(path["path"]): - return path["path"] + paths.append(path["path"]) if "searchPaths" in userPluginConfig: for path in userPluginConfig["searchPaths"]: @@ -1144,7 +1232,7 @@ def searchPluginPath(self, pluginName): pluginNames = os.listdir(path["path"]) if pluginName in pluginNames: path = os.path.join(path["path"], pluginName) - return path + paths.append(path) pluginDirs = self.getPluginDirs() dirs = [folder for folder in pluginDirs["searchPaths"] if folder not in userPluginConfig.get("searchPaths", [])] @@ -1154,7 +1242,11 @@ def searchPluginPath(self, pluginName): ) if plugins: - return plugins[0]["path"] + for plugin in plugins: + paths.append(plugin["path"]) + + if paths: + return list(set(paths)) return False @@ -1319,7 +1411,8 @@ def downloadPlugin(self, plugin): data = { "key": plugin, "origin": "prismOss", - "prism_version": self.core.version + "prism_version": self.core.version, + "opsystem": platform.system(), } serverUrl = "https://service.prism-pipeline.com" if plugin == "Hub": @@ -1330,42 +1423,45 @@ def downloadPlugin(self, plugin): import requests response = requests.get(url, data) if not isinstance(response, requests.Response): - raise Exception("Failed to connect to server.") + self.core.popup("Failed to connect to server.") if response.status_code != 200: - raise Exception("Failed to connect to server. Code %s" % response.status_code) + self.core.popup("Failed to connect to server. Code %s" % response.status_code) try: result = response.json() except: - raise Exception(str(response.content)) + self.core.popup(str(response.content)) if result.get("error"): - raise Exception("Error in response: %s" % result.get("error")) + self.core.popup("Error in response: %s" % result.get("error")) file = result["files"][0] cachePath = os.path.join(path, ".cache") zippath = os.path.join(cachePath, os.path.basename(file["url"])) try: response = requests.get(file["url"], headers=file["headers"]) - except Exception: + except Exception as e: + self.core.popup("Error in request: %s" % str(e)) return data = response.content if not data: + self.core.popup("Empty response.") return if not os.path.exists(os.path.dirname(zippath)): try: os.makedirs(os.path.dirname(zippath)) except Exception: + self.core.popup("Failed to create folder: %s\n\n%s" % (os.path.dirname(zippath), str(e))) return try: with open(zippath, "wb") as f: f.write(data) - except Exception: - pass + except Exception as e: + self.core.popup("Failed to write to file:\n\n%s" % str(e)) else: return zippath @@ -1374,13 +1470,20 @@ def updatePlugins(self, pluginUpdates): pluginNames = [] basePath = "" zipfile = importlib.import_module("zipfile") + tarfile = importlib.import_module("tarfile") for pluginUpdate in pluginUpdates: if os.path.exists(pluginUpdate.get("target")): self.removePlugin(pluginUpdate.get("target")) try: - with zipfile.ZipFile(pluginUpdate.get("zip"), "r") as zip_ref: - zip_ref.extractall(os.path.dirname(pluginUpdate.get("target"))) + target = os.path.dirname(pluginUpdate.get("target")) + zippath = pluginUpdate.get("zip") + if zippath.lower().endswith(".zip"): + with zipfile.ZipFile(zippath, "r") as zip_ref: + zip_ref.extractall(target) + elif zippath.lower().endswith(".tar.gz"): + with tarfile.open(zippath, 'r') as tar: + tar.extractall(target) except: pass else: @@ -1527,6 +1630,9 @@ def postInstallPlugins(self, plugins, basepath, load=True): if result == "Yes": self.setupIntegrations(pluginName) + if hasattr(plug, "postInstall"): + plug.postInstall() + logger.debug("installed plugin %s to %s" % (pluginName, basepath)) if load and plugins and getattr(self.core, "ps", None) and self.core.ps.isVisible(): diff --git a/Prism/Scripts/PrismUtils/PrismWidgets.py b/Prism/Scripts/PrismUtils/PrismWidgets.py index 6cfc6db2..877c3338 100644 --- a/Prism/Scripts/PrismUtils/PrismWidgets.py +++ b/Prism/Scripts/PrismUtils/PrismWidgets.py @@ -607,7 +607,12 @@ def enterEvent(self, event): def setEmptyPreview(self): imgFile = os.path.join(self.core.projects.getFallbackFolder(), "noFileBig.jpg") pmap = self.core.media.getPixmapFromPath(imgFile) - pmap = pmap.scaled(QSize(self.core.scenePreviewWidth, self.core.scenePreviewHeight)) + if pmap: + pmap = pmap.scaled(QSize(self.core.scenePreviewWidth, self.core.scenePreviewHeight)) + else: + pmap = QPixmap(self.core.scenePreviewWidth, self.core.scenePreviewHeight) + pmap.fill(Qt.black) + self.l_preview.setPixmap(pmap) @err_catcher(name=__name__) diff --git a/Prism/Scripts/PrismUtils/Products.py b/Prism/Scripts/PrismUtils/Products.py index 885b85f8..4454f74c 100644 --- a/Prism/Scripts/PrismUtils/Products.py +++ b/Prism/Scripts/PrismUtils/Products.py @@ -57,7 +57,7 @@ def __init__(self, core): def getProductNamesFromEntity(self, entity, locations=None): data = self.getProductsFromEntity(entity, locations=locations) names = {} - useTasks = self.core.getConfig("globals", "productTasks", config="project") + useTasks = self.core.products.getLinkedToTasks() for product in data: if useTasks: idf = "%s/%s/%s" % (product.get("department", "unknown"), product.get("task", "unknown"), product["product"]) @@ -86,17 +86,22 @@ def getProductPathFromEntity(self, entity, includeProduct=False): @err_catcher(name=__name__) def getProductsFromEntity(self, entity, locations=None): - locationData = self.core.paths.getExportProductBasePaths() - searchLocations = [] - for locData in locationData: - if not locations or locData in locations or "all" in locations: - searchLocations.append(locData) + if locations == "project_path": + searchLocations = ["other"] + else: + locationData = self.core.paths.getExportProductBasePaths() + searchLocations = [] + for locData in locationData: + if not locations or locData in locations or "all" in locations: + searchLocations.append(locData) key = "products" products = [] for loc in searchLocations: context = entity.copy() - context["project_path"] = locationData[loc] + if locations != "project_path": + context["project_path"] = locationData[loc] + template = self.core.projects.getResolvedProjectStructurePath( key, context=context ) @@ -159,11 +164,14 @@ def getVersionsFromSameVersionStack(self, path): @err_catcher(name=__name__) def getVersionsFromProduct(self, entity, product, locations="all"): + locations = locations or "all" if locations == "all": - locations = self.core.paths.getExportProductBasePaths() + locPaths = self.core.paths.getExportProductBasePaths() + elif locations == "project_path": + locPaths = {"_other": entity["project_path"]} versions = [] - for loc in locations: + for loc in locPaths: context = entity.copy() if "version" in context: del context["version"] @@ -175,12 +183,12 @@ def getVersionsFromProduct(self, entity, product, locations="all"): del context["paths"] context["product"] = product - context["project_path"] = locations[loc] - locVersions = self.getVersionsFromContext(context, locations={loc: locations[loc]}) + context["project_path"] = locPaths[loc] + locVersions = self.getVersionsFromContext(context, locations={loc: locPaths[loc]}) for locVersion in locVersions: locVersion["paths"] = [locVersion.get("path")] for version in versions: - if version.get("version") == locVersion.get("version"): + if version.get("version") == locVersion.get("version") and version.get("wedge") == locVersion.get("wedge"): version["paths"].append(locVersion.get("path")) break else: @@ -214,15 +222,20 @@ def getVersionsFromPath(self, path): def getVersionsFromContext(self, context, locations=None): locationData = self.core.paths.getExportProductBasePaths() searchLocations = [] - for locData in locationData: - if not locations or locData in locations or "all" in locations: - searchLocations.append(locData) + if locations and "_other" in locations: + searchLocations = locations + else: + for locData in locationData: + if not locations or locData in locations or "all" in locations: + searchLocations.append(locData) key = "productVersions" versions = [] for loc in searchLocations: ctx = context.copy() - ctx["project_path"] = locationData[loc] + if loc != "_other": + ctx["project_path"] = locationData[loc] + templates = self.core.projects.getResolvedProjectStructurePaths( key, context=ctx ) @@ -243,7 +256,7 @@ def getVersionsFromContext(self, context, locations=None): c["version"], c["wedge"] = c["version"].split("_") for version in versions: - if version.get("version") == c.get("version"): + if version.get("version") == c.get("version") and version.get("wedge") == c.get("wedge"): version["paths"].append(c.get("path")) version["locations"].update(c.get("locations")) break @@ -413,14 +426,14 @@ def getLatestVersionFromPath(self, path, includeMaster=True): return latestVersion @err_catcher(name=__name__) - def getLatestVersionpathFromProduct(self, product, entity=None, includeMaster=True, wedge=None): + def getLatestVersionpathFromProduct(self, product, entity=None, includeMaster=True, wedge=None, locations=None): if not entity: fname = self.core.getCurrentFileName() entity = self.core.getScenefileData(fname) if entity.get("type") not in ["asset", "shot"]: return - versions = self.getVersionsFromProduct(entity, product) + versions = self.getVersionsFromProduct(entity, product, locations=locations) version = self.getLatestVersionFromVersions( versions, includeMaster=includeMaster, wedge=wedge ) @@ -540,14 +553,22 @@ def setPreferredFileForVersion(self, version, preferredFile, callback=None): callback() @err_catcher(name=__name__) - def getVersionpathFromProductVersion(self, product, version, entity=None, wedge=None): + def getLocationFromPath(self, path): + locDict = self.core.paths.getExportProductBasePaths() + nPath = os.path.normpath(path) + for location in locDict: + if nPath.startswith(locDict[location]): + return location + + @err_catcher(name=__name__) + def getVersionpathFromProductVersion(self, product, version, entity=None, wedge=None, locations=None): if not entity: fname = self.core.getCurrentFileName() entity = self.core.getScenefileData(fname) if entity.get("type") not in ["asset", "shot"]: return - versions = self.getVersionsFromProduct(entity, product) + versions = self.getVersionsFromProduct(entity, product, locations=locations) filepath = None for v in versions: if v["version"] == version: @@ -867,8 +888,8 @@ def deleteMasterVersion(self, path, errorMsg=None, allowClear=True, allowRename= return True @err_catcher(name=__name__) - def getMasterVersionNumber(self, masterPath): - versionData = self.core.paths.getCachePathData(masterPath, addPathData=False, validateModTime=True) + def getMasterVersionNumber(self, masterPath, allowCache=True): + versionData = self.core.paths.getCachePathData(masterPath, addPathData=False, validateModTime=True, allowCache=allowCache) if "sourceVersion" in versionData: return versionData["sourceVersion"] @@ -887,9 +908,12 @@ def getMasterVersionLabel(self, path): return versionName @err_catcher(name=__name__) - def createProduct(self, entity, product): + def createProduct(self, entity, product, location="global"): context = entity.copy() context["product"] = product + basePath = self.core.paths.getExportProductBasePaths()[location] + context["project_path"] = basePath + path = self.core.projects.getResolvedProjectStructurePath("products", context) if not os.path.exists(path): @@ -933,12 +957,14 @@ def getPreferredFileFromFiles(self, files, relative=False): return filepath @err_catcher(name=__name__) - def ingestProductVersion(self, files, entity, product, comment=None): + def ingestProductVersion(self, files, entity, product, comment=None, version=None, location="global"): if comment is None: if len(files) > 1: comment = "ingested files" - else: + elif files: comment = "ingested file: %s" % os.path.basename(files[0]) + else: + comment = "" kwargs = { "entity": entity, @@ -946,22 +972,21 @@ def ingestProductVersion(self, files, entity, product, comment=None): "comment": comment, "user": self.core.user, } + basePath = self.core.paths.getExportProductBasePaths()[location] + kwargs["entity"]["project_path"] = basePath + if version is None: + version = self.getNextAvailableVersion(entity=entity, product=product) - version = self.getNextAvailableVersion(entity=entity, product=product) kwargs["version"] = version prefFile = self.getPreferredFileFromFiles(files, relative=True) - if not prefFile: - msg = "No file to ingest." - self.core.popup(msg) - return - createdFiles = [] targetPath = self.generateProductPath(**kwargs) + versionPath = os.path.dirname(targetPath) for file in files: - fileTargetPath = os.path.join(os.path.dirname(targetPath), os.path.basename(file)) - if not os.path.exists(os.path.dirname(fileTargetPath)): + fileTargetPath = os.path.join(versionPath, os.path.basename(file)) + if not os.path.exists(versionPath): try: - os.makedirs(os.path.dirname(fileTargetPath)) + os.makedirs(versionPath) except: self.core.popup("The directory could not be created") return @@ -995,13 +1020,14 @@ def ingestProductVersion(self, files, entity, product, comment=None): details["user"] = kwargs["user"] details["version"] = version details["comment"] = kwargs["comment"] - details["extension"] = os.path.splitext(prefFile)[1] - details["preferredFile"] = prefFile + if prefFile: + details["extension"] = os.path.splitext(prefFile)[1] + details["preferredFile"] = prefFile infoPath = self.getVersionInfoPathFromProductFilepath(targetPath) self.core.saveVersionInfo(filepath=infoPath, details=details) - return createdFiles + return {"createdFiles": createdFiles, "versionPath": versionPath} @err_catcher(name=__name__) def getUseMaster(self): @@ -1009,6 +1035,10 @@ def getUseMaster(self): "globals", "useMasterVersion", dft=True, config="project" ) + @err_catcher(name=__name__) + def getLinkedToTasks(self): + return self.core.getConfig("globals", "productTasks", config="project") + @err_catcher(name=__name__) def checkMasterVersions(self, entities, parent=None): self.dlg_masterManager = self.core.paths.masterManager(self.core, entities, "products", parent=parent) diff --git a/Prism/Scripts/PrismUtils/ProjectEntities.py b/Prism/Scripts/PrismUtils/ProjectEntities.py index 8a5f4ca3..5c93d90e 100644 --- a/Prism/Scripts/PrismUtils/ProjectEntities.py +++ b/Prism/Scripts/PrismUtils/ProjectEntities.py @@ -116,11 +116,38 @@ def setShotRange(self, entity, start, end): ) @err_catcher(name=__name__) - def getShotRange(self, entity): + def getShotRange(self, entity, handles=False): ranges = self.core.getConfig("shotRanges", config="shotinfo") or {} if entity.get("sequence") in ranges: if entity.get("shot") in ranges[entity["sequence"]]: - return ranges[entity["sequence"]][entity["shot"]] + shotRange = ranges[entity["sequence"]][entity["shot"]].copy() + if handles: + metaData = self.getMetaData(entity) + if "handles_in" in metaData: + try: + shotRange[0] = shotRange[0] - int(metaData["handles_in"]["value"]) + except: + pass + + if "handles_out" in metaData: + try: + shotRange[1] = shotRange[1] + int(metaData["handles_out"]["value"]) + except: + pass + + if "handles" in metaData: + try: + handleNum = int(metaData["handles"]["value"]) + except: + pass + else: + if "handles_in" not in metaData: + shotRange[0] = shotRange[0] - handleNum + + if "handles_out" not in metaData: + shotRange[1] = shotRange[1] + handleNum + + return shotRange @err_catcher(name=__name__) def getSequences(self, searchFilter="", locations=None): @@ -162,7 +189,9 @@ def getShots(self, searchFilter="", locations=None, getSequences=True): searchFilter.lower() not in data["sequence"].lower() and searchFilter.lower() not in data["shot"].lower() ): - continue + metaData = self.getMetaData(data) + if not metaData or searchFilter.lower() not in metaData.get("Description", {}).get("value", "").lower(): + continue data["location"] = seqDir["location"] data["type"] = "shot" @@ -316,7 +345,8 @@ def getScenefiles(self, entity=None, step=None, category=None, extensions=None, @err_catcher(name=__name__) def isValidScenefilename(self, filename, extensions=None): - if os.path.splitext(filename)[1] in [ + ext = os.path.splitext(filename)[1] + if ext in [ ".jpg", ".json", ".yml", @@ -327,6 +357,9 @@ def isValidScenefilename(self, filename, extensions=None): ]: return False + if ext in self.getBlacklistedExtensions(): + return False + sData = self.core.getScenefileData(filename) try: @@ -364,11 +397,11 @@ def isValidScenefilename(self, filename, extensions=None): unknownScene = sData["extension"] not in self.core.getPluginSceneFormats() if unknownScene: if "*" not in extensions: - logger.debug("invalid extension") + logger.debug("invalid extension: %s" % sData["extension"]) return False else: if sData["extension"] not in extensions: - logger.debug("invalid extension") + logger.debug("invalid extension: %s" % sData["extension"]) return False return True @@ -768,6 +801,9 @@ def createTasksFromPreset(self, entity, preset=None, presetName=None): paths = [] for dep in preset.get("departments", []): abbrv = self.getDepartmentAbbreviation(entity.get("type"), dep["name"]) + if not abbrv: + continue + self.createDepartment(abbrv, entity, createCat=False) for task in dep.get("tasks", []): paths.append(self.createCategory(entity, abbrv, task)) @@ -921,9 +957,13 @@ def setDescription(self, filepath, description): self.setScenefileInfo(filepath, "description", description) @err_catcher(name=__name__) - def getAssetDescription(self, assetName): + def getAssetDescription(self, assetName, projectPath=None): + pipeFolder = self.core.projects.getPipelineFolder() + if projectPath: + pipeFolder = pipeFolder.replace(os.path.normpath(self.core.projectPath), projectPath) + assetFile = os.path.join( - self.core.projects.getPipelineFolder(), + pipeFolder, "Assetinfo", "assetInfo" + self.core.configs.getProjectExtension(), ) @@ -958,13 +998,24 @@ def setAssetDescription(self, assetName, description): self.core.setConfig(data=assetInfos, configPath=assetFile) @err_catcher(name=__name__) - def getMetaData(self, entity): + def getMetaData(self, entity, projectPath=None): metadata = {} if not entity: return metadata if entity.get("type") == "asset": - data = self.core.getConfig(config="assetinfo") or {} + if projectPath: + pipeFolder = self.core.projects.getPipelineFolder() + pipeFolder = pipeFolder.replace(os.path.normpath(self.core.projectPath), projectPath) + assetFile = os.path.join( + pipeFolder, + "Assetinfo", + "assetInfo" + self.core.configs.getProjectExtension(), + ) + data = self.core.getConfig(configPath=assetFile) or {} + else: + data = self.core.getConfig(config="assetinfo") or {} + if "assets" not in data: return metadata @@ -982,7 +1033,7 @@ def getMetaData(self, entity): if "shots" not in data: return metadata - if entity["sequence"] not in data["shots"]: + if entity.get("sequence", "") not in data["shots"]: return metadata if entity["shot"] not in data["shots"][entity["sequence"]]: @@ -1077,6 +1128,12 @@ def renameSequence(self, curSeqName, newSeqName, locations=None): if curSeqName in k: os.rename(k, k.replace(curSeqName, newSeqName)) for k in i[2]: + if os.path.splitext(k)[1] == self.core.configs.preferredExtension: + filepath = os.path.join(i[0], k) + fseqName = self.core.getConfig("sequence", configPath=filepath) + if fseqName == curSeqName: + self.core.setConfig("sequence", val=newSeqName, configPath=filepath) + if curSeqName in k: os.rename(k, k.replace(curSeqName, newSeqName)) os.chdir(cwd) @@ -1153,8 +1210,15 @@ def renameShot(self, curShotData, newShotData, locations=None): if curShotData["shot"] in k: os.rename(k, k.replace(curShotData["shot"], newShotData["shot"])) for k in i[2]: + if os.path.splitext(k)[1] == self.core.configs.preferredExtension: + filepath = os.path.join(i[0], k) + shotName = self.core.getConfig("shot", configPath=filepath) + if shotName == curShotData["shot"]: + self.core.setConfig("shot", val=newShotData["shot"], configPath=filepath) + if curShotData["shot"] in k: os.rename(k, k.replace(curShotData["shot"], newShotData["shot"])) + os.chdir(cwd) oldPrvPath = self.getEntityPreviewPath(curShotData) @@ -1255,8 +1319,12 @@ def getTypeFromPath(self, path, content=None): return "folder" @err_catcher(name=__name__) - def getAsset(self, assetName): - fullAssetPath = os.path.join(self.core.assetPath, assetName) + def getAsset(self, assetName, projectPath=None): + base = self.core.assetPath + if projectPath: + base = base.replace(os.path.normpath(self.core.projectPath), projectPath) + + fullAssetPath = os.path.join(base, assetName) existed = os.path.exists(fullAssetPath) if existed: return {"type": "asset", "asset_path": assetName} @@ -1346,10 +1414,14 @@ def getAssetFoldersFromPath(self, path, pathType="asset"): return folders @err_catcher(name=__name__) - def filterAssets(self, assets, filterStr): + def filterAssets(self, assets, filterStr, projectPath=None): filteredPaths = [] for absAssetPath in assets: - assetPath = absAssetPath.replace(self.core.assetPath, "") + base = self.core.assetPath + if projectPath: + base = base.replace(os.path.normpath(self.core.projectPath), projectPath) + + assetPath = absAssetPath.replace(base, "") if self.core.useLocalFiles: localAssetPath = self.core.getAssetPath(location="local") assetPath = assetPath.replace(localAssetPath, "") @@ -1357,6 +1429,17 @@ def filterAssets(self, assets, filterStr): if filterStr.lower() in assetPath.lower(): filteredPaths.append(absAssetPath) + else: + description = self.getAssetDescription(self.getAssetNameFromPath(assetPath), projectPath=projectPath) + if filterStr.lower() in description.lower(): + filteredPaths.append(absAssetPath) + else: + entity = self.getAsset(assetPath, projectPath=projectPath) + metaData = self.getMetaData(entity, projectPath=projectPath) + if metaData and "tags" in metaData: + tags = [x.strip() for x in metaData["tags"]["value"].split(",")] + if filterStr in tags: + filteredPaths.append(absAssetPath) return filteredPaths @@ -1387,15 +1470,19 @@ def getAssetNameFromPath(self, path): return os.path.basename(path) @err_catcher(name=__name__) - def getAssetRelPathFromPath(self, path): + def getAssetRelPathFromPath(self, path, projectPath=None): path = self.core.convertPath(path, "global") - return path.replace(self.core.assetPath, "").strip("\\").strip("/") + base = self.core.assetPath + if projectPath: + base = base.replace(os.path.normpath(self.core.projectPath), projectPath) + + return path.replace(base, "").strip("\\").strip("/") @err_catcher(name=__name__) - def getScenefileData(self, fileName, preview=False): + def getScenefileData(self, fileName, preview=False, getEntityFromPath=False): data = self.core.getConfig(configPath=self.getScenefileInfoPath(fileName)) or {} data = dict(data) - if not data and fileName: + if fileName and (not data or getEntityFromPath): entityType = self.core.paths.getEntityTypeFromPath(fileName) key = None if entityType == "asset": @@ -1405,11 +1492,28 @@ def getScenefileData(self, fileName, preview=False): if key: template = self.core.projects.getTemplatePath(key) + hasData = bool(data) data["type"] = entityType data["entityType"] = entityType - data = self.core.projects.extractKeysFromPath(fileName, template, context=data) - if data.get("asset_path"): - data["asset"] = os.path.basename(data["asset_path"]) + pathData = self.core.projects.extractKeysFromPath(fileName, template, context=data) + if pathData.get("asset_path"): + pathData["asset"] = os.path.basename(pathData["asset_path"]) + + if not hasData: + data = pathData + elif getEntityFromPath: + if entityType == "asset": + if "asset" in pathData: + data["asset"] = pathData["asset"] + + if "asset_path" in pathData: + data["asset_path"] = pathData["asset_path"] + if entityType == "shot": + if "shot" in pathData: + data["shot"] = pathData["shot"] + + if "sequence" in pathData: + data["sequence"] = pathData["sequence"] if fileName: data["filename"] = fileName @@ -1590,9 +1694,14 @@ def getEntityPreviewPath(self, entity): elif entity["type"] == "shot": entityName = self.getShotName(entity) - imgName = "%s_preview.jpg" % entityName + ext = os.getenv("PRISM_ENTITY_THUMBNAIL_EXT", ".jpg") + imgName = "%s_preview%s" % (entityName, ext) + pipeFolder = self.core.projects.getPipelineFolder() + if entity.get("project_path"): + pipeFolder = pipeFolder.replace(os.path.normpath(self.core.projectPath), entity["project_path"]) + imgPath = os.path.join( - self.core.projects.getPipelineFolder(), folderName, imgName + pipeFolder, folderName, imgName ) return imgPath @@ -1630,10 +1739,17 @@ def getPresetScenes(self): self.core.callback("getPresetScenes", args=[presetScenes]) return presetScenes + @err_catcher(name=__name__) + def getBlacklistedExtensions(self): + extsStr = os.getenv("PRISM_BLACKLISTED_EXTENSIONS", "") + exts = [ext.strip() for ext in extsStr.split(",")] + return exts + @err_catcher(name=__name__) def getPresetScenesFromFolder(self, folder): presetScenes = [] if os.path.exists(folder): + blacklisted = self.getBlacklistedExtensions() for root, folders, files in os.walk(folder): for filename in sorted(files): if filename == "readme.txt": @@ -1642,6 +1758,11 @@ def getPresetScenesFromFolder(self, folder): if filename.startswith(".") or filename.startswith("_"): continue + if blacklisted: + _, ext = os.path.splitext(filename) + if ext in blacklisted: + continue + relPresetDir = root.replace(folder, "") if relPresetDir: presetName = ( @@ -1656,6 +1777,13 @@ def getPresetScenesFromFolder(self, folder): return presetScenes + @err_catcher(name=__name__) + def getScenePresetPathFromName(self, name): + scenes = self.getPresetScenes() + for scene in scenes: + if scene["label"] == name: + return scene["path"] + @err_catcher(name=__name__) def ingestScenefiles(self, files, entity, department, task, finishCallback=None, data=None, rename=True): kwargs = { @@ -1742,6 +1870,13 @@ def createSceneFromPreset( self.core.projects.getPipelineFolder(), "PresetScenes", fileName ) + if not os.path.exists(scene): + self.core.popup( + "The preset scenefile doesn't exist:\n\n%s" + % scene + ) + return + if location == "local" and self.core.useLocalFiles: filePath = self.core.convertPath(filePath, "local") @@ -1936,7 +2071,7 @@ def copySceneFile(self, filepath, entity, department, task, location=None): details["task"] = task details["extension"] = os.path.splitext(filepath)[1] details["version"] = version - self.core.saveSceneInfo(targetpath, details=details) + self.core.saveSceneInfo(targetpath, details=details, replace=True) logger.debug("Copied scene: %s" % targetpath) return targetpath @@ -2334,7 +2469,7 @@ def onAccepted(self): return connectedEntities = self.w_connectedEnities.getCurrentData(returnOne=False) - connectedEntities = [e for e in connectedEntities if e["type"] in ["asset", "shot"]] + connectedEntities = [e for e in connectedEntities if (e["type"] in ["asset", "shot"] and (e.get("asset_path") or e.get("shot")))] result = self.core.entities.setConnectedEntities(entities, connectedEntities) if not result: return @@ -2342,7 +2477,7 @@ def onAccepted(self): entityNames = [self.core.entities.getEntityName(e) for e in entities] connectedNames = [self.core.entities.getEntityName(e) for e in connectedEntities] or ["-"] msg = "Entity-Connections were set successfully:\n\n%s\n\nto:\n\n%s" % ("\n".join(entityNames), "\n".join(connectedNames)) - self.core.popup(msg, severity="info") + self.core.popup(msg, severity="info", parent=self) @err_catcher(name=__name__) def tabChanged(self): diff --git a/Prism/Scripts/PrismUtils/ProjectWidgets.py b/Prism/Scripts/PrismUtils/ProjectWidgets.py index 98f1900f..2c411cf8 100644 --- a/Prism/Scripts/PrismUtils/ProjectWidgets.py +++ b/Prism/Scripts/PrismUtils/ProjectWidgets.py @@ -46,12 +46,20 @@ class CreateProject(QDialog, CreateProject_ui.Ui_dlg_createProject): - def __init__(self, core): + def __init__(self, core, name=None, path=None, settings=None): QDialog.__init__(self) self.core = core self.prevName = "" if self.core.uiAvailable: self.loadLayout() + if name: + self.e_name.setText(name) + + if path: + self.e_path.setText(path) + + if settings: + self.settingsApplied(settings) self.enableCleanup = True self.core.callback( @@ -1444,3 +1452,1092 @@ def setupUi_(self): self.e_item.setToolTip("Folder name or comma separated list of folder names.\nParent folders can be included using slashes.") self.setWindowTitle("Create Folder...") self.l_item.setText("Folder(s):") + + +class CreateProductDlg(QDialog): + def __init__(self, origin, entity=None): + super(CreateProductDlg, self).__init__() + self.parentDlg = origin + self.core = self.parentDlg.core + self.core.parentWindow(self, parent=self.parentDlg) + self.entity = entity + self.setupUi_() + + @err_catcher(name=__name__) + def sizeHint(self): + return QSize(300, 150) + + @err_catcher(name=__name__) + def setupUi_(self): + self.setWindowTitle("Create Product") + self.lo_main = QVBoxLayout(self) + + self.w_settings = QWidget() + self.lo_settings = QGridLayout(self.w_settings) + self.lo_settings.setContentsMargins(0, 9, 0, 9) + + row = 0 + if self.core.mediaProducts.getLinkedToTasks(): + self.l_department = QLabel("Department:") + self.e_department = QLineEdit() + self.e_department.textEdited.connect(lambda x: self.enableOk()) + self.b_department = QPushButton("â–¼") + self.b_department.setMaximumSize(QSize(25, 16777215)) + self.b_department.clicked.connect(self.onDepartmentClicked) + self.lo_settings.addWidget(self.l_department, row, 0) + self.lo_settings.addWidget(self.e_department, row, 1) + self.lo_settings.addWidget(self.b_department, row, 2) + row += 1 + + self.l_task = QLabel("Task:") + self.e_task = QLineEdit() + self.e_task.textEdited.connect(lambda x: self.enableOk()) + self.b_task = QPushButton("â–¼") + self.b_task.setMaximumSize(QSize(25, 16777215)) + self.b_task.clicked.connect(self.onTaskClicked) + self.lo_settings.addWidget(self.l_task, row, 0) + self.lo_settings.addWidget(self.e_task, row, 1) + self.lo_settings.addWidget(self.b_task, row, 2) + row += 1 + + fileName = self.core.getCurrentFileName() + context = self.core.getScenefileData(fileName) + + dep = context.get("department") or "" + if self.entity.get("type") == "asset": + departments = self.core.projects.getAssetDepartments() + elif self.entity.get("type") == "shot": + departments = self.core.projects.getShotDepartments() + else: + departments = [] + + if not dep and departments: + dep = departments[0]["abbreviation"] + + task = context.get("task") or "" + if not task and dep: + tasks = self.core.entities.getCategories(self.entity, dep) + dftTasks = self.core.entities.getDefaultTasksForDepartment(self.entity.get("type"), dep) + for dftTask in dftTasks: + if dftTask not in tasks: + tasks.append(dftTask) + + if tasks: + task = sorted(tasks, key=lambda x: x.lower())[0] + + self.e_department.setText(dep) + self.e_task.setText(task) + + self.l_product = QLabel("Product:") + self.e_product = QLineEdit() + self.e_product.textEdited.connect(lambda x: self.enableOk()) + self.lo_settings.addWidget(self.l_product, row, 0) + self.lo_settings.addWidget(self.e_product, row, 1) + row += 1 + + self.l_location = QLabel("Location:") + self.cb_location = QComboBox() + locs = self.core.paths.getExportProductBasePaths() + for loc in list(locs.keys()): + self.cb_location.addItem(loc) + + self.lo_settings.addWidget(self.l_location, row, 0) + self.lo_settings.addWidget(self.cb_location, row, 1) + if len(locs) < 2: + self.l_location.setHidden(True) + self.cb_location.setHidden(True) + + row += 1 + + self.bb_main = QDialogButtonBox() + self.b_create = self.bb_main.addButton("Create", QDialogButtonBox.AcceptRole) + self.bb_main.addButton("Cancel", QDialogButtonBox.RejectRole) + self.b_create.setEnabled(False) + self.bb_main.accepted.connect(self.createClicked) + self.bb_main.rejected.connect(self.reject) + + self.lo_main.addWidget(self.w_settings) + self.lo_main.addStretch() + self.lo_main.addWidget(self.bb_main) + + @err_catcher(name=__name__) + def onDepartmentClicked(self): + tmenu = QMenu(self) + + if self.entity.get("type") == "asset": + departments = self.core.projects.getAssetDepartments() + elif self.entity.get("type") == "shot": + departments = self.core.projects.getShotDepartments() + else: + departments = [] + + for dep in departments: + tAct = QAction(dep["abbreviation"], self) + tAct.triggered.connect(lambda x=None, d=dep["abbreviation"]: self.e_department.setText(d)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def onTaskClicked(self): + tmenu = QMenu(self) + + tasks = self.core.entities.getCategories(self.entity, self.e_department.text()) + dftTasks = self.core.entities.getDefaultTasksForDepartment(self.entity.get("type"), self.e_department.text()) + for dftTask in dftTasks: + if dftTask not in tasks: + tasks.append(dftTask) + + for task in sorted(tasks, key=lambda x: x.lower()): + tAct = QAction(task, self) + tAct.triggered.connect(lambda x=None, t=task: self.e_task.setText(t)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def createClicked(self): + if self.core.mediaProducts.getLinkedToTasks(): + depText = self.core.validateLineEdit(self.e_department) + if not depText: + self.core.popup("Invalid department.") + return + + taskText = self.core.validateLineEdit(self.e_task) + if not taskText: + self.core.popup("Invalid task.") + return + + prdText = self.core.validateLineEdit(self.e_product) + if not prdText: + self.core.popup("Invalid product.") + return + + self.accept() + + @err_catcher(name=__name__) + def enableOk(self): + prdText = self.core.validateLineEdit(self.e_product) + if self.core.mediaProducts.getLinkedToTasks(): + depText = self.core.validateLineEdit(self.e_department) + taskText = self.core.validateLineEdit(self.e_task) + valid = bool(prdText and depText and taskText) + else: + valid = bool(prdText) + + self.b_create.setEnabled(valid) + + +class CreateProductVersionDlg(QDialog): + def __init__(self, origin, entity=None): + super(CreateProductVersionDlg, self).__init__() + self.parentDlg = origin + self.core = self.parentDlg.core + self.core.parentWindow(self, parent=self.parentDlg) + self.entity = entity + self.setupUi_() + + @err_catcher(name=__name__) + def sizeHint(self): + return QSize(500, 150) + + @err_catcher(name=__name__) + def setupUi_(self): + self.setWindowTitle("Create Version") + self.lo_main = QVBoxLayout(self) + + self.w_settings = QWidget() + self.lo_settings = QGridLayout(self.w_settings) + self.lo_settings.setContentsMargins(0, 9, 0, 9) + + row = 0 + self.l_version = QLabel("Version:") + self.sp_version = VersionSpinBox() + self.sp_version.core = self.core + self.sp_version.setRange(1, 99999) + self.lo_settings.addWidget(self.l_version, row, 0) + self.lo_settings.addWidget(self.sp_version, row, 1) + row += 1 + + self.l_location = QLabel("Location:") + self.cb_location = QComboBox() + locs = self.core.paths.getExportProductBasePaths() + for loc in list(locs.keys()): + self.cb_location.addItem(loc) + + self.lo_settings.addWidget(self.l_location, row, 0) + self.lo_settings.addWidget(self.cb_location, row, 1) + if len(locs) < 2: + self.l_location.setHidden(True) + self.cb_location.setHidden(True) + + row += 1 + + self.w_filePath = QWidget() + self.w_filePath.setObjectName("fileWidget") + self.l_filePathLabel = QLabel("File Path:") + self.l_filePath = QLabel("< Click or Drag & Drop files >") + self.l_filePath.setAlignment(Qt.AlignCenter) + self.l_filePath.setCursor(Qt.PointingHandCursor) + self.l_filePath.mouseReleaseEvent = self.fileMouseClickEvent + self.lo_filePath = QHBoxLayout(self.w_filePath) + self.lo_filePath.setContentsMargins(0, 20, 0, 20) + self.lo_filePath.addWidget(self.l_filePath) + self.lo_settings.addWidget(self.l_filePathLabel, row, 0) + self.lo_settings.addWidget(self.w_filePath, row, 1) + sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.w_filePath.setSizePolicy(sizePolicy) + self.w_filePath.setAcceptDrops(True) + self.w_filePath.dragEnterEvent = self.fileDragEnterEvent + self.w_filePath.dragMoveEvent = self.fileDragMoveEvent + self.w_filePath.dragLeaveEvent = self.fileDragLeaveEvent + self.w_filePath.dropEvent = self.fileDropEvent + + row += 1 + + self.bb_main = QDialogButtonBox() + self.b_create = self.bb_main.addButton("Create", QDialogButtonBox.AcceptRole) + self.bb_main.addButton("Cancel", QDialogButtonBox.RejectRole) + self.bb_main.accepted.connect(self.createClicked) + self.bb_main.rejected.connect(self.reject) + + self.lo_main.addWidget(self.w_settings) + self.lo_main.addStretch() + self.lo_main.addWidget(self.bb_main) + + @err_catcher(name=__name__) + def fileMouseClickEvent(self, event): + if event.type() == QEvent.MouseButtonRelease: + tmenu = QMenu(self) + + tAct = QAction("Select File...", self) + tAct.triggered.connect(self.browseFile) + tmenu.addAction(tAct) + + tAct = QAction("Select Folder...", self) + tAct.triggered.connect(self.browseFolder) + tmenu.addAction(tAct) + + tAct = QAction("Copy", self) + tAct.triggered.connect(lambda: self.core.copyToClipboard(self.l_filePath.text())) + tmenu.addAction(tAct) + + tAct = QAction("Open in Explorer", self) + tAct.triggered.connect(self.openFolder) + tmenu.addAction(tAct) + + tAct = QAction("Clear", self) + tAct.triggered.connect(self.clearFiles) + tmenu.addAction(tAct) + + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def fileDragEnterEvent(self, e): + if e.mimeData().hasUrls(): + e.accept() + else: + e.ignore() + + @err_catcher(name=__name__) + def fileDragMoveEvent(self, e): + if e.mimeData().hasUrls(): + e.accept() + self.w_filePath.setStyleSheet( + "QWidget#fileWidget { border-style: dashed; border-color: rgb(100, 200, 100); border-width: 2px; }" + ) + else: + e.ignore() + + @err_catcher(name=__name__) + def fileDragLeaveEvent(self, e): + self.w_filePath.setStyleSheet("") + + @err_catcher(name=__name__) + def fileDropEvent(self, e): + if e.mimeData().hasUrls(): + self.w_filePath.setStyleSheet("") + e.setDropAction(Qt.LinkAction) + e.accept() + + fname = [ + os.path.normpath(str(url.toLocalFile())) for url in e.mimeData().urls() + ] + self.l_filePath.setText("\n".join(fname)) + else: + e.ignore() + + @err_catcher(name=__name__) + def browseFolder(self): + startpath = self.l_filePath.text() or self.core.projectPath + selectedPath = QFileDialog.getExistingDirectory( + self, "Select folder", startpath + ) + + if selectedPath: + self.l_filePath.setText(selectedPath.replace("\\", "/")) + + @err_catcher(name=__name__) + def browseFile(self): + startpath = self.l_filePath.text() or self.core.projectPath + selectedFile = QFileDialog.getOpenFileName( + self, "Select file", startpath + )[0] + + if selectedFile: + self.l_filePath.setText(selectedFile.replace("\\", "/")) + + @err_catcher(name=__name__) + def openFolder(self): + path = self.l_filePath.text() + self.core.openFolder(path) + + @err_catcher(name=__name__) + def clearFiles(self): + self.l_filePath.setText("< Click or Drag & Drop files >") + + @err_catcher(name=__name__) + def createClicked(self): + self.accept() + + +class CreateIdentifierDlg(QDialog): + def __init__(self, origin, entity=None): + super(CreateIdentifierDlg, self).__init__() + self.parentDlg = origin + self.core = self.parentDlg.core + self.core.parentWindow(self, parent=self.parentDlg) + self.entity = entity + self.setupUi_() + + @err_catcher(name=__name__) + def sizeHint(self): + return QSize(300, 150) + + @err_catcher(name=__name__) + def setupUi_(self): + self.setWindowTitle("Create Identifier") + self.lo_main = QVBoxLayout(self) + + self.w_settings = QWidget() + self.lo_settings = QGridLayout(self.w_settings) + self.lo_settings.setContentsMargins(0, 9, 0, 9) + + row = 0 + if self.core.mediaProducts.getLinkedToTasks(): + self.l_department = QLabel("Department:") + self.e_department = QLineEdit() + self.e_department.textEdited.connect(lambda x: self.enableOk()) + self.b_department = QPushButton("â–¼") + self.b_department.setMaximumSize(QSize(25, 16777215)) + self.b_department.clicked.connect(self.onDepartmentClicked) + self.lo_settings.addWidget(self.l_department, row, 0) + self.lo_settings.addWidget(self.e_department, row, 1) + self.lo_settings.addWidget(self.b_department, row, 2) + row += 1 + + self.l_task = QLabel("Task:") + self.e_task = QLineEdit() + self.e_task.textEdited.connect(lambda x: self.enableOk()) + self.b_task = QPushButton("â–¼") + self.b_task.setMaximumSize(QSize(25, 16777215)) + self.b_task.clicked.connect(self.onTaskClicked) + self.lo_settings.addWidget(self.l_task, row, 0) + self.lo_settings.addWidget(self.e_task, row, 1) + self.lo_settings.addWidget(self.b_task, row, 2) + row += 1 + + fileName = self.core.getCurrentFileName() + context = self.core.getScenefileData(fileName) + + dep = context.get("department") or "" + if self.entity.get("type") == "asset": + departments = self.core.projects.getAssetDepartments() + elif self.entity.get("type") == "shot": + departments = self.core.projects.getShotDepartments() + else: + departments = [] + + if not dep and departments: + dep = departments[0]["abbreviation"] + + task = context.get("task") or "" + if not task and dep: + tasks = self.core.entities.getCategories(self.entity, dep) + dftTasks = self.core.entities.getDefaultTasksForDepartment(self.entity.get("type"), dep) + for dftTask in dftTasks: + if dftTask not in tasks: + tasks.append(dftTask) + + if tasks: + task = sorted(tasks, key=lambda x: x.lower())[0] + + self.e_department.setText(dep) + self.e_task.setText(task) + + self.l_identifier = QLabel("Identifier:") + self.e_identifier = QLineEdit() + self.e_identifier.textEdited.connect(lambda x: self.enableOk()) + self.lo_settings.addWidget(self.l_identifier, row, 0) + self.lo_settings.addWidget(self.e_identifier, row, 1) + row += 1 + + self.l_mediaType = QLabel("Type:") + self.cb_mediaType = QComboBox() + self.cb_mediaType.addItems(["3D", "2D", "Playblast", "External"]) + self.lo_settings.addWidget(self.l_mediaType, row, 0) + self.lo_settings.addWidget(self.cb_mediaType, row, 1) + row += 1 + + self.l_location = QLabel("Location:") + self.cb_location = QComboBox() + locs = self.core.paths.getRenderProductBasePaths() + for loc in list(locs.keys()): + self.cb_location.addItem(loc) + + self.lo_settings.addWidget(self.l_location, row, 0) + self.lo_settings.addWidget(self.cb_location, row, 1) + if len(locs) < 2: + self.l_location.setHidden(True) + self.cb_location.setHidden(True) + + row += 1 + + self.bb_main = QDialogButtonBox() + self.b_create = self.bb_main.addButton("Create", QDialogButtonBox.AcceptRole) + self.bb_main.addButton("Cancel", QDialogButtonBox.RejectRole) + self.b_create.setEnabled(False) + self.bb_main.accepted.connect(self.createClicked) + self.bb_main.rejected.connect(self.reject) + + self.lo_main.addWidget(self.w_settings) + self.lo_main.addStretch() + self.lo_main.addWidget(self.bb_main) + + @err_catcher(name=__name__) + def onDepartmentClicked(self): + tmenu = QMenu(self) + + if self.entity.get("type") == "asset": + departments = self.core.projects.getAssetDepartments() + elif self.entity.get("type") == "shot": + departments = self.core.projects.getShotDepartments() + else: + departments = [] + + for dep in departments: + tAct = QAction(dep["abbreviation"], self) + tAct.triggered.connect(lambda x=None, d=dep["abbreviation"]: self.e_department.setText(d)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def onTaskClicked(self): + tmenu = QMenu(self) + + tasks = self.core.entities.getCategories(self.entity, self.e_department.text()) + dftTasks = self.core.entities.getDefaultTasksForDepartment(self.entity.get("type"), self.e_department.text()) + for dftTask in dftTasks: + if dftTask not in tasks: + tasks.append(dftTask) + + for task in sorted(tasks, key=lambda x: x.lower()): + tAct = QAction(task, self) + tAct.triggered.connect(lambda x=None, t=task: self.e_task.setText(t)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def createClicked(self): + if self.core.mediaProducts.getLinkedToTasks(): + depText = self.core.validateLineEdit(self.e_department) + if not depText: + self.core.popup("Invalid department.") + return + + taskText = self.core.validateLineEdit(self.e_task) + if not taskText: + self.core.popup("Invalid task.") + return + + idfText = self.core.validateLineEdit(self.e_identifier) + if not idfText: + self.core.popup("Invalid identifier.") + return + + self.accept() + + @err_catcher(name=__name__) + def enableOk(self): + idfText = self.core.validateLineEdit(self.e_identifier) + if self.core.mediaProducts.getLinkedToTasks(): + depText = self.core.validateLineEdit(self.e_department) + taskText = self.core.validateLineEdit(self.e_task) + valid = bool(idfText and depText and taskText) + else: + valid = bool(idfText) + + self.b_create.setEnabled(valid) + + +class CreateMediaVersionDlg(QDialog): + def __init__(self, origin, entity=None): + super(CreateMediaVersionDlg, self).__init__() + self.parentDlg = origin + self.core = self.parentDlg.core + self.core.parentWindow(self, parent=self.parentDlg) + self.entity = entity + self.setupUi_() + + @err_catcher(name=__name__) + def sizeHint(self): + return QSize(300, 150) + + @err_catcher(name=__name__) + def setupUi_(self): + self.setWindowTitle("Create Version") + self.lo_main = QVBoxLayout(self) + + self.w_settings = QWidget() + self.lo_settings = QGridLayout(self.w_settings) + self.lo_settings.setContentsMargins(0, 9, 0, 9) + + row = 0 + self.l_version = QLabel("Version:") + self.sp_version = VersionSpinBox() + self.sp_version.core = self.core + self.sp_version.setRange(1, 99999) + self.lo_settings.addWidget(self.l_version, row, 0) + self.lo_settings.addWidget(self.sp_version, row, 1) + row += 1 + + self.l_location = QLabel("Location:") + self.cb_location = QComboBox() + locs = self.core.paths.getRenderProductBasePaths() + for loc in list(locs.keys()): + self.cb_location.addItem(loc) + + self.lo_settings.addWidget(self.l_location, row, 0) + self.lo_settings.addWidget(self.cb_location, row, 1) + if len(locs) < 2: + self.l_location.setHidden(True) + self.cb_location.setHidden(True) + + row += 1 + + self.bb_main = QDialogButtonBox() + self.b_create = self.bb_main.addButton("Create", QDialogButtonBox.AcceptRole) + self.bb_main.addButton("Cancel", QDialogButtonBox.RejectRole) + self.bb_main.accepted.connect(self.createClicked) + self.bb_main.rejected.connect(self.reject) + + self.lo_main.addWidget(self.w_settings) + self.lo_main.addStretch() + self.lo_main.addWidget(self.bb_main) + + @err_catcher(name=__name__) + def createClicked(self): + self.accept() + + +class IngestMediaDlg(QDialog): + def __init__(self, core, startText="", entity=None, parent=None): + QDialog.__init__(self) + self.core = core + self.entity = entity + self.core.parentWindow(self, parent=parent) + self.setupUi() + self.onMediaTypeChanged() + self.setEntity(entity) + if startText: + self.l_mediaPath.setText(startText) + + self.sp_version.setValue(1) + self.b_create.setEnabled(False) + self.connectEvents() + + @err_catcher(name=__name__) + def sizeHint(self): + return QSize(600, 200) + + @err_catcher(name=__name__) + def setupUi(self): + self.setWindowTitle("Ingest Media") + + self.lo_main = QVBoxLayout(self) + self.w_settings = QWidget() + self.lo_settings = QGridLayout(self.w_settings) + + row = 0 + + self.l_entity = QLabel("Entity:") + self.w_entity = QWidget() + self.lo_entity = QHBoxLayout(self.w_entity) + self.w_entity.setCursor(Qt.PointingHandCursor) + self.w_entity.mouseReleaseEvent = self.entityMouseClickEvent + self.l_entityPreview = QLabel() + self.l_entityName = QLabel() + self.lo_entity.addWidget(self.l_entityPreview) + self.lo_entity.addWidget(self.l_entityName) + self.lo_settings.addWidget(self.l_entity, row, 0) + self.lo_settings.addWidget(self.w_entity, row, 1) + row += 1 + + self.l_location = QLabel("Location:") + self.cb_location = QComboBox() + locs = self.core.paths.getRenderProductBasePaths() + for loc in list(locs.keys()): + self.cb_location.addItem(loc) + + self.lo_settings.addWidget(self.l_location, row, 0) + self.lo_settings.addWidget(self.cb_location, row, 1) + if len(locs) < 2: + self.l_location.setHidden(True) + self.cb_location.setHidden(True) + + row += 1 + + if self.core.mediaProducts.getLinkedToTasks(): + self.l_department = QLabel("Department:") + self.e_department = QLineEdit() + self.e_department.textEdited.connect(lambda x: self.enableOk()) + self.b_department = QPushButton("â–¼") + self.b_department.setMaximumSize(QSize(25, 16777215)) + self.b_department.clicked.connect(self.onDepartmentClicked) + self.lo_settings.addWidget(self.l_department, row, 0) + self.lo_settings.addWidget(self.e_department, row, 1) + self.lo_settings.addWidget(self.b_department, row, 2) + row += 1 + + self.l_task = QLabel("Task:") + self.e_task = QLineEdit() + self.e_task.textEdited.connect(lambda x: self.enableOk()) + self.b_task = QPushButton("â–¼") + self.b_task.setMaximumSize(QSize(25, 16777215)) + self.b_task.clicked.connect(self.onTaskClicked) + self.lo_settings.addWidget(self.l_task, row, 0) + self.lo_settings.addWidget(self.e_task, row, 1) + self.lo_settings.addWidget(self.b_task, row, 2) + row += 1 + + fileName = self.core.getCurrentFileName() + context = self.core.getScenefileData(fileName) + + dep = context.get("department") or "" + if self.entity.get("type") == "asset": + departments = self.core.projects.getAssetDepartments() + elif self.entity.get("type") == "shot": + departments = self.core.projects.getShotDepartments() + else: + departments = [] + + if not dep and departments: + dep = departments[0]["abbreviation"] + + task = context.get("task") or "" + if not task and dep: + tasks = self.core.entities.getCategories(self.entity, dep) + dftTasks = self.core.entities.getDefaultTasksForDepartment(self.entity.get("type"), dep) + for dftTask in dftTasks: + if dftTask not in tasks: + tasks.append(dftTask) + + if tasks: + task = sorted(tasks, key=lambda x: x.lower())[0] + + self.e_department.setText(dep) + self.e_task.setText(task) + + self.l_identifier = QLabel("Identifier:") + self.e_identifier = QLineEdit() + self.e_identifier.textEdited.connect(lambda x: self.enableOk()) + self.cb_identifierType = QComboBox() + self.cb_identifierType.addItem("3D", "3drenders") + self.cb_identifierType.addItem("2D", "2drenders") + self.cb_identifierType.addItem("Playblast", "playblasts") + self.cb_identifierType.addItem("External", "externalMedia") + self.cb_identifierType.currentIndexChanged.connect(self.onMediaTypeChanged) + self.w_identifier = QWidget() + self.lo_identifier = QHBoxLayout(self.w_identifier) + self.lo_identifier.setContentsMargins(0, 0, 0, 0) + self.lo_identifier.addWidget(self.e_identifier) + self.lo_identifier.addWidget(self.cb_identifierType) + self.b_identifier = QPushButton("â–¼") + self.b_identifier.setMaximumSize(QSize(25, 16777215)) + self.b_identifier.clicked.connect(self.onIdentifierClicked) + self.lo_settings.addWidget(self.l_identifier, row, 0) + self.lo_settings.addWidget(self.w_identifier, row, 1) + self.lo_settings.addWidget(self.b_identifier, row, 2) + row += 1 + + self.l_version = QLabel("Version:") + self.sp_version = VersionSpinBox() + self.sp_version.core = self.core + self.sp_version.setRange(1, 99999) + self.b_version = QPushButton("â–¼") + self.b_version.setMaximumSize(QSize(25, 16777215)) + self.b_version.clicked.connect(self.onVersionClicked) + self.lo_settings.addWidget(self.l_version, row, 0) + self.lo_settings.addWidget(self.sp_version, row, 1) + self.lo_settings.addWidget(self.b_version, row, 2) + row += 1 + + self.l_aov = QLabel("AOV:") + self.e_aov = QLineEdit() + self.e_aov.setText("rgb") + self.e_aov.textEdited.connect(lambda x: self.enableOk()) + self.b_aov = QPushButton("â–¼") + self.b_aov.setMaximumSize(QSize(25, 16777215)) + self.b_aov.clicked.connect(self.onAovClicked) + self.lo_settings.addWidget(self.l_aov, row, 0) + self.lo_settings.addWidget(self.e_aov, row, 1) + self.lo_settings.addWidget(self.b_aov, row, 2) + row += 1 + + self.w_mediaPath = QWidget() + self.w_mediaPath.setObjectName("mediaWidget") + self.l_mediaPathLabel = QLabel("Media Path:") + self.l_mediaPath = QLabel("< Click or Drag & Drop media >") + self.l_mediaPath.setAlignment(Qt.AlignCenter) + self.l_mediaPath.setCursor(Qt.PointingHandCursor) + self.l_mediaPath.mouseReleaseEvent = self.mediaMouseClickEvent + self.lo_mediaPath = QHBoxLayout(self.w_mediaPath) + self.lo_mediaPath.setContentsMargins(0, 20, 0, 20) + self.lo_mediaPath.addWidget(self.l_mediaPath) + self.lo_settings.addWidget(self.l_mediaPathLabel, row, 0) + self.lo_settings.addWidget(self.w_mediaPath, row, 1, 1, 2) + self.w_mediaPath.setAcceptDrops(True) + self.w_mediaPath.dragEnterEvent = self.mediaDragEnterEvent + self.w_mediaPath.dragMoveEvent = self.mediaDragMoveEvent + self.w_mediaPath.dragLeaveEvent = self.mediaDragLeaveEvent + self.w_mediaPath.dropEvent = self.mediaDropEvent + + row += 1 + + self.w_action = QWidget() + self.l_action = QLabel("Action:") + self.rb_copy = QRadioButton("Copy") + self.rb_move = QRadioButton("Move") + self.rb_link = QRadioButton("Link") + self.lo_action = QHBoxLayout(self.w_action) + self.lo_action.setContentsMargins(0, 0, 0, 0) + self.lo_action.addStretch() + self.lo_action.addWidget(self.rb_copy) + self.lo_action.addWidget(self.rb_move) + self.lo_action.addWidget(self.rb_link) + self.lo_settings.addWidget(self.l_action, row, 0) + self.lo_settings.addWidget(self.w_action, row, 1, 1, 2) + self.rb_copy.setChecked(True) + row += 1 + + self.bb_main = QDialogButtonBox() + self.b_create = self.bb_main.addButton("Create", QDialogButtonBox.AcceptRole) + self.bb_main.addButton("Cancel", QDialogButtonBox.RejectRole) + self.b_create.setEnabled(False) + self.bb_main.accepted.connect(self.createClicked) + self.bb_main.rejected.connect(self.reject) + + self.lo_main.addWidget(self.w_settings) + self.lo_main.addStretch() + self.lo_main.addWidget(self.bb_main) + + @err_catcher(name=__name__) + def entityMouseClickEvent(self, event): + if event.type() == QEvent.MouseButtonRelease: + if event.button() == Qt.LeftButton: + if getattr(self, "dlg_entity", None): + self.dlg_entity.close() + + self.dlg_entity = self.core.getStateManager().entityDlg(self) + self.dlg_entity.w_entities.editEntitiesOnDclick = False + self.dlg_entity.w_entities.navigate(self.entity) + self.dlg_entity.entitySelected.connect(lambda x: self.setEntity(x)) + self.dlg_entity.show() + + @err_catcher(name=__name__) + def setEntity(self, entity): + self.entity = entity + pmap = self.core.entities.getEntityPreview(self.entity) + if not pmap: + pmap = self.core.media.emptyPrvPixmap + + pmap = self.core.media.scalePixmap(pmap, 107, 60, fitIntoBounds=False, crop=True) + self.l_entityPreview.setPixmap(pmap) + entityName = "%s - %s" % (self.entity["type"].capitalize(), self.core.entities.getEntityName(self.entity)) + self.l_entityName.setText(entityName) + + @err_catcher(name=__name__) + def mediaMouseClickEvent(self, event): + if event.type() == QEvent.MouseButtonRelease: + tmenu = QMenu(self) + + tAct = QAction("Select File...", self) + tAct.triggered.connect(self.browseFile) + tmenu.addAction(tAct) + + tAct = QAction("Select Folder...", self) + tAct.triggered.connect(self.browseFolder) + tmenu.addAction(tAct) + + tAct = QAction("Copy", self) + tAct.triggered.connect(lambda: self.core.copyToClipboard(self.l_mediaPath.text())) + tmenu.addAction(tAct) + + tAct = QAction("Open in Explorer", self) + tAct.triggered.connect(self.openFolder) + tmenu.addAction(tAct) + + tAct = QAction("Clear", self) + tAct.triggered.connect(self.clearMedia) + tmenu.addAction(tAct) + + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def mediaDragEnterEvent(self, e): + if e.mimeData().hasUrls(): + e.accept() + else: + e.ignore() + + @err_catcher(name=__name__) + def mediaDragMoveEvent(self, e): + if e.mimeData().hasUrls(): + e.accept() + self.w_mediaPath.setStyleSheet( + "QWidget#mediaWidget { border-style: dashed; border-color: rgb(100, 200, 100); border-width: 2px; }" + ) + else: + e.ignore() + + @err_catcher(name=__name__) + def mediaDragLeaveEvent(self, e): + self.w_mediaPath.setStyleSheet("") + + @err_catcher(name=__name__) + def mediaDropEvent(self, e): + if e.mimeData().hasUrls(): + self.w_mediaPath.setStyleSheet("") + e.setDropAction(Qt.LinkAction) + e.accept() + + fname = [ + os.path.normpath(str(url.toLocalFile())) for url in e.mimeData().urls() + ] + self.l_mediaPath.setText("\n".join(fname)) + self.enableOk() + else: + e.ignore() + + @err_catcher(name=__name__) + def onMediaTypeChanged(self, idx=None): + curType = self.cb_identifierType.currentData() + self.l_aov.setHidden(curType != "3drenders") + self.e_aov.setHidden(curType != "3drenders") + self.b_aov.setHidden(curType != "3drenders") + self.l_action.setHidden(curType != "externalMedia") + self.w_action.setHidden(curType != "externalMedia") + self.enableOk() + + @err_catcher(name=__name__) + def onDepartmentClicked(self): + tmenu = QMenu(self) + + if self.entity.get("type") == "asset": + departments = self.core.projects.getAssetDepartments() + elif self.entity.get("type") == "shot": + departments = self.core.projects.getShotDepartments() + else: + departments = [] + + for dep in departments: + tAct = QAction(dep["abbreviation"], self) + tAct.triggered.connect(lambda x=None, d=dep["abbreviation"]: self.e_department.setText(d)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def onTaskClicked(self): + tmenu = QMenu(self) + + tasks = self.core.entities.getCategories(self.entity, self.e_department.text()) + dftTasks = self.core.entities.getDefaultTasksForDepartment(self.entity.get("type"), self.e_department.text()) + for dftTask in dftTasks: + if dftTask not in tasks: + tasks.append(dftTask) + + if not tasks: + return + + for task in sorted(tasks, key=lambda x: x.lower()): + tAct = QAction(task, self) + tAct.triggered.connect(lambda x=None, t=task: self.e_task.setText(t)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def onIdentifierClicked(self): + tmenu = QMenu(self) + + entity = self.entity.copy() + if self.core.mediaProducts.getLinkedToTasks(): + entity["department"] = self.e_department.text() + entity["task"] = self.e_task.text() + + idfs = self.core.mediaProducts.getIdentifiersByType(entity) + idfNames = [] + for mtype in idfs: + for idf in idfs[mtype]: + name = idf["identifier"] + idfNames.append(name) + + for idfName in sorted(set(idfNames)): + tAct = QAction(idfName, self) + tAct.triggered.connect(lambda x=None, t=idfName: self.e_identifier.setText(t)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def onVersionClicked(self): + tmenu = QMenu(self) + + entity = self.entity.copy() + if self.core.mediaProducts.getLinkedToTasks(): + entity["department"] = self.e_department.text() + entity["task"] = self.e_task.text() + + entity["identifier"] = self.e_identifier.text() + entity["mediaType"] = self.cb_identifierType.currentData() + versions = self.core.mediaProducts.getVersionsFromIdentifier(entity) + for version in sorted(versions, key=lambda x: x["version"], reverse=True): + name = version["version"] + tAct = QAction(name, self) + intVersion = self.core.products.getIntVersionFromVersionName(name) + if intVersion is None: + continue + + tAct.triggered.connect(lambda x=None, t=intVersion: self.sp_version.setValue(t)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def onAovClicked(self): + tmenu = QMenu(self) + + entity = self.entity.copy() + if self.core.mediaProducts.getLinkedToTasks(): + entity["department"] = self.e_department.text() + entity["task"] = self.e_task.text() + + entity["identifier"] = self.e_identifier.text() + entity["mediaType"] = self.cb_identifierType.currentData() + entity["version"] = self.core.versionFormat % self.sp_version.value() + aovs = self.core.mediaProducts.getAOVsFromVersion(entity) + for aov in sorted(aovs, key=lambda x: x["aov"]): + name = aov["aov"] + tAct = QAction(name, self) + tAct.triggered.connect(lambda x=None, t=name: self.e_aov.setText(t)) + tmenu.addAction(tAct) + + if not tmenu.isEmpty(): + tmenu.exec_(QCursor.pos()) + + @err_catcher(name=__name__) + def connectEvents(self): + self.e_identifier.textChanged.connect(lambda x: self.enableOk()) + + @err_catcher(name=__name__) + def browseFolder(self): + startpath = self.l_mediaPath.text() or self.core.projectPath + selectedPath = QFileDialog.getExistingDirectory( + self, "Select media folder", startpath + ) + + if selectedPath: + self.l_mediaPath.setText(selectedPath.replace("\\", "/")) + + @err_catcher(name=__name__) + def browseFile(self): + startpath = self.l_mediaPath.text() or self.core.projectPath + selectedFile = QFileDialog.getOpenFileName( + self, "Select media file", startpath + )[0] + + if selectedFile: + self.l_mediaPath.setText(selectedFile.replace("\\", "/")) + + @err_catcher(name=__name__) + def openFolder(self): + path = self.l_mediaPath.text() + self.core.openFolder(path) + + @err_catcher(name=__name__) + def clearMedia(self): + self.l_mediaPath.setText("< Click or Drag & Drop media >") + + @err_catcher(name=__name__) + def enableOk(self): + idfText = self.e_identifier.text() + mediaText = self.l_mediaPath.text() + mediaTextValid = bool(mediaText) and mediaText != "< Click or Drag & Drop media >" + if self.core.mediaProducts.getLinkedToTasks(): + depText = self.e_department.text() + taskText = self.e_task.text() + valid = bool(idfText and mediaTextValid and depText and taskText) + else: + valid = bool(idfText and mediaTextValid) + + if self.cb_identifierType.currentData() == "3drenders": + valid = valid and bool(self.e_aov.text()) + + self.b_create.setEnabled(valid) + + @err_catcher(name=__name__) + def createClicked(self): + if self.core.mediaProducts.getLinkedToTasks(): + depText = self.core.validateLineEdit(self.e_department) + if not depText: + self.core.popup("Invalid department.") + return + + taskText = self.core.validateLineEdit(self.e_task) + if not taskText: + self.core.popup("Invalid task.") + return + + idfText = self.core.validateLineEdit(self.e_identifier) + if not idfText: + self.core.popup("Invalid identifier.") + return + + if self.cb_identifierType.currentData() == "3drenders": + aovText = self.core.validateLineEdit(self.e_aov) + if not aovText: + self.core.popup("Invalid aov.") + return + + mediaText = self.l_mediaPath.text() + if not mediaText or mediaText == "< Click or Drag & Drop media >": + self.core.popup("Invalid media path.") + return + + self.accept() + + +class VersionSpinBox(QSpinBox): + def textFromValue(self, value): + return self.core.versionFormat % value diff --git a/Prism/Scripts/PrismUtils/Projects.py b/Prism/Scripts/PrismUtils/Projects.py index 7057d98a..ce3d27e7 100644 --- a/Prism/Scripts/PrismUtils/Projects.py +++ b/Prism/Scripts/PrismUtils/Projects.py @@ -60,8 +60,8 @@ def __init__(self, core): self.dlg_settings = None self.extraStructureItems = OrderedDict([]) self.environmentVariables = [] - self.previewWidth = 640 - self.previewHeight = 360 + self.previewWidth = 1280 + self.previewHeight = 720 @err_catcher(name=__name__) def setProject(self, startup=None, openUi=""): @@ -409,6 +409,8 @@ def changeProject(self, configPath=None, openUi="", settingsTab=None, settingsTy function=self.core.products.checkMasterVersions, label="Check Product Master Versions..." ) + else: + self.core.entities.removeEntityAction("masterVersionCheckProducts") if self.core.mediaProducts.getUseMaster(): self.core.entities.addEntityAction( @@ -417,6 +419,8 @@ def changeProject(self, configPath=None, openUi="", settingsTab=None, settingsTy function=self.core.mediaProducts.checkMasterVersions, label="Check Media Master Versions..." ) + else: + self.core.entities.removeEntityAction("masterVersionCheckMedia") logger.debug("Loaded project " + self.core.projectPath) @@ -730,6 +734,10 @@ def getDefaultProjectSettings(self): "640x360", ], ), + ("requirePublishComment", True), + ("publishCommentLength", 3), + ("defaultImportStateName", "{entity}_{product}_{version}"), + ("useStrictAssetDetection", False), ] ), ), @@ -779,20 +787,21 @@ def createProject( # check valid project name if not prjName: - self.core.popup("The project name is invalid") + self.core.popup("The project name is invalid.") return # create project folder if not os.path.isabs(prjPath): - self.core.popup("The project path is invalid") + self.core.popup("The project path is invalid.") return if not os.path.exists(prjPath): try: os.makedirs(prjPath) - except: - self.core.popup("The project folder could not be created", parent=parent) + except Exception as e: + self.core.popup("The project folder could not be created.\n\n(%s)" % str(e), parent=parent) return + elif os.listdir(prjPath): msg = "The project folder is not empty. How do you want to continue?" result = self.core.popupQuestion( @@ -1525,21 +1534,24 @@ def getResolvedProjectStructurePaths(self, key, context=None, structure=None, fa @err_catcher(name=__name__) def resolveStructurePath(self, path, context=None, structure=None, addProjectPath=True, fillContextKeys=True, fallback=None): context = context or {} + prjPath = None if "project_path" in context: if structure is None: prjPath = self.core.convertPath(context["project_path"], "global") - elif addProjectPath: - context["project_path"] = os.path.normpath(self.core.projectPath) - prjPath = context["project_path"] - else: - if structure is None: - prjPath = os.path.normpath(self.core.projectPath) + elif getattr(self.core, "projectPath", None): + if addProjectPath: + context["project_path"] = os.path.normpath(self.core.projectPath) + prjPath = context["project_path"] + else: + if structure is None: + prjPath = os.path.normpath(self.core.projectPath) if "project_path" in context and "project_name" not in context: - if hasattr(self.core, "projectPath") and context["project_path"] == self.core.projectPath: + glbPrjPath = self.core.convertPath(context["project_path"], "global") + if hasattr(self.core, "projectPath") and glbPrjPath == self.core.projectPath: context["project_name"] = self.core.projectName else: - cfgPath = self.core.configs.getProjectConfigPath(context["project_path"]) + cfgPath = self.core.configs.getProjectConfigPath(glbPrjPath) context["project_name"] = self.core.getConfig("globals", "project_name", configPath=cfgPath) or "" if structure is None: @@ -1944,8 +1956,9 @@ def getAssetDepartments(self, configData=None): if not deps: deps = self.getProjectDepartments() - deps = [{"name": d[1], "abbreviation": d[0], "defaultTasks": [d[1]]} for d in list(deps.items())] - self.setDepartments("asset", deps, configData) + if deps: + deps = [{"name": d[1], "abbreviation": d[0], "defaultTasks": [d[1]]} for d in list(deps.items())] + self.setDepartments("asset", deps, configData) return deps @@ -1965,8 +1978,9 @@ def getShotDepartments(self, configData=None): if not deps: deps = self.getProjectDepartments() - deps = [{"name": d[1], "abbreviation": d[0], "defaultTasks": [d[1]]} for d in list(deps.items())] - self.setDepartments("shot", deps, configData) + if deps: + deps = [{"name": d[1], "abbreviation": d[0], "defaultTasks": [d[1]]} for d in list(deps.items())] + self.setDepartments("shot", deps, configData) return deps @@ -2479,7 +2493,7 @@ class ProjectWidget(QWidget): def __init__(self, parent, data, minHeight=200, allowRemove=True, previewScale=1, useWidgetWidth=False): super(Projects.ProjectWidget, self).__init__() self.core = parent.core - self.parent = parent + self._parent = parent self.data = data self.status = "deselected" self.minHeight = minHeight @@ -2587,10 +2601,10 @@ def refreshUi(self): self.l_icon.setPixmap(icon) else: self.setLoadingPreview() - if self.parent.isVisible(): + if self._parent.isVisible(): self.updatePreview_threaded() else: - self.parent.signalShowing.connect(self.updatePreview_threaded) + self._parent.signalShowing.connect(self.updatePreview_threaded) name = self.getDisplayName() self.l_name.setText(name) @@ -2598,7 +2612,7 @@ def refreshUi(self): @err_catcher(name=__name__) def updatePreview(self, load=True): if hasattr(self, "loadingGif"): - self.loadingGif.setScaledSize(QSize(self.l_preview.width(), self.l_preview.width() / (300/169.0))) + self.loadingGif.setScaledSize(QSize(self.l_preview.width(), int(self.l_preview.width() / (300/169.0)))) ppixmap = self.getPreviewImage(load=load) if not ppixmap or ppixmap == "loading": @@ -2617,7 +2631,7 @@ def setLoadingPreview(self): self.loadingGif = QMovie(path, QByteArray(), self) self.loadingGif.setCacheMode(QMovie.CacheAll) self.loadingGif.setSpeed(100) - self.loadingGif.setScaledSize(QSize(self.l_preview.width(), self.l_preview.width() / (300/169.0))) + self.loadingGif.setScaledSize(QSize(self.l_preview.width(), int(self.l_preview.width() / (300/169.0)))) self.l_preview.setMovie(self.loadingGif) self.loadingGif.start() @@ -2656,7 +2670,11 @@ def getIcon(self): if "icon" not in self.data: return - icon = self.core.media.getColoredIcon(self.data["icon"], force=True) + if self.core.isStr(self.data["icon"]): + icon = self.core.media.getColoredIcon(self.data["icon"], force=True) + else: + icon = self.data["icon"] + pixmap = icon.pixmap(30, 30) return pixmap @@ -2758,9 +2776,9 @@ def mouseDoubleClickEvent(self, event): @err_catcher(name=__name__) def getContextMenu(self): - menu = QMenu(self.parent) + menu = QMenu(self._parent) - selectedProjects = self.parent.getSelectedItems() + selectedProjects = self._parent.getSelectedItems() copAct = QAction("Capture project image", self) copAct.triggered.connect(self.captureProjectPreview) @@ -2781,15 +2799,15 @@ def getContextMenu(self): clipAct.setEnabled(False) if "source" in self.data and self.data["source"] == "recent" and self.allowRemove: - expAct = QAction("Delete from recent", self.parent) + expAct = QAction("Delete from recent", self._parent) expAct.triggered.connect(self.deleteRecent) menu.addAction(expAct) - expAct = QAction("Open in Explorer", self.parent) + expAct = QAction("Open in Explorer", self._parent) expAct.triggered.connect(self.onOpenExplorerClicked) menu.addAction(expAct) - copAct = QAction("Copy path", self.parent) + copAct = QAction("Copy path", self._parent) copAct.triggered.connect(self.onCopyPathClicked) menu.addAction(copAct) @@ -2801,13 +2819,13 @@ def getContextMenu(self): @err_catcher(name=__name__) def onOpenExplorerClicked(self): - items = self.parent.getSelectedItems() + items = self._parent.getSelectedItems() for item in items: self.core.openFolder(item.data["configPath"]) @err_catcher(name=__name__) def onCopyPathClicked(self): - items = self.parent.getSelectedItems() + items = self._parent.getSelectedItems() text = os.pathsep.join(item.data["configPath"] for item in items) self.core.copyToClipboard(text) @@ -2820,13 +2838,13 @@ def rightClicked(self, pos): if not menu or menu.isEmpty(): return - if hasattr(self.parent, "allowClose"): - self.parent.allowClose = False + if hasattr(self._parent, "allowClose"): + self._parent.allowClose = False menu.exec_(QCursor.pos()) - if hasattr(self.parent, "allowClose"): - self.parent.allowClose = True + if hasattr(self._parent, "allowClose"): + self._parent.allowClose = True @err_catcher(name=__name__) def browseProjectPreview(self): @@ -2877,7 +2895,7 @@ def captureProjectPreview(self): def pasteProjectPreviewFromClipboard(self): pmap = self.core.media.getPixmapFromClipboard() if not pmap: - self.core.popup("No image in clipboard.", parent=self.parent) + self.core.popup("No image in clipboard.", parent=self._parent) return pmap = self.core.media.scalePixmap(pmap, width=self.core.projects.previewWidth, height=self.core.projects.previewHeight, fitIntoBounds=False) @@ -2889,7 +2907,7 @@ def pasteProjectPreviewFromClipboard(self): @err_catcher(name=__name__) def deleteRecent(self): - items = self.parent.getSelectedItems() + items = self._parent.getSelectedItems() for item in items: self.core.projects.setRecentPrj(item.data["configPath"], action="remove") @@ -2905,7 +2923,7 @@ def paintEvent(self, event): painter.setBrush(brush) painter.setPen(Qt.NoPen) painter.drawRoundedRect(1, 1, self.width()-2, self.height(), 10, 10) - painter.drawRect(1, self.height() / 2, self.width()-2, self.height()) + painter.drawRect(1, int(self.height() / 2), self.width()-2, self.height()) else: super(Projects.RoundedLabel, self).paintEvent(event) diff --git a/Prism/Scripts/PrismUtils/SanityChecks.py b/Prism/Scripts/PrismUtils/SanityChecks.py index 764ee9d2..f1cb4fcf 100644 --- a/Prism/Scripts/PrismUtils/SanityChecks.py +++ b/Prism/Scripts/PrismUtils/SanityChecks.py @@ -205,32 +205,45 @@ def checkFramerange(self): if int(curRange[0]) == int(shotRange[0]) and int(curRange[1]) == int(shotRange[1]): return + handleRange = self.core.entities.getShotRange(fnameData, handles=True) + hasHandles = handleRange != shotRange + if hasHandles and int(curRange[0]) == int(handleRange[0]) and int(curRange[1]) == int(handleRange[1]): + return + shotName = self.core.entities.getShotName(fnameData) msgString = ( "The framerange of the current scene doesn't match the framerange of the shot:\n\nFramerange of current scene:\n%s - %s\n\nFramerange of shot %s:\n%s - %s" % (int(curRange[0]), int(curRange[1]), shotName, shotRange[0], shotRange[1]) ) + if hasHandles: + msgString += " (%s - %s)" % (handleRange[0], handleRange[1]) if self.core.forceFramerange: self.core.setFrameRange(int(shotRange[0]), int(shotRange[1])) else: + buttons = ["Set shotrange in scene", "Skip"] + if hasHandles: + buttons.insert(1, "Set shotrange in scene (with handles)") + msg = self.core.popupQuestion( msgString, title="Framerange mismatch", - buttons=["Set shotrange in scene", "Skip"], + buttons=buttons, escapeButton="Skip", default="Skip", doExec=False, ) if not self.core.isStr(msg): - msg.buttonClicked.connect(lambda x: self.onCheckFramerangeClicked(x, shotRange)) + msg.buttonClicked.connect(lambda x: self.onCheckFramerangeClicked(x, shotRange, handleRange)) msg.show() @err_catcher(name=__name__) - def onCheckFramerangeClicked(self, button, shotRange): + def onCheckFramerangeClicked(self, button, shotRange, handleRange=None): result = button.text() if result == "Set shotrange in scene": self.core.setFrameRange(int(shotRange[0]), int(shotRange[1])) + elif result == "Set shotrange in scene (with handles)": + self.core.setFrameRange(int(handleRange[0]), int(handleRange[1])) @err_catcher(name=__name__) def checkFPS(self): diff --git a/Prism/Scripts/PrismUtils/ScreenShot.py b/Prism/Scripts/PrismUtils/ScreenShot.py index b66ee974..844028a6 100644 --- a/Prism/Scripts/PrismUtils/ScreenShot.py +++ b/Prism/Scripts/PrismUtils/ScreenShot.py @@ -117,23 +117,10 @@ def mouseReleaseEvent(self, event): else: screen = QPixmap - desktop = QApplication.desktop() - winID = desktop.winId() - if sys.version[0] == "2": - try: - winID = long(winID) - except: - pass - pos = self.mapToGlobal(rect.topLeft()) - try: - self.imgmap = screen.grabWindow( - winID, pos.x(), pos.y(), rect.width(), rect.height() - ) - except: - self.imgmap = screen.grabWindow( - int(winID), pos.x(), pos.y(), rect.width(), rect.height() - ) + self.imgmap = screen.grabWindow( + 0, pos.x(), pos.y(), rect.width(), rect.height() + ) self.close() QWidget.mouseReleaseEvent(self, event) diff --git a/Prism/Scripts/ProjectScripts/DependencyViewer.py b/Prism/Scripts/ProjectScripts/DependencyViewer.py index 23f5f1a4..967ab1e2 100644 --- a/Prism/Scripts/ProjectScripts/DependencyViewer.py +++ b/Prism/Scripts/ProjectScripts/DependencyViewer.py @@ -127,12 +127,13 @@ def rclList(self, listType, pos): else: return - iname = lw.indexAt(pos).data() - + index = lw.indexAt(pos) + iname = index.data() if iname is None: return - dirPath = lw.model().index(lw.indexAt(pos).row(), 4).data() + item = lw.itemFromIndex(index) + dirPath = item.data(4, Qt.DisplayRole) openex = QAction("Open in Explorer", self) openex.triggered.connect(lambda: self.core.openFolder(dirPath)) @@ -252,7 +253,6 @@ def updateDependencies(self, depID, versionInfo, ignore=None): ) item.setForeground(1, existColor) - depItem.addChild(item) curID = str(len(self.dependencies) + 1) diff --git a/Prism/Scripts/ProjectScripts/EditShot.py b/Prism/Scripts/ProjectScripts/EditShot.py index c7f93415..b786f6f1 100644 --- a/Prism/Scripts/ProjectScripts/EditShot.py +++ b/Prism/Scripts/ProjectScripts/EditShot.py @@ -400,8 +400,11 @@ def loadData(self): shotRange = self.core.entities.getShotRange(self.shotData) if shotRange: - self.sp_startFrame.setValue(shotRange[0]) - self.sp_endFrame.setValue(shotRange[1]) + if shotRange[0] is not None: + self.sp_startFrame.setValue(shotRange[0]) + + if shotRange[1] is not None: + self.sp_endFrame.setValue(shotRange[1]) width = self.shotPrvXres height = self.shotPrvYres diff --git a/Prism/Scripts/ProjectScripts/EntityWidget.py b/Prism/Scripts/ProjectScripts/EntityWidget.py index 3e25a491..e08c3ae4 100644 --- a/Prism/Scripts/ProjectScripts/EntityWidget.py +++ b/Prism/Scripts/ProjectScripts/EntityWidget.py @@ -157,7 +157,10 @@ def refreshEntities(self, pages=None, restoreSelection=False, defaultSelection=T if pages and page.objectName() not in pages: continue - page.refreshEntities(restoreSelection=restoreSelection, defaultSelection=defaultSelection) + if page.objectName() == self.getCurrentPageName(): + page.refreshEntities(restoreSelection=restoreSelection, defaultSelection=defaultSelection) + else: + page.dirty = True @err_catcher(name=__name__) def getPage(self, pageName): @@ -191,12 +194,26 @@ def ontabChanged(self, state): if idx != -1: self.getCurrentPage().cb_location.setCurrentIndex(idx) + if widget.dirty: + widget.refreshEntities(restoreSelection=True) + self.tabChanged.emit() self.prevTab = self.getCurrentPage() @err_catcher(name=__name__) def getCurrentData(self, returnOne=True): - return self.getCurrentPage().getCurrentData(returnOne=returnOne) + data = self.getCurrentPage().getCurrentData(returnOne=returnOne) + if not data: + if self.getCurrentPageName() == "Assets": + pageType = "asset" + elif self.getCurrentPageName() == "Shots": + pageType = "shot" + + data = {"type": pageType} + if not returnOne: + data = [data] + + return data @err_catcher(name=__name__) def getLocations(self): @@ -230,8 +247,8 @@ def navigate(self, data, clear=False): page.navigate(data) @err_catcher(name=__name__) - def syncFromWidget(self, widget): - data = widget.getCurrentData() + def syncFromWidget(self, widget, navData=None): + data = navData or widget.getCurrentData() if data: self.navigate(data) else: @@ -265,6 +282,7 @@ def __init__(self, widget, pageName, refresh=True): self.entityPreviewWidth = 107 self.entityPreviewHeight = 60 self.itemWidgets = [] + self.dirty = True self.setObjectName(self.pageName) if pageName == self.core.tr("Assets"): self.entityType = "asset" @@ -295,6 +313,8 @@ def refreshEntities(self, restoreSelection=False, defaultSelection=True): if self.getCurrentData() != prevData: self.onItemChanged() + self.dirty = False + @err_catcher(name=__name__) def setupUi(self): self.e_search = QLineEdit() @@ -514,7 +534,10 @@ def addAssetItem(self, path, itemType, parent=None, refreshItem=True): data = item.data(0, Qt.UserRole) data["paths"].append(path) item.setData(0, Qt.UserRole, data) - refreshItem = False + if parent and parent.isExpanded(): + refreshItem = True + else: + refreshItem = False else: item = QTreeWidgetItem([name, name]) entity = {"asset_path": relPath, "asset": os.path.basename(relPath), "paths": [path], "type": itemType} @@ -603,6 +626,10 @@ def itemExpanded(self, item): if self.entityType == "asset": for childnum in range(item.childCount()): self.refreshAssetItem(item.child(childnum)) + elif self.entityType == "shot": + if self.core.getConfig("browser", "showEntityPreviews", config="user", dft=True): + for childnum in range(item.childCount()): + self.refreshShotThumbnail(item.child(childnum)) @err_catcher(name=__name__) def itemCollapsed(self, item): @@ -621,135 +648,115 @@ def itemCollapsed(self, item): @err_catcher(name=__name__) def refreshShots(self, defaultSelection=True): - wasBlocked = self.tw_tree.signalsBlocked() - if not wasBlocked: - self.tw_tree.blockSignals(True) + with self.core.timeMeasure: + wasBlocked = self.tw_tree.signalsBlocked() + if not wasBlocked: + self.tw_tree.blockSignals(True) - self.tw_tree.clear() + self.tw_tree.clear() - location = self.getCurrentLocation() - if location == "all": - locations = list(self.getLocations().keys()) - else: - locations = [location] + location = self.getCurrentLocation() + if location == "all": + locations = list(self.getLocations().keys()) + else: + locations = [location] - searchFilter = "" - if self.e_search.isVisible(): - searchFilter = self.e_search.text() + searchFilter = "" + if self.e_search.isVisible(): + searchFilter = self.e_search.text() - sequences, shotData = self.core.entities.getShots( - locations=locations, searchFilter=searchFilter - ) + sequences, shotData = self.core.entities.getShots( + locations=locations, searchFilter=searchFilter + ) - shots = {} - for shot in shotData: - seqName = shot["sequence"] - shotName = shot["shot"] - shotPaths = shot["paths"] + shots = {} + for shot in shotData: + seqName = shot["sequence"] + shotName = shot["shot"] + shotPaths = shot["paths"] - if seqName not in shots: - shots[seqName] = {} + if seqName not in shots: + shots[seqName] = {} - if shotName not in shots[seqName]: - shots[seqName][shotName] = [] + if shotName not in shots[seqName]: + shots[seqName][shotName] = [] - for shotPath in shotPaths: - if shotPath not in shots[seqName][shotName]: - shots[seqName][shotName].append(shotPath) + for shotPath in shotPaths: + if shotPath not in shots[seqName][shotName]: + shots[seqName][shotName].append(shotPath) - sequences = self.core.sortNatural(shots.keys()) + sequences = self.core.sortNatural(shots.keys()) - iconPath = os.path.join( - self.core.prismRoot, "Scripts", "UserInterfacesPrism", "folder.png" - ) - folderIcon = self.core.media.getColoredIcon(iconPath) - - iconPath = os.path.join( - self.core.prismRoot, "Scripts", "UserInterfacesPrism", "sequence.png" - ) - seqIcon = self.core.media.getColoredIcon(iconPath) + iconPath = os.path.join( + self.core.prismRoot, "Scripts", "UserInterfacesPrism", "folder.png" + ) + folderIcon = self.core.media.getColoredIcon(iconPath) - iconPath = os.path.join( - self.core.prismRoot, "Scripts", "UserInterfacesPrism", "shot.png" - ) - shotIcon = self.core.media.getColoredIcon(iconPath) - usePreview = self.core.getConfig("browser", "showEntityPreviews", config="user", dft=True) - - seqItems = {} - for sequence in sequences: - parent = self.tw_tree.invisibleRootItem() - seqName = "" - seqParts = sequence.split("__") if os.getenv("PRISM_USE_SEQUENCE_FOLDERS") == "1" else [sequence] - for idx, seqPart in enumerate(seqParts): - seqName = (seqName + "/" + seqPart).strip("/") - if seqName in seqItems: - item = seqItems[seqName] - else: - icon = seqIcon if idx == (len(seqParts) - 1) else folderIcon - item = self.addSequenceItem(seqPart, parent=parent, icon=icon) - seqItems[seqName] = item - - parent = item - - seqItem = parent - for shot in self.core.sortNatural(shots[sequence]): - data = [shot] - sItem = QTreeWidgetItem(data) - entity = { - "type": "shot", - "sequence": sequence, - "shot": shot, - "paths": shots[sequence][shot], - } - sItem.setData( - 0, - Qt.UserRole, - entity, - ) - seqItem.addChild(sItem) - showIcon = True - if usePreview: - pm = self.core.entities.getEntityPreview(entity) - if not pm: - pm = self.core.media.emptyPrvPixmap - - w_entity = QWidget() - w_entity.setStyleSheet("background-color: transparent;") - lo_entity = QHBoxLayout() - lo_entity.setContentsMargins(0, 0, 0, 0) - w_entity.setLayout(lo_entity) - l_preview = QLabel() - l_label = QLabel(shot) - lo_entity.addWidget(l_preview) - lo_entity.addWidget(l_label) - lo_entity.addStretch() - if pm: - pmap = self.core.media.scalePixmap(pm, self.entityPreviewWidth, self.entityPreviewHeight, fitIntoBounds=False, crop=True) - l_preview.setPixmap(pmap) - - self.tw_tree.setItemWidget(sItem, 0, w_entity) - self.itemWidgets.append(w_entity) - showIcon = False - sItem.setText(0, "") + iconPath = os.path.join( + self.core.prismRoot, "Scripts", "UserInterfacesPrism", "sequence.png" + ) + seqIcon = self.core.media.getColoredIcon(iconPath) - if showIcon: - sItem.setIcon(0, shotIcon) + iconPath = os.path.join( + self.core.prismRoot, "Scripts", "UserInterfacesPrism", "shot.png" + ) + shotIcon = self.core.media.getColoredIcon(iconPath) + usePreview = self.core.getConfig("browser", "showEntityPreviews", config="user", dft=True) - self.tw_tree.resizeColumnToContents(0) - if defaultSelection and self.tw_tree.topLevelItemCount() > 0: - if self.tw_tree.topLevelItem(0).isExpanded(): - self.tw_tree.setCurrentItem(self.tw_tree.topLevelItem(0).child(0)) - else: - self.tw_tree.setCurrentItem(self.tw_tree.topLevelItem(0)) + seqItems = {} + for sequence in sequences: + parent = self.tw_tree.invisibleRootItem() + seqName = "" + seqParts = sequence.split("__") if os.getenv("PRISM_USE_SEQUENCE_FOLDERS") == "1" else [sequence] + for idx, seqPart in enumerate(seqParts): + seqName = (seqName + "/" + seqPart).strip("/") + if seqName in seqItems: + item = seqItems[seqName] + else: + icon = seqIcon if idx == (len(seqParts) - 1) else folderIcon + item = self.addSequenceItem(seqPart, parent=parent, icon=icon, hierarchy=seqName) + seqItems[seqName] = item + + parent = item + + seqItem = parent + for shot in self.core.sortNatural(shots[sequence]): + data = [shot] + sItem = QTreeWidgetItem(data) + entity = { + "type": "shot", + "sequence": sequence, + "shot": shot, + "paths": shots[sequence][shot], + } + sItem.setData( + 0, + Qt.UserRole, + entity, + ) + seqItem.addChild(sItem) + showIcon = True + if usePreview: + showIcon = False + + if showIcon: + sItem.setIcon(0, shotIcon) + + self.tw_tree.resizeColumnToContents(0) + if defaultSelection and self.tw_tree.topLevelItemCount() > 0: + if self.tw_tree.topLevelItem(0).isExpanded(): + self.tw_tree.setCurrentItem(self.tw_tree.topLevelItem(0).child(0)) + else: + self.tw_tree.setCurrentItem(self.tw_tree.topLevelItem(0)) - if not wasBlocked: - self.tw_tree.blockSignals(False) - self.itemChanged.emit(self.tw_tree.currentItem()) + if not wasBlocked: + self.tw_tree.blockSignals(False) + self.itemChanged.emit(self.tw_tree.currentItem()) @err_catcher(name=__name__) - def addSequenceItem(self, seqName, parent, icon): + def addSequenceItem(self, seqName, parent, icon, hierarchy): seqItem = QTreeWidgetItem([seqName]) - seqItem.setData(0, Qt.UserRole, {"type": "shot", "sequence": seqName, "shot": "_sequence"}) + seqItem.setData(0, Qt.UserRole, {"type": "shot", "sequence": seqName, "shot": "_sequence", "hierarchy": hierarchy}) seqItem.setIcon(0, icon) parent.addChild(seqItem) if seqName in self.expandedItems or ( @@ -759,6 +766,37 @@ def addSequenceItem(self, seqName, parent, icon): return seqItem + @err_catcher(name=__name__) + def refreshShotThumbnail(self, item): + if self.tw_tree.itemWidget(item, 0): + return + + if item.childCount(): + return + + entity = item.data(0, Qt.UserRole) + pm = self.core.entities.getEntityPreview(entity) + if not pm: + pm = self.core.media.emptyPrvPixmap + + w_entity = QWidget() + w_entity.setStyleSheet("background-color: transparent;") + lo_entity = QHBoxLayout() + lo_entity.setContentsMargins(0, 0, 0, 0) + w_entity.setLayout(lo_entity) + l_preview = QLabel() + l_label = QLabel(entity.get("shot")) + lo_entity.addWidget(l_preview) + lo_entity.addWidget(l_label) + lo_entity.addStretch() + if pm: + pmap = self.core.media.scalePixmap(pm, self.entityPreviewWidth, self.entityPreviewHeight, fitIntoBounds=False, crop=True) + l_preview.setPixmap(pmap) + + self.tw_tree.setItemWidget(item, 0, w_entity) + self.itemWidgets.append(w_entity) + item.setText(0, "") + @err_catcher(name=__name__) def omitEntity(self, entity): if entity["type"] in ["asset", "assetFolder"]: @@ -1059,7 +1097,7 @@ def editShotDlg(self, shotData=None): if not isinstance(sData, dict): return - if sData: + if sData and sData.get("sequence"): shotData = {"sequence": sData["sequence"]} if hasattr(self, "es") and self.core.isObjectValid(self.es): @@ -1109,12 +1147,12 @@ def getCurrentData(self, returnOne=True): for item in items: data = self.getDataFromItem(item) curData.append(data) - + if returnOne: if curData: curData = curData[0] else: - curData = {} + curData = {"type": self.entityType} return curData @@ -1141,7 +1179,7 @@ def getTopLevelItemNames(self): @err_catcher(name=__name__) def navigate(self, data): - prevData = self.getCurrentData() + prevData = self.getCurrentData(returnOne=False) wasBlocked = self.tw_tree.signalsBlocked() if not wasBlocked: self.tw_tree.blockSignals(True) @@ -1153,6 +1191,9 @@ def navigate(self, data): hItem = None for asset in data: + if self.core.isStr(asset): + continue + itemPath = asset.get("asset_path", "") hierarchy = itemPath.replace("\\", "/").split("/") hierarchy = [x for x in hierarchy if x != ""] @@ -1193,42 +1234,38 @@ def navigate(self, data): sItem = None for shot in data: - for idx in range(self.tw_tree.topLevelItemCount()): - csItem = self.tw_tree.topLevelItem(idx) - seqParts = shot.get("sequence", "").split("__") if os.getenv("PRISM_USE_SEQUENCE_FOLDERS") == "1" else [shot.get("sequence")] - if len(seqParts) > 1 and csItem.data(0, Qt.UserRole).get("sequence") == seqParts[0]: - csItem.setExpanded(True) - for fIdx in range(csItem.childCount()): - seqItem = csItem.child(fIdx) - if seqItem.data(0, Qt.UserRole).get("sequence") == seqParts[1]: - if not shot.get("shot"): - seqItem.setSelected(True) - sItem = seqItem - else: - seqItem.setExpanded(True) - for childIdx in range(seqItem.childCount()): - shotItem = seqItem.child(childIdx) - if shotItem.data(0, Qt.UserRole).get("shot") == shot["shot"]: - shotItem.setSelected(True) - break - else: - seqItem.setSelected(True) - sItem = seqItem + if os.getenv("PRISM_USE_SEQUENCE_FOLDERS") == "1": + if "hierarchy" in shot: + seqParts = shot.get("hierarchy").split("/") + else: + seqParts = shot.get("sequence", "").split("__") + else: + seqParts = [shot.get("sequence")] + + shot = shot.get("shot", "") + if shot and shot != "_sequence": + seqParts.append(shot) + + hItem = self.tw_tree.invisibleRootItem() + for sidx, seqPart in enumerate(seqParts): + for idx in range(hItem.childCount()-1, -1, -1): + cItem = hItem.child(idx) + if cItem.childCount(): + cItemName = cItem.data(0, Qt.UserRole)["sequence"] + else: + cItemName = cItem.data(0, Qt.UserRole)["shot"] + + if cItemName == seqPart: + hItem = cItem + if len(seqParts) > (sidx + 1): + hItem.setExpanded(True) + self.itemExpanded(hItem) + + hItem.setSelected(True) + sItem = hItem + break else: - if csItem.data(0, Qt.UserRole).get("sequence") == shot.get("sequence"): - if not shot.get("shot"): - csItem.setSelected(True) - sItem = csItem - else: - csItem.setExpanded(True) - for childIdx in range(csItem.childCount()): - shotItem = csItem.child(childIdx) - if shotItem.data(0, Qt.UserRole).get("shot") == shot["shot"]: - shotItem.setSelected(True) - break - else: - csItem.setSelected(True) - sItem = csItem + break if sItem: self.tw_tree.setCurrentItem(sItem) @@ -1236,7 +1273,7 @@ def navigate(self, data): if not wasBlocked: self.tw_tree.blockSignals(False) - if self.getCurrentData() != prevData: + if self.getCurrentData(returnOne=False) != prevData: self.onItemChanged() @err_catcher(name=__name__) diff --git a/Prism/Scripts/ProjectScripts/ExternalTask.py b/Prism/Scripts/ProjectScripts/ExternalTask.py deleted file mode 100644 index f10eb57a..00000000 --- a/Prism/Scripts/ProjectScripts/ExternalTask.py +++ /dev/null @@ -1,112 +0,0 @@ -# -*- coding: utf-8 -*- -# -#################################################### -# -# PRISM - Pipeline for animation and VFX projects -# -# www.prism-pipeline.com -# -# contact: contact@prism-pipeline.com -# -#################################################### -# -# -# Copyright (C) 2016-2023 Richard Frangenberg -# Copyright (C) 2023 Prism Software GmbH -# -# Licensed under GNU LGPL-3.0-or-later -# -# This file is part of Prism. -# -# Prism is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Prism is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Prism. If not, see . - - -import os -import sys - -from qtpy.QtCore import * -from qtpy.QtGui import * -from qtpy.QtWidgets import * - -from UserInterfaces import ExternalTask_ui - - -class ExternalTask(QDialog, ExternalTask_ui.Ui_dlg_ExternalTask): - def __init__(self, core, startText=""): - QDialog.__init__(self) - self.setupUi(self) - self.core = core - - self.core.parentWindow(self) - - self.e_taskPath.setText(startText) - self.e_versionName.setText("v0001") - - self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) - - self.connectEvents() - - def connectEvents(self): - self.b_browseFolder.clicked.connect(self.browseFolder) - self.b_browseFile.clicked.connect(self.browseFile) - self.b_browseFolder.customContextMenuRequested.connect(self.openFolder) - self.b_browseFile.customContextMenuRequested.connect(self.openFolder) - self.e_taskPath.textChanged.connect(lambda x: self.enableOk(x, self.e_taskPath)) - self.e_taskName.textChanged.connect(lambda x: self.enableOk(x, self.e_taskName)) - self.e_versionName.textChanged.connect( - lambda x: self.enableOk(x, self.e_versionName) - ) - - def browseFolder(self): - if self.e_taskPath.text() == "": - startpath = self.core.projectPath - else: - startpath = self.e_taskPath.text() - - selectedPath = QFileDialog.getExistingDirectory( - self, "Select external folder", startpath - ) - - if selectedPath != "": - self.e_taskPath.setText(selectedPath.replace("\\", "/")) - - def browseFile(self): - if self.e_taskPath.text() == "": - startpath = self.core.projectPath - else: - startpath = self.e_taskPath.text() - - selectedFile = QFileDialog.getOpenFileName( - self, "Select external file", startpath - )[0] - - if selectedFile != "": - self.e_taskPath.setText(selectedFile.replace("\\", "/")) - - def openFolder(self): - path = self.e_taskPath.text() - self.core.openFolder(path) - - def enableOk(self, origText, editWidget): - if editWidget != self.e_taskPath: - self.core.validateLineEdit(editWidget) - - if ( - self.e_taskPath.text() != "" - and self.e_taskName.text() != "" - and self.e_versionName.text() != "" - ): - self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) - else: - self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) diff --git a/Prism/Scripts/ProjectScripts/ItemList.py b/Prism/Scripts/ProjectScripts/ItemList.py index d151b9d8..720e6c1b 100644 --- a/Prism/Scripts/ProjectScripts/ItemList.py +++ b/Prism/Scripts/ProjectScripts/ItemList.py @@ -56,12 +56,12 @@ class ItemList(QDialog, ItemList_ui.Ui_dlg_ItemList): - def __init__(self, core, entity="passes", mode=None): + def __init__(self, core, entities=None, mode="passes"): QDialog.__init__(self) self.setupUi(self) self.core = core - self.entity = entity + self.entities = entities self.mode = mode self.tw_steps.setColumnCount(2) @@ -69,14 +69,12 @@ def __init__(self, core, entity="passes", mode=None): self.tw_steps.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) if ( - not isinstance(self.entity, collections.Mapping) - or self.entity["type"] not in ["asset", "shot", "sequence"] + mode != "departments" or ( - self.entity["type"] == "asset" + self.entities[0]["type"] == "asset" and self.core.compareVersions(self.core.projectVersion, "v1.2.1.6") == "lower" ) - or mode is not None ): self.chb_category.setVisible(False) else: @@ -86,11 +84,7 @@ def __init__(self, core, entity="passes", mode=None): self.buttonBox.clicked.connect(self.buttonboxClicked) self.btext = "Next" - if isinstance(self.entity, collections.Mapping) and self.entity["type"] in [ - "asset", - "shot", - "sequence" - ] and (self.mode != "tasks" or self.core.appPlugin.pluginName != "Standalone"): + if self.entities and (self.mode == "departments" or self.core.appPlugin.pluginName != "Standalone"): self.b_next = self.buttonBox.addButton(self.btext, QDialogButtonBox.AcceptRole) if self.mode == "tasks": toolTip = "Create tasks and create a new scene from the current scene" @@ -105,7 +99,7 @@ def __init__(self, core, entity="passes", mode=None): icon = self.core.media.getColoredIcon(iconPath) self.b_next.setIcon(icon) - if self.mode is None: + if self.mode == "departments": self.w_taskPreset = QWidget() self.lo_taskPreset = QHBoxLayout(self.w_taskPreset) self.lo_taskPreset.setContentsMargins(0, 0, 0, 0) @@ -119,7 +113,7 @@ def __init__(self, core, entity="passes", mode=None): self.chb_taskPreset.toggled.connect(lambda state: self.tw_steps.setEnabled(not state)) self.chb_taskPreset.toggled.connect(lambda state: self.chb_category.setEnabled(not state)) self.chb_taskPreset.toggled.connect(lambda state: self.enableOk()) - if self.entity["type"] in ["asset"]: + if self.entities[0]["type"] in ["asset"]: presets = self.core.projects.getAssetTaskPresets() else: presets = self.core.projects.getShotTaskPresets() @@ -152,14 +146,10 @@ def __init__(self, core, entity="passes", mode=None): @err_catcher(name=__name__) def rclList(self, pos): - if isinstance(self.entity, collections.Mapping) and self.entity["type"] in [ - "asset", - "shot", - "sequence", - ]: + if self.entities: rcmenu = QMenu(self) - if self.mode is None: + if self.mode == "departments": exp = QAction("Create new department...", self) exp.triggered.connect(self.createDepartment) rcmenu.addAction(exp) @@ -177,17 +167,13 @@ def rclList(self, pos): @err_catcher(name=__name__) def selectionChanged(self): self.enableOk() - if isinstance(self.entity, collections.Mapping) and self.entity["type"] in [ - "asset", - "shot", - "sequence", - ] and self.mode is None: + if self.mode == "departments": items = self.tw_steps.selectedItems() if len(items) == 0: txt = "Create default tasks" elif len(items) == 1: abbr = items[0].data(Qt.UserRole)["abbreviation"] - defaultTasks = self.core.entities.getDefaultTasksForDepartment(self.entity["type"], abbr) + defaultTasks = self.core.entities.getDefaultTasksForDepartment(self.entities[0]["type"], abbr) if defaultTasks: txt = "Create default tasks: \"%s\"" % "\", \"".join(defaultTasks) else: @@ -207,11 +193,7 @@ def enableOk(self): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enabled) if self.core.appPlugin.pluginName != "Standalone" or self.mode != "tasks": - if isinstance(self.entity, collections.Mapping) and self.entity["type"] in [ - "asset", - "shot", - "sequence", - ] and self.mode is None: + if self.mode == "departments": nextEnabled = len([x for x in self.tw_steps.selectedItems() if x.column() == 0]) == 1 and not self.getTaskPreset() self.b_next.setEnabled(nextEnabled) elif self.mode == "tasks": @@ -219,17 +201,10 @@ def enableOk(self): @err_catcher(name=__name__) def getTaskPreset(self): - if not isinstance(self.entity, collections.Mapping): + if not self.entities: return - if self.entity["type"] not in [ - "asset", - "shot", - "sequence", - ]: - return - - if self.mode is not None: + if self.mode != "departments": return if not self.chb_taskPreset.isChecked(): @@ -239,7 +214,7 @@ def getTaskPreset(self): @err_catcher(name=__name__) def createDepartment(self): - self.dlg_department = PrismWidgets.CreateDepartmentDlg(core=self.core, entity=self.entity["type"], parent=self) + self.dlg_department = PrismWidgets.CreateDepartmentDlg(core=self.core, entity=self.entities[0]["type"], parent=self) self.dlg_department.departmentCreated.connect(self.onDepartmentCreated) self.dlg_department.exec_() @@ -252,9 +227,9 @@ def manageDepartments(self): def manageTasks(self): self.close() dlg = self.core.prismSettings(tab="Departments", settingsType="Project") - entity = self.core.pb.sceneBrowser.getCurrentEntity() + entities = self.core.pb.sceneBrowser.getCurrentEntities() dep = self.core.pb.sceneBrowser.getCurrentDepartment() - if entity.get("type") == "asset": + if entities[0].get("type") == "asset": for idx in range(dlg.w_project.tw_assetDepartments.rowCount()): itemDep = dlg.w_project.tw_assetDepartments.item(idx, 0).data(Qt.UserRole).get("abbreviation") if itemDep == dep: @@ -264,7 +239,7 @@ def manageTasks(self): return dlg.w_project.tw_assetDepartments.selectRow(row) - elif entity.get("type") in ["shot", "sequence"]: + elif entities[0].get("type") in ["shot", "sequence"]: for idx in range(dlg.w_project.tw_shotDepartments.rowCount()): itemDep = dlg.w_project.tw_shotDepartments.item(idx, 0).data(Qt.UserRole).get("abbreviation") if itemDep == dep: @@ -288,23 +263,28 @@ def onDepartmentCreated(self, department): @err_catcher(name=__name__) def buttonboxClicked(self, button): if button.text() == "Create": - if self.mode is None: - if self.entity == "passes": - pass + if self.mode == "passes": + pass + elif self.mode == "departments": + preset = self.getTaskPreset() + if preset: + createdDirs = False + for entity in self.entities: + result = self.core.entities.createTasksFromPreset(entity, preset) + if result: + createdDirs = True + + if createdDirs: + self.core.pb.sceneBrowser.refreshDepartments() + else: - preset = self.getTaskPreset() - if preset: - createdDirs = self.core.entities.createTasksFromPreset(self.entity, preset) - if createdDirs: - self.core.pb.sceneBrowser.refreshDepartments() + steps = [] + for i in self.tw_steps.selectedItems(): + if i.column() == 0: + steps.append(i.data(Qt.UserRole)["abbreviation"]) - else: - steps = [] - for i in self.tw_steps.selectedItems(): - if i.column() == 0: - steps.append(i.data(Qt.UserRole)["abbreviation"]) + self.core.pb.sceneBrowser.createSteps(self.entities, steps, createTask=self.chb_category.isChecked()) - self.core.pb.sceneBrowser.createSteps(self.entity, steps, createTask=self.chb_category.isChecked()) elif self.mode == "tasks": tasks = [] for item in self.tw_steps.selectedItems(): @@ -316,11 +296,7 @@ def buttonboxClicked(self, button): self.accept() elif button.text() == self.btext: - if isinstance(self.entity, collections.Mapping) and self.entity["type"] in [ - "asset", - "shot", - "sequence" - ]: + if self.entities: self.stepBbClicked(button) self.accept() @@ -330,12 +306,12 @@ def buttonboxClicked(self, button): @err_catcher(name=__name__) def stepBbClicked(self, button): if button.text() == self.btext: - if self.mode is None: + if self.mode == "departments": for i in self.tw_steps.selectedItems(): if i.column() == 0: step = i.data(Qt.UserRole)["abbreviation"] - self.core.pb.sceneBrowser.createSteps(self.entity, [step], createTask=False) + self.core.pb.sceneBrowser.createSteps(self.entities, [step], createTask=False) self.close() self.core.pb.sceneBrowser.createTaskDlg() elif self.mode == "tasks": @@ -355,9 +331,7 @@ def stepBbClicked(self, button): def keyPressEvent(self, event): if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: if self.tw_steps.selectedItems(): - if isinstance(self.entity, collections.Mapping) and self.entity[ - "type" - ] in ["asset", "shot", "sequence"]: + if self.entities: if self.core.appPlugin.pluginName != "Standalone": self.stepBbClicked(self.b_next) else: diff --git a/Prism/Scripts/ProjectScripts/MediaBrowser.py b/Prism/Scripts/ProjectScripts/MediaBrowser.py index 526ad9b8..7ecb5779 100644 --- a/Prism/Scripts/ProjectScripts/MediaBrowser.py +++ b/Prism/Scripts/ProjectScripts/MediaBrowser.py @@ -50,8 +50,7 @@ from qtpy.QtGui import * from qtpy.QtWidgets import * -import ExternalTask -from PrismUtils import PrismWidgets +from PrismUtils import PrismWidgets, ProjectWidgets from PrismUtils.Decorators import err_catcher from UserInterfaces import MediaBrowser_ui @@ -86,19 +85,29 @@ def __init__(self, core, projectBrowser=None, refresh=True): @err_catcher(name=__name__) def entered(self, prevTab=None, navData=None): + if prevTab: + if hasattr(prevTab, "w_entities"): + navData = prevTab.w_entities.getCurrentData() + elif hasattr(prevTab, "getSelectedData"): + navData = prevTab.getSelectedData() + if not self.initialized: + if not navData: + navData = self.getCurrentNavData() + self.w_entities.getPage("Assets").blockSignals(True) self.w_entities.getPage("Shots").blockSignals(True) + self.w_entities.tb_entities.blockSignals(True) self.w_entities.blockSignals(True) + self.w_entities.navigate(navData) self.w_entities.refreshEntities(defaultSelection=False) self.w_entities.getPage("Assets").blockSignals(False) self.w_entities.getPage("Shots").blockSignals(False) + self.w_entities.tb_entities.blockSignals(False) self.w_entities.blockSignals(False) self.oiio = self.core.media.getOIIO() if navData: self.navigate(navData) - else: - self.navigateToCurrent() if not self.getCurrentEntity(): self.entityChanged() @@ -107,16 +116,16 @@ def entered(self, prevTab=None, navData=None): if prevTab: if hasattr(prevTab, "w_entities"): - self.w_entities.syncFromWidget(prevTab.w_entities) + self.w_entities.syncFromWidget(prevTab.w_entities, navData=navData) elif hasattr(prevTab, "getSelectedData"): - self.navigateToEntity(prevTab.getSelectedData()) + self.navigateToEntity(navData) @err_catcher(name=__name__) def loadLayout(self): import EntityWidget self.w_entities = EntityWidget.EntityWidget(core=self.core, refresh=False, mode="media") - self.splitter.insertWidget(0, self.w_entities) + self.splitter1.insertWidget(0, self.w_entities) self.w_autoUpdate.setVisible(False) @@ -148,13 +157,25 @@ def loadLayout(self): self.w_preview = MediaVersionPlayer(self) self.w_preview.layout().addStretch() - self.splitter.addWidget(self.w_preview) + self.splitter1.addWidget(self.w_preview) + + if self.projectBrowser and self.projectBrowser.act_rememberWidgetSizes.isChecked(): + if "mediaSplitter1" in brsData: + self.splitter1.setSizes(brsData["mediaSplitter1"]) self.tw_identifier.setAcceptDrops(True) self.tw_identifier.dragEnterEvent = self.taskDragEnterEvent self.tw_identifier.dragMoveEvent = self.taskDragMoveEvent self.tw_identifier.dragLeaveEvent = self.taskDragLeaveEvent self.tw_identifier.dropEvent = self.taskDropEvent + self.tw_identifier.setObjectName("tw_identifier") + + self.lw_version.setAcceptDrops(True) + self.lw_version.dragEnterEvent = self.versionDragEnterEvent + self.lw_version.dragMoveEvent = self.versionDragMoveEvent + self.lw_version.dragLeaveEvent = self.versionDragLeaveEvent + self.lw_version.dropEvent = self.versionDropEvent + self.lw_version.setObjectName("lw_version") if self.projectBrowser and len(self.projectBrowser.locations) > 1: self.VersionDelegate = VersionDelegate(self) @@ -206,6 +227,7 @@ def connectEvents(self): def saveSettings(self, data): data["browser"]["autoUpdateRenders"] = self.chb_autoUpdate.isChecked() data["browser"]["previewDisabled"] = self.w_preview.mediaPlayer.state == "disabled" + data["browser"]["mediaSplitter1"] = self.splitter1.sizes() @err_catcher(name=__name__) def updateChanged(self, state): @@ -255,19 +277,17 @@ def getCurrentData(self): else: source = "" - curData = [ - self.getCurrentEntity(), - identifier, - version, - aov, - source, - ] + curData = self.getCurrentEntity() + curData["identifier"] = identifier + curData["version"] = version + curData["aov"] = aov + curData["source"] = source return curData @err_catcher(name=__name__) def refreshRender(self): curData = self.getCurrentData() - self.showRender(*curData) + self.navigate(curData) @err_catcher(name=__name__) def getCurrentEntity(self): @@ -346,7 +366,7 @@ def updateTasks(self, restoreSelection=False): mediaTasks = self.getMediaTasks() if mediaTasks: - useTasks = self.core.getConfig("globals", "productTasks", config="project") + useTasks = self.core.mediaProducts.getLinkedToTasks() if useTasks: items = {} for pType in ["3d", "2d", "playblast", "external"]: @@ -694,10 +714,15 @@ def getSelectedContext(self): return self.getCurrentData() @err_catcher(name=__name__) - def navigateToCurrent(self): + def getCurrentNavData(self): fileName = self.core.getCurrentFileName() - fileNameData = self.core.getScenefileData(fileName) - self.showRender(entity=fileNameData) + navData = self.core.getScenefileData(fileName) + return navData + + @err_catcher(name=__name__) + def navigateToCurrent(self): + navData = self.getCurrentNavData() + self.showRender(entity=navData) @err_catcher(name=__name__) def navigate(self, data): @@ -705,7 +730,7 @@ def navigate(self, data): self.showRender(*data) else: self.showRender( - entity=data.get("entity"), + entity=data, identifier=data.get("identifier"), version=data.get("version"), aov=data.get("aov"), @@ -763,6 +788,7 @@ def showRender(self, entity=None, identifier=None, version=None, aov=None, sourc return + self.lw_version.clearSelection() self.lw_version.setCurrentItem(vMatches[0]) self.lw_version.blockSignals(False) if prevVersion != self.getCurrentVersion(): @@ -823,8 +849,8 @@ def rclList(self, pos, lw): depAct.triggered.connect(self.createIdentifierDlg) rcmenu.addAction(depAct) - exAct = QAction("Add external media", self) - exAct.triggered.connect(self.createExternalTask) + exAct = QAction("Ingest media...", self) + exAct.triggered.connect(self.ingestMediaDlg) rcmenu.addAction(exAct) elif lw == self.lw_version: @@ -836,7 +862,7 @@ def rclList(self, pos, lw): rcmenu.addAction(depAct) if identifier["mediaType"] == "externalMedia": - nvAct = QAction("Create new external version", self) + nvAct = QAction("Create new External Version...", self) nvAct.triggered.connect(self.newExternalVersion) rcmenu.addAction(nvAct) @@ -914,19 +940,20 @@ def rclList(self, pos, lw): copAct.triggered.connect(self.prepareNewVersion) rcmenu.addAction(copAct) - curLoc = self.core.mediaProducts.getLocationFromPath(path) - locMenu = QMenu("Copy to", self) - locs = self.core.paths.getRenderProductBasePaths() - for loc in locs: - if loc == curLoc: - continue + if itemName: + existingLocs = list(data.get("locations", {}).keys()) + locMenu = QMenu("Copy to", self) + locs = self.core.paths.getRenderProductBasePaths() + for loc in locs: + if loc in existingLocs: + continue - copAct = QAction(loc, self) - copAct.triggered.connect(lambda x=None, l=loc: self.copyToLocation(path, l)) - locMenu.addAction(copAct) + copAct = QAction(loc, self) + copAct.triggered.connect(lambda x=None, l=loc: self.copyToLocation(path, l)) + locMenu.addAction(copAct) - if not locMenu.isEmpty(): - rcmenu.addMenu(locMenu) + if not locMenu.isEmpty(): + rcmenu.addMenu(locMenu) self.core.callback( name="openPBListContextMenu", @@ -1001,34 +1028,17 @@ def copyToLocation(self, path, location): @err_catcher(name=__name__) def createIdentifierDlg(self): - self.newItem = PrismWidgets.CreateItem( - core=self.core, showType=False, mode="identifier" - ) - self.newItem.setModal(True) - self.core.parentWindow(self.newItem, parent=self) - self.newItem.e_item.setFocus() - self.newItem.setWindowTitle("Create Identifier") - self.newItem.l_item.setText("Identifier:") + curEntity = self.getCurrentEntity() + self.newItem = ProjectWidgets.CreateIdentifierDlg(self, entity=curEntity) + self.newItem.e_identifier.setFocus() self.newItem.accepted.connect(self.createIdentifier) - self.newItem.w_mediaType = QWidget() - self.newItem.l_mediaType = QLabel("Type:") - self.newItem.cb_mediaType = QComboBox() - self.newItem.lo_mediaType = QHBoxLayout() - self.newItem.lo_mediaType.setContentsMargins(0, 0, 0, 0) - self.newItem.w_mediaType.setLayout(self.newItem.lo_mediaType) - self.newItem.lo_mediaType.addWidget(self.newItem.l_mediaType) - self.newItem.lo_mediaType.addWidget(self.newItem.cb_mediaType) - self.newItem.cb_mediaType.addItems(["3D", "2D", "Playblast"]) - self.newItem.verticalLayout.insertWidget(2, self.newItem.w_mediaType) - self.core.callback(name="onCreateIdentifierDlgOpen", args=[self, self.newItem]) - self.newItem.show() @err_catcher(name=__name__) def createIdentifier(self): self.activateWindow() - itemName = self.newItem.e_item.text() + itemName = self.newItem.e_identifier.text() curEntity = self.getCurrentEntity() mediaTypeLabel = self.newItem.cb_mediaType.currentText() suffix = "" @@ -1040,12 +1050,21 @@ def createIdentifier(self): elif mediaTypeLabel == "Playblast": mediaType = "playblasts" suffix = " (playblast)" + elif mediaTypeLabel == "External": + mediaType = "externalMedia" + suffix = " (external)" - if self.core.getConfig("globals", "productTasks", config="project"): - curEntity["department"] = "unknown" - curEntity["task"] = "unknown" + if self.core.mediaProducts.getLinkedToTasks(): + curEntity["department"] = self.newItem.e_department.text() or "unknown" + curEntity["task"] = self.newItem.e_task.text() or "unknown" - self.core.mediaProducts.createIdentifier(entity=curEntity, identifier=itemName, identifierType=mediaType) + location = self.newItem.cb_location.currentText() + self.core.mediaProducts.createIdentifier( + entity=curEntity, + identifier=itemName, + identifierType=mediaType, + location=location, + ) self.updateTasks() if itemName is not None: matches = self.tw_identifier.findItems( @@ -1056,18 +1075,18 @@ def createIdentifier(self): @err_catcher(name=__name__) def createVersionDlg(self): - identifier = self.getCurrentIdentifier() - context = identifier.copy() - + context = self.getCurrentIdentifier() version = self.core.mediaProducts.getHighestMediaVersion(context) - self.newItem = PrismWidgets.CreateItem( - core=self.core, showType=False, mode="version", startText=version - ) - self.newItem.setModal(True) - self.core.parentWindow(self.newItem) - self.newItem.e_item.setFocus() - self.newItem.setWindowTitle("Create Version") - self.newItem.l_item.setText("Version:") + intVersion = self.core.products.getIntVersionFromVersionName(version) + self.newItem = ProjectWidgets.CreateMediaVersionDlg(self, entity=context) + if intVersion is not None: + self.newItem.sp_version.setValue(intVersion) + + location = self.core.mediaProducts.getLocationFromPath(context["path"]) + if location: + self.newItem.cb_location.setCurrentText(location) + + self.newItem.sp_version.setFocus() self.newItem.accepted.connect(self.createVersion) self.core.callback(name="onCreateVersionDlgOpen", args=[self, self.newItem]) self.newItem.show() @@ -1075,10 +1094,11 @@ def createVersionDlg(self): @err_catcher(name=__name__) def createVersion(self): self.activateWindow() - itemName = self.newItem.e_item.text() + versionName = self.core.versionFormat % self.newItem.sp_version.value() curEntity = self.getCurrentEntity() identifier = self.getCurrentIdentifier() - if self.core.getConfig("globals", "productTasks", config="project"): + location = self.newItem.cb_location.currentText() + if self.core.mediaProducts.getLinkedToTasks(): curEntity["department"] = identifier.get("department", "unknown") curEntity["task"] = identifier.get("task", "unknown") @@ -1086,12 +1106,13 @@ def createVersion(self): entity=curEntity, identifier=identifier["identifier"], identifierType=identifier["mediaType"], - version=itemName + version=versionName, + location=location, ) self.updateVersions() - if itemName is not None: + if versionName is not None: matches = self.lw_version.findItems( - itemName, Qt.MatchFlag(Qt.MatchExactly & Qt.MatchCaseSensitive) + versionName, Qt.MatchFlag(Qt.MatchExactly & Qt.MatchCaseSensitive) ) if matches: self.lw_version.setCurrentItem(matches[0]) @@ -1120,7 +1141,7 @@ def taskDragMoveEvent(self, e): if e.mimeData().hasUrls(): e.accept() self.tw_identifier.setStyleSheet( - "QWidget { border-style: dashed; border-color: rgb(100, 200, 100); border-width: 2px; }" + "QWidget#tw_identifier { border-style: dashed; border-color: rgb(100, 200, 100); border-width: 2px; }" ) else: e.ignore() @@ -1137,28 +1158,69 @@ def taskDropEvent(self, e): e.accept() if not self.getCurrentEntity(): - self.core.popup("Select an asset or a shot to add external media.") + self.core.popup("Select an asset or a shot to ingest media.") + return + + fname = [ + os.path.normpath(str(url.toLocalFile())) for url in e.mimeData().urls() + ] + self.ingestMediaDlg(filepath="\n".join(fname)) + else: + e.ignore() + + @err_catcher(name=__name__) + def versionDragEnterEvent(self, e): + if e.mimeData().hasUrls(): + e.accept() + else: + e.ignore() + + @err_catcher(name=__name__) + def versionDragMoveEvent(self, e): + if e.mimeData().hasUrls(): + e.accept() + self.lw_version.setStyleSheet( + "QWidget#lw_version { border-style: dashed; border-color: rgb(100, 200, 100); border-width: 2px; }" + ) + else: + e.ignore() + + @err_catcher(name=__name__) + def versionDragLeaveEvent(self, e): + self.lw_version.setStyleSheet("") + + @err_catcher(name=__name__) + def versionDropEvent(self, e): + if e.mimeData().hasUrls(): + self.lw_version.setStyleSheet("") + e.setDropAction(Qt.LinkAction) + e.accept() + + if not self.getCurrentEntity(): + self.core.popup("Select an asset or a shot to ingest media.") return fname = [ os.path.normpath(str(url.toLocalFile())) for url in e.mimeData().urls() ] - self.createExternalTask(filepath=os.pathsep.join(fname)) + self.ingestMediaDlg(filepath="\n".join(fname)) + self.ep.sp_version.setFocus() else: e.ignore() @err_catcher(name=__name__) - def ingestMedia(self, entity, files): + def ingestMediaToSelection(self, entity, files): identifier = self.getCurrentIdentifier() version = self.getCurrentVersion() aov = self.getCurrentAOV() if not identifier: - self.core.popup("Select an identifier to add media.") + self.ingestMediaDlg(filepath="\n".join(files)) return if not version: - self.core.popup("Select a version to add media.") + self.ingestMediaDlg(filepath="\n".join(files)) + self.ep.sp_version.setFocus() return if aov: @@ -1166,7 +1228,8 @@ def ingestMedia(self, entity, files): else: aovLabel = "" if identifier["mediaType"] == "3drenders": - self.core.popup("Select an AOV to add media.") + self.ingestMediaDlg(filepath="\n".join(files)) + self.ep.e_aov.setFocus() return entity = identifier @@ -1179,78 +1242,103 @@ def ingestMedia(self, entity, files): self.w_preview.updateSources() @err_catcher(name=__name__) - def createExternalTask(self, data=None, filepath=""): + def ingestMediaDlg(self, filepath=""): entity = self.getCurrentEntity() if entity.get("type") not in ["asset", "shot"]: self.core.popup("Invalid entity is selected. Select an asset or a shot and try again.") return - if not data: - self.ep = ExternalTask.ExternalTask(core=self.core, startText=filepath) - self.activateWindow() - result = self.ep.exec_() - - if result == 1: - taskName = self.ep.e_taskName.text() - versionName = self.ep.e_versionName.text() - targetPath = self.ep.e_taskPath.text() - if self.ep.rb_copy.isChecked(): - action = "copy" - elif self.ep.rb_move.isChecked(): - action = "move" - elif self.ep.rb_link.isChecked(): - action = "link" - else: - return + location = None + self.ep = ProjectWidgets.IngestMediaDlg(core=self.core, startText=filepath, entity=entity, parent=self) + idf = self.getCurrentIdentifier() + if idf: + location = self.core.mediaProducts.getLocationFromPath(idf["path"]) + self.ep.e_identifier.setText(idf["identifier"]) + if idf.get("mediaType"): + text = "" + if idf.get("mediaType") == "3drenders": + text = "3D" + elif idf.get("mediaType") == "2drenders": + text = "2D" + elif idf.get("mediaType") == "playblasts": + text = "Playblast" + elif idf.get("mediaType") == "externalMedia": + text = "External" + + if text: + self.ep.cb_identifierType.setCurrentText(text) - else: - taskName = data["taskName"] - versionName = data["versionName"] - targetPath = data["targetPath"] - action = data["action"] + version = self.getCurrentVersion() + if version: + location = self.core.mediaProducts.getLocationFromPath(version["path"]) + intVersion = self.core.products.getIntVersionFromVersionName(version["version"]) + if intVersion is not None: + self.ep.sp_version.setValue(intVersion + 1) - self.core.mediaProducts.createExternalMedia( - targetPath, entity, taskName, versionName, action=action - ) + if location: + self.ep.cb_location.setCurrentText(location) + + self.ep.e_identifier.setFocus() + self.activateWindow() + self.ep.accepted.connect(self.ingestMedia) + self.ep.show() - curData = [entity, taskName + " (external)", versionName, ""] + @err_catcher(name=__name__) + def ingestMedia(self, filepath=""): + entity = self.ep.entity + if entity.get("type") not in ["asset", "shot"]: + self.core.popup("Invalid entity is selected. Select an asset or a shot and try again.") + return + + identifier = self.ep.e_identifier.text() + mediaType = self.ep.cb_identifierType.currentData() + versionName = self.core.versionFormat % self.ep.sp_version.value() + aov = self.ep.e_aov.text() + targetPath = self.ep.l_mediaPath.text() + files = targetPath.split("\n") + entity = entity.copy() + location = self.ep.cb_location.currentText() + if self.core.mediaProducts.getLinkedToTasks(): + entity["department"] = self.ep.e_department.text() + entity["task"] = self.ep.e_task.text() + + if mediaType == "externalMedia": + if self.ep.rb_copy.isChecked(): + action = "copy" + elif self.ep.rb_move.isChecked(): + action = "move" + elif self.ep.rb_link.isChecked(): + action = "link" + + self.core.mediaProducts.createExternalMedia( + os.pathsep.join(files), entity, identifier, versionName, action=action, location=location + ) + else: + self.core.mediaProducts.ingestMedia(files, entity, identifier, versionName, aov, mediaType=mediaType, location=location) + + self.updateTasks() + displayName = self.core.mediaProducts.getDisplayNameForIdentifier(identifier, mediaType) + curData = [entity, displayName, versionName, ""] self.showRender(*curData) @err_catcher(name=__name__) def newExternalVersion(self): + entity = self.getCurrentEntity() identifier = self.getCurrentIdentifier() version = self.core.mediaProducts.getLatestVersionFromIdentifier(identifier) startPath = self.core.mediaProducts.getExternalPathFromVersion(version) - intVersion = self.core.products.getIntVersionFromVersionName(version["version"]) - newVersion = self.core.versionFormat % (intVersion + 1) - - self.ep = ExternalTask.ExternalTask(core=self.core) - self.ep.e_taskName.setText(identifier["identifier"]) - self.ep.w_taskName.setEnabled(False) - self.ep.e_taskPath.setText(startPath) - self.ep.e_versionName.setText(newVersion) - self.ep.enableOk(startPath, self.ep.e_taskPath) - self.ep.setWindowTitle("Create new version") - - result = self.ep.exec_() - - if result == 1: - entity = self.getCurrentEntity() - targetPath = self.ep.e_taskPath.text() - idf = identifier["identifier"] - versionName = self.ep.e_versionName.text() - self.core.mediaProducts.createExternalMedia( - targetPath, entity, idf, versionName - ) - curData = [ - self.getCurrentEntity(), - identifier["displayName"], - self.ep.e_versionName.text(), - "", - ] - self.showRender(*curData) + self.ep = ProjectWidgets.IngestMediaDlg(core=self.core, entity=entity, parent=self) + self.ep.e_identifier.setText(identifier["identifier"]) + self.ep.l_mediaPath.setText(startPath) + self.ep.sp_version.setValue(intVersion) + self.ep.enableOk(identifier["identifier"], self.ep.e_identifier) + self.ep.setWindowTitle("Create new version") + self.ep.e_version.setFocus() + self.activateWindow() + self.ep.accepted.connect(self.ingestMedia) + self.ep.show() @err_catcher(name=__name__) def getCurRenders(self): @@ -1706,7 +1794,7 @@ def createAov(self): identifier = self.origin.getCurrentIdentifier() identifierName = identifier.get("identifier") version = self.origin.getCurrentVersion().get("version") - if self.core.getConfig("globals", "productTasks", config="project"): + if self.core.mediaProducts.getLinkedToTasks(): curEntity["department"] = identifier.get("department", "unknown") curEntity["task"] = identifier.get("task", "unknown") @@ -2018,6 +2106,7 @@ def updatePreview(self, regenerateThumb=False): if validFiles: validFiles = sorted(validFiles, key=lambda x: x if "cryptomatte" not in os.path.basename(x) else "zzz" + x) baseName, extension = os.path.splitext(validFiles[0]) + extension = extension.lower() seqFiles = self.core.media.detectSequence(validFiles) if ( @@ -2038,7 +2127,7 @@ def updatePreview(self, regenerateThumb=False): imgPath = validFiles[0] if ( self.pduration == 1 - and os.path.splitext(imgPath)[1] in self.core.media.videoFormats + and os.path.splitext(imgPath)[1].lower() in self.core.media.videoFormats ): self.vidPrw = "loading" self.updatePrvInfo( @@ -2066,6 +2155,9 @@ def updatePreview(self, regenerateThumb=False): self.l_end.setText("") self.w_playerCtrls.setEnabled(False) self.sp_current.setEnabled(False) + if hasattr(self, "loadingGif") and self.loadingGif.state() == QMovie.Running: + self.l_loading.setVisible(False) + self.loadingGif.stop() @err_catcher(name=__name__) def updatePrvInfo(self, prvFile="", vidReader=None, seq=None, frame=None): @@ -2092,7 +2184,7 @@ def updatePrvInfo(self, prvFile="", vidReader=None, seq=None, frame=None): self.pwidth = resolution["width"] self.pheight = resolution["height"] - ext = os.path.splitext(prvFile)[1] + ext = os.path.splitext(prvFile)[1].lower() if ext in self.core.media.videoFormats: if len(self.seq) == 1: if self.core.isStr(vidReader) or self.state == "disabled": @@ -2348,7 +2440,7 @@ def changeImg(self, frame=0, seq=None, thread=None, regenerateThumb=False): pmsmall = QPixmap() if ( len(self.seq) == 1 - and os.path.splitext(self.seq[0])[1] + and os.path.splitext(self.seq[0])[1].lower() in self.core.media.videoFormats ): fileName = self.seq[0] @@ -2356,6 +2448,7 @@ def changeImg(self, frame=0, seq=None, thread=None, regenerateThumb=False): fileName = self.seq[curFrame] _, ext = os.path.splitext(fileName) + ext = ext.lower() if self.state == "disabled": pmsmall = self.core.media.scalePixmap(self.emptypmap, self.getThumbnailWidth(), self.getThumbnailHeight()) else: @@ -2453,7 +2546,7 @@ def changeImg(self, frame=0, seq=None, thread=None, regenerateThumb=False): ) pmsmall = self.core.media.scalePixmap( pm, self.getThumbnailWidth(), self.getThumbnailHeight() - ) + ) or QPixmap() except Exception as e: logger.debug(traceback.format_exc()) imgPath = os.path.join( @@ -2575,9 +2668,9 @@ def getMediaPreviewMenu(self): rcmenu = QMenu(self) - if self.core.appPlugin.appType == "2d" and len(self.seq) > 0 and hasattr(self.core.appPlugin, "importImages"): + if len(self.seq) > 0 and hasattr(self.core.appPlugin, "importImages"): impAct = QAction("Import images...", self) - impAct.triggered.connect(lambda: self.core.appPlugin.importImages(self)) + impAct.triggered.connect(lambda: self.core.appPlugin.importImages(mediaBrowser=self)) rcmenu.addAction(impAct) if len(self.seq) > 0: @@ -2647,7 +2740,7 @@ def getMediaPreviewMenu(self): if ( len(self.seq) == 1 - and os.path.splitext(self.seq[0])[1] + and os.path.splitext(self.seq[0])[1].lower() in self.core.media.videoFormats ): curSeqIdx = 0 @@ -2827,7 +2920,7 @@ def previewDropEvent(self, e): os.path.normpath(str(url.toLocalFile())) for url in e.mimeData().urls() ] entity = self.origin.getCurrentEntity() - self.origin.ingestMedia(entity, files) + self.origin.ingestMediaToSelection(entity, files) else: e.ignore() diff --git a/Prism/Scripts/ProjectScripts/ProductBrowser.py b/Prism/Scripts/ProjectScripts/ProductBrowser.py index 1457a213..37371557 100644 --- a/Prism/Scripts/ProjectScripts/ProductBrowser.py +++ b/Prism/Scripts/ProjectScripts/ProductBrowser.py @@ -41,7 +41,7 @@ from qtpy.QtGui import * from qtpy.QtWidgets import * -from PrismUtils import PrismWidgets +from PrismUtils import PrismWidgets, ProjectWidgets from PrismUtils.Decorators import err_catcher from UserInterfaces import ProductBrowser_ui @@ -81,32 +81,45 @@ def __init__(self, core, importState=None, refresh=True, projectBrowser=None): @err_catcher(name=__name__) def entered(self, prevTab=None, navData=None): + if prevTab: + if hasattr(prevTab, "w_entities"): + navData = prevTab.w_entities.getCurrentData() + elif hasattr(prevTab, "getSelectedData"): + navData = prevTab.getSelectedData() + if not self.initialized: + isScenefile = False + if not navData: + navPath = self.core.getCurrentFileName() + if self.importState: + impPath = self.importState.getImportPath() + if impPath and os.path.exists(os.path.dirname(impPath)): + navData = self.getDataFromPath(impPath) + + if not navData: + isScenefile = True + navData = self.getDataFromPath(navPath, scenefile=isScenefile) + self.w_entities.getPage("Assets").blockSignals(True) self.w_entities.getPage("Shots").blockSignals(True) + self.w_entities.tb_entities.blockSignals(True) self.w_entities.blockSignals(True) + self.w_entities.navigate(navData) self.w_entities.refreshEntities(defaultSelection=False) self.w_entities.getPage("Assets").blockSignals(False) self.w_entities.getPage("Shots").blockSignals(False) + self.w_entities.tb_entities.blockSignals(False) self.w_entities.blockSignals(False) if navData: - self.navigate(navData) - else: - navPath = self.core.getCurrentFileName() - if self.importState: - result = self.navigateToFile(self.importState.getImportPath()) - if result: - navPath = None - - self.navigateToFile(navPath, scenefile=True) + self.navigateToFile(data=navData, identifier=navData.get("product"), version=navData.get("version"), scenefile=isScenefile) self.initialized = True if prevTab: if hasattr(prevTab, "w_entities"): - self.w_entities.syncFromWidget(prevTab.w_entities) + self.w_entities.syncFromWidget(prevTab.w_entities, navData=navData) elif hasattr(prevTab, "getSelectedData"): - self.w_entities.navigate(prevTab.getSelectedData()) + self.w_entities.navigate(navData) @err_catcher(name=__name__) def closeEvent(self, event=None): @@ -117,7 +130,7 @@ def loadLayout(self): import EntityWidget self.w_entities = EntityWidget.EntityWidget(core=self.core, refresh=False, mode="products") - self.splitter.insertWidget(0, self.w_entities) + self.splitter1.insertWidget(0, self.w_entities) self.b_custom = QPushButton("Import custom files") self.w_entities.layout().addWidget(self.b_custom) @@ -151,6 +164,10 @@ def loadLayout(self): if len(self.w_entities.getLocations()) > 1 or (self.projectBrowser and len(self.projectBrowser.locations) > 1): self.versionLabels.insert(3, "Location") + if self.projectBrowser and self.projectBrowser.act_rememberWidgetSizes.isChecked(): + if "productsSplitter1" in brsData: + self.splitter1.setSizes(brsData["productsSplitter1"]) + self.tw_versions.setAcceptDrops(True) self.tw_versions.dragEnterEvent = self.productDragEnterEvent self.tw_versions.dragMoveEvent = self.productDragMoveEvent @@ -162,6 +179,10 @@ def loadLayout(self): self.updateSizeColumn() self.tw_versions.sortByColumn(0, Qt.DescendingOrder) + @err_catcher(name=__name__) + def saveSettings(self, data): + data["browser"]["productsSplitter1"] = self.splitter1.sizes() + @err_catcher(name=__name__) def versionHeaderChanged(self): twSorting = [ @@ -267,7 +288,7 @@ def productDropEvent(self, e): @err_catcher(name=__name__) def ingestProductVersion(self, entity, files): - if self.core.getConfig("globals", "productTasks", config="project"): + if self.core.products.getLinkedToTasks(): product = self.getCurrentProduct() or {} productName = product.get("product") product.update(entity) @@ -537,6 +558,10 @@ def rclicked(self, pos, listType): rcmenu = QMenu(viewUi) row = self.tw_versions.rowAt(pos.y()) + act_create = QAction("Create Version...", self) + act_create.triggered.connect(self.createVersionDlg) + rcmenu.addAction(act_create) + if row == -1: self.tw_versions.setCurrentIndex( self.tw_versions.model().createIndex(-1, 0) @@ -729,8 +754,9 @@ def prepareNewVersion(self): details["product"] = curProduct details["version"] = outputPathData["version"] - self.core.saveSceneInfo(nextPath + ".", details=details) + self.core.saveVersionInfo(os.path.dirname(nextPath), details=details) self.core.copyToClipboard(nextPath) + self.updateVersions(restoreSelection=True) @err_catcher(name=__name__) def copyToLocation(self, path, location): @@ -756,31 +782,24 @@ def copyToLocation(self, path, location): @err_catcher(name=__name__) def createProductDlg(self): - self.newItem = PrismWidgets.CreateItem( - core=self.core, showType=False, mode="product" - ) - self.newItem.setModal(True) - self.core.parentWindow(self.newItem) - self.newItem.e_item.setFocus() - self.newItem.setWindowTitle("Create Product") - self.newItem.l_item.setText("Productname:") + curEntity = self.getCurrentEntity() + self.newItem = ProjectWidgets.CreateProductDlg(self, entity=curEntity) + self.newItem.e_product.setFocus() self.newItem.accepted.connect(self.createProduct) - self.core.callback(name="onCreateProductDlgOpen", args=[self, self.newItem]) - self.newItem.show() @err_catcher(name=__name__) def createProduct(self): self.activateWindow() - itemName = self.newItem.e_item.text() - + itemName = self.newItem.e_product.text() curEntity = self.getCurrentEntity() - if self.core.getConfig("globals", "productTasks", config="project"): - curEntity["department"] = "unknown" - curEntity["task"] = "unknown" + if self.core.products.getLinkedToTasks(): + curEntity["department"] = self.newItem.e_department.text() or "unknown" + curEntity["task"] = self.newItem.e_task.text() or "unknown" - self.core.products.createProduct(entity=curEntity, product=itemName) + location = self.newItem.cb_location.currentText() + self.core.products.createProduct(entity=curEntity, product=itemName, location=location) selItems = self.tw_identifier.selectedItems() if len(selItems) == 1 and not selItems[0].data(0, Qt.UserRole): item = selItems[0] @@ -798,6 +817,47 @@ def createProduct(self): if items: self.tw_identifier.setCurrentItem(items[0]) + @err_catcher(name=__name__) + def createVersionDlg(self): + context = self.getCurrentProduct() + version = self.core.products.getNextAvailableVersion(context, context["product"]) + intVersion = self.core.products.getIntVersionFromVersionName(version) + self.newItem = ProjectWidgets.CreateProductVersionDlg(self, entity=context) + if intVersion is not None: + self.newItem.sp_version.setValue(intVersion) + + location = self.core.products.getLocationFromPath(context["path"]) + if location: + self.newItem.cb_location.setCurrentText(location) + + self.newItem.sp_version.setFocus() + self.newItem.accepted.connect(self.createVersion) + self.core.callback(name="onCreateVersionDlgOpen", args=[self, self.newItem]) + self.newItem.show() + + @err_catcher(name=__name__) + def createVersion(self): + self.activateWindow() + versionName = self.core.versionFormat % self.newItem.sp_version.value() + curEntity = self.getCurrentEntity() + product = self.getCurrentProduct() + location = self.newItem.cb_location.currentText() + if self.core.products.getLinkedToTasks(): + curEntity["department"] = product.get("department", "unknown") + curEntity["task"] = product.get("task", "unknown") + + files = [f for f in self.newItem.l_filePath.text().replace("< Click or Drag & Drop files >", "").split("\n") if f] + self.core.products.ingestProductVersion( + files=files, + entity=curEntity, + product=product["product"], + version=versionName, + location=location, + ) + self.updateVersions() + if versionName is not None: + self.navigateToVersion(versionName) + @err_catcher(name=__name__) def moveToGlobal(self, localPath): dstPath = self.core.convertPath(localPath, "global") @@ -1025,7 +1085,7 @@ def updateIdentifiers(self, item=None, restoreSelection=False): identifiers = self.getIdentifiers() identifierNames = sorted(identifiers.keys(), key=lambda s: s.lower()) groups, groupItems = self.createGroupItems(identifiers) - useTasks = self.core.getConfig("globals", "productTasks", config="project") + useTasks = self.core.products.getLinkedToTasks() if useTasks: items = {} for tn in identifierNames: @@ -1242,7 +1302,7 @@ def addVersionToTable(self, filepath, versionName, comment, user, data=None): else: item = VersionItem(versionName) - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) item.setData(Qt.UserRole, data) self.tw_versions.setItem(row, self.versionLabels.index("Version"), item) @@ -1250,11 +1310,11 @@ def addVersionToTable(self, filepath, versionName, comment, user, data=None): comment = "" item = QTableWidgetItem(comment) - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) self.tw_versions.setItem(row, self.versionLabels.index("Comment"), item) item = QTableWidgetItem(depExt) - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) self.tw_versions.setItem(row, self.versionLabels.index("Type"), item) if (data.get("locations", {}) and len(self.w_entities.getLocations()) > 1) or (self.projectBrowser and len(self.projectBrowser.locations) > 1): self.locationLabels = {} @@ -1308,13 +1368,13 @@ def addVersionToTable(self, filepath, versionName, comment, user, data=None): item = QTableWidgetItem() else: item = QTableWidgetItem(", ".join(data.get("locations", {}))) - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) item.setData(Qt.UserRole, data.get("locations", {})) self.tw_versions.setItem(row, self.versionLabels.index("Location"), item) item = QTableWidgetItem(user) - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) self.tw_versions.setItem(row, self.versionLabels.index("User"), item) if self.core.getConfig("globals", "showFileSizes", config="user"): @@ -1328,11 +1388,11 @@ def addVersionToTable(self, filepath, versionName, comment, user, data=None): sizeStr = "%.2f mb" % size item = QTableWidgetItem(sizeStr) - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) self.tw_versions.setItem(row, self.versionLabels.index("Size"), item) item = QTableWidgetItem() - item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) + item.setTextAlignment(Qt.AlignCenter) item.setData(Qt.DisplayRole, dateStamp) self.tw_versions.setItem(row, self.versionLabels.index("Date"), item) @@ -1381,22 +1441,30 @@ def getCurrentProductName(self): def navigate(self, data): self.navigateToFile(data=data, identifier=data.get("product"), version=data.get("version")) + @err_catcher(name=__name__) + def getDataFromPath(self, path, scenefile=False): + if os.path.exists(path): + fileName = path + else: + fileName = os.path.dirname(path) + if not os.path.exists(fileName): + return False + + fileName = os.path.normpath(fileName) + if scenefile: + data = self.core.getScenefileData(fileName) + else: + data = self.core.paths.getCachePathData(fileName) + + return data + @err_catcher(name=__name__) def navigateToFile(self, fileName=None, identifier=None, version=None, scenefile=False, data=None): if not data: if not fileName: return False - if not os.path.exists(fileName): - fileName = os.path.dirname(fileName) - if not os.path.exists(fileName): - return False - - fileName = os.path.normpath(fileName) - if scenefile: - data = self.core.getScenefileData(fileName) - else: - data = self.core.paths.getCachePathData(fileName) + data = self.getDataFromPath(fileName) or {} if not identifier: identifier = data.get("product") or "" diff --git a/Prism/Scripts/ProjectScripts/ProjectBrowser.py b/Prism/Scripts/ProjectScripts/ProjectBrowser.py index dff5055a..246b3f3c 100644 --- a/Prism/Scripts/ProjectScripts/ProjectBrowser.py +++ b/Prism/Scripts/ProjectScripts/ProjectBrowser.py @@ -36,6 +36,7 @@ import sys import platform import logging +from datetime import datetime if sys.version[0] == "3": pVersion = 3 @@ -87,6 +88,7 @@ class ProjectBrowser(QMainWindow, ProjectBrowser_ui.Ui_mw_ProjectBrowser): showing = Signal(object) def __init__(self, core): + startTime = datetime.now() QMainWindow.__init__(self) self.setupUi(self) self.core = core @@ -109,6 +111,8 @@ def __init__(self, core): self.loadLayout() self.connectEvents() self.core.callback(name="onProjectBrowserStartup", args=[self]) + endTime = datetime.now() + logger.debug("Project Browser startup duration: %s" % (endTime - startTime)) @err_catcher(name=__name__) def connectEvents(self): @@ -121,6 +125,7 @@ def connectEvents(self): self.actionAutoplay.toggled.connect(self.mediaBrowser.triggerAutoplay) self.act_filesizes.toggled.connect(self.triggerShowFileSizes) self.act_rememberTab.toggled.connect(self.triggerRememberTab) + self.act_rememberWidgetSizes.toggled.connect(self.triggerRememberWidgetSizes) self.tbw_project.currentChanged.connect(self.tabChanged) def enterEvent(self, event): @@ -164,6 +169,13 @@ def loadLayout(self): self.actionWebsite.setIcon(icon) self.helpMenu.addAction(self.actionWebsite) + self.actionDiscord = QAction(self.core.tr("Discord"), self) + self.actionDiscord.triggered.connect(lambda: self.core.openWebsite("discord")) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "discord.png") + icon = self.core.media.getColoredIcon(path) + self.actionDiscord.setIcon(icon) + self.helpMenu.addAction(self.actionDiscord) + self.actionWebsite = QAction(self.core.tr("Tutorials"), self) self.actionWebsite.triggered.connect(lambda: self.core.openWebsite("tutorials")) path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "tutorials.png") @@ -181,7 +193,7 @@ def loadLayout(self): self.helpMenu.addAction(self.actionWebsite) self.actionAbout = QAction(self.core.tr("About..."), self) - self.actionAbout.triggered.connect(self.core.showAbout) + self.actionAbout.triggered.connect(lambda x=None: self.core.showAbout()) path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "info.png") icon = self.core.media.getColoredIcon(path) self.actionAbout.setIcon(icon) @@ -199,6 +211,11 @@ def loadLayout(self): self.act_rememberTab.setChecked(True) self.menuTools.insertAction(self.actionAutoplay, self.act_rememberTab) + self.act_rememberWidgetSizes = QAction("Remember widget sizes", self) + self.act_rememberWidgetSizes.setCheckable(True) + self.act_rememberWidgetSizes.setChecked(False) + self.menuTools.insertAction(self.actionAutoplay, self.act_rememberWidgetSizes) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "configure.png") icon = self.core.media.getColoredIcon(path) self.actionPrismSettings.setIcon(icon) @@ -313,6 +330,9 @@ def loadLayout(self): if "rememberTab" in glbData: self.act_rememberTab.setChecked(glbData["rememberTab"]) + if "rememberWidgetSizes" in brsData: + self.act_rememberWidgetSizes.setChecked(brsData["rememberWidgetSizes"]) + self.sceneBrowser = SceneBrowser.SceneBrowser( core=self.core, projectBrowser=self, refresh=False ) @@ -651,13 +671,19 @@ def triggerShowFileSizes(self, checked=False): def triggerRememberTab(self, checked=False): self.core.setConfig("globals", "rememberTab", checked) + @err_catcher(name=__name__) + def triggerRememberWidgetSizes(self, checked=False): + self.core.setConfig("browser", "rememberWidgetSizes", checked) + @err_catcher(name=__name__) def triggerCloseLoad(self, checked=False): self.core.setConfig("browser", self.closeParm, checked) @err_catcher(name=__name__) def showTab(self, tab): - if tab != self.tbw_project.currentWidget().property("tabType"): + if tab == self.tbw_project.currentWidget().property("tabType"): + return True + else: for i in range(self.tbw_project.count()): if self.tbw_project.widget(i).property("tabType") == tab: idx = i diff --git a/Prism/Scripts/ProjectScripts/SceneBrowser.py b/Prism/Scripts/ProjectScripts/SceneBrowser.py index 75bd13d8..ac7c31d9 100644 --- a/Prism/Scripts/ProjectScripts/SceneBrowser.py +++ b/Prism/Scripts/ProjectScripts/SceneBrowser.py @@ -115,36 +115,45 @@ def __init__(self, core, projectBrowser=None, refresh=True): @err_catcher(name=__name__) def entered(self, prevTab=None, navData=None): + if prevTab: + if hasattr(prevTab, "w_entities"): + navData = prevTab.w_entities.getCurrentData() + elif hasattr(prevTab, "getSelectedData"): + navData = prevTab.getSelectedData() + if not self.initialized: + if not navData: + navData = self.getCurrentNavData() + self.w_entities.getPage("Assets").blockSignals(True) self.w_entities.getPage("Shots").blockSignals(True) + self.w_entities.tb_entities.blockSignals(True) self.w_entities.blockSignals(True) + self.w_entities.navigate(navData) self.w_entities.refreshEntities(defaultSelection=False) self.w_entities.getPage("Assets").blockSignals(False) self.w_entities.getPage("Shots").blockSignals(False) + self.w_entities.tb_entities.blockSignals(False) self.w_entities.blockSignals(False) if navData: result = self.navigate(navData) - else: - result = self.navigateToCurrent() - - if not result: - self.entityChanged() + if not result: + self.entityChanged() self.initialized = True if prevTab: if hasattr(prevTab, "w_entities"): - self.w_entities.syncFromWidget(prevTab.w_entities) + self.w_entities.syncFromWidget(prevTab.w_entities, navData=navData) elif hasattr(prevTab, "getSelectedData"): - self.w_entities.navigate(prevTab.getSelectedData()) + self.w_entities.navigate(navData) @err_catcher(name=__name__) def loadLayout(self): import EntityWidget self.w_entities = EntityWidget.EntityWidget(core=self.core, refresh=False) - self.splitter_5.insertWidget(0, self.w_entities) + self.splitter1.insertWidget(0, self.w_entities) self.tw_scenefiles.setShowGrid(False) @@ -188,6 +197,16 @@ def loadLayout(self): else: self.sceneLayoutItemsToggled(False, refresh=False) + if self.projectBrowser.act_rememberWidgetSizes.isChecked(): + if "scenefileSplitter1" in brsData: + self.splitter1.setSizes(brsData["scenefileSplitter1"]) + + if "scenefileSplitter2" in brsData: + self.splitter2.setSizes(brsData["scenefileSplitter2"]) + + if "scenefileSplitter3" in brsData: + self.splitter3.setSizes(brsData["scenefileSplitter3"]) + if self.core.compareVersions(self.core.projectVersion, "v1.2.1.6") == "lower": self.w_tasks.setVisible(False) @@ -372,11 +391,20 @@ def saveSettings(self, data): "show" ] + data["browser"]["scenefileSplitter1"] = self.splitter1.sizes() + data["browser"]["scenefileSplitter2"] = self.splitter2.sizes() + data["browser"]["scenefileSplitter3"] = self.splitter3.sizes() + @err_catcher(name=__name__) - def navigateToCurrent(self): + def getCurrentNavData(self): fileName = self.core.getCurrentFileName() - fileNameData = self.core.getScenefileData(fileName) - return self.navigate(fileNameData) + navData = self.core.getScenefileData(fileName) + return navData + + @err_catcher(name=__name__) + def navigateToCurrent(self): + navData = self.getCurrentNavData() + return self.navigate(navData) @err_catcher(name=__name__) def navigate(self, data): @@ -384,7 +412,12 @@ def navigate(self, data): if not isinstance(data, dict): return - prevEntity = self.getCurrentEntity() + prevEntities = self.getCurrentEntities() + if len(prevEntities) == 1: + prevEntity = prevEntities[0] + else: + prevEntity = None + prevDep = self.getCurrentDepartment() self.lw_departments.blockSignals(True) if data.get("type") in ["asset", "assetFolder"]: @@ -522,7 +555,10 @@ def mouseClickEvent(self, event, widget): if event.button() == Qt.LeftButton: index = widget.indexAt(event.pos()) if index.data() is None: - widget.setCurrentIndex(widget.model().createIndex(-1, 0)) + widget.setCurrentIndex( + widget.model().createIndex(-1, 0) + ) + widget.mouseClickEvent(event) @err_catcher(name=__name__) @@ -856,7 +892,7 @@ def rightClickedList(self, widget, pos): if i: rcmenu.addAction(i) elif "path" in locals(): - widget.setCurrentIndex(widget.model().createIndex(-1, 0)) + widget.setCurrentItem(None) openex = QAction("Open in explorer", self) openex.triggered.connect(lambda: self.core.openFolder(path)) rcmenu.addAction(openex) @@ -874,8 +910,8 @@ def rightClickedList(self, widget, pos): @err_catcher(name=__name__) def rclFile(self, pos): - if self.tw_scenefiles.selectedIndexes() != []: - idx = self.tw_scenefiles.selectedIndexes()[0] + idx = self.tw_scenefiles.indexAt(pos) + if idx != -1: irow = idx.row() filepath = self.core.fixPath( self.tw_scenefiles.model().index(irow, 0).data(Qt.UserRole) @@ -885,6 +921,9 @@ def rclFile(self, pos): ) else: filepath = "" + self.tw_scenefiles.setCurrentIndex( + self.tw_scenefiles.model().createIndex(-1, 0) + ) self.openScenefileContextMenu(filepath) @@ -1233,56 +1272,19 @@ def pastefile(self, location=None): self.refreshScenefiles() @err_catcher(name=__name__) - def getStep(self, departments): - entity = self.getCurrentEntity() - self.ss = ItemList.ItemList(core=self.core, entity=entity) - iconPath = os.path.join( - self.core.prismRoot, "Scripts", "UserInterfacesPrism", "create.png" - ) - icon = self.core.media.getColoredIcon(iconPath) - self.ss.buttonBox.buttons()[0].setIcon(icon) - - iconPath = os.path.join( - self.core.prismRoot, "Scripts", "UserInterfacesPrism", "delete.png" - ) - icon = self.core.media.getColoredIcon(iconPath) - self.ss.buttonBox.buttons()[-1].setIcon(icon) - - self.ss.setWindowTitle("Add Departments") - self.core.parentWindow(self.ss, parent=self) - self.ss.tw_steps.setFocus() - self.ss.tw_steps.doubleClicked.connect(lambda x=None, b=self.ss.buttonBox.buttons()[0]:self.ss.buttonboxClicked(b)) - - self.ss.tw_steps.setColumnCount(1) - self.ss.tw_steps.setHorizontalHeaderLabels(["Department"]) - self.ss.tw_steps.horizontalHeader().setVisible(False) - for department in departments: - rc = self.ss.tw_steps.rowCount() - self.ss.tw_steps.insertRow(rc) - name = "%s (%s)" % (department["name"], department["abbreviation"]) - nameItem = QTableWidgetItem(name) - nameItem.setData(Qt.UserRole, department) - self.ss.tw_steps.setItem(rc, 0, nameItem) - - self.core.callback(name="onDepartmentDlgOpen", args=[self, self.ss]) - if not getattr(self.ss, "allowShow", True): - return False - - self.ss.exec_() - - @err_catcher(name=__name__) - def createSteps(self, entity, steps, createTask=True): + def createSteps(self, entities, steps, createTask=True): if len(steps) > 0: - navData = entity.copy() + navData = entities[0] createdDirs = [] for step in steps: - result = self.core.entities.createDepartment( - step, entity, createCat=createTask - ) - if result: - createdDirs.append(step) - navData["department"] = self.core.entities.getLongDepartmentName(entity["type"], step) or step + for entity in entities: + result = self.core.entities.createDepartment( + step, entity, createCat=createTask + ) + if result: + createdDirs.append(step) + navData["department"] = self.core.entities.getLongDepartmentName(entity["type"], step) or step if createdDirs: self.refreshDepartments() @@ -1603,11 +1605,16 @@ def refreshScenefileList(self, sceneData): row.append(item) - item = QStandardItem(data.get("version", "")) + version = data.get("version", "") + item = QStandardItem(version) item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) row.append(item) - item = QStandardItem(data.get("comment", "")) + comment = data.get("comment", "") + if not comment and not version: + comment = os.path.basename(data.get("filename", "")) + + item = QStandardItem(comment) item.setTextAlignment(Qt.Alignment(Qt.AlignCenter)) row.append(item) @@ -1764,6 +1771,7 @@ def refreshShotinfo(self): else: startFrame = "?" endFrame = "?" + suffix = "" shotRange = self.core.entities.getShotRange(curEntity) if shotRange: @@ -1773,8 +1781,19 @@ def refreshShotinfo(self): if shotRange[1] is not None: endFrame = shotRange[1] + handleRange = self.core.entities.getShotRange(curEntity, handles=True) + if handleRange != shotRange: + if handleRange[0] is not None: + handleStartFrame = handleRange[0] + + if handleRange[1] is not None: + handleEndFrame = handleRange[1] + + suffix = " (%s - %s)" % (handleStartFrame, handleEndFrame) + + rangeStr = "%s - %s%s" % (startFrame, endFrame, suffix) l_range1 = QLabel("Framerange: ") - l_range2 = QLabel("%s - %s" % (startFrame, endFrame)) + l_range2 = QLabel(rangeStr) self.lo_entityInfo.addWidget(l_range1, 0, 0) self.lo_entityInfo.addWidget(l_range2, 0, 1) @@ -2113,13 +2132,13 @@ def setRecent(self): @err_catcher(name=__name__) def createTaskDlg(self): - entity = self.getCurrentEntity() + entities = self.getCurrentEntities() curDep = self.getCurrentDepartment() - presets = self.core.entities.getDefaultTasksForDepartment(entity["type"], curDep) or [] - existingTasks = self.core.entities.getCategories(entity, step=curDep) + presets = self.core.entities.getDefaultTasksForDepartment(entities[0]["type"], curDep) or [] + existingTasks = self.core.entities.getCategories(entities[0], step=curDep) presets = [p for p in presets if p not in existingTasks] - self.newItem = ItemList.ItemList(core=self.core, entity=entity, mode="tasks") + self.newItem = ItemList.ItemList(core=self.core, entities=entities, mode="tasks") self.newItem.setModal(True) self.newItem.tw_steps.setColumnCount(1) self.newItem.tw_steps.setHorizontalHeaderLabels(["Department"]) @@ -2159,13 +2178,14 @@ def createTaskDlg(self): def createTask(self, tasks): self.activateWindow() - curEntity = self.getCurrentEntity() + curEntities = self.getCurrentEntities() curDep = self.getCurrentDepartment() for task in tasks: - self.core.entities.createCategory( - entity=curEntity, step=curDep, category=task - ) + for curEntity in curEntities: + self.core.entities.createCategory( + entity=curEntity, step=curDep, category=task + ) self.refreshTasks() for i in range(self.lw_tasks.model().rowCount()): @@ -2177,12 +2197,12 @@ def createTask(self, tasks): @err_catcher(name=__name__) def createDepartmentDlg(self): - entity = self.getCurrentEntity() - basePath = self.core.getEntityPath(reqEntity="step", entity=entity) + entities = self.getCurrentEntities() + basePath = self.core.getEntityPath(reqEntity="step", entity=entities[0]) - if entity.get("type", "") == "asset": + if entities[0].get("type", "") == "asset": deps = self.core.projects.getAssetDepartments() - elif entity.get("type", "") in ["shot", "sequence"]: + elif entities[0].get("type", "") in ["shot", "sequence"]: deps = self.core.projects.getShotDepartments() else: return @@ -2192,7 +2212,40 @@ def createDepartmentDlg(self): if not os.path.exists(os.path.join(basePath, dep["abbreviation"])): validDeps.append(dep) - self.getStep(validDeps) + self.ss = ItemList.ItemList(core=self.core, entities=entities, mode="departments") + iconPath = os.path.join( + self.core.prismRoot, "Scripts", "UserInterfacesPrism", "create.png" + ) + icon = self.core.media.getColoredIcon(iconPath) + self.ss.buttonBox.buttons()[0].setIcon(icon) + + iconPath = os.path.join( + self.core.prismRoot, "Scripts", "UserInterfacesPrism", "delete.png" + ) + icon = self.core.media.getColoredIcon(iconPath) + self.ss.buttonBox.buttons()[-1].setIcon(icon) + + self.ss.setWindowTitle("Add Departments") + self.core.parentWindow(self.ss, parent=self) + self.ss.tw_steps.setFocus() + self.ss.tw_steps.doubleClicked.connect(lambda x=None, b=self.ss.buttonBox.buttons()[0]:self.ss.buttonboxClicked(b)) + + self.ss.tw_steps.setColumnCount(1) + self.ss.tw_steps.setHorizontalHeaderLabels(["Department"]) + self.ss.tw_steps.horizontalHeader().setVisible(False) + for department in validDeps: + rc = self.ss.tw_steps.rowCount() + self.ss.tw_steps.insertRow(rc) + name = "%s (%s)" % (department["name"], department["abbreviation"]) + nameItem = QTableWidgetItem(name) + nameItem.setData(Qt.UserRole, department) + self.ss.tw_steps.setItem(rc, 0, nameItem) + + self.core.callback(name="onDepartmentDlgOpen", args=[self, self.ss]) + if not getattr(self.ss, "allowShow", True): + return False + + self.ss.exec_() @err_catcher(name=__name__) def copyToGlobal(self, localPath): @@ -2556,11 +2609,11 @@ def setupUi(self): self.lo_main.addLayout(self.lo_info) self.lo_main.addItem(self.spacer7) self.lo_main.addLayout(self.lo_description) - self.lo_main.addStretch() + self.lo_main.addStretch(1000) self.locationLabels = {} if len(self.browser.projectBrowser.locations) > 1: self.spacer7 = QSpacerItem(0, 10, QSizePolicy.Fixed, QSizePolicy.Fixed) - self.spacer8 = QSpacerItem(0, 10, QSizePolicy.Fixed, QSizePolicy.Fixed) + self.spacer8 = QSpacerItem(0, 20, QSizePolicy.Fixed, QSizePolicy.Fixed) self.lo_location = QVBoxLayout() self.lo_location.addItem(self.spacer7) @@ -2598,6 +2651,9 @@ def refreshUi(self): user = self.getUser() icon = self.getIcon() + if not comment and not version: + comment = os.path.basename(self.data.get("filename", "")) + self.refreshPreview() self.l_version.setText(version) self.setIcon(icon) diff --git a/Prism/Scripts/ProjectScripts/StateManager.py b/Prism/Scripts/ProjectScripts/StateManager.py index 7d0d2723..3f6ee543 100644 --- a/Prism/Scripts/ProjectScripts/StateManager.py +++ b/Prism/Scripts/ProjectScripts/StateManager.py @@ -131,6 +131,7 @@ def __init__(self, core, stateDataPath=None, forceStates=[], standalone=False): self.publishPaused = False self.entityDlg = EntityDlg self.applyChangesToSelection = True + self.collapsedFolders = [] stateFiles = [] pluginUiPath = os.path.join( @@ -285,31 +286,64 @@ def loadState(self, clsDef, filename=None): @err_catcher(name=__name__) def loadLayout(self): - self.actionVersionUp = QAction("Version Up scenefile on publish", self) + self.actionSaveBeforePub = QAction("Save scenefile before publish", self) + self.actionSaveBeforePub.setCheckable(True) + self.actionSaveBeforePub.setChecked(self.core.getConfig("stateManager", "saveSceneBeforePublish", dft=True)) + self.actionSaveBeforePub.toggled.connect(self.onSaveBeforePubToggled) + self.menuAbout.insertAction(self.actionCopyStates, self.actionSaveBeforePub) + + self.actionVersionUp = QAction("Version Up scenefile before publish", self) self.actionVersionUp.setCheckable(True) self.actionVersionUp.setChecked(self.core.getConfig("stateManager", "versionUpSceneOnPublish", dft=True)) + self.actionVersionUp.setEnabled(self.actionSaveBeforePub.isChecked()) self.actionVersionUp.toggled.connect(self.onVersionUpToggled) self.menuAbout.insertAction(self.actionCopyStates, self.actionVersionUp) + + self.actionSaveDuringPub = QAction("Save scenefile during publish", self) + self.actionSaveDuringPub.setCheckable(True) + self.actionSaveDuringPub.setChecked(self.core.getConfig("stateManager", "saveSceneDuringPublish", dft=True)) + self.actionSaveDuringPub.toggled.connect(self.onSaveDuringPubToggled) + self.menuAbout.insertAction(self.actionCopyStates, self.actionSaveDuringPub) + self.menuAbout.insertSeparator(self.actionCopyStates) helpMenu = QMenu("Help", self) - self.actionWebsite = QAction("Visit website", self) + self.actionWebsite = QAction(self.core.tr("Visit website"), self) self.actionWebsite.triggered.connect(lambda: self.core.openWebsite("home")) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "open-web.png") + icon = self.core.media.getColoredIcon(path) + self.actionWebsite.setIcon(icon) helpMenu.addAction(self.actionWebsite) - self.actionWebsite = QAction("Tutorials", self) + self.actionDiscord = QAction(self.core.tr("Discord"), self) + self.actionDiscord.triggered.connect(lambda: self.core.openWebsite("discord")) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "discord.png") + icon = self.core.media.getColoredIcon(path) + self.actionDiscord.setIcon(icon) + helpMenu.addAction(self.actionDiscord) + + self.actionWebsite = QAction(self.core.tr("Tutorials"), self) self.actionWebsite.triggered.connect(lambda: self.core.openWebsite("tutorials")) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "tutorials.png") + icon = self.core.media.getColoredIcon(path) + self.actionWebsite.setIcon(icon) helpMenu.addAction(self.actionWebsite) - self.actionWebsite = QAction("Documentation", self) + self.actionWebsite = QAction(self.core.tr("Documentation"), self) self.actionWebsite.triggered.connect( lambda: self.core.openWebsite("documentation") ) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "book.png") + icon = self.core.media.getColoredIcon(path) + self.actionWebsite.setIcon(icon) helpMenu.addAction(self.actionWebsite) - self.actionAbout = QAction("About...", self) + self.actionAbout = QAction(self.core.tr("About..."), self) self.actionAbout.triggered.connect(self.core.showAbout) + path = os.path.join(self.core.prismRoot, "Scripts", "UserInterfacesPrism", "info.png") + icon = self.core.media.getColoredIcon(path) + self.actionAbout.setIcon(icon) helpMenu.addAction(self.actionAbout) self.menubar.addMenu(helpMenu) @@ -379,6 +413,15 @@ def loadLayout(self): def onVersionUpToggled(self, state): self.core.setConfig("stateManager", "versionUpSceneOnPublish", val=state, config="user") + @err_catcher(name=__name__) + def onSaveBeforePubToggled(self, state): + self.core.setConfig("stateManager", "saveSceneBeforePublish", val=state, config="user") + self.actionVersionUp.setEnabled(state) + + @err_catcher(name=__name__) + def onSaveDuringPubToggled(self, state): + self.core.setConfig("stateManager", "saveSceneDuringPublish", val=state, config="user") + @err_catcher(name=__name__) def showRenderPresets(self): rsUi = self.stateTypes["Render Settings"]() @@ -1211,6 +1254,22 @@ def deleteState(self, state=None, **kwargs): self.activeList.clearSelection() self.saveStatesToScene() + @err_catcher(name=__name__) + def requestImportPaths(self): + result = self.core.callback("requestImportPath", self) + for res in result: + if isinstance(res, dict) and res.get("importPaths") is not None: + return res["importPaths"] + + import ProductBrowser + + ts = ProductBrowser.ProductBrowser(core=self.core) + self.core.parentWindow(ts) + ts.exec_() + + importPaths = [ts.productPath] if ts.productPath else [] + return importPaths + @err_catcher(name=__name__) def createPressed(self, stateType, renderer=None): curSel = self.getCurrentItem(self.activeList) @@ -1230,30 +1289,28 @@ def createPressed(self, stateType, renderer=None): ) if createEmptyState: - stateType = "ImportFile" - productPath = None + productPaths = [None] else: - import ProductBrowser - - ts = ProductBrowser.ProductBrowser(core=self.core) - self.core.parentWindow(ts) - ts.exec_() - - productPath = ts.productPath - if not productPath: + productPaths = self.requestImportPaths() + if not productPaths: return - extension = os.path.splitext(productPath)[1] - stateType = ( - getattr(self.core.appPlugin, "sm_getImportHandlerType", lambda x: None)( - extension + for productPath in productPaths: + if productPath: + extension = os.path.splitext(productPath)[1] + stateType = ( + getattr(self.core.appPlugin, "sm_getImportHandlerType", lambda x: None)( + extension + ) + or "ImportFile" ) - or "ImportFile" - ) + else: + stateType = "ImportFile" - state = self.createState(stateType, parent=parent, importPath=productPath, setActive=True) - if not createEmptyState and ts.customProduct and state: - state.ui.e_name.setText(os.path.basename(productPath)) + state = self.createState(stateType, parent=parent, importPath=productPath, setActive=True) + data = self.core.paths.getCachePathData(productPath) + if not createEmptyState and data.get("product") and state: + state.ui.e_name.setText(os.path.basename(productPath)) self.activateWindow() @@ -1877,6 +1934,9 @@ def publish( "username": self.core.getConfig("globals", "username"), } + if saveScene is None: + saveScene = self.actionSaveBeforePub.isChecked() + if saveScene is None or saveScene is True: if executeState: increment = False if incrementScene is None else incrementScene @@ -1924,6 +1984,7 @@ def publish( text = 'Executing "%s" - please wait..' % curUi.state.text(0) self.pubMsg = self.core.waitPopup(self.core, text) with self.pubMsg: + QApplication.processEvents() self.curExecutedState = curUi if getattr(curUi, "canSetVersion", False): result = curUi.executeState( @@ -1957,6 +2018,7 @@ def publish( text = 'Executing "%s" - please wait..' % curUi.state.text(0) self.pubMsg = self.core.waitPopup(self.core, text) with self.pubMsg: + QApplication.processEvents() self.curExecutedState = curUi exResult = curUi.executeState(parent=self) self.curExecutedState = None @@ -2005,7 +2067,14 @@ def publish( result = True if successPopup: - self.core.popup(msgStr, title=actionString, severity="info", parent=self) + if len(self.publishResult) == 1 and hasattr(self.publishResult[0]["state"], "showLastPathMenu"): + buttons = ["Output Options...", "OK"] + msg = self.core.popupQuestion(msgStr, title=actionString, buttons=buttons, icon=QMessageBox.Information, parent=self, doExec=False) + msg.buttons()[0].clicked.disconnect() + msg.buttons()[0].clicked.connect(lambda x=None, m=msg: self.onOutputOptionsClicked(m)) + msg.exec_() + else: + self.core.popup(msgStr, title=actionString, severity="info", parent=self) else: infoString = "" for i in self.publishResult: @@ -2025,6 +2094,12 @@ def publish( return result + @err_catcher(name=__name__) + def onOutputOptionsClicked(self, msg): + msg.close() + msg.deleteLater() + self.publishResult[0]["state"].showLastPathMenu() + @err_catcher(name=__name__) def runSantityChecks(self, executeState): result = [] @@ -2254,6 +2329,9 @@ def getFrameRangeTypeToolTip(self, rangeType): * Multiple elements can be combined by a "," in any order. Example: "34, 5-10x2, 3, 150-200, 60" will render frames 34, 5, 7, 9, 3, 150, 151 ... 200, 60 +* Frames can be exluded by starting an element with "^". + Example: "1-10, ^5-7" will render frames 1, 2, 3, 4, 8, 9, 10 + Each framenumber will be evaluated not more than once. Specifying a frame multiple times in an expression like "2, 3, 3, 4" will render frame 3 only once. This can be used to render a few frames across the whole range before rendering every frame from start to end. diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/Folder.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/Folder.py index 8b8b9211..7d4650ab 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/Folder.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/Folder.py @@ -66,14 +66,10 @@ def loadData(self, data): if "listtype" in data: self.listType = data["listtype"] if "stateenabled" in data and self.listType == "Export": - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) if "stateexpanded" in data: if not data["stateexpanded"]: self.stateManager.collapsedFolders.append(self.state) @@ -142,6 +138,6 @@ def getStateProps(self): return { "statename": self.e_name.text(), "listtype": self.listType, - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), "stateexpanded": self.state.isExpanded(), } diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py index 391c2a50..777a6907 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/ConvertUI.py @@ -1,5 +1,5 @@ fname = "default_ImportFile" -# fname = "default_Export" +fname = "default_Export" fname = "default_ImageRender" fname = "default_Playblast" # fname = "default_RenderSettings" diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export.ui b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export.ui index 2b260293..c801c6ad 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export.ui +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export.ui @@ -6,8 +6,8 @@ 0 0 - 340 - 955 + 400 + 1259 @@ -285,12 +285,6 @@ - - - 55 - 16777215 - - 99999 @@ -301,12 +295,6 @@ - - - 55 - 16777215 - - 99999 @@ -1114,8 +1102,8 @@ 0 0 - 269 - 69 + 289 + 138 diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export_ui.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export_ui.py index c842cb01..1702e66a 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export_ui.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Export_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'default_Export.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -16,7 +16,7 @@ class Ui_wg_Export(object): def setupUi(self, wg_Export): if not wg_Export.objectName(): wg_Export.setObjectName(u"wg_Export") - wg_Export.resize(340, 955) + wg_Export.resize(400, 1259) self.verticalLayout = QVBoxLayout(wg_Export) self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) @@ -62,7 +62,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_10.addWidget(self.label_4) - self.horizontalSpacer_5 = QSpacerItem(37, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_5 = QSpacerItem(37, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_10.addItem(self.horizontalSpacer_5) @@ -71,7 +71,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_10.addWidget(self.l_context) - self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_10.addItem(self.horizontalSpacer) @@ -100,7 +100,7 @@ def setupUi(self, wg_Export): self.l_taskName = QLabel(self.w_taskname) self.l_taskName.setObjectName(u"l_taskName") - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.l_taskName.sizePolicy().hasHeightForWidth()) @@ -130,7 +130,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_6.addWidget(self.label_3) - self.horizontalSpacer_2 = QSpacerItem(37, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_2 = QSpacerItem(37, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_6.addItem(self.horizontalSpacer_2) @@ -156,7 +156,6 @@ def setupUi(self, wg_Export): self.sp_rangeEnd = QSpinBox(self.f_frameRange_2) self.sp_rangeEnd.setObjectName(u"sp_rangeEnd") - self.sp_rangeEnd.setMaximumSize(QSize(55, 16777215)) self.sp_rangeEnd.setMaximum(99999) self.sp_rangeEnd.setValue(1100) @@ -164,7 +163,6 @@ def setupUi(self, wg_Export): self.sp_rangeStart = QSpinBox(self.f_frameRange_2) self.sp_rangeStart.setObjectName(u"sp_rangeStart") - self.sp_rangeStart.setMaximumSize(QSize(55, 16777215)) self.sp_rangeStart.setMaximum(99999) self.sp_rangeStart.setValue(1001) @@ -182,7 +180,7 @@ def setupUi(self, wg_Export): self.gridLayout.addWidget(self.l_rangeStartInfo, 0, 0, 1, 1) - self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.gridLayout.addItem(self.horizontalSpacer_7, 0, 4, 1, 1) @@ -205,7 +203,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_20.addWidget(self.l_master) - self.horizontalSpacer_29 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_29 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_20.addItem(self.horizontalSpacer_29) @@ -229,7 +227,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_11.addWidget(self.l_outPath) - self.horizontalSpacer_6 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_6 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_11.addItem(self.horizontalSpacer_6) @@ -253,7 +251,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_9.addWidget(self.l_outType) - self.horizontalSpacer_3 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_3 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_9.addItem(self.horizontalSpacer_3) @@ -333,7 +331,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_15.addWidget(self.l_additionalOptions) - self.horizontalSpacer_11 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_11 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_15.addItem(self.horizontalSpacer_11) @@ -357,7 +355,7 @@ def setupUi(self, wg_Export): self.horizontalLayout.addWidget(self.l_wholeScene) - self.horizontalSpacer_4 = QSpacerItem(185, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_4 = QSpacerItem(185, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(self.horizontalSpacer_4) @@ -421,7 +419,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_14.addWidget(self.l_manager) - self.horizontalSpacer_19 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_19 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_14.addItem(self.horizontalSpacer_19) @@ -444,7 +442,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_21.addWidget(self.l_rjPrio) - self.horizontalSpacer_16 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_16 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_21.addItem(self.horizontalSpacer_16) @@ -468,7 +466,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_22.addWidget(self.label_15) - self.horizontalSpacer_17 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_17 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_22.addItem(self.horizontalSpacer_17) @@ -492,7 +490,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_28.addWidget(self.l_rjTimeout) - self.horizontalSpacer_23 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_23 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_28.addItem(self.horizontalSpacer_23) @@ -517,7 +515,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_26.addWidget(self.label_18) - self.horizontalSpacer_20 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_20 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_26.addItem(self.horizontalSpacer_20) @@ -540,7 +538,7 @@ def setupUi(self, wg_Export): self.horizontalLayout_29.addWidget(self.l_dlConcurrentTasks) - self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_29.addItem(self.horizontalSpacer_24) @@ -568,7 +566,7 @@ def setupUi(self, wg_Export): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 269, 69)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 289, 138)) self.horizontalLayout_12 = QHBoxLayout(self.scrollAreaWidgetContents) self.horizontalLayout_12.setObjectName(u"horizontalLayout_12") self.l_pathLast = QLabel(self.scrollAreaWidgetContents) diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender.ui b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender.ui index 68da0233..4e7aa503 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender.ui +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender.ui @@ -6,8 +6,8 @@ 0 0 - 340 - 1103 + 400 + 1484 @@ -279,12 +279,6 @@ - - - 55 - 16777215 - - 99999 @@ -295,12 +289,6 @@ - - - 55 - 16777215 - - 99999 @@ -1494,8 +1482,8 @@ 0 0 - 289 - 69 + 327 + 138 diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender_ui.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender_ui.py index eb312d03..1d5aaa10 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender_ui.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_ImageRender_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'default_ImageRender.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -16,7 +16,7 @@ class Ui_wg_ImageRender(object): def setupUi(self, wg_ImageRender): if not wg_ImageRender.objectName(): wg_ImageRender.setObjectName(u"wg_ImageRender") - wg_ImageRender.resize(340, 1103) + wg_ImageRender.resize(400, 1484) self.verticalLayout = QVBoxLayout(wg_ImageRender) self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) @@ -60,7 +60,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_11.addWidget(self.label_7) - self.horizontalSpacer_5 = QSpacerItem(37, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_5 = QSpacerItem(37, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_11.addItem(self.horizontalSpacer_5) @@ -69,7 +69,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_11.addWidget(self.l_context) - self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_11.addItem(self.horizontalSpacer_3) @@ -98,7 +98,7 @@ def setupUi(self, wg_ImageRender): self.l_taskName = QLabel(self.f_taskname) self.l_taskName.setObjectName(u"l_taskName") - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.l_taskName.sizePolicy().hasHeightForWidth()) @@ -128,7 +128,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout.addWidget(self.label_3) - self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(self.horizontalSpacer_2) @@ -155,7 +155,6 @@ def setupUi(self, wg_ImageRender): self.sp_rangeEnd = QSpinBox(self.w_frameRangeValues) self.sp_rangeEnd.setObjectName(u"sp_rangeEnd") - self.sp_rangeEnd.setMaximumSize(QSize(55, 16777215)) self.sp_rangeEnd.setMaximum(99999) self.sp_rangeEnd.setValue(1100) @@ -163,7 +162,6 @@ def setupUi(self, wg_ImageRender): self.sp_rangeStart = QSpinBox(self.w_frameRangeValues) self.sp_rangeStart.setObjectName(u"sp_rangeStart") - self.sp_rangeStart.setMaximumSize(QSize(55, 16777215)) self.sp_rangeStart.setMaximum(99999) self.sp_rangeStart.setValue(1001) @@ -181,7 +179,7 @@ def setupUi(self, wg_ImageRender): self.gridLayout.addWidget(self.l_rangeStartInfo, 0, 0, 1, 1) - self.horizontalSpacer_13 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_13 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.gridLayout.addItem(self.horizontalSpacer_13, 0, 4, 1, 1) @@ -204,7 +202,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_15.addWidget(self.l_frameExpression) - self.horizontalSpacer_14 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_14 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_15.addItem(self.horizontalSpacer_14) @@ -228,7 +226,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_2.addWidget(self.label) - self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_2.addItem(self.horizontalSpacer) @@ -253,7 +251,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_9.addWidget(self.label_4) - self.horizontalSpacer_9 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_9 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_9.addItem(self.horizontalSpacer_9) @@ -302,7 +300,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_14.addWidget(self.l_renderPreset) - self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_14.addItem(self.horizontalSpacer_7) @@ -332,7 +330,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_17.addWidget(self.l_outPath_2) - self.horizontalSpacer_28 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_28 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_17.addItem(self.horizontalSpacer_28) @@ -356,7 +354,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_16.addWidget(self.l_outPath) - self.horizontalSpacer_27 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_27 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_16.addItem(self.horizontalSpacer_27) @@ -379,7 +377,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_5.addWidget(self.label_5) - self.horizontalSpacer_6 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_6 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_5.addItem(self.horizontalSpacer_6) @@ -404,7 +402,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_6.addWidget(self.label_6) - self.horizontalSpacer_12 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_12 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_6.addItem(self.horizontalSpacer_12) @@ -437,7 +435,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_13.addWidget(self.l_manager) - self.horizontalSpacer_19 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_19 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_13.addItem(self.horizontalSpacer_19) @@ -460,7 +458,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_21.addWidget(self.l_rjPrio) - self.horizontalSpacer_16 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_16 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_21.addItem(self.horizontalSpacer_16) @@ -484,7 +482,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_22.addWidget(self.label_15) - self.horizontalSpacer_17 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_17 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_22.addItem(self.horizontalSpacer_17) @@ -508,7 +506,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_28.addWidget(self.l_rjTimeout) - self.horizontalSpacer_23 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_23 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_28.addItem(self.horizontalSpacer_23) @@ -533,7 +531,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_26.addWidget(self.label_18) - self.horizontalSpacer_20 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_20 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_26.addItem(self.horizontalSpacer_20) @@ -556,7 +554,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_27.addWidget(self.label_19) - self.horizontalSpacer_22 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_22 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_27.addItem(self.horizontalSpacer_22) @@ -579,7 +577,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_23.addWidget(self.label_16) - self.horizontalSpacer_18 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_18 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_23.addItem(self.horizontalSpacer_18) @@ -602,7 +600,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_24.addWidget(self.label_17) - self.horizontalSpacer_21 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_21 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_24.addItem(self.horizontalSpacer_21) @@ -645,7 +643,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_29.addWidget(self.l_dlConcurrentTasks) - self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_29.addItem(self.horizontalSpacer_24) @@ -670,7 +668,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_30.addWidget(self.l_dlGPUpt) - self.horizontalSpacer_25 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_25 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_30.addItem(self.horizontalSpacer_25) @@ -695,7 +693,7 @@ def setupUi(self, wg_ImageRender): self.horizontalLayout_31.addWidget(self.l_dlGPUdevices) - self.horizontalSpacer_26 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_26 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_31.addItem(self.horizontalSpacer_26) @@ -746,7 +744,7 @@ def setupUi(self, wg_ImageRender): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 289, 69)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 327, 138)) self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.l_pathLast = QLabel(self.scrollAreaWidgetContents) diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast.ui b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast.ui index 189e5799..36d95c3c 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast.ui +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast.ui @@ -6,8 +6,8 @@ 0 0 - 340 - 598 + 342 + 822 @@ -204,12 +204,6 @@ - - - 55 - 16777215 - - 99999 @@ -220,12 +214,6 @@ - - - 55 - 16777215 - - 99999 @@ -905,8 +893,8 @@ 0 0 - 289 - 69 + 269 + 138 diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast_ui.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast_ui.py index eb5a124e..53305c34 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast_ui.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/StateUserInterfaces/default_Playblast_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'default_Playblast.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -16,7 +16,7 @@ class Ui_wg_Playblast(object): def setupUi(self, wg_Playblast): if not wg_Playblast.objectName(): wg_Playblast.setObjectName(u"wg_Playblast") - wg_Playblast.resize(340, 598) + wg_Playblast.resize(342, 822) self.verticalLayout = QVBoxLayout(wg_Playblast) self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) @@ -62,7 +62,7 @@ def setupUi(self, wg_Playblast): self.l_taskName = QLabel(self.widget_10) self.l_taskName.setObjectName(u"l_taskName") - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.l_taskName.sizePolicy().hasHeightForWidth()) @@ -92,7 +92,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout.addWidget(self.label_3) - self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(self.horizontalSpacer_2) @@ -118,7 +118,6 @@ def setupUi(self, wg_Playblast): self.sp_rangeEnd = QSpinBox(self.f_frameRange_2) self.sp_rangeEnd.setObjectName(u"sp_rangeEnd") - self.sp_rangeEnd.setMaximumSize(QSize(55, 16777215)) self.sp_rangeEnd.setMaximum(99999) self.sp_rangeEnd.setValue(1100) @@ -126,7 +125,6 @@ def setupUi(self, wg_Playblast): self.sp_rangeStart = QSpinBox(self.f_frameRange_2) self.sp_rangeStart.setObjectName(u"sp_rangeStart") - self.sp_rangeStart.setMaximumSize(QSize(55, 16777215)) self.sp_rangeStart.setMaximum(99999) self.sp_rangeStart.setValue(1001) @@ -144,7 +142,7 @@ def setupUi(self, wg_Playblast): self.gridLayout.addWidget(self.l_rangeStartInfo, 0, 0, 1, 1) - self.horizontalSpacer_14 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_14 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.gridLayout.addItem(self.horizontalSpacer_14, 0, 4, 1, 1) @@ -167,7 +165,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_2.addWidget(self.label) - self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_2.addItem(self.horizontalSpacer) @@ -192,7 +190,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_9.addWidget(self.label_6) - self.horizontalSpacer_13 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_13 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_9.addItem(self.horizontalSpacer_13) @@ -242,7 +240,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_18.addWidget(self.l_outPath_2) - self.horizontalSpacer_28 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_28 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_18.addItem(self.horizontalSpacer_28) @@ -266,7 +264,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_17.addWidget(self.l_location) - self.horizontalSpacer_27 = QSpacerItem(113, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_27 = QSpacerItem(113, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_17.addItem(self.horizontalSpacer_27) @@ -290,7 +288,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_3.addWidget(self.label_4) - self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_3.addItem(self.horizontalSpacer_3) @@ -320,7 +318,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_13.addWidget(self.l_manager) - self.horizontalSpacer_19 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_19 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_13.addItem(self.horizontalSpacer_19) @@ -343,7 +341,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_21.addWidget(self.l_rjPrio) - self.horizontalSpacer_16 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_16 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_21.addItem(self.horizontalSpacer_16) @@ -367,7 +365,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_22.addWidget(self.label_15) - self.horizontalSpacer_17 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_17 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_22.addItem(self.horizontalSpacer_17) @@ -391,7 +389,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_28.addWidget(self.l_rjTimeout) - self.horizontalSpacer_23 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_23 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_28.addItem(self.horizontalSpacer_23) @@ -416,7 +414,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_26.addWidget(self.label_18) - self.horizontalSpacer_20 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_20 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_26.addItem(self.horizontalSpacer_20) @@ -439,7 +437,7 @@ def setupUi(self, wg_Playblast): self.horizontalLayout_29.addWidget(self.l_dlConcurrentTasks) - self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout_29.addItem(self.horizontalSpacer_24) @@ -472,7 +470,7 @@ def setupUi(self, wg_Playblast): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 289, 69)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 269, 138)) self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.l_pathLast = QLabel(self.scrollAreaWidgetContents) diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Code.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Code.py index 64df526b..ff38b0b5 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Code.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Code.py @@ -73,14 +73,10 @@ def loadData(self, data): if "code" in data: self.te_code.setPlainText(data["code"]) if "stateenabled" in data and self.listType == "Export": - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.core.callback("onStateSettingsLoaded", self, data) @@ -202,7 +198,7 @@ def getStateProps(self): { "statename": self.e_name.text(), "code": self.te_code.toPlainText(), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } ) return stateProps diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Export.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Export.py index d8d54349..6e5ce979 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Export.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Export.py @@ -200,14 +200,10 @@ def loadData(self, data): lePath = self.core.fixPath(data["lastexportpath"]) self.setLastPath(lePath) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) getattr(self.core.appPlugin, "sm_export_loadData", lambda x, y: None)( self, data @@ -487,7 +483,7 @@ def changeTask(self): @err_catcher(name=__name__) def preDelete(self, item): - self.core.appPlugin.sm_export_preDelete(self) + getattr(self.core.appPlugin, "sm_export_preDelete", lambda x: None)(self) @err_catcher(name=__name__) def rcObjects(self, pos): @@ -498,7 +494,7 @@ def rcObjects(self, pos): createMenu = QMenu() - if not item is None: + if item is not None: actRemove = QAction("Remove", self) actRemove.triggered.connect(lambda: self.removeItem(item)) createMenu.addAction(actRemove) @@ -523,7 +519,7 @@ def removeItem(self, item): items = self.lw_objects.selectedItems() for item in reversed(items): rowNum = self.lw_objects.row(item) - self.core.appPlugin.sm_export_removeSetItem(self, self.nodes[rowNum]) + getattr(self.core.appPlugin, "sm_export_removeSetItem", lambda x, y: None)(self, self.nodes[rowNum]) del self.nodes[rowNum] self.lw_objects.takeItem(rowNum) @@ -1090,6 +1086,7 @@ def executeState(self, parent, useVersion="next"): details["version"] = hVersion details["sourceScene"] = fileName details["product"] = self.getTaskname() + details["comment"] = self.stateManager.publishComment if startFrame != endFrame: details["fps"] = self.core.getFPS() @@ -1199,7 +1196,7 @@ def getStateProps(self): "rjsuspended": str(self.chb_rjSuspended.isChecked()), "dlconcurrent": self.sp_dlConcurrentTasks.value(), "lastexportpath": self.l_pathLast.text().replace("\\", "/"), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } ) getattr(self.core.appPlugin, "sm_export_getStateProps", lambda x, y: None)( diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImageRender.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImageRender.py index 0c173554..77943f0c 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImageRender.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImageRender.py @@ -246,14 +246,10 @@ def loadData(self, data): self.l_pathLast.setText(lePath) self.l_pathLast.setToolTip(lePath) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) self.core.callback("onStateSettingsLoaded", self, data) @@ -1049,6 +1045,7 @@ def getOutputName(self, useVersion="next"): singleFrame=singleFrame, returnDetails=True, mediaType=self.mediaType, + state=self, ) outputFolder = os.path.dirname(outputPathData["path"]) @@ -1093,6 +1090,7 @@ def executeState(self, parent, useVersion="next"): ] outputName, outputPath, hVersion = self.getOutputName(useVersion=useVersion) + expandedOutputPath = os.path.expandvars(outputPath) outLength = len(outputName) if platform.system() == "Windows" and os.getenv("PRISM_IGNORE_PATH_LENGTH") != "1" and outLength > 255: @@ -1102,8 +1100,8 @@ def executeState(self, parent, useVersion="next"): % outLength ] - if not os.path.exists(os.path.dirname(outputPath)): - os.makedirs(os.path.dirname(outputPath)) + if not os.path.exists(os.path.dirname(expandedOutputPath)): + os.makedirs(os.path.dirname(expandedOutputPath)) details = context.copy() if "filename" in details: @@ -1118,9 +1116,9 @@ def executeState(self, parent, useVersion="next"): details["comment"] = self.stateManager.publishComment if self.mediaType == "3drenders": - infopath = os.path.dirname(outputPath) + infopath = os.path.dirname(expandedOutputPath) else: - infopath = outputPath + infopath = expandedOutputPath self.core.saveVersionInfo( filepath=infopath, details=details @@ -1167,12 +1165,14 @@ def executeState(self, parent, useVersion="next"): + " - error - %s" % res.get("details", "preRender hook returned False") ] - if not os.path.exists(os.path.dirname(rSettings["outputName"])): - os.makedirs(os.path.dirname(rSettings["outputName"])) + if not os.path.exists(os.path.expandvars(os.path.dirname(rSettings["outputName"]))): + os.makedirs(os.path.expandvars(os.path.dirname(rSettings["outputName"]))) + + if self.stateManager.actionSaveDuringPub.isChecked(): + self.core.saveScene(versionUp=False, prismReq=False) - self.core.saveScene(versionUp=False, prismReq=False) if self.core.getConfig("globals", "backupScenesOnPublish", config="project"): - self.core.entities.backupScenefile(os.path.dirname(rSettings["outputName"]), bufferMinutes=0) + self.core.entities.backupScenefile(os.path.expandvars(os.path.dirname(rSettings["outputName"])), bufferMinutes=0) if not self.gb_submit.isHidden() and self.gb_submit.isChecked(): handleMaster = "media" if self.isUsingMasterVersion() else False @@ -1209,7 +1209,7 @@ def executeState(self, parent, useVersion="next"): return [self.state.text(0) + " - publish paused"] else: if updateMaster: - self.handleMasterVersion(outputName) + self.handleMasterVersion(os.path.expandvars(outputName)) kwargs = { "state": self, @@ -1318,7 +1318,7 @@ def getStateProps(self): "dlgpudevices": self.le_dlGPUdevices.text(), "lastexportpath": self.l_pathLast.text().replace("\\", "/"), "enablepasses": str(self.gb_passes.isChecked()), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } self.core.callback("onStateGetSettings", self, stateProps) return stateProps diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImportFile.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImportFile.py index dcfc7a0f..ecde46ee 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImportFile.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_ImportFile.py @@ -69,9 +69,8 @@ def setup( self.stateNameTemplate = self.core.getConfig( "globals", "defaultImportStateName", - dft=stateNameTemplate, configPath=self.core.prismIni, - ) + ) or stateNameTemplate self.e_name.setText(self.stateNameTemplate) self.l_name.setVisible(False) self.e_name.setVisible(False) @@ -98,12 +97,12 @@ def setup( and not createEmptyState and not self.stateManager.standalone ): - import ProductBrowser - - ts = ProductBrowser.ProductBrowser(core=core, importState=self) - core.parentWindow(ts) - ts.exec_() - importPath = ts.productPath + importPaths = self.requestImportPaths() + if importPaths: + importPath = importPaths[-1] + if len(importPaths) > 1: + for importPath in importPaths[:-1]: + stateManager.importFile(importPath) if importPath: self.setImportPath(importPath) @@ -132,6 +131,20 @@ def setStateMode(self, stateMode): self.stateMode = stateMode self.l_class.setText(stateMode) + @err_catcher(name=__name__) + def requestImportPaths(self): + result = self.core.callback("requestImportPath", self) + for res in result: + if isinstance(res, dict) and res.get("importPaths") is not None: + return res["importPaths"] + + import ProductBrowser + ts = ProductBrowser.ProductBrowser(core=self.core, importState=self) + self.core.parentWindow(ts) + ts.exec_() + importPath = [ts.productPath] + return importPath + @err_catcher(name=__name__) def loadData(self, data): if "statename" in data: diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Playblast.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Playblast.py index b28da2c8..28ad1592 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Playblast.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_Playblast.py @@ -180,14 +180,10 @@ def loadData(self, data): self.l_pathLast.setText(lePath) self.l_pathLast.setToolTip(lePath) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) getattr(self.core.appPlugin, "sm_playblast_loadData", lambda x, y: None)( self, data @@ -731,7 +727,8 @@ def executeState(self, parent, useVersion="next"): self.updateLastPath(outputName) self.stateManager.saveStatesToScene() - self.core.saveScene(versionUp=False, prismReq=False) + if self.stateManager.actionSaveDuringPub.isChecked(): + self.core.saveScene(versionUp=False, prismReq=False) try: if not self.gb_submit.isHidden() and self.gb_submit.isChecked(): @@ -907,7 +904,7 @@ def getStateProps(self): "rjsuspended": str(self.chb_rjSuspended.isChecked()), "dlconcurrent": self.sp_dlConcurrentTasks.value(), "lastexportpath": self.l_pathLast.text().replace("\\", "/"), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), "outputformat": str(self.cb_formats.currentText()), } ) diff --git a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_RenderSettings.py b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_RenderSettings.py index a87baeab..35a05fa2 100644 --- a/Prism/Scripts/ProjectScripts/StateManagerNodes/default_RenderSettings.py +++ b/Prism/Scripts/ProjectScripts/StateManagerNodes/default_RenderSettings.py @@ -112,25 +112,17 @@ def loadData(self, data): if idx != -1: self.cb_presetOption.setCurrentIndex(idx) if "editsettings" in data: - self.chb_editSettings.setChecked( - eval( - data["editsettings"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ) - ) + if type(data["editsettings"]) == bool: + self.chb_editSettings.setChecked(data["editsettings"]) + if "rendersettings" in data: settings = self.core.writeYaml(data=data["rendersettings"]) self.te_settings.setPlainText(settings) if "stateenabled" in data: - self.state.setCheckState( - 0, - eval( - data["stateenabled"] - .replace("PySide.QtCore.", "") - .replace("PySide2.QtCore.", "") - ), - ) + if type(data["stateenabled"]) == int: + self.state.setCheckState( + 0, Qt.CheckState(data["stateenabled"]), + ) getattr(self.core.appPlugin, "sm_renderSettings_loadData", lambda x, y: None)( self, data @@ -336,11 +328,11 @@ def getStateProps(self): { "statename": self.e_name.text(), "presetoption": self.cb_presetOption.currentText(), - "editsettings": str(self.chb_editSettings.isChecked()), + "editsettings": self.chb_editSettings.isChecked(), "rendersettings": self.core.readYaml( data=self.te_settings.toPlainText() ), - "stateenabled": str(self.state.checkState(0)), + "stateenabled": self.core.getCheckStateValue(self.state.checkState(0)), } ) diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/ConvertUI.py b/Prism/Scripts/ProjectScripts/UserInterfaces/ConvertUI.py index 22f39a7f..1d19ee62 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/ConvertUI.py +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/ConvertUI.py @@ -1,8 +1,8 @@ fname = "ProjectBrowser" fname = "MediaBrowser" -fname = "SceneBrowser" -fname = "CombineMedia" -fname = "EditShot" +# fname = "SceneBrowser" +# fname = "CombineMedia" +# fname = "EditShot" # fname = "StateManager" # fname = "ProductBrowser" # fname = "ItemList" diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/ExternalTask.ui b/Prism/Scripts/ProjectScripts/UserInterfaces/ExternalTask.ui deleted file mode 100644 index b6e5f5ab..00000000 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/ExternalTask.ui +++ /dev/null @@ -1,240 +0,0 @@ - - - dlg_ExternalTask - - - - 0 - 0 - 708 - 185 - - - - Add external media - - - - - - - 0 - - - - - - 0 - - - - - Identifier: - - - - - - - External path: - - - - - - - - - - - 0 - - - - - - 0 - - - - - - - - - - - - - - - - - Versionname: - - - - - - - - - - - - - - - - - - - - - - Qt::NoFocus - - - Qt::CustomContextMenu - - - ...(file) - - - - - - - Qt::NoFocus - - - Qt::CustomContextMenu - - - ...(folder) - - - - - - - - - - - - - - - - - 0 - - - - - Action: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Copy - - - true - - - - - - - Move - - - - - - - Link - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - dlg_ExternalTask - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - dlg_ExternalTask - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/ExternalTask_ui.py b/Prism/Scripts/ProjectScripts/UserInterfaces/ExternalTask_ui.py deleted file mode 100644 index bbf0240a..00000000 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/ExternalTask_ui.py +++ /dev/null @@ -1,183 +0,0 @@ -# -*- coding: utf-8 -*- - -################################################################################ -## Form generated from reading UI file 'ExternalTask.ui' -## -## Created by: Qt User Interface Compiler version 6.4.2 -## -## WARNING! All changes made in this file will be lost when recompiling UI file! -################################################################################ - -from qtpy.QtCore import * # type: ignore -from qtpy.QtGui import * # type: ignore -from qtpy.QtWidgets import * # type: ignore - -class Ui_dlg_ExternalTask(object): - def setupUi(self, dlg_ExternalTask): - if not dlg_ExternalTask.objectName(): - dlg_ExternalTask.setObjectName(u"dlg_ExternalTask") - dlg_ExternalTask.resize(708, 185) - self.verticalLayout = QVBoxLayout(dlg_ExternalTask) - self.verticalLayout.setObjectName(u"verticalLayout") - self.widget = QWidget(dlg_ExternalTask) - self.widget.setObjectName(u"widget") - self.horizontalLayout_5 = QHBoxLayout(self.widget) - self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") - self.widget_2 = QWidget(self.widget) - self.widget_2.setObjectName(u"widget_2") - self.verticalLayout_3 = QVBoxLayout(self.widget_2) - self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) - self.verticalLayout_3.setObjectName(u"verticalLayout_3") - self.label_2 = QLabel(self.widget_2) - self.label_2.setObjectName(u"label_2") - - self.verticalLayout_3.addWidget(self.label_2) - - self.label = QLabel(self.widget_2) - self.label.setObjectName(u"label") - - self.verticalLayout_3.addWidget(self.label) - - - self.horizontalLayout_5.addWidget(self.widget_2) - - self.widget_4 = QWidget(self.widget) - self.widget_4.setObjectName(u"widget_4") - self.verticalLayout_2 = QVBoxLayout(self.widget_4) - self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) - self.verticalLayout_2.setObjectName(u"verticalLayout_2") - self.widget_3 = QWidget(self.widget_4) - self.widget_3.setObjectName(u"widget_3") - self.horizontalLayout_3 = QHBoxLayout(self.widget_3) - self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") - self.w_taskName = QWidget(self.widget_3) - self.w_taskName.setObjectName(u"w_taskName") - self.horizontalLayout_2 = QHBoxLayout(self.w_taskName) - self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") - self.e_taskName = QLineEdit(self.w_taskName) - self.e_taskName.setObjectName(u"e_taskName") - - self.horizontalLayout_2.addWidget(self.e_taskName) - - - self.horizontalLayout_3.addWidget(self.w_taskName) - - self.w_versionName = QWidget(self.widget_3) - self.w_versionName.setObjectName(u"w_versionName") - self.horizontalLayout_4 = QHBoxLayout(self.w_versionName) - self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") - self.label_3 = QLabel(self.w_versionName) - self.label_3.setObjectName(u"label_3") - - self.horizontalLayout_4.addWidget(self.label_3) - - self.e_versionName = QLineEdit(self.w_versionName) - self.e_versionName.setObjectName(u"e_versionName") - - self.horizontalLayout_4.addWidget(self.e_versionName) - - - self.horizontalLayout_3.addWidget(self.w_versionName) - - - self.verticalLayout_2.addWidget(self.widget_3) - - self.w_taskPath = QWidget(self.widget_4) - self.w_taskPath.setObjectName(u"w_taskPath") - self.horizontalLayout = QHBoxLayout(self.w_taskPath) - self.horizontalLayout.setObjectName(u"horizontalLayout") - self.e_taskPath = QLineEdit(self.w_taskPath) - self.e_taskPath.setObjectName(u"e_taskPath") - - self.horizontalLayout.addWidget(self.e_taskPath) - - self.b_browseFile = QPushButton(self.w_taskPath) - self.b_browseFile.setObjectName(u"b_browseFile") - self.b_browseFile.setFocusPolicy(Qt.NoFocus) - self.b_browseFile.setContextMenuPolicy(Qt.CustomContextMenu) - - self.horizontalLayout.addWidget(self.b_browseFile) - - self.b_browseFolder = QPushButton(self.w_taskPath) - self.b_browseFolder.setObjectName(u"b_browseFolder") - self.b_browseFolder.setFocusPolicy(Qt.NoFocus) - self.b_browseFolder.setContextMenuPolicy(Qt.CustomContextMenu) - - self.horizontalLayout.addWidget(self.b_browseFolder) - - - self.verticalLayout_2.addWidget(self.w_taskPath) - - - self.horizontalLayout_5.addWidget(self.widget_4) - - - self.verticalLayout.addWidget(self.widget) - - self.widget_5 = QWidget(dlg_ExternalTask) - self.widget_5.setObjectName(u"widget_5") - self.horizontalLayout_6 = QHBoxLayout(self.widget_5) - self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_6.setObjectName(u"horizontalLayout_6") - self.label_4 = QLabel(self.widget_5) - self.label_4.setObjectName(u"label_4") - - self.horizontalLayout_6.addWidget(self.label_4) - - self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) - - self.horizontalLayout_6.addItem(self.horizontalSpacer) - - self.rb_copy = QRadioButton(self.widget_5) - self.rb_copy.setObjectName(u"rb_copy") - self.rb_copy.setChecked(True) - - self.horizontalLayout_6.addWidget(self.rb_copy) - - self.rb_move = QRadioButton(self.widget_5) - self.rb_move.setObjectName(u"rb_move") - - self.horizontalLayout_6.addWidget(self.rb_move) - - self.rb_link = QRadioButton(self.widget_5) - self.rb_link.setObjectName(u"rb_link") - - self.horizontalLayout_6.addWidget(self.rb_link) - - - self.verticalLayout.addWidget(self.widget_5) - - self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) - - self.verticalLayout.addItem(self.verticalSpacer) - - self.buttonBox = QDialogButtonBox(dlg_ExternalTask) - self.buttonBox.setObjectName(u"buttonBox") - self.buttonBox.setOrientation(Qt.Horizontal) - self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok) - - self.verticalLayout.addWidget(self.buttonBox) - - - self.retranslateUi(dlg_ExternalTask) - self.buttonBox.accepted.connect(dlg_ExternalTask.accept) - self.buttonBox.rejected.connect(dlg_ExternalTask.reject) - - QMetaObject.connectSlotsByName(dlg_ExternalTask) - # setupUi - - def retranslateUi(self, dlg_ExternalTask): - dlg_ExternalTask.setWindowTitle(QCoreApplication.translate("dlg_ExternalTask", u"Add external media", None)) - self.label_2.setText(QCoreApplication.translate("dlg_ExternalTask", u"Identifier:", None)) - self.label.setText(QCoreApplication.translate("dlg_ExternalTask", u"External path:", None)) - self.label_3.setText(QCoreApplication.translate("dlg_ExternalTask", u"Versionname:", None)) - self.b_browseFile.setText(QCoreApplication.translate("dlg_ExternalTask", u"...(file)", None)) - self.b_browseFolder.setText(QCoreApplication.translate("dlg_ExternalTask", u"...(folder)", None)) - self.label_4.setText(QCoreApplication.translate("dlg_ExternalTask", u"Action:", None)) - self.rb_copy.setText(QCoreApplication.translate("dlg_ExternalTask", u"Copy", None)) - self.rb_move.setText(QCoreApplication.translate("dlg_ExternalTask", u"Move", None)) - self.rb_link.setText(QCoreApplication.translate("dlg_ExternalTask", u"Link", None)) - # retranslateUi - diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser.ui b/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser.ui index 6a37585c..50f18e77 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser.ui +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser.ui @@ -15,7 +15,7 @@ - + Qt::Horizontal diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser_ui.py b/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser_ui.py index 5285b5d2..7a47b713 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser_ui.py +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/MediaBrowser_ui.py @@ -19,10 +19,10 @@ def setupUi(self, w_mediaBrowser): w_mediaBrowser.resize(714, 393) self.horizontalLayout = QHBoxLayout(w_mediaBrowser) self.horizontalLayout.setObjectName(u"horizontalLayout") - self.splitter = QSplitter(w_mediaBrowser) - self.splitter.setObjectName(u"splitter") - self.splitter.setOrientation(Qt.Horizontal) - self.w_identifier = QWidget(self.splitter) + self.splitter1 = QSplitter(w_mediaBrowser) + self.splitter1.setObjectName(u"splitter1") + self.splitter1.setOrientation(Qt.Horizontal) + self.w_identifier = QWidget(self.splitter1) self.w_identifier.setObjectName(u"w_identifier") sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(8) @@ -75,8 +75,8 @@ def setupUi(self, w_mediaBrowser): self.verticalLayout_8.addWidget(self.w_autoUpdate) - self.splitter.addWidget(self.w_identifier) - self.w_version = QWidget(self.splitter) + self.splitter1.addWidget(self.w_identifier) + self.w_version = QWidget(self.splitter1) self.w_version.setObjectName(u"w_version") sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy1.setHorizontalStretch(9) @@ -99,9 +99,9 @@ def setupUi(self, w_mediaBrowser): self.verticalLayout_11.addWidget(self.lw_version) - self.splitter.addWidget(self.w_version) + self.splitter1.addWidget(self.w_version) - self.horizontalLayout.addWidget(self.splitter) + self.horizontalLayout.addWidget(self.splitter1) self.retranslateUi(w_mediaBrowser) diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser.ui b/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser.ui index 34b5e354..087070ca 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser.ui +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser.ui @@ -15,7 +15,7 @@ - + Qt::Horizontal diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser_ui.py b/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser_ui.py index eba914c4..a5fce194 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser_ui.py +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/ProductBrowser_ui.py @@ -19,10 +19,10 @@ def setupUi(self, dlg_ProductBrowser): dlg_ProductBrowser.resize(1294, 696) self.verticalLayout_4 = QVBoxLayout(dlg_ProductBrowser) self.verticalLayout_4.setObjectName(u"verticalLayout_4") - self.splitter = QSplitter(dlg_ProductBrowser) - self.splitter.setObjectName(u"splitter") - self.splitter.setOrientation(Qt.Horizontal) - self.w_tasks = QWidget(self.splitter) + self.splitter1 = QSplitter(dlg_ProductBrowser) + self.splitter1.setObjectName(u"splitter1") + self.splitter1.setOrientation(Qt.Horizontal) + self.w_tasks = QWidget(self.splitter1) self.w_tasks.setObjectName(u"w_tasks") sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(10) @@ -49,8 +49,8 @@ def setupUi(self, dlg_ProductBrowser): self.verticalLayout_3.addWidget(self.tw_identifier) - self.splitter.addWidget(self.w_tasks) - self.w_versions = QWidget(self.splitter) + self.splitter1.addWidget(self.w_tasks) + self.w_versions = QWidget(self.splitter1) self.w_versions.setObjectName(u"w_versions") sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy1.setHorizontalStretch(30) @@ -97,9 +97,9 @@ def setupUi(self, dlg_ProductBrowser): self.verticalLayout_2.addWidget(self.tw_versions) - self.splitter.addWidget(self.w_versions) + self.splitter1.addWidget(self.w_versions) - self.verticalLayout_4.addWidget(self.splitter) + self.verticalLayout_4.addWidget(self.splitter1) self.retranslateUi(dlg_ProductBrowser) diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser.ui b/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser.ui index 89ae4390..7ee1fa5b 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser.ui +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser.ui @@ -15,7 +15,7 @@ - + Qt::Horizontal @@ -27,11 +27,20 @@ - + + 0 + + + 0 + + + 0 + + 0 - + Qt::Vertical @@ -43,11 +52,20 @@ - + + 0 + + + 0 + + + 0 + + 0 - + Qt::Horizontal @@ -59,13 +77,31 @@ - + + 0 + + + 0 + + + 0 + + 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -101,13 +137,31 @@ - + + 0 + + + 0 + + + 0 + + 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -198,7 +252,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -269,13 +332,31 @@ - + + 0 + + + 0 + + + 0 + + 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -303,7 +384,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -352,7 +442,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -405,7 +504,16 @@ - + + 0 + + + 0 + + + 0 + + 0 diff --git a/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser_ui.py b/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser_ui.py index 5f36ed07..65ba6e9b 100644 --- a/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser_ui.py +++ b/Prism/Scripts/ProjectScripts/UserInterfaces/SceneBrowser_ui.py @@ -19,10 +19,10 @@ def setupUi(self, w_sceneBrowser): w_sceneBrowser.resize(801, 499) self.horizontalLayout = QHBoxLayout(w_sceneBrowser) self.horizontalLayout.setObjectName(u"horizontalLayout") - self.splitter_5 = QSplitter(w_sceneBrowser) - self.splitter_5.setObjectName(u"splitter_5") - self.splitter_5.setOrientation(Qt.Horizontal) - self.verticalWidget_3 = QWidget(self.splitter_5) + self.splitter1 = QSplitter(w_sceneBrowser) + self.splitter1.setObjectName(u"splitter1") + self.splitter1.setOrientation(Qt.Horizontal) + self.verticalWidget_3 = QWidget(self.splitter1) self.verticalWidget_3.setObjectName(u"verticalWidget_3") sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(10) @@ -30,12 +30,12 @@ def setupUi(self, w_sceneBrowser): sizePolicy.setHeightForWidth(self.verticalWidget_3.sizePolicy().hasHeightForWidth()) self.verticalWidget_3.setSizePolicy(sizePolicy) self.verticalLayout_2 = QVBoxLayout(self.verticalWidget_3) - self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setObjectName(u"verticalLayout_2") - self.splitter_2 = QSplitter(self.verticalWidget_3) - self.splitter_2.setObjectName(u"splitter_2") - self.splitter_2.setOrientation(Qt.Vertical) - self.horizontalWidget_2 = QWidget(self.splitter_2) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.splitter2 = QSplitter(self.verticalWidget_3) + self.splitter2.setObjectName(u"splitter2") + self.splitter2.setOrientation(Qt.Vertical) + self.horizontalWidget_2 = QWidget(self.splitter2) self.horizontalWidget_2.setObjectName(u"horizontalWidget_2") sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy1.setHorizontalStretch(0) @@ -43,23 +43,23 @@ def setupUi(self, w_sceneBrowser): sizePolicy1.setHeightForWidth(self.horizontalWidget_2.sizePolicy().hasHeightForWidth()) self.horizontalWidget_2.setSizePolicy(sizePolicy1) self.horizontalLayout_13 = QHBoxLayout(self.horizontalWidget_2) - self.horizontalLayout_13.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_13.setObjectName(u"horizontalLayout_13") - self.splitter = QSplitter(self.horizontalWidget_2) - self.splitter.setObjectName(u"splitter") - self.splitter.setOrientation(Qt.Horizontal) - self.widget_23 = QWidget(self.splitter) + self.horizontalLayout_13.setContentsMargins(0, 0, 0, 0) + self.splitter3 = QSplitter(self.horizontalWidget_2) + self.splitter3.setObjectName(u"splitter3") + self.splitter3.setOrientation(Qt.Horizontal) + self.widget_23 = QWidget(self.splitter3) self.widget_23.setObjectName(u"widget_23") sizePolicy.setHeightForWidth(self.widget_23.sizePolicy().hasHeightForWidth()) self.widget_23.setSizePolicy(sizePolicy) self.verticalLayout_22 = QVBoxLayout(self.widget_23) - self.verticalLayout_22.setContentsMargins(0, 0, 0, 0) self.verticalLayout_22.setObjectName(u"verticalLayout_22") + self.verticalLayout_22.setContentsMargins(0, 0, 0, 0) self.w_departmentsHeader = QWidget(self.widget_23) self.w_departmentsHeader.setObjectName(u"w_departmentsHeader") self.verticalLayout_3 = QVBoxLayout(self.w_departmentsHeader) - self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.l_departments = QLabel(self.w_departmentsHeader) self.l_departments.setObjectName(u"l_departments") @@ -76,8 +76,8 @@ def setupUi(self, w_sceneBrowser): self.verticalLayout_22.addWidget(self.lw_departments) - self.splitter.addWidget(self.widget_23) - self.w_tasks = QWidget(self.splitter) + self.splitter3.addWidget(self.widget_23) + self.w_tasks = QWidget(self.splitter3) self.w_tasks.setObjectName(u"w_tasks") sizePolicy2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy2.setHorizontalStretch(15) @@ -85,13 +85,13 @@ def setupUi(self, w_sceneBrowser): sizePolicy2.setHeightForWidth(self.w_tasks.sizePolicy().hasHeightForWidth()) self.w_tasks.setSizePolicy(sizePolicy2) self.verticalLayout_23 = QVBoxLayout(self.w_tasks) - self.verticalLayout_23.setContentsMargins(0, 0, 0, 0) self.verticalLayout_23.setObjectName(u"verticalLayout_23") + self.verticalLayout_23.setContentsMargins(0, 0, 0, 0) self.w_tasksHeader = QWidget(self.w_tasks) self.w_tasksHeader.setObjectName(u"w_tasksHeader") self.verticalLayout_5 = QVBoxLayout(self.w_tasksHeader) - self.verticalLayout_5.setContentsMargins(0, 0, 0, 0) self.verticalLayout_5.setObjectName(u"verticalLayout_5") + self.verticalLayout_5.setContentsMargins(0, 0, 0, 0) self.l_tasks = QLabel(self.w_tasksHeader) self.l_tasks.setObjectName(u"l_tasks") @@ -109,12 +109,12 @@ def setupUi(self, w_sceneBrowser): self.verticalLayout_23.addWidget(self.lw_tasks) - self.splitter.addWidget(self.w_tasks) + self.splitter3.addWidget(self.w_tasks) - self.horizontalLayout_13.addWidget(self.splitter) + self.horizontalLayout_13.addWidget(self.splitter3) - self.splitter_2.addWidget(self.horizontalWidget_2) - self.gb_entityInfo = QGroupBox(self.splitter_2) + self.splitter2.addWidget(self.horizontalWidget_2) + self.gb_entityInfo = QGroupBox(self.splitter2) self.gb_entityInfo.setObjectName(u"gb_entityInfo") sizePolicy3 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy3.setHorizontalStretch(0) @@ -141,8 +141,8 @@ def setupUi(self, w_sceneBrowser): self.widget_8.setObjectName(u"widget_8") self.horizontalLayout_14 = QHBoxLayout(self.widget_8) self.horizontalLayout_14.setSpacing(0) - self.horizontalLayout_14.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_14.setObjectName(u"horizontalLayout_14") + self.horizontalLayout_14.setContentsMargins(0, 0, 0, 0) self.horizontalSpacer_6 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_14.addItem(self.horizontalSpacer_6) @@ -167,12 +167,12 @@ def setupUi(self, w_sceneBrowser): self.lo_entityDetails.addWidget(self.widget_8) - self.splitter_2.addWidget(self.gb_entityInfo) + self.splitter2.addWidget(self.gb_entityInfo) - self.verticalLayout_2.addWidget(self.splitter_2) + self.verticalLayout_2.addWidget(self.splitter2) - self.splitter_5.addWidget(self.verticalWidget_3) - self.widget_4 = QWidget(self.splitter_5) + self.splitter1.addWidget(self.verticalWidget_3) + self.widget_4 = QWidget(self.splitter1) self.widget_4.setObjectName(u"widget_4") sizePolicy5 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy5.setHorizontalStretch(50) @@ -180,13 +180,13 @@ def setupUi(self, w_sceneBrowser): sizePolicy5.setHeightForWidth(self.widget_4.sizePolicy().hasHeightForWidth()) self.widget_4.setSizePolicy(sizePolicy5) self.verticalLayout_4 = QVBoxLayout(self.widget_4) - self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.verticalLayout_4.setObjectName(u"verticalLayout_4") + self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.w_scenefileHeader = QWidget(self.widget_4) self.w_scenefileHeader.setObjectName(u"w_scenefileHeader") self.horizontalLayout_12 = QHBoxLayout(self.w_scenefileHeader) - self.horizontalLayout_12.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_12.setObjectName(u"horizontalLayout_12") + self.horizontalLayout_12.setContentsMargins(0, 0, 0, 0) self.l_scenefiles = QLabel(self.w_scenefileHeader) self.l_scenefiles.setObjectName(u"l_scenefiles") sizePolicy6 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) @@ -206,8 +206,8 @@ def setupUi(self, w_sceneBrowser): self.widget.setSizePolicy(sizePolicy7) self.horizontalLayout_2 = QHBoxLayout(self.widget) self.horizontalLayout_2.setSpacing(0) - self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.b_sceneLayoutItems = QToolButton(self.widget) self.b_sceneLayoutItems.setObjectName(u"b_sceneLayoutItems") self.b_sceneLayoutItems.setCheckable(True) @@ -238,8 +238,8 @@ def setupUi(self, w_sceneBrowser): self.w_scenePage1 = QWidget() self.w_scenePage1.setObjectName(u"w_scenePage1") self.horizontalLayout_3 = QHBoxLayout(self.w_scenePage1) - self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) self.tw_scenefiles = QTableView(self.w_scenePage1) self.tw_scenefiles.setObjectName(u"tw_scenefiles") self.tw_scenefiles.setContextMenuPolicy(Qt.CustomContextMenu) @@ -263,8 +263,8 @@ def setupUi(self, w_sceneBrowser): self.w_scenePage2 = QWidget() self.w_scenePage2.setObjectName(u"w_scenePage2") self.verticalLayout = QVBoxLayout(self.w_scenePage2) - self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName(u"verticalLayout") + self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.sa_scenefileItems = QScrollArea(self.w_scenePage2) self.sa_scenefileItems.setObjectName(u"sa_scenefileItems") self.sa_scenefileItems.setContextMenuPolicy(Qt.CustomContextMenu) @@ -280,9 +280,9 @@ def setupUi(self, w_sceneBrowser): self.verticalLayout_4.addWidget(self.sw_scenefiles) - self.splitter_5.addWidget(self.widget_4) + self.splitter1.addWidget(self.widget_4) - self.horizontalLayout.addWidget(self.splitter_5) + self.horizontalLayout.addWidget(self.splitter1) self.retranslateUi(w_sceneBrowser) diff --git a/Prism/Scripts/ProjectSettings.py b/Prism/Scripts/ProjectSettings.py index b15f38cb..94e887d3 100644 --- a/Prism/Scripts/ProjectSettings.py +++ b/Prism/Scripts/ProjectSettings.py @@ -895,6 +895,9 @@ def saveSettings(self, changeProject=True, configPath=None, export=False): self.sp_prjResolutionWidth.value(), self.sp_prjResolutionHeight.value(), ] + cData["globals"]["versionPadding"] = self.sp_curPversionPadding.value() + cData["globals"]["framePadding"] = self.sp_curPframePadding.value() + cData["globals"]["prism_version"] = self.e_version.text() cData["globals"]["useMasterVersion"] = self.chb_curPuseMaster.isChecked() cData["globals"][ "useMasterRenderVersion" @@ -1093,6 +1096,12 @@ def loadSettings(self, configPath=None): if "resolution" in gblData: self.sp_prjResolutionWidth.setValue(gblData["resolution"][0]) self.sp_prjResolutionHeight.setValue(gblData["resolution"][1]) + if "versionPadding" in gblData: + self.sp_curPversionPadding.setValue(gblData["versionPadding"]) + if "framePadding" in gblData: + self.sp_curPframePadding.setValue(gblData["framePadding"]) + if "prism_version" in gblData: + self.e_version.setText(gblData["prism_version"]) if "useMasterVersion" in gblData: self.chb_curPuseMaster.setChecked(gblData["useMasterVersion"]) if "useMasterRenderVersion" in gblData: @@ -1160,7 +1169,7 @@ def refreshAssetDepartments(self, departments=None, configData=None): if configData is None: configData = self.projectData - departments = self.core.projects.getAssetDepartments(configData=configData) + departments = self.core.projects.getAssetDepartments(configData=configData) or [] self.tw_assetDepartments.setRowCount(0) for dep in departments: @@ -1185,7 +1194,7 @@ def refreshShotDepartments(self, departments=None, configData=None): if configData is None: configData = self.projectData - departments = self.core.projects.getShotDepartments(configData=configData) + departments = self.core.projects.getShotDepartments(configData=configData) or [] self.tw_shotDepartments.setRowCount(0) for dep in departments: diff --git a/Prism/Scripts/UserInterfacesPrism/ProjectSettings.ui b/Prism/Scripts/UserInterfacesPrism/ProjectSettings.ui index affef8a9..d1ac5589 100644 --- a/Prism/Scripts/UserInterfacesPrism/ProjectSettings.ui +++ b/Prism/Scripts/UserInterfacesPrism/ProjectSettings.ui @@ -32,7 +32,7 @@ - 1 + 0 @@ -49,8 +49,8 @@ 0 0 - 643 - 652 + 626 + 713 @@ -834,6 +834,165 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Version Padding: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + 1 + + + 4 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Frame Padding: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + 1 + + + 4 + + + + + + + + + + Specifies the version of the project. Prism can change the behavior of specific features to provide backwards compatibility with projects, which were created with older Prism versions. + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 300 + 0 + + + + + + + + Project Version: + + + + + + diff --git a/Prism/Scripts/UserInterfacesPrism/ProjectSettings_ui.py b/Prism/Scripts/UserInterfacesPrism/ProjectSettings_ui.py index a2dbf429..22f82ba8 100644 --- a/Prism/Scripts/UserInterfacesPrism/ProjectSettings_ui.py +++ b/Prism/Scripts/UserInterfacesPrism/ProjectSettings_ui.py @@ -39,7 +39,7 @@ def setupUi(self, dlg_ProjectSettings): self.sa_general.setWidgetResizable(True) self.w_saGeneralContent = QWidget() self.w_saGeneralContent.setObjectName(u"w_saGeneralContent") - self.w_saGeneralContent.setGeometry(QRect(0, 0, 643, 652)) + self.w_saGeneralContent.setGeometry(QRect(0, 0, 626, 713)) self.verticalLayout_8 = QVBoxLayout(self.w_saGeneralContent) self.verticalLayout_8.setObjectName(u"verticalLayout_8") self.verticalLayout_8.setContentsMargins(0, 0, 0, 0) @@ -419,6 +419,79 @@ def setupUi(self, dlg_ProjectSettings): self.verticalLayout_9.addWidget(self.w_prjResolution) + self.w_curPversionPadding = QWidget(self.w_prjSettings) + self.w_curPversionPadding.setObjectName(u"w_curPversionPadding") + self.horizontalLayout_25 = QHBoxLayout(self.w_curPversionPadding) + self.horizontalLayout_25.setObjectName(u"horizontalLayout_25") + self.horizontalLayout_25.setContentsMargins(0, 0, 0, 0) + self.label_14 = QLabel(self.w_curPversionPadding) + self.label_14.setObjectName(u"label_14") + + self.horizontalLayout_25.addWidget(self.label_14) + + self.horizontalSpacer_26 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_25.addItem(self.horizontalSpacer_26) + + self.sp_curPversionPadding = QSpinBox(self.w_curPversionPadding) + self.sp_curPversionPadding.setObjectName(u"sp_curPversionPadding") + self.sp_curPversionPadding.setMinimumSize(QSize(60, 0)) + self.sp_curPversionPadding.setMinimum(1) + self.sp_curPversionPadding.setValue(4) + + self.horizontalLayout_25.addWidget(self.sp_curPversionPadding) + + + self.verticalLayout_9.addWidget(self.w_curPversionPadding) + + self.w_curPframePadding = QWidget(self.w_prjSettings) + self.w_curPframePadding.setObjectName(u"w_curPframePadding") + self.horizontalLayout_26 = QHBoxLayout(self.w_curPframePadding) + self.horizontalLayout_26.setObjectName(u"horizontalLayout_26") + self.horizontalLayout_26.setContentsMargins(0, 0, 0, 0) + self.label_15 = QLabel(self.w_curPframePadding) + self.label_15.setObjectName(u"label_15") + + self.horizontalLayout_26.addWidget(self.label_15) + + self.horizontalSpacer_27 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_26.addItem(self.horizontalSpacer_27) + + self.sp_curPframePadding = QSpinBox(self.w_curPframePadding) + self.sp_curPframePadding.setObjectName(u"sp_curPframePadding") + self.sp_curPframePadding.setMinimumSize(QSize(60, 0)) + self.sp_curPframePadding.setMinimum(1) + self.sp_curPframePadding.setValue(4) + + self.horizontalLayout_26.addWidget(self.sp_curPframePadding) + + + self.verticalLayout_9.addWidget(self.w_curPframePadding) + + self.w_expectedPrjPath_2 = QWidget(self.w_prjSettings) + self.w_expectedPrjPath_2.setObjectName(u"w_expectedPrjPath_2") + self.gridLayout_5 = QGridLayout(self.w_expectedPrjPath_2) + self.gridLayout_5.setObjectName(u"gridLayout_5") + self.gridLayout_5.setContentsMargins(0, 0, 0, 0) + self.horizontalSpacer_24 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.gridLayout_5.addItem(self.horizontalSpacer_24, 0, 1, 1, 1) + + self.e_version = QLineEdit(self.w_expectedPrjPath_2) + self.e_version.setObjectName(u"e_version") + self.e_version.setMinimumSize(QSize(300, 0)) + + self.gridLayout_5.addWidget(self.e_version, 0, 2, 1, 1) + + self.l_expectedPrjPath_2 = QLabel(self.w_expectedPrjPath_2) + self.l_expectedPrjPath_2.setObjectName(u"l_expectedPrjPath_2") + + self.gridLayout_5.addWidget(self.l_expectedPrjPath_2, 0, 0, 1, 1) + + + self.verticalLayout_9.addWidget(self.w_expectedPrjPath_2) + self.w_reqPlugins = QWidget(self.w_prjSettings) self.w_reqPlugins.setObjectName(u"w_reqPlugins") self.gridLayout_2 = QGridLayout(self.w_reqPlugins) @@ -926,7 +999,7 @@ def setupUi(self, dlg_ProjectSettings): self.buttonBox.accepted.connect(dlg_ProjectSettings.accept) self.buttonBox.rejected.connect(dlg_ProjectSettings.reject) - self.tw_settings.setCurrentIndex(1) + self.tw_settings.setCurrentIndex(0) QMetaObject.connectSlotsByName(dlg_ProjectSettings) @@ -967,6 +1040,12 @@ def retranslateUi(self, dlg_ProjectSettings): self.l_prjResolution.setText(QCoreApplication.translate("dlg_ProjectSettings", u"Resolution:", None)) self.chb_prjResolution.setText("") self.l_prjResolutionX.setText(QCoreApplication.translate("dlg_ProjectSettings", u"x", None)) + self.label_14.setText(QCoreApplication.translate("dlg_ProjectSettings", u"Version Padding:", None)) + self.label_15.setText(QCoreApplication.translate("dlg_ProjectSettings", u"Frame Padding:", None)) +#if QT_CONFIG(tooltip) + self.w_expectedPrjPath_2.setToolTip(QCoreApplication.translate("dlg_ProjectSettings", u"Specifies the version of the project. Prism can change the behavior of specific features to provide backwards compatibility with projects, which were created with older Prism versions.", None)) +#endif // QT_CONFIG(tooltip) + self.l_expectedPrjPath_2.setText(QCoreApplication.translate("dlg_ProjectSettings", u"Project Version:", None)) self.b_reqPlugins.setText(QCoreApplication.translate("dlg_ProjectSettings", u"...", None)) self.l_reqPlugins.setText(QCoreApplication.translate("dlg_ProjectSettings", u"Required Plugins:", None)) self.b_expectedPrjPath.setText(QCoreApplication.translate("dlg_ProjectSettings", u"...", None)) diff --git a/Prism/Scripts/UserInterfacesPrism/UserSettings.ui b/Prism/Scripts/UserInterfacesPrism/UserSettings.ui index a0ed5d3e..547fae02 100644 --- a/Prism/Scripts/UserInterfacesPrism/UserSettings.ui +++ b/Prism/Scripts/UserInterfacesPrism/UserSettings.ui @@ -39,7 +39,7 @@ 0 0 - 597 + 457 739 @@ -804,13 +804,6 @@ - - - - HighDPI support (requires complete application restart) (experimental) - - - diff --git a/Prism/Scripts/UserInterfacesPrism/UserSettings_ui.py b/Prism/Scripts/UserInterfacesPrism/UserSettings_ui.py index f2ef4e50..ddb7e3a9 100644 --- a/Prism/Scripts/UserInterfacesPrism/UserSettings_ui.py +++ b/Prism/Scripts/UserInterfacesPrism/UserSettings_ui.py @@ -33,7 +33,7 @@ def setupUi(self, dlg_UserSettings): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 597, 739)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 457, 739)) self.horizontalLayout_9 = QHBoxLayout(self.scrollAreaWidgetContents) self.horizontalLayout_9.setSpacing(20) self.horizontalLayout_9.setObjectName(u"horizontalLayout_9") @@ -415,11 +415,6 @@ def setupUi(self, dlg_UserSettings): self.lo_miscellaneous.addWidget(self.w_startTray) - self.chb_highDPI = QCheckBox(self.gb_miscellaneous) - self.chb_highDPI.setObjectName(u"chb_highDPI") - - self.lo_miscellaneous.addWidget(self.chb_highDPI) - self.chb_errorReports = QCheckBox(self.gb_miscellaneous) self.chb_errorReports.setObjectName(u"chb_errorReports") self.chb_errorReports.setChecked(True) @@ -630,7 +625,6 @@ def retranslateUi(self, dlg_UserSettings): self.chb_mediaThumbnails.setText(QCoreApplication.translate("dlg_UserSettings", u"Automatically generate thumbnails for media", None)) self.chb_trayStartup.setText(QCoreApplication.translate("dlg_UserSettings", u"Show Prism tray icon on system startup", None)) self.b_startTray.setText(QCoreApplication.translate("dlg_UserSettings", u"Start Prism tray now", None)) - self.chb_highDPI.setText(QCoreApplication.translate("dlg_UserSettings", u"HighDPI support (requires complete application restart) (experimental)", None)) self.chb_errorReports.setText(QCoreApplication.translate("dlg_UserSettings", u"Send anonymous error reports", None)) self.chb_debug.setText(QCoreApplication.translate("dlg_UserSettings", u"Debug mode", None)) self.l_styleSheet.setText(QCoreApplication.translate("dlg_UserSettings", u"Standalone Style Sheet:", None)) diff --git a/Prism/Scripts/UserInterfacesPrism/discord.png b/Prism/Scripts/UserInterfacesPrism/discord.png new file mode 100644 index 00000000..baececa9 Binary files /dev/null and b/Prism/Scripts/UserInterfacesPrism/discord.png differ diff --git a/Prism/Scripts/UserInterfacesPrism/stylesheets/blue_moon/style.qss b/Prism/Scripts/UserInterfacesPrism/stylesheets/blue_moon/style.qss index 622e1894..7f736bd0 100644 --- a/Prism/Scripts/UserInterfacesPrism/stylesheets/blue_moon/style.qss +++ b/Prism/Scripts/UserInterfacesPrism/stylesheets/blue_moon/style.qss @@ -143,6 +143,11 @@ QMenuBar::item { background-color: @tableBackground; } +QMenuBar::item { + color: @white; + background-color: transparent; +} + QMenuBar::item { padding: 6px; } @@ -556,7 +561,7 @@ QTabBar::tab { QTabBar::tab:top, QTabBar::tab:bottom { - min-width: 14ex; + min-width: 14px; min-height: 20px; padding: 0px 8px 0px 8px; margin: 0px 0px 10px 0px; @@ -564,7 +569,7 @@ QTabBar::tab:bottom { QTabBar::tab:left, QTabBar::tab:right { - min-height: 14ex; + min-height: 14px; min-width: 20px; padding: 8px 0px 8px 0px; } diff --git a/Prism/prism.bat b/Prism/prism.bat new file mode 100644 index 00000000..dedb6b9b --- /dev/null +++ b/Prism/prism.bat @@ -0,0 +1,2 @@ +start "" "%~dp0/Python311/pythonw.exe" "%~dp0/Scripts/PrismCore.py" +::PAUSE \ No newline at end of file diff --git a/Prism/setup.bat b/Prism/setup.bat index 3d08c31c..8c2fe30c 100644 --- a/Prism/setup.bat +++ b/Prism/setup.bat @@ -1,2 +1,2 @@ -"%~dp0/Python39/python.exe" "%~dp0/Scripts/PrismInstaller.py" +"%~dp0/Python311/python.exe" "%~dp0/Scripts/PrismInstaller.py" ::PAUSE \ No newline at end of file diff --git a/Prism/uninstall.bat b/Prism/uninstall.bat index 7c3e0606..64a12e04 100644 --- a/Prism/uninstall.bat +++ b/Prism/uninstall.bat @@ -1,2 +1,2 @@ -"%~dp0/Python39/python.exe" "%~dp0/Scripts/PrismInstaller.py" uninstall +"%~dp0/Python311/python.exe" "%~dp0/Scripts/PrismInstaller.py" uninstall ::PAUSE \ No newline at end of file diff --git a/README.md b/README.md index 16e9de89..29bf496d 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ Download the installer from the [website](https://prism-pipeline.com/downloads/) (This version allows you to automatically update Prism and install new plugins) or to install Prism manually: - -* Download or clone this repository and download the [Prism dependencies](https://www.dropbox.com/scl/fi/zh9t0im2qsd6mtlmpd9vs/Prism_dependencies_v2.0.0.zip?rlkey=o0lhrixa5klm3bell35wuhvsy&dl=1). +* Download or clone this repository and download the [Prism dependencies](https://www.dropbox.com/scl/fi/bmgsht89nb9u04sqprzrp/Prism_dependencies_v2.0.5.zip?rlkey=wy0rtw56chky6kt1mnxnept0t&st=jkiho4o9&dl=1). * Extract the dependencies and copy the extracted folders into the "Prism" folder of this repository.