Initial refactor of add_job_window

This commit is contained in:
Brett Williams
2026-01-04 19:42:47 -06:00
parent d8af7c878e
commit 7b0d9a0b9f
3 changed files with 101 additions and 74 deletions

View File

@@ -19,9 +19,10 @@ class FFMPEG(BaseRenderEngine):
from src.engines.ffmpeg.ffmpeg_worker import FFMPEGRenderWorker from src.engines.ffmpeg.ffmpeg_worker import FFMPEGRenderWorker
return FFMPEGRenderWorker return FFMPEGRenderWorker
def ui_options(self): @staticmethod
def ui_options(system_info):
from src.engines.ffmpeg.ffmpeg_ui import FFMPEGUI from src.engines.ffmpeg.ffmpeg_ui import FFMPEGUI
return FFMPEGUI.get_options(self) return FFMPEGUI.get_options(system_info)
def supported_extensions(self): def supported_extensions(self):
help_text = (subprocess.check_output([self.engine_path(), '-h', 'full'], stderr=subprocess.STDOUT, help_text = (subprocess.check_output([self.engine_path(), '-h', 'full'], stderr=subprocess.STDOUT,

View File

@@ -1,16 +1,13 @@
import copy
import os.path import os.path
import pathlib
import socket import socket
import threading
import psutil import psutil
from PyQt6.QtCore import QThread, pyqtSignal, Qt, pyqtSlot from PyQt6.QtCore import QThread, pyqtSignal, Qt, pyqtSlot
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QSpinBox, QComboBox, QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QSpinBox, QComboBox,
QGroupBox, QCheckBox, QProgressBar, QPlainTextEdit, QDoubleSpinBox, QMessageBox, QListWidget, QListWidgetItem QGroupBox, QCheckBox, QProgressBar, QPlainTextEdit, QDoubleSpinBox, QMessageBox, QListWidget, QListWidgetItem,
QTabWidget
) )
from requests import Response
from src.api.server_proxy import RenderServerProxy from src.api.server_proxy import RenderServerProxy
from src.engines.engine_manager import EngineManager from src.engines.engine_manager import EngineManager
@@ -59,6 +56,7 @@ class NewRenderJobForm(QWidget):
self.scene_file_input = None self.scene_file_input = None
self.scene_file_browse_button = None self.scene_file_browse_button = None
self.job_name_input = None self.job_name_input = None
self.tabs = None
# Job / Server Data # Job / Server Data
self.server_proxy = RenderServerProxy(socket.gethostname()) self.server_proxy = RenderServerProxy(socket.gethostname())
@@ -78,100 +76,112 @@ class NewRenderJobForm(QWidget):
self.show() self.show()
def setup_ui(self): def setup_ui(self):
# Main Layout # Main widget layout
main_layout = QVBoxLayout(self) main_layout = QVBoxLayout(self)
# Loading File Group # Tabs
self.tabs = QTabWidget()
# ==================== Loading Section (outside tabs) ====================
self.load_file_group = QGroupBox("Loading") self.load_file_group = QGroupBox("Loading")
load_file_layout = QVBoxLayout(self.load_file_group) load_file_layout = QVBoxLayout(self.load_file_group)
# progress bar
progress_layout = QHBoxLayout() progress_layout = QHBoxLayout()
self.process_label = QLabel("Processing")
self.process_progress_bar = QProgressBar() self.process_progress_bar = QProgressBar()
self.process_progress_bar.setMinimum(0) self.process_progress_bar.setMinimum(0)
self.process_progress_bar.setMaximum(0) self.process_progress_bar.setMaximum(0) # Indeterminate
self.process_label = QLabel("Processing")
progress_layout.addWidget(self.process_label) progress_layout.addWidget(self.process_label)
progress_layout.addWidget(self.process_progress_bar) progress_layout.addWidget(self.process_progress_bar)
load_file_layout.addLayout(progress_layout) load_file_layout.addLayout(progress_layout)
main_layout.addWidget(self.load_file_group)
# Project Group # Scene File
self.project_group = QGroupBox("Project")
server_layout = QVBoxLayout(self.project_group)
# File Path
scene_file_picker_layout = QHBoxLayout() scene_file_picker_layout = QHBoxLayout()
scene_file_picker_layout.addWidget(QLabel("File:"))
self.scene_file_input = QLineEdit() self.scene_file_input = QLineEdit()
self.scene_file_input.setText(self.project_path) self.scene_file_input.setText(self.project_path)
self.scene_file_browse_button = QPushButton("Browse...") self.scene_file_browse_button = QPushButton("Browse...")
self.scene_file_browse_button.clicked.connect(self.browse_scene_file) self.scene_file_browse_button.clicked.connect(self.browse_scene_file)
scene_file_picker_layout.addWidget(QLabel("File:"))
scene_file_picker_layout.addWidget(self.scene_file_input) scene_file_picker_layout.addWidget(self.scene_file_input)
scene_file_picker_layout.addWidget(self.scene_file_browse_button) scene_file_picker_layout.addWidget(self.scene_file_browse_button)
server_layout.addLayout(scene_file_picker_layout) main_layout.addLayout(scene_file_picker_layout)
# Server List main_layout.addWidget(self.load_file_group)
main_layout.addWidget(self.tabs)
# ==================== Tab 1: Job Settings ====================
self.project_group = QWidget()
project_layout = QVBoxLayout(self.project_group) # Fixed: proper parent
# Server / Hostname
server_list_layout = QHBoxLayout() server_list_layout = QHBoxLayout()
server_list_layout.setSpacing(0) server_list_layout.addWidget(QLabel("Render Target:"))
self.server_input = QComboBox() self.server_input = QComboBox()
server_list_layout.addWidget(QLabel("Hostname:"), 1) server_list_layout.addWidget(self.server_input)
server_list_layout.addWidget(self.server_input, 3) project_layout.addLayout(server_list_layout)
server_layout.addLayout(server_list_layout)
main_layout.addWidget(self.project_group)
self.update_server_list()
# Priority # Priority
priority_layout = QHBoxLayout() priority_layout = QHBoxLayout()
priority_layout.addWidget(QLabel("Priority:"), 1) priority_layout.addWidget(QLabel("Priority:"))
self.priority_input = QComboBox() self.priority_input = QComboBox()
self.priority_input.addItems(["High", "Medium", "Low"]) self.priority_input.addItems(["High", "Medium", "Low"])
self.priority_input.setCurrentIndex(1) self.priority_input.setCurrentIndex(1)
priority_layout.addWidget(self.priority_input, 3) priority_layout.addWidget(self.priority_input)
server_layout.addLayout(priority_layout) project_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 # Split Jobs Options
self.output_settings_group = QGroupBox("Output Settings") self.enable_splitjobs = QCheckBox("Automatically split render across multiple servers")
project_layout.addWidget(self.enable_splitjobs)
self.splitjobs_same_os = QCheckBox("Only render on same OS")
project_layout.addWidget(self.splitjobs_same_os)
project_layout.addStretch() # Push everything up
# ==================== Tab 2: Output Settings ====================
self.output_settings_group = QWidget()
output_settings_layout = QVBoxLayout(self.output_settings_group) output_settings_layout = QVBoxLayout(self.output_settings_group)
# output path
# Render Name
render_name_layout = QHBoxLayout() render_name_layout = QHBoxLayout()
render_name_layout.addWidget(QLabel("Render name:")) render_name_layout.addWidget(QLabel("Render name:"))
self.render_name_input = QLineEdit() self.render_name_input = QLineEdit()
render_name_layout.addWidget(self.render_name_input) render_name_layout.addWidget(self.render_name_input)
output_settings_layout.addLayout(render_name_layout) output_settings_layout.addLayout(render_name_layout)
# file format
# File Format
file_format_layout = QHBoxLayout() file_format_layout = QHBoxLayout()
file_format_layout.addWidget(QLabel("Format:")) file_format_layout.addWidget(QLabel("Format:"))
self.file_format_combo = QComboBox() self.file_format_combo = QComboBox()
# You can populate this later based on engine
file_format_layout.addWidget(self.file_format_combo) file_format_layout.addWidget(self.file_format_combo)
output_settings_layout.addLayout(file_format_layout) output_settings_layout.addLayout(file_format_layout)
# frame range
frame_range_layout = QHBoxLayout(self.output_settings_group) # Frame Range
frame_range_layout = QHBoxLayout()
frame_range_layout.addWidget(QLabel("Frames:"))
self.start_frame_input = QSpinBox() self.start_frame_input = QSpinBox()
self.start_frame_input.setRange(1, 99999) self.start_frame_input.setRange(1, 99999)
self.end_frame_input = QSpinBox() self.end_frame_input = QSpinBox()
self.end_frame_input.setRange(1, 99999) self.end_frame_input.setRange(1, 99999)
frame_range_layout.addWidget(QLabel("Frames:"))
frame_range_layout.addWidget(self.start_frame_input) frame_range_layout.addWidget(self.start_frame_input)
frame_range_layout.addWidget(QLabel("to")) frame_range_layout.addWidget(QLabel("to"))
frame_range_layout.addWidget(self.end_frame_input) frame_range_layout.addWidget(self.end_frame_input)
output_settings_layout.addLayout(frame_range_layout) output_settings_layout.addLayout(frame_range_layout)
# resolution
resolution_layout = QHBoxLayout(self.output_settings_group) # Resolution & FPS
resolution_layout = QHBoxLayout()
resolution_layout.addWidget(QLabel("Resolution:"))
self.resolution_x_input = QSpinBox() self.resolution_x_input = QSpinBox()
self.resolution_x_input.setRange(1, 9999) # Assuming max resolution width 9999 self.resolution_x_input.setRange(1, 9999)
self.resolution_x_input.setValue(1920) self.resolution_x_input.setValue(1920)
self.resolution_y_input = QSpinBox() self.resolution_y_input = QSpinBox()
self.resolution_y_input.setRange(1, 9999) # Assuming max resolution height 9999 self.resolution_y_input.setRange(1, 9999)
self.resolution_y_input.setValue(1080) self.resolution_y_input.setValue(1080)
self.frame_rate_input = QDoubleSpinBox() 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.setDecimals(3)
self.frame_rate_input.setRange(1.0, 999.0)
self.frame_rate_input.setValue(23.976) self.frame_rate_input.setValue(23.976)
resolution_layout.addWidget(QLabel("Resolution:"))
resolution_layout.addWidget(self.resolution_x_input) resolution_layout.addWidget(self.resolution_x_input)
resolution_layout.addWidget(QLabel("x")) resolution_layout.addWidget(QLabel("x"))
resolution_layout.addWidget(self.resolution_y_input) resolution_layout.addWidget(self.resolution_y_input)
@@ -179,28 +189,31 @@ class NewRenderJobForm(QWidget):
resolution_layout.addWidget(self.frame_rate_input) resolution_layout.addWidget(self.frame_rate_input)
resolution_layout.addWidget(QLabel("fps")) resolution_layout.addWidget(QLabel("fps"))
output_settings_layout.addLayout(resolution_layout) output_settings_layout.addLayout(resolution_layout)
# add group to layout
main_layout.addWidget(self.output_settings_group)
# Engine Group output_settings_layout.addStretch()
self.engine_group = QGroupBox("Engine Settings")
# ==================== Tab 3: Engine Settings ====================
self.engine_group = QWidget()
engine_group_layout = QVBoxLayout(self.engine_group) engine_group_layout = QVBoxLayout(self.engine_group)
engine_layout = QHBoxLayout() engine_layout = QHBoxLayout()
engine_layout.addWidget(QLabel("Engine:")) engine_layout.addWidget(QLabel("Engine:"))
self.engine_type = QComboBox() self.engine_type = QComboBox()
self.engine_type.currentIndexChanged.connect(self.engine_changed) self.engine_type.currentIndexChanged.connect(self.engine_changed)
engine_layout.addWidget(self.engine_type) engine_layout.addWidget(self.engine_type)
# Version
engine_layout.addWidget(QLabel("Version:")) engine_layout.addWidget(QLabel("Version:"))
self.engine_version_combo = QComboBox() self.engine_version_combo = QComboBox()
self.engine_version_combo.addItem('latest') self.engine_version_combo.addItem('latest')
engine_layout.addWidget(self.engine_version_combo) engine_layout.addWidget(self.engine_version_combo)
engine_group_layout.addLayout(engine_layout) engine_group_layout.addLayout(engine_layout)
# dynamic options
# Dynamic engine options
self.engine_options_layout = QVBoxLayout() self.engine_options_layout = QVBoxLayout()
engine_group_layout.addLayout(self.engine_options_layout) engine_group_layout.addLayout(self.engine_options_layout)
# Raw Args # Raw Args
raw_args_layout = QHBoxLayout(self.engine_group) raw_args_layout = QHBoxLayout()
raw_args_layout.addWidget(QLabel("Raw Args:")) raw_args_layout.addWidget(QLabel("Raw Args:"))
self.raw_args = QLineEdit() self.raw_args = QLineEdit()
raw_args_layout.addWidget(self.raw_args) raw_args_layout.addWidget(self.raw_args)
@@ -208,24 +221,33 @@ class NewRenderJobForm(QWidget):
args_help_button.clicked.connect(self.args_help_button_clicked) args_help_button.clicked.connect(self.args_help_button_clicked)
raw_args_layout.addWidget(args_help_button) raw_args_layout.addWidget(args_help_button)
engine_group_layout.addLayout(raw_args_layout) engine_group_layout.addLayout(raw_args_layout)
main_layout.addWidget(self.engine_group) engine_group_layout.addStretch()
# Cameras Group # ==================== Tab 4: Cameras ====================
self.cameras_group = QGroupBox("Cameras") self.cameras_group = QWidget()
cameras_layout = QVBoxLayout(self.cameras_group) cameras_layout = QVBoxLayout(self.cameras_group)
self.cameras_list = QListWidget() self.cameras_list = QListWidget()
self.cameras_group.setHidden(True)
cameras_layout.addWidget(self.cameras_list) cameras_layout.addWidget(self.cameras_list)
main_layout.addWidget(self.cameras_group)
# Notes Group # ==================== Tab 5: Misc / Notes ====================
self.notes_group = QGroupBox("Additional Notes") self.notes_group = QWidget()
notes_layout = QVBoxLayout(self.notes_group) notes_layout = QVBoxLayout(self.notes_group)
self.notes_input = QPlainTextEdit() self.notes_input = QPlainTextEdit()
notes_layout.addWidget(self.notes_input) notes_layout.addWidget(self.notes_input)
main_layout.addWidget(self.notes_group)
# Submit Button # == Create Tabs
self.tabs.addTab(self.project_group, "Job Settings")
self.tabs.addTab(self.output_settings_group, "Output Settings")
self.tabs.addTab(self.engine_group, "Engine Settings")
self.tabs.addTab(self.cameras_group, "Cameras")
self.tabs.addTab(self.notes_group, "Notes")
self.update_server_list()
index = self.tabs.indexOf(self.cameras_group)
if index != -1:
self.tabs.setTabEnabled(index, False)
# ==================== Submit Section (outside tabs) ====================
self.submit_button = QPushButton("Submit Job") self.submit_button = QPushButton("Submit Job")
self.submit_button.clicked.connect(self.submit_job) self.submit_button.clicked.connect(self.submit_job)
main_layout.addWidget(self.submit_button) main_layout.addWidget(self.submit_button)
@@ -240,7 +262,9 @@ class NewRenderJobForm(QWidget):
self.submit_progress_label.setHidden(True) self.submit_progress_label.setHidden(True)
main_layout.addWidget(self.submit_progress_label) main_layout.addWidget(self.submit_progress_label)
# Initial engine state
self.toggle_engine_enablement(False) self.toggle_engine_enablement(False)
self.tabs.setCurrentIndex(0)
def update_engine_info(self): def update_engine_info(self):
# get the engine info and add them all to the ui # get the engine info and add them all to the ui
@@ -330,8 +354,9 @@ class NewRenderJobForm(QWidget):
# Cameras # Cameras
self.cameras_list.clear() self.cameras_list.clear()
index = self.tabs.indexOf(self.cameras_group)
if self.project_info.get('cameras'): if self.project_info.get('cameras'):
self.cameras_group.setHidden(False) self.tabs.setTabEnabled(index, True)
found_active = False found_active = False
for camera in self.project_info['cameras']: for camera in self.project_info['cameras']:
# create the list items and make them checkable # create the list items and make them checkable
@@ -344,7 +369,7 @@ class NewRenderJobForm(QWidget):
if not found_active: if not found_active:
self.cameras_list.item(0).setCheckState(Qt.CheckState.Checked) self.cameras_list.item(0).setCheckState(Qt.CheckState.Checked)
else: else:
self.cameras_group.setHidden(True) self.tabs.setTabEnabled(index, False)
# Dynamic Engine Options # Dynamic Engine Options
clear_layout(self.engine_options_layout) # clear old options clear_layout(self.engine_options_layout) # clear old options
@@ -369,12 +394,13 @@ class NewRenderJobForm(QWidget):
def toggle_engine_enablement(self, enabled=False): def toggle_engine_enablement(self, enabled=False):
"""Toggle on/off all the render settings""" """Toggle on/off all the render settings"""
self.project_group.setHidden(not enabled) indexes = [self.tabs.indexOf(self.project_group),
self.output_settings_group.setHidden(not enabled) self.tabs.indexOf(self.output_settings_group),
self.engine_group.setHidden(not enabled) self.tabs.indexOf(self.engine_group),
self.notes_group.setHidden(not enabled) self.tabs.indexOf(self.cameras_group),
if not enabled: self.tabs.indexOf(self.notes_group)]
self.cameras_group.setHidden(True) for idx in indexes:
self.tabs.setTabEnabled(idx, enabled)
self.submit_button.setEnabled(enabled) self.submit_button.setEnabled(enabled)
def after_job_submission(self, error_string): def after_job_submission(self, error_string):

View File

@@ -307,7 +307,7 @@ class MainWindow(QMainWindow):
get_time_elapsed(start_time, end_time) get_time_elapsed(start_time, end_time)
name = job.get('name') or os.path.basename(job.get('input_path', '')) name = job.get('name') or os.path.basename(job.get('input_path', ''))
engine_name = f"{job.get('renderer', '')}-{job.get('renderer_version')}" engine_name = f"{job.get('engine', '')}-{job.get('engine_version')}"
priority = str(job.get('priority', '')) priority = str(job.get('priority', ''))
total_frames = str(job.get('total_frames', '')) total_frames = str(job.get('total_frames', ''))
date_created_string = iso_datestring_to_formatted_datestring(job['date_created']) date_created_string = iso_datestring_to_formatted_datestring(job['date_created'])