mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Merge pull request #114
* Better exception handling / error reporting for add job screen * Don't supress exceptions for potentially long running functions in bl… * Increase Blender pack_project_file timeout to 120s
This commit is contained in:
@@ -55,7 +55,9 @@ class Blender(BaseRenderEngine):
|
|||||||
return subprocess.run([self.renderer_path(), '-b', project_path, '--python-expr', python_expression],
|
return subprocess.run([self.renderer_path(), '-b', project_path, '--python-expr', python_expression],
|
||||||
capture_output=True, timeout=timeout, creationflags=_creationflags)
|
capture_output=True, timeout=timeout, creationflags=_creationflags)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error running python expression in blender: {e}")
|
err_msg = f"Error running python expression in blender: {e}"
|
||||||
|
logger.error(err_msg)
|
||||||
|
raise ChildProcessError(err_msg)
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError(f'Project file not found: {project_path}')
|
raise FileNotFoundError(f'Project file not found: {project_path}')
|
||||||
|
|
||||||
@@ -70,9 +72,16 @@ class Blender(BaseRenderEngine):
|
|||||||
command = [self.renderer_path(), '-b', '--python', script_path]
|
command = [self.renderer_path(), '-b', '--python', script_path]
|
||||||
if project_path:
|
if project_path:
|
||||||
command.insert(2, project_path)
|
command.insert(2, project_path)
|
||||||
return subprocess.run(command, capture_output=True, timeout=timeout, creationflags=_creationflags)
|
result = subprocess.run(command, capture_output=True, timeout=timeout, creationflags=_creationflags)
|
||||||
|
return result
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
err_msg = f"Timed out after {timeout}s while running python script in blender: {script_path}"
|
||||||
|
logger.error(err_msg)
|
||||||
|
raise TimeoutError(err_msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error running python script in blender: {e}")
|
err_msg = f"Error running python script in blender: {e}"
|
||||||
|
logger.error(err_msg)
|
||||||
|
raise ChildProcessError(err_msg)
|
||||||
|
|
||||||
def get_project_info(self, project_path, timeout=10):
|
def get_project_info(self, project_path, timeout=10):
|
||||||
scene_info = {}
|
scene_info = {}
|
||||||
@@ -89,10 +98,12 @@ class Blender(BaseRenderEngine):
|
|||||||
elif line.startswith('Error'):
|
elif line.startswith('Error'):
|
||||||
logger.error(f"get_scene_info error: {line.strip()}")
|
logger.error(f"get_scene_info error: {line.strip()}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'Error getting file details for .blend file: {e}')
|
msg = f'Error getting file details for .blend file: {e}'
|
||||||
|
logger.error(msg)
|
||||||
|
raise ChildProcessError(msg)
|
||||||
return scene_info
|
return scene_info
|
||||||
|
|
||||||
def pack_project_file(self, project_path, timeout=30):
|
def pack_project_file(self, project_path, timeout=None):
|
||||||
# Credit to L0Lock for pack script - https://blender.stackexchange.com/a/243935
|
# Credit to L0Lock for pack script - https://blender.stackexchange.com/a/243935
|
||||||
try:
|
try:
|
||||||
logger.info(f"Starting to pack Blender file: {project_path}")
|
logger.info(f"Starting to pack Blender file: {project_path}")
|
||||||
@@ -115,7 +126,9 @@ class Blender(BaseRenderEngine):
|
|||||||
logger.info(f'Blender file packed successfully to {new_path}')
|
logger.info(f'Blender file packed successfully to {new_path}')
|
||||||
return new_path
|
return new_path
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'Error packing .blend file: {e}')
|
msg = f'Error packing .blend file: {e}'
|
||||||
|
logger.error(msg)
|
||||||
|
raise ChildProcessError(msg)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_arguments(self):
|
def get_arguments(self):
|
||||||
@@ -172,7 +185,7 @@ class Blender(BaseRenderEngine):
|
|||||||
return render_engines
|
return render_engines
|
||||||
|
|
||||||
def perform_presubmission_tasks(self, project_path):
|
def perform_presubmission_tasks(self, project_path):
|
||||||
packed_path = self.pack_project_file(project_path, timeout=30)
|
packed_path = self.pack_project_file(project_path, timeout=120)
|
||||||
return packed_path
|
return packed_path
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ class NewRenderJobForm(QWidget):
|
|||||||
self.cameras_group.setHidden(True)
|
self.cameras_group.setHidden(True)
|
||||||
self.submit_button.setEnabled(enabled)
|
self.submit_button.setEnabled(enabled)
|
||||||
|
|
||||||
def after_job_submission(self, result):
|
def after_job_submission(self, error_string):
|
||||||
|
|
||||||
# UI cleanup
|
# UI cleanup
|
||||||
self.submit_progress.setMaximum(0)
|
self.submit_progress.setMaximum(0)
|
||||||
@@ -389,7 +389,7 @@ class NewRenderJobForm(QWidget):
|
|||||||
self.toggle_renderer_enablement(True)
|
self.toggle_renderer_enablement(True)
|
||||||
|
|
||||||
self.msg_box = QMessageBox()
|
self.msg_box = QMessageBox()
|
||||||
if result.ok:
|
if not error_string:
|
||||||
self.msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
self.msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||||
self.msg_box.setIcon(QMessageBox.Icon.Information)
|
self.msg_box.setIcon(QMessageBox.Icon.Information)
|
||||||
self.msg_box.setText("Job successfully submitted to server. Submit another?")
|
self.msg_box.setText("Job successfully submitted to server. Submit another?")
|
||||||
@@ -400,7 +400,7 @@ class NewRenderJobForm(QWidget):
|
|||||||
else:
|
else:
|
||||||
self.msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
|
self.msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||||
self.msg_box.setIcon(QMessageBox.Icon.Critical)
|
self.msg_box.setIcon(QMessageBox.Icon.Critical)
|
||||||
self.msg_box.setText(result.text or "Unknown error")
|
self.msg_box.setText(error_string)
|
||||||
self.msg_box.setWindowTitle("Error")
|
self.msg_box.setWindowTitle("Error")
|
||||||
self.msg_box.exec()
|
self.msg_box.exec()
|
||||||
|
|
||||||
@@ -431,7 +431,7 @@ class NewRenderJobForm(QWidget):
|
|||||||
class SubmitWorker(QThread):
|
class SubmitWorker(QThread):
|
||||||
"""Worker class called to submit all the jobs to the server and update the UI accordingly"""
|
"""Worker class called to submit all the jobs to the server and update the UI accordingly"""
|
||||||
|
|
||||||
message_signal = pyqtSignal(Response)
|
message_signal = pyqtSignal(str)
|
||||||
update_ui_signal = pyqtSignal(str, str)
|
update_ui_signal = pyqtSignal(str, str)
|
||||||
|
|
||||||
def __init__(self, window):
|
def __init__(self, window):
|
||||||
@@ -447,61 +447,69 @@ class SubmitWorker(QThread):
|
|||||||
self.update_ui_signal.emit(hostname, percent)
|
self.update_ui_signal.emit(hostname, percent)
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
hostname = self.window.server_input.currentText()
|
try:
|
||||||
job_json = {'owner': psutil.Process().username() + '@' + socket.gethostname(),
|
hostname = self.window.server_input.currentText()
|
||||||
'renderer': self.window.renderer_type.currentText().lower(),
|
job_json = {'owner': psutil.Process().username() + '@' + socket.gethostname(),
|
||||||
'engine_version': self.window.renderer_version_combo.currentText(),
|
'renderer': self.window.renderer_type.currentText().lower(),
|
||||||
'args': {'raw': self.window.raw_args.text(),
|
'engine_version': self.window.renderer_version_combo.currentText(),
|
||||||
'export_format': self.window.file_format_combo.currentText()},
|
'args': {'raw': self.window.raw_args.text(),
|
||||||
'output_path': self.window.render_name_input.text(),
|
'export_format': self.window.file_format_combo.currentText()},
|
||||||
'start_frame': self.window.start_frame_input.value(),
|
'output_path': self.window.render_name_input.text(),
|
||||||
'end_frame': self.window.end_frame_input.value(),
|
'start_frame': self.window.start_frame_input.value(),
|
||||||
'priority': self.window.priority_input.currentIndex() + 1,
|
'end_frame': self.window.end_frame_input.value(),
|
||||||
'notes': self.window.notes_input.toPlainText(),
|
'priority': self.window.priority_input.currentIndex() + 1,
|
||||||
'enable_split_jobs': self.window.enable_splitjobs.isChecked(),
|
'notes': self.window.notes_input.toPlainText(),
|
||||||
'split_jobs_same_os': self.window.splitjobs_same_os.isChecked(),
|
'enable_split_jobs': self.window.enable_splitjobs.isChecked(),
|
||||||
'name': self.window.render_name_input.text()}
|
'split_jobs_same_os': self.window.splitjobs_same_os.isChecked(),
|
||||||
|
'name': self.window.render_name_input.text()}
|
||||||
|
|
||||||
# get the dynamic args
|
# get the dynamic args
|
||||||
for i in range(self.window.renderer_options_layout.count()):
|
for i in range(self.window.renderer_options_layout.count()):
|
||||||
item = self.window.renderer_options_layout.itemAt(i)
|
item = self.window.renderer_options_layout.itemAt(i)
|
||||||
layout = item.layout() # get the layout
|
layout = item.layout() # get the layout
|
||||||
for x in range(layout.count()):
|
for x in range(layout.count()):
|
||||||
z = layout.itemAt(x)
|
z = layout.itemAt(x)
|
||||||
widget = z.widget()
|
widget = z.widget()
|
||||||
if isinstance(widget, QComboBox):
|
if isinstance(widget, QComboBox):
|
||||||
job_json['args'][self.window.current_engine_options[i]['name']] = widget.currentText()
|
job_json['args'][self.window.current_engine_options[i]['name']] = widget.currentText()
|
||||||
elif isinstance(widget, QLineEdit):
|
elif isinstance(widget, QLineEdit):
|
||||||
job_json['args'][self.window.current_engine_options[i]['name']] = widget.text()
|
job_json['args'][self.window.current_engine_options[i]['name']] = widget.text()
|
||||||
|
|
||||||
# determine if any cameras are checked
|
# determine if any cameras are checked
|
||||||
selected_cameras = []
|
selected_cameras = []
|
||||||
if self.window.cameras_list.count() and not self.window.cameras_group.isHidden():
|
if self.window.cameras_list.count() and not self.window.cameras_group.isHidden():
|
||||||
for index in range(self.window.cameras_list.count()):
|
for index in range(self.window.cameras_list.count()):
|
||||||
item = self.window.cameras_list.item(index)
|
item = self.window.cameras_list.item(index)
|
||||||
if item.checkState() == Qt.CheckState.Checked:
|
if item.checkState() == Qt.CheckState.Checked:
|
||||||
selected_cameras.append(item.text().rsplit('-', 1)[0].strip()) # cleanup to just camera name
|
selected_cameras.append(item.text().rsplit('-', 1)[0].strip()) # cleanup to just camera name
|
||||||
|
|
||||||
# process cameras into nested format
|
# process cameras into nested format
|
||||||
input_path = self.window.scene_file_input.text()
|
input_path = self.window.scene_file_input.text()
|
||||||
if selected_cameras:
|
if selected_cameras:
|
||||||
job_list = []
|
job_list = []
|
||||||
for cam in selected_cameras:
|
for cam in selected_cameras:
|
||||||
job_copy = copy.deepcopy(job_json)
|
job_copy = copy.deepcopy(job_json)
|
||||||
job_copy['args']['camera'] = cam
|
job_copy['args']['camera'] = cam
|
||||||
job_copy['name'] = job_copy['name'].replace(' ', '-') + "_" + cam.replace(' ', '')
|
job_copy['name'] = job_copy['name'].replace(' ', '-') + "_" + cam.replace(' ', '')
|
||||||
job_copy['output_path'] = job_copy['name']
|
job_copy['output_path'] = job_copy['name']
|
||||||
job_list.append(job_copy)
|
job_list.append(job_copy)
|
||||||
else:
|
else:
|
||||||
job_list = [job_json]
|
job_list = [job_json]
|
||||||
|
|
||||||
# presubmission tasks
|
# presubmission tasks
|
||||||
engine = EngineManager.engine_with_name(self.window.renderer_type.currentText().lower())
|
engine = EngineManager.engine_with_name(self.window.renderer_type.currentText().lower())
|
||||||
input_path = engine().perform_presubmission_tasks(input_path)
|
input_path = engine().perform_presubmission_tasks(input_path)
|
||||||
# submit
|
# submit
|
||||||
result = self.window.server_proxy.post_job_to_server(file_path=input_path, job_list=job_list,
|
err_msg = ""
|
||||||
callback=create_callback)
|
result = self.window.server_proxy.post_job_to_server(file_path=input_path, job_list=job_list,
|
||||||
self.message_signal.emit(result)
|
callback=create_callback)
|
||||||
|
if not (result and result.ok):
|
||||||
|
err_msg = "Error posting job to server."
|
||||||
|
|
||||||
|
self.message_signal.emit(err_msg)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.message_signal.emit(str(e))
|
||||||
|
|
||||||
|
|
||||||
class GetProjectInfoWorker(QThread):
|
class GetProjectInfoWorker(QThread):
|
||||||
|
|||||||
Reference in New Issue
Block a user