gui: add a persisted user config; remember the last Sud file dialog dir

New client/user_config.py - a small JSON-backed key/value store at
~/.config/brewpi/gui.json for GUI preferences that should survive
restarts (distinct from the server's config.json and from sude/*.json
schedules). First use: Save/Load Sud's file dialogs now open in
whichever directory was last used, instead of always starting at cwd.
This commit is contained in:
2026-06-21 11:46:01 +02:00
parent 8bb402416a
commit a44060a9bd
2 changed files with 36 additions and 2 deletions
+9 -2
View File
@@ -5,6 +5,7 @@ import json
import sys import sys
import threading import threading
import time import time
from pathlib import Path
import matplotlib import matplotlib
matplotlib.use("Qt5Agg") matplotlib.use("Qt5Agg")
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
@@ -15,6 +16,7 @@ from ws.client.ws_client import WsClient
from ws.message import MessageDispatcherSync from ws.message import MessageDispatcherSync
from queue import Queue from queue import Queue
import asyncio import asyncio
from user_config import UserConfig
# str(SudState.X) values (as sent in the 'State' message) that count as # str(SudState.X) values (as sent in the 'State' message) that count as
# "running" for enabling/disabling the Start/Pause/Stop toolbar actions. # "running" for enabling/disabling the Start/Pause/Stop toolbar actions.
@@ -234,6 +236,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
QtWidgets.QMainWindow.__init__(self) QtWidgets.QMainWindow.__init__(self)
self.setWindowIcon(QtGui.QIcon('res/beer.png')) self.setWindowIcon(QtGui.QIcon('res/beer.png'))
self.user_message_signal.connect(self.on_user_message_signal) self.user_message_signal.connect(self.on_user_message_signal)
self.user_config = UserConfig()
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
@@ -569,16 +572,20 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
# Pick the destination up front, on the GUI thread - the actual # Pick the destination up front, on the GUI thread - the actual
# write happens in on_sud_changed(), which runs off a worker thread # write happens in on_sud_changed(), which runs off a worker thread
# and must not touch Qt (e.g. open a file dialog) itself. # and must not touch Qt (e.g. open a file dialog) itself.
path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save Sud", filter="JSON files (*.json)") start_dir = self.user_config.get('sud_dir', '')
path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save Sud", start_dir, filter="JSON files (*.json)")
if not path: if not path:
return return
self.user_config.set('sud_dir', str(Path(path).parent))
self.sud_save_path = path self.sud_save_path = path
self.msg_sud.send({'Save': True}) self.msg_sud.send({'Save': True})
def on_action_sud_load(self): def on_action_sud_load(self):
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Load Sud", filter="JSON files (*.json)") start_dir = self.user_config.get('sud_dir', '')
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Load Sud", start_dir, filter="JSON files (*.json)")
if not path: if not path:
return return
self.user_config.set('sud_dir', str(Path(path).parent))
with open(path) as f: with open(path) as f:
data = json.load(f) data = json.load(f)
self.msg_sud.send({'Load': data}) self.msg_sud.send({'Load': data})
+27
View File
@@ -0,0 +1,27 @@
import json
from pathlib import Path
DEFAULT_PATH = Path.home() / ".config" / "brewpi" / "gui.json"
class UserConfig:
"""Small persisted key/value store for GUI preferences that should
survive across restarts (e.g. the last directory used for a file
dialog) - local user state, not version-controlled config."""
def __init__(self, path=DEFAULT_PATH):
self.path = path
self.data = {}
if self.path.exists():
try:
self.data = json.loads(self.path.read_text())
except (json.JSONDecodeError, OSError):
self.data = {}
def get(self, key, default=None):
return self.data.get(key, default)
def set(self, key, value):
self.data[key] = value
self.path.parent.mkdir(parents=True, exist_ok=True)
self.path.write_text(json.dumps(self.data, indent='\t'))