Coverage for graphica/gui/splash.py: 91.9%
60 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
1"""起動画面。重いライブラリ(pandas・scipy・matplotlib)を読む前に出すので、ここでは PySide6 と軽いモジュールだけを使う。"""
2from PySide6.QtCore import QRectF, Qt
3from PySide6.QtGui import QColor, QFont, QIcon, QPainter, QPainterPath, QPixmap
4from PySide6.QtWidgets import QApplication, QSplashScreen
6from graphica.core.i18n import tr
7from graphica.core.version import APP_NAME, __version__
8from graphica.gui.resources import resource_path
9from graphica.gui.theme import DARK_TOKENS, LIGHT_TOKENS
11SPLASH_WIDTH = 440
12SPLASH_HEIGHT = 220
13_ICON_SIZE = 88
14_CORNER_RADIUS = 14
17def _render_background(dark, device_pixel_ratio):
18 colors = DARK_TOKENS if dark else LIGHT_TOKENS
19 pixmap = QPixmap(int(SPLASH_WIDTH * device_pixel_ratio), int(SPLASH_HEIGHT * device_pixel_ratio))
20 pixmap.setDevicePixelRatio(device_pixel_ratio)
21 pixmap.fill(Qt.GlobalColor.transparent)
23 painter = QPainter(pixmap)
24 painter.setRenderHint(QPainter.RenderHint.Antialiasing)
25 outline = QPainterPath()
26 outline.addRoundedRect(QRectF(0.5, 0.5, SPLASH_WIDTH - 1, SPLASH_HEIGHT - 1), _CORNER_RADIUS, _CORNER_RADIUS)
27 painter.fillPath(outline, QColor(colors["surface"]))
28 painter.setPen(QColor(colors["border"]))
29 painter.drawPath(outline)
31 icon_top = 40
32 icon = QIcon(resource_path("Graphica.ico")).pixmap(_ICON_SIZE, _ICON_SIZE)
33 painter.drawPixmap(32, icon_top, _ICON_SIZE, _ICON_SIZE, icon)
35 text_left = 32 + _ICON_SIZE + 24
36 name_font = QFont()
37 name_font.setPointSize(22)
38 name_font.setBold(True)
39 painter.setFont(name_font)
40 painter.setPen(QColor(colors["text_primary"]))
41 painter.drawText(QRectF(text_left, icon_top + 8, SPLASH_WIDTH - text_left - 24, 40),
42 Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, APP_NAME)
44 version_font = QFont()
45 version_font.setPointSize(10)
46 painter.setFont(version_font)
47 painter.setPen(QColor(colors["text_secondary"]))
48 painter.drawText(QRectF(text_left, icon_top + 50, SPLASH_WIDTH - text_left - 24, 24),
49 Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter,
50 tr("バージョン {version}").format(version=__version__))
51 painter.end()
52 return pixmap
55class StartupSplash(QSplashScreen):
56 """アイコン・名前・版と、下に今の段階の文字を出す。最初のウィンドウを出したら finish() で閉じる。"""
58 def __init__(self, dark=False):
59 app = QApplication.instance()
60 ratio = app.devicePixelRatio() if app is not None else 1.0
61 super().__init__(_render_background(dark, ratio))
62 self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
63 self._text_color = QColor((DARK_TOKENS if dark else LIGHT_TOKENS)["text_secondary"])
65 def drawContents(self, painter):
66 # 既定は端から 5px で文字が枠に近すぎるので、下に余白を取る
67 painter.setPen(self._text_color)
68 painter.drawText(self.rect().adjusted(24, 0, -24, -22),
69 Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter, self.message())
71 def show_progress(self, text):
72 """文字を出して、すぐに描かせる(このあと重い処理でイベントループが止まるので)。"""
73 self.showMessage(text, Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter, self._text_color)
74 app = QApplication.instance()
75 if app is not None: 75 ↛ exitline 75 didn't return from function 'show_progress' because the condition on line 75 was always true
76 app.processEvents()
79def show_startup_splash(dark=False):
80 splash = StartupSplash(dark)
81 splash.show()
82 splash.show_progress(tr("起動しています…"))
83 return splash