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

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 

5 

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 

10 

11SPLASH_WIDTH = 440 

12SPLASH_HEIGHT = 220 

13_ICON_SIZE = 88 

14_CORNER_RADIUS = 14 

15 

16 

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) 

22 

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) 

30 

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) 

34 

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) 

43 

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 

53 

54 

55class StartupSplash(QSplashScreen): 

56 """アイコン・名前・版と、下に今の段階の文字を出す。最初のウィンドウを出したら finish() で閉じる。""" 

57 

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"]) 

64 

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()) 

70 

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() 

77 

78 

79def show_startup_splash(dark=False): 

80 splash = StartupSplash(dark) 

81 splash.show() 

82 splash.show_progress(tr("起動しています…")) 

83 return splash