"""QApplication bootstrap and global styling.""" # This file is part of ISO Scalpel. # Copyright (C) 2025 Jeremy Anderson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see # or write to the Free Software Foundation, Inc., 51 Franklin Street, # Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import os import sys from PySide6.QtGui import QFont from PySide6.QtWidgets import QApplication from . import __app_name__ from .main_window import MainWindow # Clean, modern stylesheet: neutral greys with an emerald accent (no # indigo/blue), tuned for ISO Scalpel's split-nav interface. STYLE = """ QMainWindow, QWidget { background: #f7f7f8; } QMenuBar { background: #e9eaee; border-bottom: 1px solid #d4d6dc; } QMenuBar::item { padding: 4px 10px; background: transparent; } QMenuBar::item:selected { background: #dfe2e8; } QMenu { background: #ffffff; border: 1px solid #c8ccd2; } QMenu::item { padding: 5px 22px 5px 22px; } QMenu::item:selected { background: #d4f1e0; color: #065f46; } QToolBar { background: #e9eaee; border: 0; border-bottom: 1px solid #d4d6dc; spacing: 2px; padding: 2px; } QToolBar QToolButton { padding: 4px 6px; border-radius: 4px; } QToolBar QToolButton:hover { background: #dfe2e8; } QTreeView { background: #ffffff; alternate-background-color: #f3f4f7; border: 1px solid #d4d6dc; selection-background-color: #a7e8c5; selection-color: #064e3b; } QTreeView::item { padding: 2px 0; } QHeaderView::section { background: #eef0f3; border: 0; border-right: 1px solid #d4d6dc; border-bottom: 1px solid #d4d6dc; padding: 4px 6px; font-weight: 600; } QStatusBar { background: #e9eaee; border-top: 1px solid #d4d6dc; } QStatusBar::item { border: 0; } QSplitter::handle { background: #d4d6dc; } QSplitter::handle:horizontal { width: 3px; } QGroupBox { border: 1px solid #d4d6dc; border-radius: 4px; margin-top: 10px; padding-top: 8px; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 4px; } QPushButton { padding: 5px 14px; border: 1px solid #b8bdc6; border-radius: 4px; background: #ffffff; } QPushButton:hover { background: #eef5f0; } QPushButton:pressed { background: #d4ead9; } QPushButton:default { border-color: #34a96b; } QLineEdit, QComboBox, QSpinBox { padding: 3px 6px; border: 1px solid #b8bdc6; border-radius: 4px; background: #fff; } QComboBox::drop-down { border: 0; width: 18px; } QComboBox QAbstractItemView { border: 1px solid #b8bdc6; selection-background-color: #a7e8c5; } QDialogButtonBox QPushButton { min-width: 78px; } QProgressBar { border: 1px solid #b8bdc6; border-radius: 4px; text-align: center; background: #fff; } QProgressBar::chunk { background: #34a96b; } QTabWidget::pane { border: 1px solid #d4d6dc; border-top: 0; } QTabBar::tab { padding: 4px 10px; background: #eef0f3; border: 1px solid #d4d6dc; border-bottom: 0; border-top-left-radius: 4px; border-top-right-radius: 4px; margin-right: 2px; } QTabBar::tab:selected { background: #ffffff; border-bottom: 2px solid #34a96b; } QTabBar::tab:hover:!selected { background: #e4e8ee; } QFrame#Breadcrumb { background: #ffffff; border: 1px solid #d4d6dc; border-radius: 4px; } """ def run(argv: list[str] | None = None, *, debug_layout: bool = False) -> int: if argv is None: argv = sys.argv app = QApplication.instance() or QApplication(argv) app.setApplicationName(__app_name__) app.setOrganizationName("ISO Scalpel") app.setApplicationDisplayName(__app_name__) app.setStyle("Fusion") app.setStyleSheet(STYLE) # default font f = QFont() f.setPointSize(10) app.setFont(f) win = MainWindow() win.show() # Open a file passed on the command line, if any. for arg in argv[1:]: if arg.startswith("-"): continue if os.path.isfile(arg): win._open_path(arg) break if debug_layout: _dump_widget_tree(win) return app.exec() def _dump_widget_tree(widget, indent: int = 0) -> None: """Print the widget tree with visibility + size info. Used by ``--debug-layout`` to diagnose rendering issues where a widget exists but doesn't appear on screen. """ prefix = " " * indent name = widget.__class__.__name__ obj_name = widget.objectName() or "-" w = widget.width() h = widget.height() vis = widget.isVisible() min_hint = widget.minimumSizeHint() print(f"{prefix}{name}({obj_name}) size={w}x{h} visible={vis} " f"minHint={min_hint.width()}x{min_hint.height()}") for child in widget.children(): if child.isWidgetType(): _dump_widget_tree(child, indent + 1) if __name__ == "__main__": # pragma: no cover raise SystemExit(run()) def run_argv() -> int: """Console-script entry point used by ``pipx install iso-scalpel``. Equivalent to :func:`run`, but reads :data:`sys.argv` directly so it can be wired up as a ``[project.scripts]`` target in ``pyproject.toml``. """ return run(list(sys.argv))