Compare commits
4 Commits
f274d5c8b3
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bf30ba844 | |||
| 1d4e419548 | |||
| 03084f1cd5 | |||
| fa2d64cd1f |
@@ -5,16 +5,18 @@ import json
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Try to import PySide6 for the GUI
|
# Try to import PySide6 for the GUI
|
||||||
try:
|
try:
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QListWidget, QPushButton, QInputDialog, QMessageBox, QLabel,
|
QListWidget, QListWidgetItem, QPushButton, QInputDialog, QMessageBox, QLabel,
|
||||||
QFileDialog
|
QFileDialog
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtGui import QIcon
|
||||||
|
from PySide6.QtCore import Qt, QSize
|
||||||
PYSIDE_AVAILABLE = True
|
PYSIDE_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
PYSIDE_AVAILABLE = False
|
PYSIDE_AVAILABLE = False
|
||||||
@@ -206,7 +208,7 @@ class DisplayProfileManagerGUI(QMainWindow):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle("KDE Display Profile Manager")
|
self.setWindowTitle("KDE Display Profile Manager")
|
||||||
self.setMinimumSize(400, 300)
|
self.setMinimumSize(420, 300)
|
||||||
|
|
||||||
# Ensure default directory exists
|
# Ensure default directory exists
|
||||||
DEFAULT_PROFILE_DIR.mkdir(parents=True, exist_ok=True)
|
DEFAULT_PROFILE_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -219,23 +221,35 @@ class DisplayProfileManagerGUI(QMainWindow):
|
|||||||
self.setCentralWidget(central_widget)
|
self.setCentralWidget(central_widget)
|
||||||
layout = QVBoxLayout(central_widget)
|
layout = QVBoxLayout(central_widget)
|
||||||
|
|
||||||
layout.addWidget(QLabel("Available Profiles:"))
|
# Header layout
|
||||||
|
header_layout = QHBoxLayout()
|
||||||
|
header_layout.addWidget(QLabel("Available Profiles:"))
|
||||||
|
header_layout.addStretch()
|
||||||
|
|
||||||
|
self.refresh_btn = QPushButton()
|
||||||
|
self.refresh_btn.setIcon(QIcon.fromTheme("view-refresh"))
|
||||||
|
self.refresh_btn.setIconSize(QSize(20, 20))
|
||||||
|
self.refresh_btn.setToolTip("Refresh Profiles")
|
||||||
|
self.refresh_btn.setFlat(True)
|
||||||
|
self.refresh_btn.clicked.connect(self.refresh_profiles)
|
||||||
|
header_layout.addWidget(self.refresh_btn)
|
||||||
|
|
||||||
|
layout.addLayout(header_layout)
|
||||||
|
|
||||||
self.profile_list = QListWidget()
|
self.profile_list = QListWidget()
|
||||||
|
self.profile_list.itemDoubleClicked.connect(self.on_load_clicked)
|
||||||
layout.addWidget(self.profile_list)
|
layout.addWidget(self.profile_list)
|
||||||
|
|
||||||
btn_layout = QHBoxLayout()
|
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 = QPushButton("Save Current")
|
||||||
self.save_btn.clicked.connect(self.on_save_clicked)
|
self.save_btn.clicked.connect(self.on_save_clicked)
|
||||||
btn_layout.addWidget(self.save_btn)
|
btn_layout.addWidget(self.save_btn)
|
||||||
|
|
||||||
self.refresh_btn = QPushButton("Refresh")
|
self.load_btn = QPushButton("Load Selected")
|
||||||
self.refresh_btn.clicked.connect(self.refresh_profiles)
|
self.load_btn.clicked.connect(self.on_load_clicked)
|
||||||
btn_layout.addWidget(self.refresh_btn)
|
btn_layout.addWidget(self.load_btn)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
layout.addLayout(btn_layout)
|
layout.addLayout(btn_layout)
|
||||||
|
|
||||||
@@ -244,7 +258,44 @@ class DisplayProfileManagerGUI(QMainWindow):
|
|||||||
if DEFAULT_PROFILE_DIR.exists():
|
if DEFAULT_PROFILE_DIR.exists():
|
||||||
profiles = sorted(DEFAULT_PROFILE_DIR.glob("*.json"))
|
profiles = sorted(DEFAULT_PROFILE_DIR.glob("*.json"))
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
self.profile_list.addItem(profile.stem)
|
name = profile.stem
|
||||||
|
# Create item without text to prevent double-rendering/blurriness
|
||||||
|
item = QListWidgetItem(self.profile_list)
|
||||||
|
item.setData(Qt.UserRole, name)
|
||||||
|
|
||||||
|
# Custom widget for the row
|
||||||
|
widget = QWidget()
|
||||||
|
row_layout = QHBoxLayout(widget)
|
||||||
|
row_layout.setContentsMargins(5, 2, 5, 2)
|
||||||
|
row_layout.setSpacing(5)
|
||||||
|
|
||||||
|
label = QLabel(name)
|
||||||
|
# Ensure the label doesn't inherit transparency issues
|
||||||
|
label.setStyleSheet("background: transparent;")
|
||||||
|
row_layout.addWidget(label)
|
||||||
|
row_layout.addStretch()
|
||||||
|
|
||||||
|
# Copy button
|
||||||
|
copy_btn = QPushButton()
|
||||||
|
copy_btn.setIcon(QIcon.fromTheme("edit-copy"))
|
||||||
|
copy_btn.setIconSize(QSize(16, 16))
|
||||||
|
copy_btn.setFlat(True)
|
||||||
|
copy_btn.setToolTip("Copy Load Command")
|
||||||
|
copy_btn.clicked.connect(lambda checked=False, n=name: self.copy_profile_cmd(n))
|
||||||
|
row_layout.addWidget(copy_btn)
|
||||||
|
|
||||||
|
# Delete button
|
||||||
|
del_btn = QPushButton()
|
||||||
|
del_btn.setIcon(QIcon.fromTheme("user-trash"))
|
||||||
|
del_btn.setIconSize(QSize(16, 16))
|
||||||
|
del_btn.setFlat(True)
|
||||||
|
del_btn.setToolTip("Delete Profile")
|
||||||
|
del_btn.clicked.connect(lambda checked=False, n=name: self.delete_profile(n))
|
||||||
|
row_layout.addWidget(del_btn)
|
||||||
|
|
||||||
|
item.setSizeHint(widget.sizeHint())
|
||||||
|
self.profile_list.addItem(item)
|
||||||
|
self.profile_list.setItemWidget(item, widget)
|
||||||
|
|
||||||
def get_default_profile_name(self):
|
def get_default_profile_name(self):
|
||||||
existing_names = []
|
existing_names = []
|
||||||
@@ -276,13 +327,16 @@ class DisplayProfileManagerGUI(QMainWindow):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.critical(self, "Error", f"Failed to save profile: {e}")
|
QMessageBox.critical(self, "Error", f"Failed to save profile: {e}")
|
||||||
|
|
||||||
def on_load_clicked(self):
|
def on_load_clicked(self, item=None):
|
||||||
selected_item = self.profile_list.currentItem()
|
if not isinstance(item, QListWidgetItem):
|
||||||
if not selected_item:
|
item = self.profile_list.currentItem()
|
||||||
|
|
||||||
|
if not item:
|
||||||
QMessageBox.warning(self, "No Selection", "Please select a profile to load.")
|
QMessageBox.warning(self, "No Selection", "Please select a profile to load.")
|
||||||
return
|
return
|
||||||
|
|
||||||
profile_name = selected_item.text()
|
# Retrieve name from UserRole data instead of item.text()
|
||||||
|
profile_name = item.data(Qt.UserRole)
|
||||||
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -291,11 +345,40 @@ class DisplayProfileManagerGUI(QMainWindow):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.critical(self, "Error", f"Failed to load profile: {e}")
|
QMessageBox.critical(self, "Error", f"Failed to load profile: {e}")
|
||||||
|
|
||||||
|
def copy_profile_cmd(self, profile_name):
|
||||||
|
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
||||||
|
script_path = os.path.abspath(__file__)
|
||||||
|
cmd = f"python3 {shlex.quote(script_path)} load {shlex.quote(str(profile_path))}"
|
||||||
|
|
||||||
|
clipboard = QApplication.clipboard()
|
||||||
|
clipboard.setText(cmd)
|
||||||
|
|
||||||
|
QMessageBox.information(self, "Command Copied", f"The following command has been copied to your clipboard:\n\n{cmd}")
|
||||||
|
|
||||||
|
def delete_profile(self, profile_name):
|
||||||
|
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
||||||
|
|
||||||
|
reply = QMessageBox.question(self, "Confirm Delete",
|
||||||
|
f"Are you sure you want to delete profile '{profile_name}'?",
|
||||||
|
QMessageBox.Yes | QMessageBox.No)
|
||||||
|
|
||||||
|
if reply == QMessageBox.Yes:
|
||||||
|
try:
|
||||||
|
os.remove(profile_path)
|
||||||
|
self.refresh_profiles()
|
||||||
|
QMessageBox.information(self, "Success", f"Profile '{profile_name}' deleted.")
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Error", f"Failed to delete profile: {e}")
|
||||||
|
|
||||||
def show_gui():
|
def show_gui():
|
||||||
if not PYSIDE_AVAILABLE:
|
if not PYSIDE_AVAILABLE:
|
||||||
print("Error: PySide6 is not installed. Please install it to use the GUI.", file=sys.stderr)
|
print("Error: PySide6 is not installed. Please install it to use the GUI.", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Use 'Round' instead of 'PassThrough' as it is often more stable for font rendering
|
||||||
|
if hasattr(Qt, "HighDpiScaleFactorRoundingPolicy"):
|
||||||
|
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.Round)
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = DisplayProfileManagerGUI()
|
window = DisplayProfileManagerGUI()
|
||||||
window.show()
|
window.show()
|
||||||
|
|||||||
Reference in New Issue
Block a user