Skip to content

Commit 14eab3f

Browse files
authored
7504 event loop (#8)
- Added a workaround for processing Qt events on other platforms than Windows, which does not need it. - Fixed legacy Sequencer render command line to make it cross platform: spaces are perfectly well handled by subprocess.call without any need to protect the strings, if using a list for the call. - Fixed UE5.0EA discovery for other platforms than Windows.
1 parent 54a3db4 commit 14eab3f

File tree

3 files changed

+85
-55
lines changed

3 files changed

+85
-55
lines changed

engine.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,36 @@ def init_engine(self):
106106

107107
def init_qt_app(self):
108108
self.logger.debug("%s: Initializing QtApp for Unreal", self)
109-
110109
from sgtk.platform.qt5 import QtWidgets
111110

112111
if not QtWidgets.QApplication.instance():
113112
self._qt_app = QtWidgets.QApplication(sys.argv)
114113
self._qt_app.setQuitOnLastWindowClosed(False)
115-
unreal.log("Created QApplication instance: {0}".format(self._qt_app))
116114
else:
117115
self._qt_app = QtWidgets.QApplication.instance()
118116

117+
# On other platforms than Windows, we need to process the Qt events otherwise
118+
# UIs are "frozen". We use a slate tick callback to do that on a regular basis.
119+
# It is not clear why this is not needed on Windows, possibly because a
120+
# dedicated Windows event dispatcher is used instead of a regular
121+
# QAbstractEventDispatcher
122+
if sys.platform != "win32":
123+
unreal.register_slate_post_tick_callback(self._process_qt_events_cb)
119124
# Make the QApplication use the dark theme. Must be called after the QApplication is instantiated
120125
self._initialize_dark_look_and_feel()
121126

127+
@staticmethod
128+
def _process_qt_events_cb(delta_time):
129+
"""
130+
An Unreal tick callback to process QT events.
131+
132+
:param float delta_time: delta time since the last run.
133+
"""
134+
from sgtk.platform.qt5 import QtWidgets
135+
qapp = QtWidgets.QApplication.instance()
136+
if qapp:
137+
qapp.processEvents()
138+
122139
def post_app_init(self):
123140
"""
124141
Called when all apps have initialized
@@ -256,7 +273,6 @@ def _create_dialog(self, title, bundle, widget, parent):
256273
"icon_256.png"))
257274

258275
dialog.setWindowIcon(QtGui.QIcon(unreal_icon))
259-
260276
return dialog
261277

262278
def _define_qt_base(self):

hooks/tk-multi-publish2/basic/publish_movie.py

Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,23 @@ def publish(self, settings, item):
541541
self.logger.info("Rendering %s with the Movie Render Queue with %s presets." % (publish_path, presets.get_name()))
542542
else:
543543
self.logger.info("Rendering %s with the Movie Render Queue." % publish_path)
544-
self._unreal_render_sequence_with_movie_queue(publish_path, unreal_map_path, unreal_asset_path, presets)
544+
res, _ = self._unreal_render_sequence_with_movie_queue(
545+
publish_path,
546+
unreal_map_path,
547+
unreal_asset_path,
548+
presets
549+
)
545550
else:
546551
self.logger.info("Rendering %s with the Level Sequencer." % publish_path)
547-
self._unreal_render_sequence_with_sequencer(publish_path, unreal_map_path, unreal_asset_path)
548-
552+
res, _ = self._unreal_render_sequence_with_sequencer(
553+
publish_path,
554+
unreal_map_path,
555+
unreal_asset_path
556+
)
557+
if not res:
558+
raise RuntimeError(
559+
"Unable to render %s" % publish_path
560+
)
549561
# Increment the version number
550562
self._unreal_asset_set_version(unreal_asset_path, item.properties["version_number"])
551563

@@ -696,50 +708,47 @@ def _unreal_render_sequence_with_sequencer(self, output_path, unreal_map_path, s
696708
return False, None
697709

698710
# Render the sequence to a movie file using the following command-line arguments
699-
cmdline_args = []
700-
701-
# Note that any command-line arguments (usually paths) that could contain spaces must be enclosed between quotes
702-
unreal_exec_path = '"{}"'.format(sys.executable)
703-
704-
# Get the Unreal project to load
705-
unreal_project_filename = "{}.uproject".format(unreal.SystemLibrary.get_game_name())
706-
unreal_project_path = os.path.join(unreal.SystemLibrary.get_project_directory(), unreal_project_filename)
707-
unreal_project_path = '"{}"'.format(unreal_project_path)
708-
709-
# Important to keep the order for these arguments
710-
cmdline_args.append(unreal_exec_path) # Unreal executable path
711-
cmdline_args.append(unreal_project_path) # Unreal project
712-
cmdline_args.append(unreal_map_path) # Level to load for rendering the sequence
713-
714-
# Command-line arguments for Sequencer Render to Movie
715-
# See: https://docs.unrealengine.com/en-us/Engine/Sequencer/Workflow/RenderingCmdLine
716-
sequence_path = "-LevelSequence={}".format(sequence_path)
717-
cmdline_args.append(sequence_path) # The sequence to render
718-
719-
output_path = '-MovieFolder="{}"'.format(output_folder)
720-
cmdline_args.append(output_path) # output folder, must match the work template
721-
722-
movie_name_arg = "-MovieName={}".format(movie_name)
723-
cmdline_args.append(movie_name_arg) # output filename
724-
725-
cmdline_args.append("-game")
726-
cmdline_args.append("-MovieSceneCaptureType=/Script/MovieSceneCapture.AutomatedLevelSequenceCapture")
727-
cmdline_args.append("-ResX=1280")
728-
cmdline_args.append("-ResY=720")
729-
cmdline_args.append("-ForceRes")
730-
cmdline_args.append("-Windowed")
731-
cmdline_args.append("-MovieCinematicMode=yes")
732-
cmdline_args.append("-MovieFormat=Video")
733-
cmdline_args.append("-MovieFrameRate=24")
734-
cmdline_args.append("-MovieQuality=75")
735-
cmdline_args.append("-NoTextureStreaming")
736-
cmdline_args.append("-NoLoadingScreen")
737-
cmdline_args.append("-NoScreenMessages")
738-
739-
unreal.log("Sequencer command-line arguments: {}".format(cmdline_args))
740-
741-
# Send the arguments as a single string because some arguments could contain spaces and we don't want those to be quoted
742-
subprocess.call(" ".join(cmdline_args))
711+
cmdline_args = [
712+
sys.executable, # Unreal executable path
713+
"%s" % os.path.join(
714+
unreal.SystemLibrary.get_project_directory(),
715+
"%s.uproject" % unreal.SystemLibrary.get_game_name(),
716+
), # Unreal project
717+
unreal_map_path, # Level to load for rendering the sequence
718+
# Command-line arguments for Sequencer Render to Movie
719+
# See: https://docs.unrealengine.com/en-us/Engine/Sequencer/Workflow/RenderingCmdLine
720+
#
721+
"-LevelSequence=%s" % sequence_path, # The sequence to render
722+
"-MovieFolder=%s" % output_folder, # Output folder, must match the work template
723+
"-MovieName=%s" % movie_name, # Output filename
724+
"-game",
725+
"-MovieSceneCaptureType=/Script/MovieSceneCapture.AutomatedLevelSequenceCapture",
726+
"-ResX=1280",
727+
"-ResY=720",
728+
"-ForceRes",
729+
"-Windowed",
730+
"-MovieCinematicMode=yes",
731+
"-MovieFormat=Video",
732+
"-MovieFrameRate=24",
733+
"-MovieQuality=75",
734+
"-NoTextureStreaming",
735+
"-NoLoadingScreen",
736+
"-NoScreenMessages",
737+
]
738+
739+
unreal.log(
740+
"Sequencer command-line arguments: {}".format(
741+
" ".join(cmdline_args)
742+
)
743+
)
744+
745+
# Make a shallow copy of the current environment and clear some variables
746+
run_env = copy.copy(os.environ)
747+
# Prevent SG TK to try to bootstrap in the new process
748+
if "UE_SHOTGUN_BOOTSTRAP" in run_env:
749+
del run_env["UE_SHOTGUN_BOOTSTRAP"]
750+
751+
subprocess.call(cmdline_args, env=run_env)
743752

744753
return os.path.isfile(output_path), output_path
745754

@@ -773,6 +782,9 @@ def _unreal_render_sequence_with_movie_queue(self, output_path, unreal_map_path,
773782
output_setting.output_resolution = unreal.IntPoint(1280, 720)
774783
output_setting.file_name_format = movie_name
775784
output_setting.override_existing_output = True # Overwrite existing files
785+
# If needed we could enforce a frame rate, like for the Sequencer code.
786+
# output_setting.output_frame_rate = unreal.FrameRate(24)
787+
# output_setting.use_custom_frame_rate = True
776788
# Remove problematic settings
777789
for setting, reason in self._check_render_settings(config):
778790
self.logger.warning("Disabling %s: %s." % (setting.get_name(), reason))

startup.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,23 @@ class EngineLauncher(SoftwareLauncher):
2121
# matching against supplied versions and products. Similar to the glob
2222
# strings, these allow us to alter the regex matching for any of the
2323
# variable components of the path in one place
24-
COMPONENT_REGEX_LOOKUP = {"version": r"\d+\.\d+", "major": r"\d+"}
24+
# We match 4.27, 5.0EA
25+
COMPONENT_REGEX_LOOKUP = {"version": r"\d+\.\d+\w*", "major": r"\d+"}
2526

2627
# This dictionary defines a list of executable template strings for each
2728
# of the supported operating systems. The templates are used for both
2829
# globbing and regex matches by replacing the named format placeholders
2930
# with an appropriate glob or regex string.
30-
# Note: Windows is handled differently by checking registries and Linux is
31-
# not yet supported.
31+
# Note: Software entities need to be manually added by users in SG for Linux
32+
# since there is no standard installation path.
3233
EXECUTABLE_TEMPLATES = {
3334
"darwin": [
34-
"/Users/Shared/Epic Games/UE_{version}/Engine/Binaries/Mac/UE{major}Editor.app"
35+
"/Users/Shared/Epic Games/UE_{version}/Engine/Binaries/Mac/UE{major}Editor.app",
36+
"/Users/Shared/Epic Games/UE_{version}/Engine/Binaries/Mac/UnrealEditor.app"
3537
],
3638
"win32": [
3739
"C:/Program Files/Epic Games/UE_{version}/Engine/Binaries/Win64/UE{major}Editor.exe",
38-
"C:/Program Files/Epic Games/UE_{version}EA/Engine/Binaries/Win64/UnrealEditor.exe"
40+
"C:/Program Files/Epic Games/UE_{version}/Engine/Binaries/Win64/UnrealEditor.exe"
3941
],
4042
}
4143

0 commit comments

Comments
 (0)