138 lines
5.5 KiB
Python
138 lines
5.5 KiB
Python
"""QApplication bootstrap and global styling."""
|
|
|
|
# This file is part of ISO Scalpel.
|
|
# Copyright (C) 2025 Jeremy Anderson <info@dcos.net>
|
|
#
|
|
# 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 <https://www.gnu.org/licenses/>
|
|
# 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))
|