LCOV - code coverage report
Current view: top level - src/pairinteraction_gui - main_window.py (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 102 0.0 %
Date: 2025-04-29 15:59:54 Functions: 0 22 0.0 %

          Line data    Source code
       1             : # SPDX-FileCopyrightText: 2025 Pairinteraction Developers
       2             : # SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4           0 : import logging
       5           0 : from typing import TYPE_CHECKING, Optional, TypeVar
       6             : 
       7           0 : from PySide6.QtCore import QObject, QSize, Qt
       8           0 : from PySide6.QtGui import QAction, QActionGroup, QCloseEvent, QIcon, QKeySequence, QShortcut
       9           0 : from PySide6.QtWidgets import (
      10             :     QDockWidget,
      11             :     QMainWindow,
      12             :     QMessageBox,
      13             :     QSizePolicy,
      14             :     QStatusBar,
      15             :     QToolBar,
      16             :     QWidget,
      17             : )
      18             : 
      19           0 : from pairinteraction_gui.app import Application
      20           0 : from pairinteraction_gui.page import (
      21             :     LifetimesPage,
      22             :     OneAtomPage,
      23             :     TwoAtomsPage,
      24             : )
      25           0 : from pairinteraction_gui.qobjects import NamedStackedWidget
      26             : 
      27             : if TYPE_CHECKING:
      28             :     from pairinteraction_gui.page import BasePage
      29             : 
      30             :     ChildType = TypeVar("ChildType", bound=QObject)
      31             : 
      32           0 : logger = logging.getLogger(__name__)
      33             : 
      34             : 
      35           0 : class MainWindow(QMainWindow):
      36             :     """Main window for the PairInteraction GUI application."""
      37             : 
      38           0 :     def __init__(self) -> None:
      39             :         """Initialize the main window."""
      40           0 :         super().__init__()
      41             : 
      42           0 :         self.setWindowTitle("PairInteraction")
      43           0 :         self.resize(1200, 800)
      44             : 
      45           0 :         self.apply_modern_style()
      46             : 
      47           0 :         self.statusbar = self.setup_statusbar()
      48           0 :         self.dockwidget = self.setup_dockwidget()
      49             : 
      50           0 :         self.stacked_pages = self.setup_stacked_pages()
      51           0 :         self.toolbar = self.setup_toolbar()
      52             : 
      53           0 :         self.init_keyboard_shortcuts()
      54           0 :         self.connect_signals()
      55             : 
      56           0 :     def connect_signals(self) -> None:
      57             :         """Connect signals to slots."""
      58           0 :         self.signals = Application.instance().signals
      59             : 
      60           0 :         self.signals.ask_download_database.connect(self.ask_download_database)
      61             : 
      62           0 :     def findChild(  # type: ignore [override] # explicitly override type hints
      63             :         self, type_: type["ChildType"], name: str, options: Optional["Qt.FindChildOption"] = None
      64             :     ) -> "ChildType":
      65           0 :         if options is None:
      66           0 :             options = Qt.FindChildOption.FindChildrenRecursively
      67           0 :         return super().findChild(type_, name, options)  # type: ignore [return-value] # explicitly override type hints
      68             : 
      69           0 :     def apply_modern_style(self) -> None:
      70             :         """Apply modern styling to the application."""
      71           0 :         self.setStyleSheet("""
      72             :             QMainWindow {
      73             :                 background-color: #ffffff;
      74             :             }
      75             :             QStatusBar {
      76             :                 background-color: #343a40;
      77             :                 color: #ffffff;
      78             :             }
      79             :         """)
      80             : 
      81           0 :     def setup_statusbar(self) -> QStatusBar:
      82             :         """Set up the status bar.
      83             : 
      84             :         The status bar message is set to "Ready" by default.
      85             :         It can be updated with a new message by either from the main window instance:
      86             :             `self.statusbar.showMessage("Ready", timeout=0)`
      87             :         or from outside the main window instance:
      88             :             `QApplication.sendEvent(self, QStatusTipEvent("Ready"))`
      89             :         """
      90           0 :         statusbar = QStatusBar(self)
      91           0 :         statusbar.setFixedHeight(25)
      92           0 :         self.setStatusBar(statusbar)
      93           0 :         statusbar.showMessage("Ready", timeout=0)
      94           0 :         return statusbar
      95             : 
      96           0 :     def setup_dockwidget(self) -> QDockWidget:
      97             :         """Create a configuration dock widget for the main window."""
      98           0 :         dockwidget = QDockWidget()
      99           0 :         dockwidget.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea)
     100           0 :         dockwidget.setTitleBarWidget(QWidget())  # This removes the title bar
     101             : 
     102           0 :         dockwidget.setMinimumWidth(375)
     103           0 :         dockwidget.setStyleSheet("""
     104             :             QToolBox {
     105             :                 background-color: white;
     106             :                 border: 1px solid #ffffff;
     107             :                 border-radius: 5px;
     108             :             }
     109             :             QToolBox::tab {
     110             :                 background-color: #343a40;
     111             :                 border: 1px solid #ffffff;
     112             :                 border-radius: 4px;
     113             :                 color: #ffffff;
     114             :                 font-weight: bold;
     115             :                 font-size: 15px;
     116             :             }
     117             :             QToolBox::tab:selected {
     118             :                 background-color: #007bff;
     119             :             }
     120             :             QToolBox::tab:hover:!selected {
     121             :                 background-color: #495057;
     122             :             }
     123             :             QLabel {
     124             :                 color: black;
     125             :                 font-size: 14px;
     126             :             }
     127             :         """)
     128           0 :         dockwidget.setVisible(False)
     129           0 :         self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dockwidget)
     130           0 :         return dockwidget
     131             : 
     132           0 :     def setup_stacked_pages(self) -> NamedStackedWidget["BasePage"]:
     133             :         """Set up the different pages for each toolbar option."""
     134           0 :         stacked_pages = NamedStackedWidget["BasePage"]()
     135           0 :         self.setCentralWidget(stacked_pages)
     136             : 
     137           0 :         stacked_pages.addNamedWidget(OneAtomPage(), "system_atom")
     138           0 :         stacked_pages.addNamedWidget(TwoAtomsPage(), "system_pair")
     139           0 :         stacked_pages.addNamedWidget(LifetimesPage(), "lifetimes")
     140             :         # stacked_pages.addNamedWidget(C6Page(), "c6")
     141             : 
     142             :         # stacked_pages.addNamedWidget(SettingsPage(), "settings")
     143             :         # stacked_pages.addNamedWidget(AboutPage(), "about")
     144           0 :         return stacked_pages
     145             : 
     146           0 :     def setup_toolbar(self) -> QToolBar:
     147             :         """Set up the toolbar with icon buttons."""
     148           0 :         toolbar = QToolBar("Sidebar")
     149           0 :         toolbar.setMovable(False)
     150           0 :         toolbar.setOrientation(Qt.Orientation.Vertical)
     151           0 :         toolbar.setIconSize(QSize(32, 32))
     152           0 :         toolbar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
     153           0 :         toolbar.setStyleSheet("""
     154             :             QToolBar {
     155             :                 background-color: #343a40;
     156             :                 border: none;
     157             :                 spacing: 15px;
     158             :                 padding: 10px 5px;
     159             :             }
     160             :             QToolButton {
     161             :                 border: none;
     162             :                 padding: 8px;
     163             :                 margin: 5px;
     164             :                 color: #ffffff;
     165             :                 font-weight: bold;
     166             :                 font-size: 15px;
     167             :             }
     168             :             QToolButton:hover {
     169             :                 background-color: #495057;
     170             :             }
     171             :             QToolButton:pressed {
     172             :                 background-color: #000000;
     173             :             }
     174             :             QToolButton:checked {
     175             :                 background-color: #007bff;
     176             :             }
     177             :         """)
     178             : 
     179           0 :         toolbar_group = QActionGroup(self)
     180           0 :         toolbar_group.setExclusive(True)
     181             : 
     182           0 :         for name, page in self.stacked_pages.items():
     183             :             # add a spacer widget
     184           0 :             if name == "about":
     185           0 :                 spacer_widget = QWidget()
     186           0 :                 spacer_widget.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
     187           0 :                 toolbar.addWidget(spacer_widget)
     188             : 
     189           0 :             action = QAction(self)
     190           0 :             action.setObjectName(name)
     191           0 :             action.setText(page.title)
     192           0 :             action.setToolTip(page.tooltip)
     193           0 :             action.setCheckable(True)
     194           0 :             if page.icon_path:
     195           0 :                 action.setIcon(QIcon(str(page.icon_path)))
     196             : 
     197           0 :             toolbar.addAction(action)
     198           0 :             toolbar_group.addAction(action)
     199             : 
     200           0 :             action.triggered.connect(lambda checked, name=name: self.stacked_pages.setCurrentNamedWidget(name))
     201             : 
     202           0 :         default_page = "system_atom"
     203           0 :         self.findChild(QAction, default_page).setChecked(True)
     204           0 :         self.stacked_pages.setCurrentNamedWidget(default_page)
     205             : 
     206           0 :         self.addToolBar(Qt.ToolBarArea.LeftToolBarArea, toolbar)
     207             : 
     208           0 :         return toolbar
     209             : 
     210           0 :     def init_keyboard_shortcuts(self) -> None:
     211             :         """Initialize keyboard shortcuts."""
     212             :         # Add Ctrl+W shortcut to close the window
     213           0 :         close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
     214           0 :         close_shortcut.activated.connect(lambda: logger.info("Ctrl+W detected. Shutting down gracefully..."))
     215           0 :         close_shortcut.activated.connect(self.close)
     216             : 
     217           0 :     def closeEvent(self, event: QCloseEvent) -> None:
     218             :         """Make sure to also call Application.quit() when closing the window."""
     219           0 :         logger.debug("Close event triggered.")
     220           0 :         Application.quit()
     221           0 :         event.accept()
     222             : 
     223           0 :     def ask_download_database(self, species: str) -> bool:
     224           0 :         msg_box = QMessageBox()
     225           0 :         msg_box.setWindowTitle("Download missing database tables?")
     226           0 :         msg_box.setText(f"Database tables for {species} not found.")
     227           0 :         msg_box.setInformativeText("Would you like to download the missing database tables?")
     228           0 :         msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
     229             : 
     230           0 :         download = msg_box.exec() == QMessageBox.StandardButton.Yes
     231           0 :         if download:
     232           0 :             from pairinteraction._wrapped import Database
     233           0 :             from pairinteraction.cli import download_databases
     234             : 
     235           0 :             Database._global_database = None
     236           0 :             download_databases([species])
     237           0 :         return download

Generated by: LCOV version 1.16