From 71a71dcc5e3894ff8ea500d4b908746fd1a5e885 Mon Sep 17 00:00:00 2001 From: Dawson Matthews Date: Sun, 8 Mar 2026 18:36:45 -0600 Subject: [PATCH] gui works --- .gitignore | 3 +- kde-display-profile-manager.py | 131 +++++++++++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index bc6052a..2ca600f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ profiles/ -*.json \ No newline at end of file +*.json +__pycache__/ \ No newline at end of file diff --git a/kde-display-profile-manager.py b/kde-display-profile-manager.py index 202cfc7..7b9b82c 100755 --- a/kde-display-profile-manager.py +++ b/kde-display-profile-manager.py @@ -5,8 +5,22 @@ import json import subprocess import sys import os +from pathlib import Path + +# Try to import PySide6 for the GUI +try: + from PySide6.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QListWidget, QPushButton, QInputDialog, QMessageBox, QLabel, + QFileDialog + ) + from PySide6.QtCore import Qt + PYSIDE_AVAILABLE = True +except ImportError: + PYSIDE_AVAILABLE = False VERBOSE = True +DEFAULT_PROFILE_DIR = Path.home() / ".local/share/kde-display-profiles" def log(*args): if VERBOSE: @@ -35,8 +49,8 @@ def save_profile(profile_path): subprocess.run(['kscreen-doctor', '--json'], stdout=f, check=True) log("Profile saved successfully.") except Exception as e: - print(f"Error saving profile: {e}", file=sys.stderr) - sys.exit(1) + log(f"Error saving profile: {e}") + raise e def apply_attribute(output_name, attribute, value, value_map=None): if value is None: @@ -61,15 +75,14 @@ def apply_attribute(output_name, attribute, value, value_map=None): def load_profile(profile_path): log(f"Loading display profile from {profile_path}...") if not os.path.exists(profile_path): - print(f"Profile file not found: {profile_path}", file=sys.stderr) - sys.exit(1) + raise FileNotFoundError(f"Profile file not found: {profile_path}") try: with open(profile_path, 'r') as f: profile = json.load(f) except Exception as e: - print(f"Error reading profile JSON: {e}", file=sys.stderr) - sys.exit(1) + log(f"Error reading profile JSON: {e}") + raise e outputs = profile.get('outputs', []) @@ -179,9 +192,108 @@ def load_profile(profile_path): log("Display configuration restored.") +class DisplayProfileManagerGUI(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("KDE Display Profile Manager") + self.setMinimumSize(400, 300) + + # Ensure default directory exists + DEFAULT_PROFILE_DIR.mkdir(parents=True, exist_ok=True) + + self.setup_ui() + self.refresh_profiles() + + def setup_ui(self): + central_widget = QWidget() + self.setCentralWidget(central_widget) + layout = QVBoxLayout(central_widget) + + layout.addWidget(QLabel("Available Profiles:")) + + self.profile_list = QListWidget() + layout.addWidget(self.profile_list) + + btn_layout = QHBoxLayout() + self.load_btn = QPushButton("Load Profile") + self.load_btn.clicked.connect(self.on_load_clicked) + btn_layout.addWidget(self.load_btn) + + self.save_btn = QPushButton("Save Current") + self.save_btn.clicked.connect(self.on_save_clicked) + btn_layout.addWidget(self.save_btn) + + self.refresh_btn = QPushButton("Refresh") + self.refresh_btn.clicked.connect(self.refresh_profiles) + btn_layout.addWidget(self.refresh_btn) + + layout.addLayout(btn_layout) + + def refresh_profiles(self): + self.profile_list.clear() + if DEFAULT_PROFILE_DIR.exists(): + profiles = sorted(DEFAULT_PROFILE_DIR.glob("*.json")) + for profile in profiles: + self.profile_list.addItem(profile.stem) + + def get_default_profile_name(self): + existing_names = [] + if DEFAULT_PROFILE_DIR.exists(): + existing_names = [p.stem for p in DEFAULT_PROFILE_DIR.glob("*.json")] + + i = 1 + while f"Profile {i}" in existing_names: + i += 1 + return f"Profile {i}" + + def on_save_clicked(self): + default_name = self.get_default_profile_name() + name, ok = QInputDialog.getText(self, "Save Profile", "Profile Name:", text=default_name) + + if ok and name: + profile_path = DEFAULT_PROFILE_DIR / f"{name}.json" + if profile_path.exists(): + reply = QMessageBox.question(self, "Overwrite?", + f"Profile '{name}' already exists. Overwrite?", + QMessageBox.Yes | QMessageBox.No) + if reply == QMessageBox.No: + return + + try: + save_profile(str(profile_path)) + self.refresh_profiles() + QMessageBox.information(self, "Success", f"Profile '{name}' saved.") + except Exception as e: + QMessageBox.critical(self, "Error", f"Failed to save profile: {e}") + + def on_load_clicked(self): + selected_item = self.profile_list.currentItem() + if not selected_item: + QMessageBox.warning(self, "No Selection", "Please select a profile to load.") + return + + profile_name = selected_item.text() + profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json" + + try: + load_profile(str(profile_path)) + QMessageBox.information(self, "Success", f"Profile '{profile_name}' loaded.") + except Exception as e: + QMessageBox.critical(self, "Error", f"Failed to load profile: {e}") + +def show_gui(): + if not PYSIDE_AVAILABLE: + print("Error: PySide6 is not installed. Please install it to use the GUI.", file=sys.stderr) + sys.exit(1) + + app = QApplication(sys.argv) + window = DisplayProfileManagerGUI() + window.show() + sys.exit(app.exec()) + def main(): parser = argparse.ArgumentParser(description="KDE Display Profile Manager") - subparsers = parser.add_subparsers(dest="command", required=True) + subparsers = parser.add_subparsers(dest="command", required=False) # Save command save_parser = subparsers.add_parser("save", help="Save current display configuration to a profile") @@ -191,12 +303,17 @@ def main(): load_parser = subparsers.add_parser("load", help="Load a display configuration from a profile") load_parser.add_argument("profile", help="Path to the profile file") + # GUI command + subparsers.add_parser("gui", help="Launch the GUI manager") + args = parser.parse_args() if args.command == "save": save_profile(args.profile) elif args.command == "load": load_profile(args.profile) + elif args.command == "gui" or args.command is None: + show_gui() if __name__ == "__main__": main()