mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Add job polish (#63)
* Remove legacy client * Misc cleanup * Add message box after submission success / fail * Use a new is_localhost method to handle localhost not including '.local' * Code cleanup * Fix issue where engine browser would think we're downloading forever * Add message box after submission success / fail * Use a new is_localhost method to handle localhost not including '.local' * Code cleanup * Fix issue where engine browser would think we're downloading forever * Add pubsub messages to serverproxy_manager.py * Add resolution, fps and renderer versions to add_job.py * Add cameras to add_job.py * Add message box after submission success / fail * Use a new is_localhost method to handle localhost not including '.local' * Code cleanup * Fix issue where engine browser would think we're downloading forever * Add message box after submission success / fail * Code cleanup * Add cameras to add_job.py * Add dynamic engine options and output format * Move UI work out of BG threads and add engine presubmission tasks * Submit dynamic args when creating a new job * Hide groups and show messagebox after submission * Choose file when pressing New Job in main window now
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
import copy
|
||||
import os.path
|
||||
import pathlib
|
||||
import socket
|
||||
import threading
|
||||
|
||||
import psutil
|
||||
from PyQt6.QtCore import QThread, pyqtSignal, Qt
|
||||
from PyQt6.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QSpinBox, QComboBox,
|
||||
QGroupBox, QCheckBox, QProgressBar, QPlainTextEdit
|
||||
QGroupBox, QCheckBox, QProgressBar, QPlainTextEdit, QDoubleSpinBox, QMessageBox, QListWidget, QListWidgetItem
|
||||
)
|
||||
from requests import Response
|
||||
|
||||
from src.api.server_proxy import RenderServerProxy
|
||||
from src.engines.engine_manager import EngineManager
|
||||
@@ -15,10 +19,20 @@ from src.utilities.zeroconf_server import ZeroconfServer
|
||||
|
||||
|
||||
class NewRenderJobForm(QWidget):
|
||||
def __init__(self):
|
||||
def __init__(self, project_path=None):
|
||||
super().__init__()
|
||||
|
||||
self.project_path = project_path
|
||||
|
||||
# UI
|
||||
self.current_engine_options = None
|
||||
self.file_format_combo = None
|
||||
self.renderer_options_layout = None
|
||||
self.cameras_list = None
|
||||
self.cameras_group = None
|
||||
self.renderer_version_combo = None
|
||||
self.worker_thread = None
|
||||
self.msg_box = None
|
||||
self.engine_help_viewer = None
|
||||
self.raw_args = None
|
||||
self.submit_progress_label = None
|
||||
@@ -34,21 +48,21 @@ class NewRenderJobForm(QWidget):
|
||||
self.priority_input = None
|
||||
self.end_frame_input = None
|
||||
self.start_frame_input = None
|
||||
self.output_path_browse_button = None
|
||||
self.output_path_input = None
|
||||
self.scene_file_input = None
|
||||
self.scene_file_browse_button = None
|
||||
self.job_name_input = None
|
||||
|
||||
# Setup
|
||||
self.setWindowTitle("New Job")
|
||||
self.setup_ui()
|
||||
|
||||
# Job / Server Data
|
||||
self.server_proxy = RenderServerProxy(socket.gethostname())
|
||||
self.renderer_info = None
|
||||
self.project_info = None
|
||||
|
||||
# Setup
|
||||
self.setWindowTitle("New Job")
|
||||
self.setup_ui()
|
||||
self.setup_project()
|
||||
|
||||
# get renderer info in bg thread
|
||||
t = threading.Thread(target=self.update_renderer_info)
|
||||
t.start()
|
||||
@@ -59,39 +73,12 @@ class NewRenderJobForm(QWidget):
|
||||
# Main Layout
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
# Server Group
|
||||
# Server List
|
||||
server_group = QGroupBox("Server")
|
||||
server_layout = QVBoxLayout(server_group)
|
||||
server_list_layout = QHBoxLayout()
|
||||
server_list_layout.setSpacing(0)
|
||||
self.server_input = QComboBox()
|
||||
server_list_layout.addWidget(QLabel("Hostname:"), 1)
|
||||
server_list_layout.addWidget(self.server_input, 3)
|
||||
server_layout.addLayout(server_list_layout)
|
||||
main_layout.addWidget(server_group)
|
||||
self.update_server_list()
|
||||
# Priority
|
||||
priority_layout = QHBoxLayout()
|
||||
priority_layout.addWidget(QLabel("Priority:"), 1)
|
||||
self.priority_input = QComboBox()
|
||||
self.priority_input.addItems(["High", "Medium", "Low"])
|
||||
self.priority_input.setCurrentIndex(1)
|
||||
priority_layout.addWidget(self.priority_input, 3)
|
||||
server_layout.addLayout(priority_layout)
|
||||
# Splitjobs
|
||||
self.enable_splitjobs = QCheckBox("Automatically split render across multiple servers")
|
||||
self.enable_splitjobs.setEnabled(True)
|
||||
server_layout.addWidget(self.enable_splitjobs)
|
||||
self.splitjobs_same_os = QCheckBox("Only render on same OS")
|
||||
self.splitjobs_same_os.setEnabled(True)
|
||||
server_layout.addWidget(self.splitjobs_same_os)
|
||||
|
||||
# Scene File Group
|
||||
scene_file_group = QGroupBox("Project")
|
||||
scene_file_layout = QVBoxLayout(scene_file_group)
|
||||
scene_file_picker_layout = QHBoxLayout()
|
||||
self.scene_file_input = QLineEdit()
|
||||
self.scene_file_input.setText(self.project_path)
|
||||
self.scene_file_browse_button = QPushButton("Browse...")
|
||||
self.scene_file_browse_button.clicked.connect(self.browse_scene_file)
|
||||
scene_file_picker_layout.addWidget(self.scene_file_input)
|
||||
@@ -110,10 +97,51 @@ class NewRenderJobForm(QWidget):
|
||||
scene_file_layout.addLayout(progress_layout)
|
||||
main_layout.addWidget(scene_file_group)
|
||||
|
||||
# Server Group
|
||||
# Server List
|
||||
self.server_group = QGroupBox("Server")
|
||||
server_layout = QVBoxLayout(self.server_group)
|
||||
server_list_layout = QHBoxLayout()
|
||||
server_list_layout.setSpacing(0)
|
||||
self.server_input = QComboBox()
|
||||
server_list_layout.addWidget(QLabel("Hostname:"), 1)
|
||||
server_list_layout.addWidget(self.server_input, 3)
|
||||
server_layout.addLayout(server_list_layout)
|
||||
main_layout.addWidget(self.server_group)
|
||||
self.update_server_list()
|
||||
# Priority
|
||||
priority_layout = QHBoxLayout()
|
||||
priority_layout.addWidget(QLabel("Priority:"), 1)
|
||||
self.priority_input = QComboBox()
|
||||
self.priority_input.addItems(["High", "Medium", "Low"])
|
||||
self.priority_input.setCurrentIndex(1)
|
||||
priority_layout.addWidget(self.priority_input, 3)
|
||||
server_layout.addLayout(priority_layout)
|
||||
# Splitjobs
|
||||
self.enable_splitjobs = QCheckBox("Automatically split render across multiple servers")
|
||||
self.enable_splitjobs.setEnabled(True)
|
||||
server_layout.addWidget(self.enable_splitjobs)
|
||||
self.splitjobs_same_os = QCheckBox("Only render on same OS")
|
||||
self.splitjobs_same_os.setEnabled(True)
|
||||
server_layout.addWidget(self.splitjobs_same_os)
|
||||
|
||||
# Output Settings Group
|
||||
output_settings_group = QGroupBox("Output Settings")
|
||||
output_settings_layout = QVBoxLayout(output_settings_group)
|
||||
frame_range_layout = QHBoxLayout(output_settings_group)
|
||||
self.output_settings_group = QGroupBox("Output Settings")
|
||||
output_settings_layout = QVBoxLayout(self.output_settings_group)
|
||||
# output path
|
||||
output_path_layout = QHBoxLayout()
|
||||
output_path_layout.addWidget(QLabel("Render name:"))
|
||||
self.output_path_input = QLineEdit()
|
||||
output_path_layout.addWidget(self.output_path_input)
|
||||
output_settings_layout.addLayout(output_path_layout)
|
||||
# file format
|
||||
file_format_layout = QHBoxLayout()
|
||||
file_format_layout.addWidget(QLabel("Format:"))
|
||||
self.file_format_combo = QComboBox()
|
||||
file_format_layout.addWidget(self.file_format_combo)
|
||||
output_settings_layout.addLayout(file_format_layout)
|
||||
# frame range
|
||||
frame_range_layout = QHBoxLayout(self.output_settings_group)
|
||||
self.start_frame_input = QSpinBox()
|
||||
self.start_frame_input.setRange(1, 99999)
|
||||
self.end_frame_input = QSpinBox()
|
||||
@@ -123,39 +151,70 @@ class NewRenderJobForm(QWidget):
|
||||
frame_range_layout.addWidget(QLabel("to"))
|
||||
frame_range_layout.addWidget(self.end_frame_input)
|
||||
output_settings_layout.addLayout(frame_range_layout)
|
||||
# output path
|
||||
output_path_layout = QHBoxLayout()
|
||||
output_path_layout.addWidget(QLabel("Render name:"))
|
||||
self.output_path_input = QLineEdit()
|
||||
# self.output_path_browse_button = QPushButton("Browse...")
|
||||
# self.output_path_browse_button.clicked.connect(self.browse_output_path)
|
||||
output_path_layout.addWidget(self.output_path_input)
|
||||
output_path_layout.addWidget(self.output_path_browse_button)
|
||||
output_settings_layout.addLayout(output_path_layout)
|
||||
main_layout.addWidget(output_settings_group)
|
||||
# resolution
|
||||
resolution_layout = QHBoxLayout(self.output_settings_group)
|
||||
self.resolution_x_input = QSpinBox()
|
||||
self.resolution_x_input.setRange(1, 9999) # Assuming max resolution width 9999
|
||||
self.resolution_x_input.setValue(1920)
|
||||
self.resolution_y_input = QSpinBox()
|
||||
self.resolution_y_input.setRange(1, 9999) # Assuming max resolution height 9999
|
||||
self.resolution_y_input.setValue(1080)
|
||||
self.frame_rate_input = QDoubleSpinBox()
|
||||
self.frame_rate_input.setRange(1, 9999) # Assuming max resolution width 9999
|
||||
self.frame_rate_input.setDecimals(3)
|
||||
self.frame_rate_input.setValue(23.976)
|
||||
resolution_layout.addWidget(QLabel("Resolution:"))
|
||||
resolution_layout.addWidget(self.resolution_x_input)
|
||||
resolution_layout.addWidget(QLabel("x"))
|
||||
resolution_layout.addWidget(self.resolution_y_input)
|
||||
resolution_layout.addWidget(QLabel("@"))
|
||||
resolution_layout.addWidget(self.frame_rate_input)
|
||||
resolution_layout.addWidget(QLabel("fps"))
|
||||
output_settings_layout.addLayout(resolution_layout)
|
||||
# add group to layout
|
||||
main_layout.addWidget(self.output_settings_group)
|
||||
|
||||
# Renderer Group
|
||||
renderer_group = QGroupBox("Renderer Settings")
|
||||
renderer_layout = QVBoxLayout(renderer_group)
|
||||
self.renderer_group = QGroupBox("Renderer Settings")
|
||||
renderer_group_layout = QVBoxLayout(self.renderer_group)
|
||||
renderer_layout = QHBoxLayout()
|
||||
renderer_layout.addWidget(QLabel("Renderer:"))
|
||||
self.renderer_type = QComboBox()
|
||||
self.renderer_type.currentIndexChanged.connect(self.renderer_changed)
|
||||
renderer_layout.addWidget(self.renderer_type)
|
||||
# Version
|
||||
renderer_layout.addWidget(QLabel("Version:"))
|
||||
self.renderer_version_combo = QComboBox()
|
||||
renderer_layout.addWidget(self.renderer_version_combo)
|
||||
renderer_group_layout.addLayout(renderer_layout)
|
||||
# dynamic options
|
||||
self.renderer_options_layout = QVBoxLayout()
|
||||
renderer_group_layout.addLayout(self.renderer_options_layout)
|
||||
# Raw Args
|
||||
raw_args_layout = QHBoxLayout(renderer_group)
|
||||
raw_args_layout = QHBoxLayout(self.renderer_group)
|
||||
raw_args_layout.addWidget(QLabel("Raw Args:"))
|
||||
self.raw_args = QLineEdit()
|
||||
raw_args_layout.addWidget(self.raw_args)
|
||||
args_help_button = QPushButton("?")
|
||||
args_help_button.clicked.connect(self.args_help_button_clicked)
|
||||
raw_args_layout.addWidget(args_help_button)
|
||||
renderer_layout.addLayout(raw_args_layout)
|
||||
main_layout.addWidget(renderer_group)
|
||||
renderer_group_layout.addLayout(raw_args_layout)
|
||||
main_layout.addWidget(self.renderer_group)
|
||||
|
||||
# Cameras Group
|
||||
self.cameras_group = QGroupBox("Cameras")
|
||||
cameras_layout = QVBoxLayout(self.cameras_group)
|
||||
self.cameras_list = QListWidget()
|
||||
self.cameras_group.setHidden(True)
|
||||
cameras_layout.addWidget(self.cameras_list)
|
||||
main_layout.addWidget(self.cameras_group)
|
||||
|
||||
# Notes Group
|
||||
notes_group = QGroupBox("Additional Notes")
|
||||
notes_layout = QVBoxLayout(notes_group)
|
||||
self.notes_group = QGroupBox("Additional Notes")
|
||||
notes_layout = QVBoxLayout(self.notes_group)
|
||||
self.notes_input = QPlainTextEdit()
|
||||
notes_layout.addWidget(self.notes_input)
|
||||
main_layout.addWidget(notes_group)
|
||||
main_layout.addWidget(self.notes_group)
|
||||
|
||||
# Submit Button
|
||||
self.submit_button = QPushButton("Submit Job")
|
||||
@@ -177,6 +236,17 @@ class NewRenderJobForm(QWidget):
|
||||
def update_renderer_info(self):
|
||||
self.renderer_info = self.server_proxy.get_renderer_info()
|
||||
self.renderer_type.addItems(self.renderer_info.keys())
|
||||
self.renderer_changed()
|
||||
|
||||
def renderer_changed(self):
|
||||
# load the version numbers
|
||||
current_renderer = self.renderer_type.currentText().lower() or self.renderer_type.itemText(0)
|
||||
self.renderer_version_combo.clear()
|
||||
self.file_format_combo.clear()
|
||||
if current_renderer:
|
||||
renderer_vers = [version_info['version'] for version_info in self.renderer_info[current_renderer]['versions']]
|
||||
self.renderer_version_combo.addItems(renderer_vers)
|
||||
self.file_format_combo.addItems(self.renderer_info[current_renderer]['supported_export_formats'])
|
||||
|
||||
def update_server_list(self):
|
||||
clients = ZeroconfServer.found_hostnames()
|
||||
@@ -184,34 +254,26 @@ class NewRenderJobForm(QWidget):
|
||||
self.server_input.addItems(clients)
|
||||
|
||||
def browse_scene_file(self):
|
||||
|
||||
def get_project_info():
|
||||
self.process_progress_bar.setHidden(False)
|
||||
self.process_label.setHidden(False)
|
||||
self.toggle_renderer_enablement(False)
|
||||
output_name, _ = os.path.splitext(os.path.basename(file_name))
|
||||
self.output_path_input.setText(output_name)
|
||||
|
||||
engine = EngineManager.engine_for_project_path(file_name)
|
||||
self.project_info = engine().get_scene_info(file_name)
|
||||
|
||||
index = self.renderer_type.findText(engine.name().lower())
|
||||
if index >= 0:
|
||||
self.renderer_type.setCurrentIndex(index)
|
||||
|
||||
self.update_project_ui()
|
||||
|
||||
self.process_progress_bar.setHidden(True)
|
||||
self.process_label.setHidden(True)
|
||||
self.toggle_renderer_enablement(True)
|
||||
|
||||
file_name, _ = QFileDialog.getOpenFileName(self, "Select Scene File")
|
||||
if file_name:
|
||||
self.scene_file_input.setText(file_name)
|
||||
# analyze the file
|
||||
update_thread = threading.Thread(target=get_project_info)
|
||||
update_thread.start()
|
||||
self.setup_project()
|
||||
|
||||
def setup_project(self):
|
||||
# UI stuff on main thread
|
||||
self.process_progress_bar.setHidden(False)
|
||||
self.process_label.setHidden(False)
|
||||
self.toggle_renderer_enablement(False)
|
||||
|
||||
output_name, _ = os.path.splitext(os.path.basename(self.scene_file_input.text()))
|
||||
output_name = output_name.replace(' ', '_')
|
||||
self.output_path_input.setText(output_name)
|
||||
file_name = self.scene_file_input.text()
|
||||
|
||||
# setup bg worker
|
||||
self.worker_thread = GetProjectInfoWorker(window=self, project_path=file_name)
|
||||
self.worker_thread.message_signal.connect(self.post_get_project_info_update)
|
||||
self.worker_thread.start()
|
||||
|
||||
def browse_output_path(self):
|
||||
directory = QFileDialog.getExistingDirectory(self, "Select Output Directory")
|
||||
@@ -226,66 +288,233 @@ class NewRenderJobForm(QWidget):
|
||||
|
||||
# -------- Update --------
|
||||
|
||||
def update_project_ui(self):
|
||||
self.start_frame_input.setValue(self.project_info.get('frame_start'))
|
||||
self.end_frame_input.setValue(self.project_info.get('frame_end'))
|
||||
def post_get_project_info_update(self):
|
||||
"""Called by the GetProjectInfoWorker - Do not call directly."""
|
||||
try:
|
||||
# Set the best renderer we can find
|
||||
input_path = self.scene_file_input.text()
|
||||
engine = EngineManager.engine_for_project_path(input_path)
|
||||
index = self.renderer_type.findText(engine.name().lower())
|
||||
if index >= 0:
|
||||
self.renderer_type.setCurrentIndex(index)
|
||||
|
||||
self.output_path_input.setText(os.path.basename(input_path))
|
||||
|
||||
# cleanup progress UI
|
||||
self.process_progress_bar.setHidden(True)
|
||||
self.process_label.setHidden(True)
|
||||
self.toggle_renderer_enablement(True)
|
||||
|
||||
# Load scene data
|
||||
self.start_frame_input.setValue(self.project_info.get('frame_start'))
|
||||
self.end_frame_input.setValue(self.project_info.get('frame_end'))
|
||||
self.resolution_x_input.setValue(self.project_info.get('resolution_x'))
|
||||
self.resolution_y_input.setValue(self.project_info.get('resolution_y'))
|
||||
self.frame_rate_input.setValue(self.project_info.get('fps'))
|
||||
|
||||
# Cameras
|
||||
self.cameras_list.clear()
|
||||
if self.project_info.get('cameras'):
|
||||
self.cameras_group.setHidden(False)
|
||||
found_active = False
|
||||
for camera in self.project_info['cameras']:
|
||||
# create the list items and make them checkable
|
||||
item = QListWidgetItem(f"{camera['name']} - {camera['lens']}mm")
|
||||
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||
is_checked = camera['is_active'] or len(self.project_info['cameras']) == 1
|
||||
found_active = found_active or is_checked
|
||||
item.setCheckState(Qt.CheckState.Checked if is_checked else Qt.CheckState.Unchecked)
|
||||
self.cameras_list.addItem(item)
|
||||
if not found_active:
|
||||
self.cameras_list.item(0).setCheckState(Qt.CheckState.Checked)
|
||||
else:
|
||||
self.cameras_group.setHidden(True)
|
||||
|
||||
# Dynamic Engine Options
|
||||
engine_name = self.renderer_type.currentText()
|
||||
engine = EngineManager.engine_with_name(engine_name)
|
||||
# clear old options
|
||||
clear_layout(self.renderer_options_layout)
|
||||
# dynamically populate option list
|
||||
self.current_engine_options = engine().get_options()
|
||||
for option in self.current_engine_options:
|
||||
h_layout = QHBoxLayout()
|
||||
label = QLabel(option['name'].capitalize() + ':')
|
||||
h_layout.addWidget(label)
|
||||
if option.get('options'):
|
||||
combo_box = QComboBox()
|
||||
for opt in option['options']:
|
||||
combo_box.addItem(opt)
|
||||
h_layout.addWidget(combo_box)
|
||||
else:
|
||||
text_box = QLineEdit()
|
||||
h_layout.addWidget(text_box)
|
||||
self.renderer_options_layout.addLayout(h_layout)
|
||||
except AttributeError as e:
|
||||
pass
|
||||
|
||||
def toggle_renderer_enablement(self, enabled=False):
|
||||
self.start_frame_input.setEnabled(enabled)
|
||||
self.end_frame_input.setEnabled(enabled)
|
||||
self.notes_input.setEnabled(enabled)
|
||||
self.output_path_input.setEnabled(enabled)
|
||||
"""Toggle on/off all the render settings"""
|
||||
self.server_group.setHidden(not enabled)
|
||||
self.output_settings_group.setHidden(not enabled)
|
||||
self.renderer_group.setHidden(not enabled)
|
||||
self.notes_group.setHidden(not enabled)
|
||||
if not enabled:
|
||||
self.cameras_group.setHidden(True)
|
||||
self.submit_button.setEnabled(enabled)
|
||||
|
||||
def after_job_submission(self, result):
|
||||
|
||||
# UI cleanup
|
||||
self.submit_progress.setMaximum(0)
|
||||
self.submit_button.setHidden(False)
|
||||
self.submit_progress.setHidden(True)
|
||||
self.submit_progress_label.setHidden(True)
|
||||
self.process_progress_bar.setHidden(True)
|
||||
self.process_label.setHidden(True)
|
||||
self.toggle_renderer_enablement(True)
|
||||
|
||||
self.msg_box = QMessageBox()
|
||||
if result.ok:
|
||||
self.msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||
self.msg_box.setIcon(QMessageBox.Icon.Information)
|
||||
self.msg_box.setText("Job successfully submitted to server. Submit another?")
|
||||
self.msg_box.setWindowTitle("Success")
|
||||
x = self.msg_box.exec()
|
||||
if x == QMessageBox.StandardButton.No:
|
||||
self.close()
|
||||
else:
|
||||
self.msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||
self.msg_box.setIcon(QMessageBox.Icon.Critical)
|
||||
self.msg_box.setText(result.text or "Unknown error")
|
||||
self.msg_box.setWindowTitle("Error")
|
||||
self.msg_box.exec()
|
||||
|
||||
# -------- Submit Job Calls --------
|
||||
|
||||
def submit_job(self):
|
||||
def submit_job_worker():
|
||||
def create_callback(encoder):
|
||||
encoder_len = encoder.len
|
||||
def callback(monitor):
|
||||
percent = f"{monitor.bytes_read / encoder_len * 100:.0f}"
|
||||
self.submit_progress_label.setText(f"Transferring to {hostname} - {percent}%")
|
||||
self.submit_progress.setMaximum(100)
|
||||
self.submit_progress.setValue(int(percent))
|
||||
|
||||
return callback
|
||||
# Pre-worker UI
|
||||
self.submit_progress.setHidden(False)
|
||||
self.submit_progress_label.setHidden(False)
|
||||
self.submit_button.setHidden(True)
|
||||
self.submit_progress.setMaximum(0)
|
||||
|
||||
self.submit_progress.setHidden(False)
|
||||
self.submit_progress_label.setHidden(False)
|
||||
self.submit_button.setHidden(True)
|
||||
# submit job in background thread
|
||||
self.worker_thread = SubmitWorker(window=self)
|
||||
self.worker_thread.message_signal.connect(self.after_job_submission)
|
||||
self.worker_thread.start()
|
||||
|
||||
hostname = self.server_input.currentText()
|
||||
job_json = {'owner': psutil.Process().username() + '@' + socket.gethostname(),
|
||||
'renderer': self.renderer_type.currentText().lower(),
|
||||
# 'input_path': self.scene_file_input.text(),
|
||||
# 'output_path': os.path.join(os.path.dirname(self.chosen_file), self.output_entry.get()),
|
||||
'args': {'raw': self.raw_args.text()},
|
||||
'output_path': self.output_path_input.text(),
|
||||
'start_frame': self.start_frame_input.value(),
|
||||
'end_frame': self.end_frame_input.value(),
|
||||
'priority': self.priority_input.currentIndex() + 1,
|
||||
'notes': self.notes_input.toPlainText(),
|
||||
'enable_split_jobs': self.enable_splitjobs.isChecked()}
|
||||
|
||||
input_path = self.scene_file_input.text()
|
||||
class SubmitWorker(QThread):
|
||||
"""Worker class called to submit all the jobs to the server and update the UI accordingly"""
|
||||
|
||||
message_signal = pyqtSignal(Response)
|
||||
|
||||
def __init__(self, window):
|
||||
super().__init__()
|
||||
self.window = window
|
||||
|
||||
def run(self):
|
||||
def create_callback(encoder):
|
||||
encoder_len = encoder.len
|
||||
|
||||
def callback(monitor):
|
||||
percent = f"{monitor.bytes_read / encoder_len * 100:.0f}"
|
||||
self.window.submit_progress_label.setText(f"Transferring to {hostname} - {percent}%")
|
||||
self.window.submit_progress.setMaximum(100)
|
||||
self.window.submit_progress.setValue(int(percent))
|
||||
|
||||
return callback
|
||||
|
||||
hostname = self.window.server_input.currentText()
|
||||
job_json = {'owner': psutil.Process().username() + '@' + socket.gethostname(),
|
||||
'renderer': self.window.renderer_type.currentText().lower(),
|
||||
'renderer_version': self.window.renderer_version_combo.currentText(),
|
||||
'args': {'raw': self.window.raw_args.text()},
|
||||
'output_path': self.window.output_path_input.text(),
|
||||
'start_frame': self.window.start_frame_input.value(),
|
||||
'end_frame': self.window.end_frame_input.value(),
|
||||
'priority': self.window.priority_input.currentIndex() + 1,
|
||||
'notes': self.window.notes_input.toPlainText(),
|
||||
'enable_split_jobs': self.window.enable_splitjobs.isChecked(),
|
||||
'split_jobs_same_os': self.window.splitjobs_same_os.isChecked()}
|
||||
|
||||
# get the dynamic args
|
||||
for i in range(self.window.renderer_options_layout.count()):
|
||||
item = self.window.renderer_options_layout.itemAt(i)
|
||||
layout = item.layout() # get the layout
|
||||
for x in range(layout.count()):
|
||||
z = layout.itemAt(x)
|
||||
widget = z.widget()
|
||||
if isinstance(widget, QComboBox):
|
||||
job_json['args'][self.window.current_engine_options[i]['name']] = widget.currentText()
|
||||
elif isinstance(widget, QLineEdit):
|
||||
job_json['args'][self.window.current_engine_options[i]['name']] = widget.text()
|
||||
|
||||
# determine if any cameras are checked
|
||||
selected_cameras = []
|
||||
if self.window.cameras_list.count() and not self.window.cameras_group.isHidden():
|
||||
for index in range(self.window.cameras_list.count()):
|
||||
item = self.window.cameras_list.item(index)
|
||||
if item.checkState() == Qt.CheckState.Checked:
|
||||
selected_cameras.append(item.text().rsplit('-', 1)[0].strip()) # cleanup to just camera name
|
||||
|
||||
# process cameras into nested format
|
||||
input_path = self.window.scene_file_input.text()
|
||||
if selected_cameras:
|
||||
job_list = []
|
||||
for cam in selected_cameras:
|
||||
job_copy = copy.deepcopy(job_json)
|
||||
job_copy['args']['camera'] = cam
|
||||
job_copy['name'] = pathlib.Path(input_path).stem.replace(' ', '_') + "-" + cam.replace(' ', '')
|
||||
job_list.append(job_copy)
|
||||
else:
|
||||
job_list = [job_json]
|
||||
self.submit_progress.setMaximum(0)
|
||||
result = self.server_proxy.post_job_to_server(file_path=input_path, job_list=job_list,
|
||||
callback=create_callback)
|
||||
self.submit_progress.setMaximum(0)
|
||||
|
||||
# todo: error handle
|
||||
print(result.json())
|
||||
self.submit_button.setHidden(False)
|
||||
self.submit_progress.setHidden(True)
|
||||
self.submit_progress_label.setHidden(True)
|
||||
# presubmission tasks
|
||||
engine = EngineManager.engine_with_name(self.window.renderer_type.currentText().lower())
|
||||
input_path = engine().perform_presubmission_tasks(input_path)
|
||||
# submit
|
||||
result = self.window.server_proxy.post_job_to_server(file_path=input_path, job_list=job_list,
|
||||
callback=create_callback)
|
||||
self.message_signal.emit(result)
|
||||
|
||||
|
||||
class GetProjectInfoWorker(QThread):
|
||||
"""Worker class called to retrieve information about a project file on a background thread and update the UI"""
|
||||
|
||||
# submit thread
|
||||
worker_thread = threading.Thread(target=submit_job_worker)
|
||||
worker_thread.start()
|
||||
message_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, window, project_path):
|
||||
super().__init__()
|
||||
self.window = window
|
||||
self.project_path = project_path
|
||||
|
||||
def run(self):
|
||||
engine = EngineManager.engine_for_project_path(self.project_path)
|
||||
self.window.project_info = engine().get_project_info(self.project_path)
|
||||
self.message_signal.emit()
|
||||
|
||||
|
||||
def clear_layout(layout):
|
||||
if layout is not None:
|
||||
# Go through the layout's items in reverse order
|
||||
for i in reversed(range(layout.count())):
|
||||
# Take the item at the current position
|
||||
item = layout.takeAt(i)
|
||||
|
||||
# Check if the item is a widget
|
||||
if item.widget():
|
||||
# Remove the widget and delete it
|
||||
widget_to_remove = item.widget()
|
||||
widget_to_remove.setParent(None)
|
||||
widget_to_remove.deleteLater()
|
||||
elif item.layout():
|
||||
# If the item is a sub-layout, clear its contents recursively
|
||||
clear_layout(item.layout())
|
||||
# Then delete the layout
|
||||
item.layout().deleteLater()
|
||||
|
||||
# Run the application
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user