import sys, os, subprocess from PySide6.QtWidgets import QApplication, QDialog, QButtonGroup, QFileDialog, QMessageBox from PySide6.QtCore import Slot from PySide6.QtGui import QIntValidator # Import the generated UI class from the ui_form.py file from ui_read_disk import Ui_ReadDialog class ReadDiskWindow(QDialog): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_ReadDialog() self.ui.setupUi(self) # --- Set up Logical Button Groups --- self.flippy_type_group = QButtonGroup(self) self.flippy_type_group.addButton(self.ui.rb_panasonic, 1) self.flippy_type_group.addButton(self.ui.rb_teac, 2) self.pin2_setting_group = QButtonGroup(self) self.pin2_setting_group.addButton(self.ui.rb_pin2_high, 1) self.pin2_setting_group.addButton(self.ui.rb_pin2_low, 2) # --- Connect Signals to Slots --- self.ui.cb_fake_index.toggled.connect(self.on_update_settings) self.ui.cb_bitrate.toggled.connect(self.on_update_settings) self.ui.cb_double_step.toggled.connect(self.on_update_settings) self.ui.cb_revs.toggled.connect(self.on_update_settings) self.ui.cb_drive_select.toggled.connect(self.on_update_settings) self.ui.cb_pllspec.toggled.connect(self.on_update_settings) self.ui.cb_retries.toggled.connect(self.on_update_settings) self.ui.cb_head_sets.toggled.connect(self.on_update_settings) self.ui.cb_head_swap.toggled.connect(self.on_update_settings) self.ui.cb_ss_legacy.toggled.connect(self.on_update_settings) self.ui.cb_cylinder_sets.toggled.connect(self.on_update_settings) self.ui.cb_rev_track_data.toggled.connect(self.on_update_settings) self.ui.cb_hard_sectors.toggled.connect(self.on_update_settings) self.ui.cb_no_clobber.toggled.connect(self.on_update_settings) self.ui.cb_raw.toggled.connect(self.on_update_settings) self.ui.cb_pin2.toggled.connect(self.on_update_settings) self.ui.rb_pin2_high.toggled.connect(self.on_update_settings) self.ui.rb_pin2_low.toggled.connect(self.on_update_settings) self.ui.cb_flippy.toggled.connect(self.on_update_settings) self.ui.rb_panasonic.toggled.connect(self.on_update_settings) self.ui.rb_teac.toggled.connect(self.on_update_settings) self.ui.cb_adjust_speed.toggled.connect(self.on_update_settings) self.ui.combo_drive_select.currentIndexChanged.connect(self.on_update_settings) self.ui.combo_fake_index.currentIndexChanged.connect(self.on_update_settings) self.ui.combo_adjust_speed.currentIndexChanged.connect(self.on_update_settings) self.ui.combo_disktype.currentIndexChanged.connect(self.on_update_settings) self.ui.combo_format.currentIndexChanged.connect(self.on_update_settings) self.ui.le_fake_index.textChanged.connect(self.on_update_settings) self.ui.le_lowpass.textChanged.connect(self.on_update_settings) self.ui.le_period.textChanged.connect(self.on_update_settings) self.ui.le_phase.textChanged.connect(self.on_update_settings) self.ui.le_bitrate.textChanged.connect(self.on_update_settings) self.ui.le_revs.textChanged.connect(self.on_update_settings) self.ui.le_double_step.textChanged.connect(self.on_update_settings) self.ui.le_retries.textChanged.connect(self.on_update_settings) self.ui.le_head_sets.textChanged.connect(self.on_update_settings) self.ui.le_cylinder_sets.textChanged.connect(self.on_update_settings) self.ui.le_adjust_speed.textChanged.connect(self.on_update_settings) self.ui.le_suffix.textChanged.connect(self.on_update_settings) self.ui.le_fileprefix.textChanged.connect(self.on_update_settings) #self.ui.btn_suffix_inc.clicked.connect(self.on_update_settings) #self.ui.btn_suffix_dec.clicked.connect(self.on_update_settings) # self.ui.btn_path_select.clicked.connect(self.on_path_select) self.ui.btn_file_select.clicked.connect(self.on_update_settings) self.ui.cb_format.toggled.connect(self.on_update_settings) self.ui.cb_inc.toggled.connect(self.on_update_settings) # --- Connect Buttons to Dialog's Built-in Slots --- # The "Launch" button will "accept" the dialog (like OK) # The "Back" button will "reject" the dialog (like Cancel) self.ui.btn_launch.clicked.connect(self.on_launch) self.ui.btn_back.clicked.connect(self.reject) # Initialize command attribute self.command = "" def calc_suffix(self, inc: int) -> None: # Increments or decrements the numeric value in the suffix text field. try: current_value = int(self.ui.le_suffix.text()) current_value = current_value + inc except ValueError: current_value = 0 self.ui.le_suffix.setText(str(current_value)) @Slot() def on_btn_suffix_inc_clicked(self): self.calc_suffix(1) @Slot() def on_btn_suffix_dec_clicked(self): self.calc_suffix(-1) @Slot() def on_btn_file_select_clicked(self): file, ok = QFileDialog.getOpenFileName(self, "Select File", self.ui.le_filepath.text()) if file == "": return file_path, file_name = os.path.split(file) name, extension = os.path.splitext(file_name) self.ui.le_filepath.setText(file_path) self.ui.le_fileprefix.setText(name) self.ui.le_suffix.setText("") self.ui.combo_disktype.setCurrentText(extension) self.on_update_settings() @Slot() def on_btn_path_select_clicked(self): path = QFileDialog.getExistingDirectory(self, "Select Folder") self.ui.le_filepath.setText(path) self.on_update_settings() @Slot() def on_launch(self): try: completed = subprocess.run(self.command, check=True) except FileNotFoundError as e: QMessageBox.critical(self, "Error", f"Command not found: {e}") except subprocess.CalledProcessError as e: QMessageBox.critical(self, "Error", f"Failed to launch command: {e}") if self.ui.cb_inc.isChecked(): self.calc_suffix(1) def get_settings(self): """ A helper method to gather all settings from the UI into a dictionary. This is a clean way to pass the data out of the dialog. """ settings = { "fileprefix": self.ui.le_fileprefix.text(), "filepath": self.ui.le_filepath.text(), "double_step_enabled": self.ui.cb_double_step.isChecked(), "double_step_value": self.ui.le_double_step.text(), "fake_index_enabled": self.ui.cb_fake_index.isChecked(), "fake_index_value": self.ui.le_fake_index.text(), "fake_index_unit": self.ui.combo_fake_index.currentText(), "bitrate_enabled": self.ui.cb_bitrate.isChecked(), "bitrate_value": self.ui.le_bitrate.text(), "revs_enabled": self.ui.cb_revs.isChecked(), "revs_value": self.ui.le_revs.text(), "drive_select_enabled": self.ui.cb_drive_select.isChecked(), "drive_select_value": self.ui.combo_drive_select.currentText(), "retries_enabled": self.ui.cb_retries.isChecked(), "retries_value": self.ui.le_retries.text(), "pllspec_enabled": self.ui.cb_pllspec.isChecked(), "pll_period": self.ui.le_period.text(), "pll_phase": self.ui.le_phase.text(), "pll_lowpass": self.ui.le_lowpass.text(), "head_sets_enabled": self.ui.cb_head_sets.isChecked(), "head_sets_value": self.ui.le_head_sets.text(), "head_swap_enabled": self.ui.cb_head_swap.isChecked(), "ss_legacy_enabled": self.ui.cb_ss_legacy.isChecked(), "cylinder_sets_enabled": self.ui.cb_cylinder_sets.isChecked(), "cylinder_sets_value": self.ui.le_cylinder_sets.text(), "rev_track_data_enabled": self.ui.cb_rev_track_data.isChecked(), "hard_sectors_enabled": self.ui.cb_hard_sectors.isChecked(), "no_clobber_enabled": self.ui.cb_no_clobber.isChecked(), "raw_enabled": self.ui.cb_raw.isChecked(), "pin2_enabled": self.ui.cb_pin2.isChecked(), "pin2_high": self.ui.rb_pin2_high.isChecked(), "pin2_low": self.ui.rb_pin2_low.isChecked(), "flippy_enabled": self.ui.cb_flippy.isChecked(), "flippy_panasonic": self.ui.rb_panasonic.isChecked(), "flippy_teac": self.ui.rb_teac.isChecked(),'"' + "adjust_speed_enabled": self.ui.cb_adjust_speed.isChecked(), "adjust_speed_value": self.ui.le_adjust_speed.text(), "adjust_speed_unit": self.ui.combo_adjust_speed.currentText(), "suffix_value": self.ui.le_suffix.text(), "inc_enabled": self.ui.cb_inc.isChecked(), "format_enabled": self.ui.cb_format.isChecked(), "format_value": self.ui.combo_format.currentText(), "disktype_value": self.ui.combo_disktype.currentText(), } return settings def set_settings(self, settings): self.ui.le_fileprefix.setText(settings.get("fileprefix", "")) self.ui.le_filepath.setText(settings.get("filepath", "")) self.ui.cb_double_step.setChecked(settings.get("double_step_enabled", False)) self.ui.le_double_step.setText(settings.get("double_step_value", "")) self.ui.cb_fake_index.setChecked(settings.get("fake_index_enabled", False)) self.ui.le_fake_index.setText(settings.get("fake_index_value", "")) self.ui.combo_fake_index.setCurrentText(settings.get("fake_index_unit", "")) self.ui.cb_bitrate.setChecked(settings.get("bitrate_enabled", False)) self.ui.le_bitrate.setText(settings.get("bitrate_value", "")) self.ui.cb_revs.setChecked(settings.get("revs_enabled", False)) self.ui.le_revs.setText(settings.get("revs_value", "")) self.ui.cb_drive_select.setChecked(settings.get("drive_select_enabled", False)) self.ui.combo_drive_select.setCurrentText(settings.get("drive_select_value", "")) self.ui.cb_retries.setChecked(settings.get("retries_enabled", False)) self.ui.le_retries.setText(settings.get("retries_value", "")) self.ui.cb_pllspec.setChecked(settings.get("pllspec_enabled", False)) self.ui.le_period.setText(settings.get("pll_period", "")) self.ui.le_phase.setText(settings.get("pll_phase", "")) self.ui.le_lowpass.setText(settings.get("pll_lowpass", "")) self.ui.cb_head_sets.setChecked(settings.get("head_sets_enabled", False)) self.ui.le_head_sets.setText(settings.get("head_sets_value", "")) self.ui.cb_head_swap.setChecked(settings.get("head_swap_enabled", False)) self.ui.cb_ss_legacy.setChecked(settings.get("ss_legacy_enabled", False)) self.ui.cb_cylinder_sets.setChecked(settings.get("cylinder_sets_enabled", False)) self.ui.le_cylinder_sets.setText(settings.get("cylinder_sets_value", "")) self.ui.cb_rev_track_data.setChecked(settings.get("rev_track_data_enabled", False)) self.ui.cb_hard_sectors.setChecked(settings.get("hard_sectors_enabled", False)) self.ui.cb_no_clobber.setChecked(settings.get("no_clobber_enabled", False)) self.ui.cb_raw.setChecked(settings.get("raw_enabled", False)) self.ui.cb_pin2.setChecked(settings.get("pin2_enabled", False)) self.ui.rb_pin2_high.setChecked(settings.get("pin2_high", False)) self.ui.rb_pin2_low.setChecked(settings.get("pin2_low", False)) self.ui.cb_flippy.setChecked(settings.get("flippy_enabled", False)) self.ui.rb_panasonic.setChecked(settings.get("flippy_panasonic", False)) self.ui.rb_teac.setChecked(settings.get("flippy_teac", False)) self.ui.cb_adjust_speed.setChecked(settings.get("adjust_speed_enabled", False)) self.ui.le_adjust_speed.setText(settings.get("adjust_speed_value", "")) self.ui.combo_adjust_speed.setCurrentText(settings.get("adjust_speed_unit", "")) self.ui.le_suffix.setText(settings.get("suffix_value", "")) self.ui.cb_inc.setChecked(settings.get("inc_enabled", False)) self.ui.cb_format.setChecked(settings.get("format_enabled", False)) self.ui.combo_format.setCurrentText(settings.get("format_value", "")) self.ui.combo_disktype.setCurrentText(settings.get("disktype_value", "")) # Note: command is not set here, as it is generated from UI state def on_update_settings(self, *args, **kwargs): fileprefix = self.ui.le_fileprefix.text() drive = "" bitrate = "" legacy_ss = "" revs = "" retries = "" fake_index = "" pll = "" tracks = "" densel = "" no_clobber = "" raw = "" reverse = "" hard_sectors = "" adjust_speed = "" disktype = "" if self.ui.cb_revs.isChecked(): revs = "--revs=" + self.ui.le_revs.text() if self.ui.cb_drive_select.isChecked(): drive = "--drive=" + self.ui.combo_drive_select.currentText() if self.ui.cb_bitrate.isChecked(): bitrate = "::bitrate=" + self.ui.le_bitrate.text() if self.ui.cb_ss_legacy.isChecked(): legacy_ss = "::legacy_ss" if self.ui.cb_retries.isChecked(): retries = "--retries=" + self.ui.le_retries.text() if self.ui.cb_fake_index.isChecked(): command = " ".join(self.command) fake_index = "--fake-index=" + self.ui.le_fake_index.text() + self.ui.combo_fake_index.currentText() if self.ui.cb_pllspec.isChecked(): pll = "--pll=period=" + self.ui.le_period.text() + ":phase=m" + self.ui.le_phase.text() if self.ui.le_lowpass.text() != "": pll = pll + ":lowpass=" + self.ui.le_lowpass.text() # --tracks combines multiple settings into a single argument if self.ui.cb_double_step.isChecked(): tracks = "step=" + self.ui.le_double_step.text() if self.ui.cb_cylinder_sets.isChecked(): if tracks != "": tracks = tracks + ":" tracks = tracks + "c=" + self.ui.le_cylinder_sets.text() if self.ui.cb_head_sets.isChecked(): if tracks != "": tracks = tracks + ":" tracks = tracks + "h=" + self.ui.le_head_sets.text() if self.ui.cb_head_swap.isChecked(): if tracks != "": tracks = tracks + ":" tracks = tracks + "hswap" if self.ui.cb_flippy.isChecked(): if tracks != "": tracks = tracks + ":" if self.ui.rb_panasonic.isChecked(): tracks = tracks + "h1.off=-8" else: tracks = tracks + "h0.off=+8" if tracks != "": tracks = "--tracks=" + tracks if self.ui.cb_rev_track_data.isChecked(): reverse = "--reverse" if self.ui.cb_hard_sectors.isChecked(): hard_sectors = "--hard-sectors" if self.ui.cb_no_clobber.isChecked(): no_clobber = "--no-clobber" if self.ui.cb_pin2.isChecked(): if self.ui.rb_pin2_high.isChecked(): densel = "--densel H" else: densel = "--densel L" if self.ui.cb_raw.isChecked(): raw = "--raw" if self.ui.cb_adjust_speed.isChecked(): adjust_speed = "--adjust-speed=" + self.ui.le_adjust_speed.text() + self.ui.combo_adjust_speed.currentText() suffix = self.ui.le_suffix.text() if suffix != "": suffix = "-" + suffix filepath = self.ui.le_filepath.text() disktype = self.ui.combo_disktype.currentText() generated_filename = fileprefix + suffix + disktype filename = os.path.join(filepath, generated_filename) + bitrate + legacy_ss self.command = [ item for item in [ "gw", "read", tracks, revs, drive, densel, adjust_speed, no_clobber, raw, reverse, hard_sectors, fake_index, pll, retries, filename ] if item ] self.ui.le_filename.setText(generated_filename) self.ui.te_command_line.setPlainText(" ".join(self.command))