Files
gwui/main.py
2025-07-15 21:00:09 +02:00

234 lines
9.2 KiB
Python

import sys, glob, json, os
from PySide6 import QtWidgets
from PySide6.QtWidgets import (
QApplication,
QWidget,
QRadioButton,
QButtonGroup,
QGroupBox,
QVBoxLayout,
QHBoxLayout,
QLabel,
QFileDialog,
QInputDialog,
QMessageBox,
)
from PySide6.QtCore import Slot
from readdisk import ReadDiskWindow
from ui_main_window import Ui_MainWindow
from ui_read_disk import Ui_ReadDialog
# Inherit only from the Qt base class
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.settings = self.get_defaults()
# Create an instance of the UI class
self.ui = Ui_MainWindow()
# Call its setupUi method, passing in the current window (self)
self.ui.setupUi(self)
# 1. Create the logical group. Pass `self` for parentage.
self.logical_button_group = QButtonGroup(self)
# 2. Add all radio buttons from different containers.
# Assign a unique integer ID to each one. This is best practice.
self.logical_button_group.addButton(self.ui.rb_read, 1)
self.logical_button_group.addButton(self.ui.rb_write, 2)
self.logical_button_group.addButton(self.ui.rb_clean_heads, 3)
self.logical_button_group.addButton(self.ui.rb_erase_disk, 4)
self.logical_button_group.addButton(self.ui.rb_convert_files, 5)
self.logical_button_group.addButton(self.ui.rb_info, 6)
self.logical_button_group.addButton(self.ui.rb_measure, 7)
self.logical_button_group.addButton(self.ui.rb_pin_level, 8)
self.logical_button_group.addButton(self.ui.rb_reset, 9)
self.logical_button_group.addButton(self.ui.rb_rpm, 10)
self.logical_button_group.addButton(self.ui.rb_seek, 11)
self.logical_button_group.addButton(self.ui.rb_delays, 12)
self.logical_button_group.addButton(self.ui.rb_update_firmware, 13)
self.profile = "GWUIDefault"
self.ui.rb_read.setChecked(True)
# Populate combo_port with /dev/ttyACM* devices
self.populate_serial_ports()
self.profile = "GWUIDefault"
# Populate combo_profile with available profiles
self.populate_profiles()
self.ui.combo_profile.currentTextChanged.connect(self.on_combo_profile_changed)
self.load_settings(self.profile)
self.ui.combo_port.setCurrentText(self.settings.get("device", "Auto"))
self.ui.combo_port.currentTextChanged.connect(self.on_combo_port_text_changed)
self.ui.action_quit.triggered.connect(self.close)
self.ui.pb_close.clicked.connect(self.close)
self.ui.pb_execute.clicked.connect(self.on_execute)
self.ui.pb_refresh_ports.clicked.connect(self.populate_serial_ports)
self.ui.pb_add_profile.clicked.connect(self.on_pb_add_profile)
self.ui.pb_delete_profile.clicked.connect(self.on_pb_delete_profile)
def on_combo_profile_changed(self, profile_name: str):
if profile_name:
self.profile = profile_name
self.load_settings(self.profile)
self.ui.combo_port.setCurrentText(self.settings.get("device", "Auto"))
def on_pb_add_profile(self):
text, ok = QInputDialog.getText(self, "Add Profile", "Enter new profile name:")
if ok and text:
self.profile = text
self.settings = self.get_defaults()
self.save_settings(self.profile)
self.populate_profiles()
self.ui.combo_port.setCurrentText(self.settings.get("device", "Auto"))
def on_pb_delete_profile(self):
"""Delete the currently selected profile and load the previous one, or defaults if none remain."""
config_path = self.get_config_path(self.profile)
try:
if os.path.exists(config_path):
os.remove(config_path)
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to delete profile: {e}")
return
# Refresh profile list
self.populate_profiles()
profiles = [self.ui.combo_profile.itemText(i) for i in range(self.ui.combo_profile.count())]
if profiles:
# Select the previous profile in the list, or the first if deleted was first
idx = profiles.index(self.profile) if self.profile in profiles else 0
if idx > 0:
idx -= 1
self.profile = profiles[idx]
self.ui.combo_profile.setCurrentText(self.profile)
self.load_settings(self.profile)
self.ui.combo_port.setCurrentText(self.settings.get("device", "Auto"))
else:
# No profiles left, create default
self.profile = "GWUIDefault"
self.settings = self.get_defaults()
self.save_settings(self.profile)
self.populate_profiles()
self.ui.combo_profile.setCurrentText(self.profile)
self.ui.combo_port.setCurrentText(self.settings.get("device", "Auto"))
def on_combo_port_text_changed(self, text: str) -> None:
self.settings["device"] = text
self.save_settings(self.profile)
def get_defaults(self):
return {
"fileprefix": "disk-image",
"filepath": "",
"device": "Auto",
"double_step_enabled": False,
"double_step_value": "1",
"fake_index_enabled": False,
"fake_index_value": "300",
"fake_index_unit": "",
"bitrate_enabled": False,
"bitrate_value": "250",
"revs_enabled": False,
"revs_value": "3",
"drive_select_enabled": False,
"drive_select_value": "A",
"retries_enabled": False,
"retriesAttributeError: 'MainWindow' object has no attribute 'profile'_value": "3",
"pllspec_enabled": False,
"pll_period": "",
"pll_phase": "",
"pll_lowpass": "",
"head_sets_enabled": False,
"head_sets_value": "0-1",
"head_swap_enabled": False,
"ss_legacy_enabled": False,
"cylinder_sets_enabled": False,
"cylinder_sets_value": "0-34,35-79",
"rev_track_data_enabled": False,
"hard_sectors_enabled": False,
"no_clobber_enabled": False,
"raw_enabled": False,
"pin2_enabled": False,
"pin2_high": False,
"pin2_low": False,
"flippy_enabled": False,
"flippy_panasonic": True,
"flippy_teac": False,
"adjust_speed_enabled": False,
"adjust_speed_value": "300",
"adjust_speed_unit": "",
"suffix_value": "",
"inc_enabled": True,
"format_enabled": False,
"format_value": "",
"disktype_value": ""
}
def get_config_path(self, profile_name: str) -> str:
"""Return the full path for a profile config file in ~/.config/gwui/"""
config_dir = os.path.expanduser("~/.config/gwui")
if not os.path.exists(config_dir):
os.makedirs(config_dir, exist_ok=True)
return os.path.join(config_dir, profile_name + ".json")
@Slot()
def on_execute(self):
read_disk_window = ReadDiskWindow()
# read_disk_window.set_port(self.ui.combo_port.currentText())
read_disk_window.set_settings(self.settings)
read_disk_window.exec()
self.settings = read_disk_window.get_settings()
self.settings["device"] = self.ui.combo_port.currentText()
self.save_settings(self.profile)
def load_settings(self, profile_name: str):
config_path = self.get_config_path(profile_name)
if profile_name:
if os.path.exists(config_path):
with open(config_path, 'r') as f:
self.settings = json.load(f)
return True
return False
def save_settings(self, profile_name: str):
config_path = self.get_config_path(profile_name)
if profile_name:
with open(config_path, 'w') as f:
json.dump(self.settings, f, indent=2)
def populate_serial_ports(self):
"""Populate the combo_port with all /dev/ttyACM* devices."""
ports = glob.glob('/dev/ttyACM*')
self.ui.combo_port.clear()
self.ui.combo_port.addItem("Auto")
self.ui.combo_port.addItems(ports)
def populate_profiles(self):
"""Populate combo_profile with all config files in ~/.config/gwui/ (minus .json)."""
config_dir = os.path.expanduser("~/.config/gwui")
if not os.path.exists(config_dir):
os.makedirs(config_dir, exist_ok=True)
profiles = []
for fname in os.listdir(config_dir):
if fname.endswith('.json'):
profiles.append(fname[:-5])
self.ui.combo_profile.blockSignals(True)
self.ui.combo_profile.clear()
self.ui.combo_profile.addItems(sorted(profiles))
# Optionally, set current profile
if self.profile in profiles:
self.ui.combo_profile.setCurrentText(self.profile)
self.ui.combo_profile.blockSignals(False)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())