Merge branch 'develop' into enhancement/OP-7120-blender_output-node-exr

This commit is contained in:
Libor Batek 2024-02-20 09:11:41 +01:00 committed by GitHub
commit 1b5d52b168
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 205 additions and 269 deletions

View file

@ -35,6 +35,10 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
- 3.18.7-nightly.5
- 3.18.7-nightly.4
- 3.18.7-nightly.3
- 3.18.7-nightly.2
- 3.18.7-nightly.1
- 3.18.6
- 3.18.6-nightly.2
@ -131,10 +135,6 @@ body:
- 3.15.11-nightly.1
- 3.15.10
- 3.15.10-nightly.2
- 3.15.10-nightly.1
- 3.15.9
- 3.15.9-nightly.2
- 3.15.9-nightly.1
validations:
required: true
- type: dropdown

View file

@ -1,53 +1,12 @@
## How to contribute to Pype
## How to contribute to OpenPype
We are always happy for any contributions for OpenPype improvements. Before making a PR and starting working on an issue, please read these simple guidelines.
OpenPype has reached the end of its life and is now in a limited maintenance mode (read more at https://community.ynput.io/t/openpype-end-of-life-timeline/877). As such we're no longer accepting contributions unless they are also ported to AYON at the same time.
#### **Did you find a bug?**
## Getting my PR merged during this period
1. Check in the issues and our [bug triage[(https://github.com/pypeclub/pype/projects/2) to make sure it wasn't reported already.
2. Ask on our [discord](http://pype.community/chat) Often, what appears as a bug, might be the intended behaviour for someone else.
3. Create a new issue.
4. Use the issue template for you PR please.
- Each OpenPype PR MUST have a corresponding AYON PR in github. Without AYON compatibility features will not be merged! Luckily most of the code is compatible, albeit sometimes in a different place after refactor. Porting from OpenPype to AYON should be really easy.
- Please keep the corresponding OpenPype and AYON PR names the same so they can be easily identified.
Inside each PR, put a link to the corresponding PR from the other product. OpenPype PRs should point to AYON PR and vice versa.
#### **Did you write a patch that fixes a bug?**
- Open a new GitHub pull request with the patch.
- Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
#### **Do you intend to add a new feature or change an existing one?**
- Open a new thread in the [github discussions](https://github.com/pypeclub/pype/discussions/new)
- Do not open issue until the suggestion is discussed. We will convert accepted suggestions into backlog and point them to the relevant discussion thread to keep the context.
- If you are already working on a new feature and you'd like it eventually merged to the main codebase, please consider making a DRAFT PR as soon as possible. This makes it a lot easier to give feedback, discuss the code and functionalit, plus it prevents multiple people tackling the same problem independently.
#### **Do you have questions about the source code?**
Open a new question on [github discussions](https://github.com/pypeclub/pype/discussions/new)
## Branching Strategy
As we move to 3.x as the primary supported version of pype and only keep 2.15 on bug bugfixes and client sponsored feature requests, we need to be very careful with merging strategy.
We also use this opportunity to switch the branch naming. 3.0 production branch will no longer be called MASTER, but will be renamed to MAIN. Develop will stay as it is.
A few important notes about 2.x and 3.x development:
- 3.x features are not backported to 2.x unless specifically requested
- 3.x bugs and hotfixes can be ported to 2.x if they are relevant or severe
- 2.x features and bugs MUST be ported to 3.x at the same time
## Pull Requests
- Each 2.x PR MUST have a corresponding 3.x PR in github. Without 3.x PR, 2.x features will not be merged! Luckily most of the code is compatible, albeit sometimes in a different place after refactor. Porting from 2.x to 3.x should be really easy.
- Please keep the corresponding 2 and 3 PR names the same so they can be easily identified from the PR list page.
- Each 2.x PR should be labeled with `2.x-dev` label.
Inside each PR, put a link to the corresponding PR for the other version
Of course if you want to contribute, feel free to make a PR to only 2.x/develop or develop, based on what you are using. While reviewing the PRs, we might convert the code to corresponding PR for the other release ourselves.
We might also change the target of you PR to and intermediate branch, rather than `develop` if we feel it requires some extra work on our end. That way, we preserve all your commits so you don't loose out on the contribution credits.
If a PR is targeted at 2.x release it must be labelled with 2x-dev label in Github.
AYON repository structure is a lot more granular compared to OpenPype. If you're unsure what repository your AYON equivalent PR should target, feel free to make OpenPype PR first and ask.

View file

@ -9,8 +9,13 @@ OpenPype
## Important Notice!
OpenPype as a standalone product has reach end of it's life and this repository is now used as a pipeline core code for [AYON](https://ynput.io/ayon/). You can read more details about the end of life process here https://community.ynput.io/t/openpype-end-of-life-timeline/877
OpenPype as a standalone product has reach end of it's life and this repository is now being phased out in favour of [ayon-core](https://github.com/ynput/ayon-core). You can read more details about the end of life process here https://community.ynput.io/t/openpype-end-of-life-timeline/877
As such, we no longer accept Pull Requests that are not ported to AYON at the same time!
```
Please refer to https://github.com/ynput/OpenPype/blob/develop/CONTRIBUTING.md for more information about the current PR process.
```
Introduction
------------

View file

@ -29,6 +29,7 @@ class RenderCreator(Creator):
# Settings
mark_for_review = True
force_setting_values = True
def create(self, subset_name_from_ui, data, pre_create_data):
stub = api.get_stub() # only after After Effects is up
@ -96,7 +97,9 @@ class RenderCreator(Creator):
self._add_instance_to_context(new_instance)
stub.rename_item(comp.id, subset_name)
set_settings(True, True, [comp.id], print_msg=False)
if self.force_setting_values:
set_settings(True, True, [comp.id], print_msg=False)
def get_pre_create_attr_defs(self):
output = [
@ -173,6 +176,7 @@ class RenderCreator(Creator):
)
self.mark_for_review = plugin_settings["mark_for_review"]
self.force_setting_values = plugin_settings["force_setting_values"]
self.default_variants = plugin_settings.get(
"default_variants",
plugin_settings.get("defaults") or []

View file

@ -15,6 +15,7 @@ from openpype.hosts.fusion.api.lib import (
)
from openpype.pipeline import get_current_asset_name
from openpype.resources import get_openpype_icon_filepath
from openpype.tools.utils import get_qt_app
from .pipeline import FusionEventHandler
from .pulse import FusionPulse
@ -174,7 +175,8 @@ class OpenPypeMenu(QtWidgets.QWidget):
def launch_openpype_menu():
app = QtWidgets.QApplication(sys.argv)
app = get_qt_app()
pype_menu = OpenPypeMenu()

View file

@ -59,10 +59,11 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
rt.callbacks.addScript(rt.Name('filePostOpen'),
lib.check_colorspace)
rt.callbacks.addScript(rt.Name('postWorkspaceChange'),
self._deferred_menu_creation)
def has_unsaved_changes(self):
# TODO: how to get it from 3dsmax?
return True
def workfile_has_unsaved_changes(self):
return rt.getSaveRequired()
def get_workfile_extensions(self):
return [".max"]

View file

@ -1,11 +1,9 @@
import pyblish.api
import os
from openpype.pipeline import registered_host
class SaveCurrentScene(pyblish.api.ContextPlugin):
"""Save current scene
"""
"""Save current scene"""
label = "Save current file"
order = pyblish.api.ExtractorOrder - 0.49
@ -13,9 +11,13 @@ class SaveCurrentScene(pyblish.api.ContextPlugin):
families = ["maxrender", "workfile"]
def process(self, context):
from pymxs import runtime as rt
folder = rt.maxFilePath
file = rt.maxFileName
current = os.path.join(folder, file)
assert context.data["currentFile"] == current
rt.saveMaxFile(current)
host = registered_host()
current_file = host.get_current_workfile()
assert context.data["currentFile"] == current_file
if host.workfile_has_unsaved_changes():
self.log.info(f"Saving current file: {current_file}")
host.save_workfile(current_file)
else:
self.log.debug("No unsaved changes, skipping file save..")

View file

@ -7,6 +7,9 @@
local pythonpath = systemTools.getEnvVariable "MAX_PYTHONPATH"
systemTools.setEnvVariable "PYTHONPATH" pythonpath
/*opens the create menu on startup to ensure users are presented with a useful default view.*/
max create mode
python.ExecuteFile startup
)
)

View file

@ -3142,119 +3142,6 @@ def fix_incompatible_containers():
"ReferenceLoader", type="string")
def _null(*args):
pass
class shelf():
'''A simple class to build shelves in maya. Since the build method is empty,
it should be extended by the derived class to build the necessary shelf
elements. By default it creates an empty shelf called "customShelf".'''
###########################################################################
'''This is an example shelf.'''
# class customShelf(_shelf):
# def build(self):
# self.addButon(label="button1")
# self.addButon("button2")
# self.addButon("popup")
# p = cmds.popupMenu(b=1)
# self.addMenuItem(p, "popupMenuItem1")
# self.addMenuItem(p, "popupMenuItem2")
# sub = self.addSubMenu(p, "subMenuLevel1")
# self.addMenuItem(sub, "subMenuLevel1Item1")
# sub2 = self.addSubMenu(sub, "subMenuLevel2")
# self.addMenuItem(sub2, "subMenuLevel2Item1")
# self.addMenuItem(sub2, "subMenuLevel2Item2")
# self.addMenuItem(sub, "subMenuLevel1Item2")
# self.addMenuItem(p, "popupMenuItem3")
# self.addButon("button3")
# customShelf()
###########################################################################
def __init__(self, name="customShelf", iconPath="", preset={}):
self.name = name
self.iconPath = iconPath
self.labelBackground = (0, 0, 0, 0)
self.labelColour = (.9, .9, .9)
self.preset = preset
self._cleanOldShelf()
cmds.setParent(self.name)
self.build()
def build(self):
'''This method should be overwritten in derived classes to actually
build the shelf elements. Otherwise, nothing is added to the shelf.'''
for item in self.preset['items']:
if not item.get('command'):
item['command'] = self._null
if item['type'] == 'button':
self.addButon(item['name'],
command=item['command'],
icon=item['icon'])
if item['type'] == 'menuItem':
self.addMenuItem(item['parent'],
item['name'],
command=item['command'],
icon=item['icon'])
if item['type'] == 'subMenu':
self.addMenuItem(item['parent'],
item['name'],
command=item['command'],
icon=item['icon'])
def addButon(self, label, icon="commandButton.png",
command=_null, doubleCommand=_null):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
cmds.setParent(self.name)
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
cmds.shelfButton(width=37, height=37, image=icon, label=label,
command=command, dcc=doubleCommand,
imageOverlayLabel=label, olb=self.labelBackground,
olc=self.labelColour)
def addMenuItem(self, parent, label, command=_null, icon=""):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
return cmds.menuItem(p=parent, label=label, c=command, i="")
def addSubMenu(self, parent, label, icon=None):
'''
Adds a sub menu item with the specified label and icon to
the specified parent popup menu.
'''
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
return cmds.menuItem(p=parent, label=label, i=icon, subMenu=1)
def _cleanOldShelf(self):
'''
Checks if the shelf exists and empties it if it does
or creates it if it does not.
'''
if cmds.shelfLayout(self.name, ex=1):
if cmds.shelfLayout(self.name, q=1, ca=1):
for each in cmds.shelfLayout(self.name, q=1, ca=1):
cmds.deleteUI(each)
else:
cmds.shelfLayout(self.name, p="ShelfLayout")
def update_content_on_context_change():
"""
This will update scene content to match new asset on context change

View file

@ -7,6 +7,7 @@ from openpype.hosts.maya.api import lib
from openpype.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
PublishValidationError
)
@ -38,7 +39,8 @@ class ValidateRigJointsHidden(pyblish.api.InstancePlugin):
invalid = self.get_invalid(instance)
if invalid:
raise ValueError("Visible joints found: {0}".format(invalid))
raise PublishValidationError(
"Visible joints found: {0}".format(invalid))
@classmethod
def repair(cls, instance):

View file

@ -46,24 +46,5 @@ if bool(int(os.environ.get(key, "0"))):
lowestPriority=True
)
# Build a shelf.
shelf_preset = settings['maya'].get('project_shelf')
if shelf_preset:
icon_path = os.path.join(
os.environ['OPENPYPE_PROJECT_SCRIPTS'],
project_name,
"icons")
icon_path = os.path.abspath(icon_path)
for i in shelf_preset['imports']:
import_string = "from {} import {}".format(project_name, i)
print(import_string)
exec(import_string)
cmds.evalDeferred(
"mlib.shelf(name=shelf_preset['name'], iconPath=icon_path,"
" preset=shelf_preset)"
)
print("Finished OpenPype usersetup.")

View file

@ -1348,7 +1348,9 @@ def _remove_old_knobs(node):
def exposed_write_knobs(settings, plugin_name, instance_node):
exposed_knobs = settings["nuke"]["create"][plugin_name]["exposed_knobs"]
exposed_knobs = settings["nuke"]["create"][plugin_name].get(
"exposed_knobs", []
)
if exposed_knobs:
instance_node.addKnob(nuke.Text_Knob('', 'Write Knobs'))
write_node = nuke.allNodes(group=instance_node, filter="Write")[0]

View file

@ -112,8 +112,6 @@ class AlembicCameraLoader(load.LoaderPlugin):
project_name = get_current_project_name()
version_doc = get_version_by_id(project_name, representation["parent"])
object_name = container["node"]
# get main variables
version_data = version_doc.get("data", {})
vname = version_doc.get("name", None)
@ -139,7 +137,7 @@ class AlembicCameraLoader(load.LoaderPlugin):
file = get_representation_path(representation).replace("\\", "/")
with maintained_selection():
camera_node = nuke.toNode(object_name)
camera_node = container["node"]
camera_node['selected'].setValue(True)
# collect input output dependencies
@ -154,9 +152,10 @@ class AlembicCameraLoader(load.LoaderPlugin):
xpos = camera_node.xpos()
ypos = camera_node.ypos()
nuke.nodeCopy("%clipboard%")
camera_name = camera_node.name()
nuke.delete(camera_node)
nuke.nodePaste("%clipboard%")
camera_node = nuke.toNode(object_name)
camera_node = nuke.toNode(camera_name)
camera_node.setXYpos(xpos, ypos)
# link to original input nodes

View file

@ -65,7 +65,7 @@ class ValidateExposedKnobs(
group_node = instance.data["transientData"]["node"]
nuke_settings = instance.context.data["project_settings"]["nuke"]
create_settings = nuke_settings["create"][plugin]
exposed_knobs = create_settings["exposed_knobs"]
exposed_knobs = create_settings.get("exposed_knobs", [])
unexposed_knobs = []
for knob in exposed_knobs:
if knob not in group_node.knobs():

View file

@ -129,7 +129,7 @@ class ValidateNukeWriteNode(
and key != "file"
and key != "tile_color"
):
check.append([key, node_value, write_node[key].value()])
check.append([key, fixed_values, write_node[key].value()])
if check:
self._make_error(check)
@ -137,7 +137,7 @@ class ValidateNukeWriteNode(
def _make_error(self, check):
# sourcery skip: merge-assign-and-aug-assign, move-assign-in-block
dbg_msg = "Write node's knobs values are not correct!\n"
msg_add = "Knob '{0}' > Correct: `{1}` > Wrong: `{2}`"
msg_add = "Knob '{0}' > Expected: `{1}` > Current: `{2}`"
details = [
msg_add.format(item[0], item[1], item[2])

View file

@ -713,6 +713,11 @@ def swap_clips(from_clip, to_clip, to_in_frame, to_out_frame):
bool: True if successfully replaced
"""
# copy ACES input transform from timeline clip to new media item
mediapool_item_from_timeline = from_clip.GetMediaPoolItem()
_idt = mediapool_item_from_timeline.GetClipProperty('IDT')
to_clip.SetClipProperty('IDT', _idt)
_clip_prop = to_clip.GetClipProperty
to_clip_name = _clip_prop("File Name")
# add clip item as take to timeline

View file

@ -477,14 +477,16 @@ class ClipLoader:
)
_clip_property = media_pool_item.GetClipProperty
source_in = int(_clip_property("Start"))
source_out = int(_clip_property("End"))
# Read trimming from timeline item
timeline_item_in = timeline_item.GetLeftOffset()
timeline_item_len = timeline_item.GetDuration()
timeline_item_out = timeline_item_in + timeline_item_len
lib.swap_clips(
timeline_item,
media_pool_item,
source_in,
source_out
timeline_item_in,
timeline_item_out
)
print("Loading clips: `{}`".format(self.data["clip_name"]))

View file

@ -32,6 +32,7 @@ SHARED_DATA_KEY = "openpype.traypublisher.instances"
class HiddenTrayPublishCreator(HiddenCreator):
host_name = "traypublisher"
settings_category = "traypublisher"
def collect_instances(self):
instances_by_identifier = cache_and_get_instances(
@ -68,6 +69,7 @@ class HiddenTrayPublishCreator(HiddenCreator):
class TrayPublishCreator(Creator):
create_allow_context_change = True
host_name = "traypublisher"
settings_category = "traypublisher"
def collect_instances(self):
instances_by_identifier = cache_and_get_instances(

View file

@ -381,15 +381,19 @@ or updating already created. Publishing will create OTIO file.
"""
self.asset_name_check = []
tracks = otio_timeline.each_child(
descended_from_type=otio.schema.Track
)
tracks = [
track for track in otio_timeline.each_child(
descended_from_type=otio.schema.Track)
if track.kind == "Video"
]
# media data for audio sream and reference solving
# media data for audio stream and reference solving
media_data = self._get_media_source_metadata(media_path)
for track in tracks:
# set track name
track.name = f"{sequence_file_name} - {otio_timeline.name}"
try:
track_start_frame = (
abs(track.source_range.start_time.value)
@ -398,19 +402,19 @@ or updating already created. Publishing will create OTIO file.
except AttributeError:
track_start_frame = 0
for clip in track.each_child():
if not self._validate_clip_for_processing(clip):
for otio_clip in track.each_child():
if not self._validate_clip_for_processing(otio_clip):
continue
# get available frames info to clip data
self._create_otio_reference(clip, media_path, media_data)
self._create_otio_reference(otio_clip, media_path, media_data)
# convert timeline range to source range
self._restore_otio_source_range(clip)
self._restore_otio_source_range(otio_clip)
base_instance_data = self._get_base_instance_data(
clip,
otio_clip,
instance_data,
track_start_frame
)
@ -429,7 +433,7 @@ or updating already created. Publishing will create OTIO file.
continue
instance = self._make_subset_instance(
clip,
otio_clip,
_fpreset,
deepcopy(base_instance_data),
parenting_data

View file

@ -79,6 +79,7 @@ class CollectShotInstance(pyblish.api.InstancePlugin):
clip for clip in otio_timeline.each_child(
descended_from_type=otio.schema.Clip)
if clip.name == otio_clip.name
if clip.parent().kind == "Video"
]
otio_clip = clips.pop()

@ -1 +1 @@
Subproject commit 63266607ceb972a61484f046634ddfc9eb0b5757
Subproject commit a4755d2869694fcf58c98119298cde8d204e2ce4

View file

@ -132,7 +132,7 @@ class PostFtrackHook(PostLaunchHook):
if key in already_tested:
continue
value = value.lower()
value = [i.lower() for i in value]
if actual_status in value or "__any__" in value:
if key != "__ignore__":
next_status_name = key

View file

@ -17,6 +17,7 @@ class WidgetUserIdle(QtWidgets.QWidget):
self.setWindowFlags(
QtCore.Qt.WindowCloseButtonHint
| QtCore.Qt.WindowMinimizeButtonHint
| QtCore.Qt.WindowStaysOnTopHint
)
self._is_showed = False

View file

@ -2,6 +2,7 @@ import copy
import os
import subprocess
import tempfile
import re
import pyblish.api
from openpype.lib import (
@ -14,9 +15,10 @@ from openpype.lib import (
path_to_subprocess_arg,
run_subprocess,
)
from openpype.lib.transcoding import convert_colorspace
from openpype.lib.transcoding import VIDEO_EXTENSIONS
from openpype.lib.transcoding import (
convert_colorspace,
VIDEO_EXTENSIONS,
)
class ExtractThumbnail(pyblish.api.InstancePlugin):
@ -35,6 +37,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
"traypublisher",
"substancepainter",
"nuke",
"aftereffects"
]
enabled = False
@ -49,6 +52,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
# attribute presets from settings
oiiotool_defaults = None
ffmpeg_args = None
subsets = []
product_names = []
def process(self, instance):
# run main process
@ -103,6 +108,26 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
self.log.debug("Skipping crypto passes.")
return
# We only want to process the subsets needed from settings.
def validate_string_against_patterns(input_str, patterns):
for pattern in patterns:
if re.match(pattern, input_str):
return True
return False
product_names = self.subsets + self.product_names
if product_names:
result = validate_string_against_patterns(
instance.data["subset"], product_names
)
if not result:
self.log.debug(
"Subset \"{}\" did not match any valid subsets: {}".format(
instance.data["subset"], product_names
)
)
return
# first check for any explicitly marked representations for thumbnail
explicit_repres = self._get_explicit_repres_for_thumbnail(instance)
if explicit_repres:

View file

@ -17,7 +17,7 @@ class ValidateResources(pyblish.api.InstancePlugin):
"""
order = ValidateContentsOrder
label = "Resources"
label = "Validate Resources"
def process(self, instance):

View file

@ -15,7 +15,8 @@
"default_variants": [
"Main"
],
"mark_for_review": true
"mark_for_review": true,
"force_setting_values": true
}
},
"publish": {

View file

@ -70,6 +70,7 @@
},
"ExtractThumbnail": {
"enabled": true,
"subsets": [],
"integrate_thumbnail": false,
"background_color": [
0,

View file

@ -42,6 +42,12 @@
"key": "mark_for_review",
"label": "Review",
"default": true
},
{
"type": "boolean",
"key": "force_setting_values",
"label": "Force resolution and duration values from Asset",
"default": true
}
]
}

View file

@ -202,6 +202,12 @@
"key": "enabled",
"label": "Enabled"
},
{
"type": "list",
"object_type": "text",
"key": "subsets",
"label": "Subsets"
},
{
"type": "boolean",
"key": "integrate_thumbnail",

View file

@ -2329,8 +2329,25 @@ class PublisherController(BasePublisherController):
result = pyblish.plugin.process(
plugin, self._publish_context, None, action.id
)
exception = result.get("error")
if exception:
self._emit_event(
"publish.action.failed",
{
"title": "Action failed",
"message": "Action failed.",
"traceback": "".join(
traceback.format_exception(exception)
),
"label": action.__name__,
"identifier": action.id
}
)
self._publish_report.add_action_result(action, result)
self.emit_card_message("Action finished.")
def _publish_next_process(self):
# Validations of progress before using iterator
# - same conditions may be inside iterator but they may be used

View file

@ -42,7 +42,7 @@ from .widgets import (
)
class PublisherWindow(QtWidgets.QWidget):
class PublisherWindow(QtWidgets.QDialog):
"""Main window of publisher."""
default_width = 1300
default_height = 800
@ -50,7 +50,7 @@ class PublisherWindow(QtWidgets.QWidget):
publish_footer_spacer = 2
def __init__(self, parent=None, controller=None, reset_on_show=None):
super(PublisherWindow, self).__init__()
super(PublisherWindow, self).__init__(parent)
self.setObjectName("PublishWindow")
@ -294,12 +294,6 @@ class PublisherWindow(QtWidgets.QWidget):
controller.event_system.add_callback(
"publish.process.stopped", self._on_publish_stop
)
controller.event_system.add_callback(
"publish.process.instance.changed", self._on_instance_change
)
controller.event_system.add_callback(
"publish.process.plugin.changed", self._on_plugin_change
)
controller.event_system.add_callback(
"show.card.message", self._on_overlay_message
)
@ -321,6 +315,9 @@ class PublisherWindow(QtWidgets.QWidget):
controller.event_system.add_callback(
"convertors.find.failed", self._on_convertor_error
)
controller.event_system.add_callback(
"publish.action.failed", self._on_action_error
)
controller.event_system.add_callback(
"export_report.request", self._export_report
)
@ -328,7 +325,6 @@ class PublisherWindow(QtWidgets.QWidget):
"copy_report.request", self._copy_report
)
# Store extra header widget for TrayPublisher
# - can be used to add additional widgets to header between context
# label and help button
@ -491,8 +487,14 @@ class PublisherWindow(QtWidgets.QWidget):
app.removeEventFilter(self)
def keyPressEvent(self, event):
# Ignore escape button to close window
if event.key() == QtCore.Qt.Key_Escape:
if event.key() in {
# Ignore escape button to close window
QtCore.Qt.Key_Escape,
# Ignore enter keyboard event which by default triggers
# first available button in QDialog
QtCore.Qt.Key_Enter,
QtCore.Qt.Key_Return,
}:
event.accept()
return
@ -558,18 +560,6 @@ class PublisherWindow(QtWidgets.QWidget):
self._reset_on_show = False
self.reset()
def _make_sure_on_top(self):
"""Raise window to top and activate it.
This may not work for some DCCs without Qt.
"""
if not self._window_is_visible:
self.show()
self.setWindowState(QtCore.Qt.WindowActive)
self.raise_()
def _checks_before_save(self, explicit_save):
"""Save of changes may trigger some issues.
@ -882,12 +872,6 @@ class PublisherWindow(QtWidgets.QWidget):
if self._is_on_create_tab():
self._go_to_publish_tab()
def _on_instance_change(self):
self._make_sure_on_top()
def _on_plugin_change(self):
self._make_sure_on_top()
def _on_publish_validated_change(self, event):
if event["value"]:
self._validate_btn.setEnabled(False)
@ -898,7 +882,6 @@ class PublisherWindow(QtWidgets.QWidget):
self._comment_input.setText("")
def _on_publish_stop(self):
self._make_sure_on_top()
self._set_publish_overlay_visibility(False)
self._reset_btn.setEnabled(True)
self._stop_btn.setEnabled(False)
@ -1012,6 +995,18 @@ class PublisherWindow(QtWidgets.QWidget):
event["title"], new_failed_info, "Convertor:"
)
def _on_action_error(self, event):
self.add_error_message_dialog(
event["title"],
[{
"message": event["message"],
"traceback": event["traceback"],
"label": event["label"],
"identifier": event["identifier"]
}],
"Action:"
)
def _update_create_overlay_size(self):
metrics = self._create_overlay_button.fontMetrics()
height = int(metrics.height())

View file

@ -32,6 +32,7 @@ from .lib import (
set_style_property,
DynamicQThread,
qt_app_context,
get_qt_app,
get_openpype_qt_app,
get_asset_icon,
get_asset_icon_by_name,

View file

@ -154,11 +154,15 @@ def qt_app_context():
yield app
def get_openpype_qt_app():
"""Main Qt application initialized for OpenPype processed.
def get_qt_app():
"""Get Qt application.
This function should be used only inside OpenPype process and never inside
other processes.
The function initializes new Qt application if it is not already
initialized. It also sets some attributes to the application to
ensure that it will work properly on high DPI displays.
Returns:
QtWidgets.QApplication: Current Qt application.
"""
app = QtWidgets.QApplication.instance()
@ -184,6 +188,17 @@ def get_openpype_qt_app():
app = QtWidgets.QApplication(sys.argv)
return app
def get_openpype_qt_app():
"""Main Qt application initialized for OpenPype processed.
This function should be used only inside OpenPype process and never inside
other processes.
"""
app = get_qt_app()
app.setWindowIcon(QtGui.QIcon(get_app_icon_path()))
return app

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.18.7-nightly.1"
__version__ = "3.18.7-nightly.5"

View file

@ -7,6 +7,8 @@ class CreateRenderPlugin(BaseSettingsModel):
default_factory=list,
title="Default Variants"
)
force_setting_values: bool = SettingsField(
True, title="Force resolution and duration values from Asset")
class AfterEffectsCreatorPlugins(BaseSettingsModel):

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring addon version."""
__version__ = "0.1.2"
__version__ = "0.1.3"

View file

@ -176,6 +176,10 @@ class ExtractThumbnailOIIODefaultsModel(BaseSettingsModel):
class ExtractThumbnailModel(BaseSettingsModel):
_isGroup = True
enabled: bool = SettingsField(True)
product_names: list[str] = SettingsField(
default_factory=list,
title="Product names"
)
integrate_thumbnail: bool = SettingsField(
True,
title="Integrate Thumbnail Representation"
@ -844,6 +848,7 @@ DEFAULT_PUBLISH_VALUES = {
},
"ExtractThumbnail": {
"enabled": True,
"product_names": [],
"integrate_thumbnail": True,
"target_size": {
"type": "source"

View file

@ -1 +1 @@
__version__ = "0.1.4"
__version__ = "0.1.5"