From 7b0d9a0b9f55726471387cd0c55f930f8692a72d Mon Sep 17 00:00:00 2001 From: Brett Williams Date: Sun, 4 Jan 2026 19:42:47 -0600 Subject: [PATCH] Initial refactor of add_job_window --- src/engines/ffmpeg/ffmpeg_engine.py | 5 +- src/ui/add_job_window.py | 168 ++++++++++++++++------------ src/ui/main_window.py | 2 +- 3 files changed, 101 insertions(+), 74 deletions(-) diff --git a/src/engines/ffmpeg/ffmpeg_engine.py b/src/engines/ffmpeg/ffmpeg_engine.py index 2f9fd2d..967a810 100644 --- a/src/engines/ffmpeg/ffmpeg_engine.py +++ b/src/engines/ffmpeg/ffmpeg_engine.py @@ -19,9 +19,10 @@ class FFMPEG(BaseRenderEngine): from src.engines.ffmpeg.ffmpeg_worker import FFMPEGRenderWorker return FFMPEGRenderWorker - def ui_options(self): + @staticmethod + def ui_options(system_info): from src.engines.ffmpeg.ffmpeg_ui import FFMPEGUI - return FFMPEGUI.get_options(self) + return FFMPEGUI.get_options(system_info) def supported_extensions(self): help_text = (subprocess.check_output([self.engine_path(), '-h', 'full'], stderr=subprocess.STDOUT, diff --git a/src/ui/add_job_window.py b/src/ui/add_job_window.py index 07a1ad4..547c6ef 100644 --- a/src/ui/add_job_window.py +++ b/src/ui/add_job_window.py @@ -1,16 +1,13 @@ -import copy import os.path -import pathlib import socket -import threading import psutil from PyQt6.QtCore import QThread, pyqtSignal, Qt, pyqtSlot from PyQt6.QtWidgets import ( 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.engines.engine_manager import EngineManager @@ -59,6 +56,7 @@ class NewRenderJobForm(QWidget): self.scene_file_input = None self.scene_file_browse_button = None self.job_name_input = None + self.tabs = None # Job / Server Data self.server_proxy = RenderServerProxy(socket.gethostname()) @@ -78,100 +76,112 @@ class NewRenderJobForm(QWidget): self.show() def setup_ui(self): - # Main Layout + # Main widget layout main_layout = QVBoxLayout(self) - # Loading File Group + # Tabs + self.tabs = QTabWidget() + + # ==================== Loading Section (outside tabs) ==================== self.load_file_group = QGroupBox("Loading") load_file_layout = QVBoxLayout(self.load_file_group) - # progress bar + progress_layout = QHBoxLayout() + self.process_label = QLabel("Processing") self.process_progress_bar = QProgressBar() self.process_progress_bar.setMinimum(0) - self.process_progress_bar.setMaximum(0) - self.process_label = QLabel("Processing") + self.process_progress_bar.setMaximum(0) # Indeterminate progress_layout.addWidget(self.process_label) progress_layout.addWidget(self.process_progress_bar) load_file_layout.addLayout(progress_layout) - main_layout.addWidget(self.load_file_group) - # Project Group - self.project_group = QGroupBox("Project") - server_layout = QVBoxLayout(self.project_group) - # File Path + # Scene File scene_file_picker_layout = QHBoxLayout() + scene_file_picker_layout.addWidget(QLabel("File:")) 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(QLabel("File:")) scene_file_picker_layout.addWidget(self.scene_file_input) scene_file_picker_layout.addWidget(self.scene_file_browse_button) - server_layout.addLayout(scene_file_picker_layout) - # Server List + main_layout.addLayout(scene_file_picker_layout) + 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.setSpacing(0) + server_list_layout.addWidget(QLabel("Render Target:")) 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.project_group) - self.update_server_list() + server_list_layout.addWidget(self.server_input) + project_layout.addLayout(server_list_layout) + # Priority priority_layout = QHBoxLayout() - priority_layout.addWidget(QLabel("Priority:"), 1) + priority_layout.addWidget(QLabel("Priority:")) 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) + priority_layout.addWidget(self.priority_input) + project_layout.addLayout(priority_layout) - # Output Settings Group - self.output_settings_group = QGroupBox("Output Settings") + # Split Jobs Options + 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 path + + # Render Name render_name_layout = QHBoxLayout() render_name_layout.addWidget(QLabel("Render name:")) self.render_name_input = QLineEdit() render_name_layout.addWidget(self.render_name_input) output_settings_layout.addLayout(render_name_layout) - # file format + + # File Format file_format_layout = QHBoxLayout() file_format_layout.addWidget(QLabel("Format:")) self.file_format_combo = QComboBox() + # You can populate this later based on engine 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) + + # Frame Range + frame_range_layout = QHBoxLayout() + frame_range_layout.addWidget(QLabel("Frames:")) self.start_frame_input = QSpinBox() self.start_frame_input.setRange(1, 99999) self.end_frame_input = QSpinBox() 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(QLabel("to")) frame_range_layout.addWidget(self.end_frame_input) 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.setRange(1, 9999) # Assuming max resolution width 9999 + self.resolution_x_input.setRange(1, 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.setRange(1, 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.setRange(1.0, 999.0) 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) @@ -179,28 +189,31 @@ class NewRenderJobForm(QWidget): 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) - # Engine Group - self.engine_group = QGroupBox("Engine Settings") + output_settings_layout.addStretch() + + # ==================== Tab 3: Engine Settings ==================== + self.engine_group = QWidget() engine_group_layout = QVBoxLayout(self.engine_group) + engine_layout = QHBoxLayout() engine_layout.addWidget(QLabel("Engine:")) self.engine_type = QComboBox() self.engine_type.currentIndexChanged.connect(self.engine_changed) engine_layout.addWidget(self.engine_type) - # Version + engine_layout.addWidget(QLabel("Version:")) self.engine_version_combo = QComboBox() self.engine_version_combo.addItem('latest') engine_layout.addWidget(self.engine_version_combo) engine_group_layout.addLayout(engine_layout) - # dynamic options + + # Dynamic engine options self.engine_options_layout = QVBoxLayout() engine_group_layout.addLayout(self.engine_options_layout) + # Raw Args - raw_args_layout = QHBoxLayout(self.engine_group) + raw_args_layout = QHBoxLayout() raw_args_layout.addWidget(QLabel("Raw Args:")) self.raw_args = QLineEdit() raw_args_layout.addWidget(self.raw_args) @@ -208,24 +221,33 @@ class NewRenderJobForm(QWidget): args_help_button.clicked.connect(self.args_help_button_clicked) raw_args_layout.addWidget(args_help_button) engine_group_layout.addLayout(raw_args_layout) - main_layout.addWidget(self.engine_group) + engine_group_layout.addStretch() - # Cameras Group - self.cameras_group = QGroupBox("Cameras") + # ==================== Tab 4: Cameras ==================== + self.cameras_group = QWidget() 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 - self.notes_group = QGroupBox("Additional Notes") + # ==================== Tab 5: Misc / Notes ==================== + self.notes_group = QWidget() notes_layout = QVBoxLayout(self.notes_group) self.notes_input = QPlainTextEdit() 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.clicked.connect(self.submit_job) main_layout.addWidget(self.submit_button) @@ -240,7 +262,9 @@ class NewRenderJobForm(QWidget): self.submit_progress_label.setHidden(True) main_layout.addWidget(self.submit_progress_label) + # Initial engine state self.toggle_engine_enablement(False) + self.tabs.setCurrentIndex(0) def update_engine_info(self): # get the engine info and add them all to the ui @@ -330,8 +354,9 @@ class NewRenderJobForm(QWidget): # Cameras self.cameras_list.clear() + index = self.tabs.indexOf(self.cameras_group) if self.project_info.get('cameras'): - self.cameras_group.setHidden(False) + self.tabs.setTabEnabled(index, True) found_active = False for camera in self.project_info['cameras']: # create the list items and make them checkable @@ -344,7 +369,7 @@ class NewRenderJobForm(QWidget): if not found_active: self.cameras_list.item(0).setCheckState(Qt.CheckState.Checked) else: - self.cameras_group.setHidden(True) + self.tabs.setTabEnabled(index, False) # Dynamic Engine Options clear_layout(self.engine_options_layout) # clear old options @@ -369,12 +394,13 @@ class NewRenderJobForm(QWidget): def toggle_engine_enablement(self, enabled=False): """Toggle on/off all the render settings""" - self.project_group.setHidden(not enabled) - self.output_settings_group.setHidden(not enabled) - self.engine_group.setHidden(not enabled) - self.notes_group.setHidden(not enabled) - if not enabled: - self.cameras_group.setHidden(True) + indexes = [self.tabs.indexOf(self.project_group), + self.tabs.indexOf(self.output_settings_group), + self.tabs.indexOf(self.engine_group), + self.tabs.indexOf(self.cameras_group), + self.tabs.indexOf(self.notes_group)] + for idx in indexes: + self.tabs.setTabEnabled(idx, enabled) self.submit_button.setEnabled(enabled) def after_job_submission(self, error_string): diff --git a/src/ui/main_window.py b/src/ui/main_window.py index 601787e..d362623 100644 --- a/src/ui/main_window.py +++ b/src/ui/main_window.py @@ -307,7 +307,7 @@ class MainWindow(QMainWindow): get_time_elapsed(start_time, end_time) 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', '')) total_frames = str(job.get('total_frames', '')) date_created_string = iso_datestring_to_formatted_datestring(job['date_created'])