diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-30 19:46:12 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-30 19:46:12 +0100 |
| commit | 1953399f9bf0cf18ba54961583632d703eeaa07e (patch) | |
| tree | c25283c05705137b2926d4ad466922d53f8d8f12 | |
| parent | 27dc1b71ff62dd4e8d91de239bc5ad70c1bc7428 (diff) | |
| download | upscale-qt-1953399f9bf0cf18ba54961583632d703eeaa07e.tar.gz upscale-qt-1953399f9bf0cf18ba54961583632d703eeaa07e.zip | |
Add Ctrl+Q and ask before quitting with an unsaved upscale
Quit and window close share closeEvent, so both ask. The unsaved check
moves into discard_ok, used by Clear as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | test_upscale_qt.py | 19 | ||||
| -rwxr-xr-x | upscale-qt | 21 |
3 files changed, 37 insertions, 4 deletions
@@ -18,6 +18,7 @@ Small PyQt6 app to upscale an image with an ESRGAN model through `sd-cli` (stabl - Save As (Ctrl+Shift+S) writes a new file. Overwrite replaces the original after a confirmation, in the same format. EXIF metadata is not kept. - Clear (Ctrl+W) empties the view and deletes the temp files. It asks first if the upscaled image was not saved. - Settings holds the upscalers folder. +- Ctrl+Q quits. Quitting or closing the window asks first if the upscaled image was not saved. ### Model descriptions diff --git a/test_upscale_qt.py b/test_upscale_qt.py index 66423e8..7175e92 100644 --- a/test_upscale_qt.py +++ b/test_upscale_qt.py @@ -114,6 +114,25 @@ def test_main_window(): assert not w.a_clear.isEnabled() and not w.a_up.isEnabled() +def test_quit_asks_when_unsaved(): + from PyQt6.QtWidgets import QMessageBox + w = uq.Main() + assert w.a_quit.shortcut().toString() == 'Ctrl+Q' + path = os.path.join(TMP, 'q.png') + QImage(4, 4, QImage.Format.Format_RGB32).save(path) + w.load(path) + w.jobs[0].result = QImage(16, 16, QImage.Format.Format_RGB32) + answers = [] + real = QMessageBox.question + try: + QMessageBox.question = lambda *a: answers.append(a) or QMessageBox.StandardButton.No + assert not w.close() and len(answers) == 1 # declined, stays open + w.jobs[0].saved = True + assert w.close() and len(answers) == 1 # saved, no question + finally: + QMessageBox.question = real + + if __name__ == '__main__': for name, fn in list(globals().items()): if name.startswith('test_'): @@ -249,6 +249,10 @@ class Main(QMainWindow): self.a_over = act('Overwrite', 'document-save', self.overwrite) self.a_clear = act('Clear', 'edit-clear', self.clear, 'Ctrl+W') act('Settings', 'preferences-system', self.settings) + self.a_quit = QAction('Quit', self) # window-wide, not on the toolbar + self.a_quit.setShortcut(QKeySequence.StandardKey.Quit) + self.a_quit.triggered.connect(self.close) + self.addAction(self.a_quit) self.proc = QProcess(self) self.proc.finished.connect(self.proc_done) @@ -394,12 +398,18 @@ class Main(QMainWindow): else: QMessageBox.warning(self, 'upscale-qt', f'Cannot write {job.path}, original untouched') - def clear(self): + def discard_ok(self, title): + """True unless an unsaved upscale exists and the user keeps it.""" job = self.job() - if job.result is not None and not job.saved and QMessageBox.question( - self, 'Clear', 'The upscaled image is not saved. Discard it?' - ) != QMessageBox.StandardButton.Yes: + return (job is None or job.result is None or job.saved + or QMessageBox.question( + self, title, 'The upscaled image is not saved. Discard it?' + ) == QMessageBox.StandardButton.Yes) + + def clear(self): + if not self.discard_ok('Clear'): return + job = self.job() for f in (job.input, job.out): if f and os.path.exists(f): os.remove(f) # 4x PNGs run to hundreds of MB @@ -414,6 +424,9 @@ class Main(QMainWindow): self.reload_models() def closeEvent(self, e): + if not self.discard_ok('Quit'): + e.ignore() + return if self.proc.state() != QProcess.ProcessState.NotRunning: self.proc.kill() self.proc.waitForFinished(2000) |
