gui: robust window activation + headless --selftest/--version flags
Center the main window on the active screen and raise()/activateWindow() it on launch, so it cannot open off-screen or behind the focused frame on a tiling compositor (Sway/i3/Hyprland) — the usual cause of reports that 'the GUI won't start / is stuck': it launched, but was not visible. Add lightweight non-GUI flags handled before the QApplication is built: --version print GUI + pinned-CLI version and Qt binding --help usage --selftest build the full UI, spin the event loop once, exit 0 so a successful launch can be verified with no display (headless/CI/remote). Also emit a one-line 'window open' notice to stderr so a terminal launch is not mistaken for a hung shell.
This commit is contained in:
parent
5cb04bf837
commit
e15329a7c9
1 changed files with 49 additions and 6 deletions
|
|
@ -23,7 +23,7 @@ try:
|
||||||
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
|
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
|
||||||
QSpinBox, QMessageBox, QStatusBar, QScrollArea
|
QSpinBox, QMessageBox, QStatusBar, QScrollArea
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt, Signal, QObject, QThread
|
from PySide6.QtCore import Qt, Signal, QObject, QThread, QTimer
|
||||||
from PySide6.QtGui import QPalette, QColor, QIcon, QPixmap
|
from PySide6.QtGui import QPalette, QColor, QIcon, QPixmap
|
||||||
QT_BINDING = "PySide6"
|
QT_BINDING = "PySide6"
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -34,7 +34,7 @@ except ImportError:
|
||||||
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
|
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
|
||||||
QSpinBox, QMessageBox, QStatusBar, QScrollArea
|
QSpinBox, QMessageBox, QStatusBar, QScrollArea
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import Qt, pyqtSignal as Signal, QObject, QThread
|
from PyQt6.QtCore import Qt, pyqtSignal as Signal, QObject, QThread, QTimer
|
||||||
from PyQt6.QtGui import QPalette, QColor, QIcon, QPixmap
|
from PyQt6.QtGui import QPalette, QColor, QIcon, QPixmap
|
||||||
QT_BINDING = "PyQt6"
|
QT_BINDING = "PyQt6"
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -858,9 +858,26 @@ class ZuptWindow(QMainWindow):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
compress_files = extract_file = None
|
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
if args:
|
|
||||||
|
# Lightweight non-GUI flags first, so `vaptvupt-gui --version|--help|--selftest`
|
||||||
|
# work with no display and aren't mistaken for files to compress. `--selftest`
|
||||||
|
# is a headless-friendly smoke test: it builds the whole UI and spins the event
|
||||||
|
# loop once, then exits 0 — the reliable way to confirm the GUI stack launches
|
||||||
|
# on a machine where the window itself is hard to see (tiling WM, remote, CI).
|
||||||
|
if args and args[0] in ("--version", "-V", "version"):
|
||||||
|
print(f"vaptvupt-gui {ZUPT_VER_NUMBER} ({QT_BINDING}) | CLI: {VAPTVUPT}")
|
||||||
|
return 0
|
||||||
|
if args and args[0] in ("--help", "-h", "help"):
|
||||||
|
print("usage: vaptvupt-gui [ARCHIVE.zupt | --extract ARCHIVE.zupt |\n"
|
||||||
|
" --compress FILE [FILE ...]]\n"
|
||||||
|
" vaptvupt-gui --selftest # verify the GUI launches (no window kept)\n"
|
||||||
|
" vaptvupt-gui --version")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
compress_files = extract_file = None
|
||||||
|
selftest = ("--selftest" in args[:1])
|
||||||
|
if args and not selftest:
|
||||||
if args[0] == "--compress" and len(args) > 1: compress_files = args[1:]
|
if args[0] == "--compress" and len(args) > 1: compress_files = args[1:]
|
||||||
elif args[0] == "--extract" and len(args) > 1: extract_file = args[1]
|
elif args[0] == "--extract" and len(args) > 1: extract_file = args[1]
|
||||||
elif args[0].endswith(".zupt"): extract_file = args[0]
|
elif args[0].endswith(".zupt"): extract_file = args[0]
|
||||||
|
|
@ -882,8 +899,34 @@ def main():
|
||||||
pal.setColor(role, QColor(c))
|
pal.setColor(role, QColor(c))
|
||||||
app.setPalette(pal)
|
app.setPalette(pal)
|
||||||
win = ZuptWindow(compress_files=compress_files, extract_file=extract_file)
|
win = ZuptWindow(compress_files=compress_files, extract_file=extract_file)
|
||||||
|
|
||||||
|
if selftest:
|
||||||
|
win.show()
|
||||||
|
QTimer.singleShot(400, app.quit)
|
||||||
|
rc = app.exec()
|
||||||
|
print(f"selftest OK — {QT_BINDING}: window + {win.tabs.count()} tabs built, "
|
||||||
|
f"event loop ran (rc={rc}); CLI={VAPTVUPT}")
|
||||||
|
return rc
|
||||||
|
|
||||||
|
# Center on the active screen and force focus. Without this the window can
|
||||||
|
# open off-screen or behind the last-focused frame on a tiling compositor
|
||||||
|
# (Sway/i3/Hyprland) — the usual cause of "the GUI won't start / is stuck":
|
||||||
|
# it launched, but you can't see it.
|
||||||
|
scr = app.primaryScreen()
|
||||||
|
if scr is not None:
|
||||||
|
fg = win.frameGeometry()
|
||||||
|
fg.moveCenter(scr.availableGeometry().center())
|
||||||
|
win.move(fg.topLeft())
|
||||||
win.show()
|
win.show()
|
||||||
sys.exit(app.exec())
|
win.raise_()
|
||||||
|
win.activateWindow()
|
||||||
|
|
||||||
|
# A GUI blocks the launching shell, so a working launch otherwise looks like
|
||||||
|
# a "stuck" terminal. Emit one line to stderr so it's unambiguous.
|
||||||
|
sys.stderr.write(f"VaptVupt {ZUPT_VER_NUMBER} GUI started — window open "
|
||||||
|
f"(close it to exit).\n")
|
||||||
|
sys.stderr.flush()
|
||||||
|
return app.exec()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
sys.exit(main())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue