Skip to content
Snippets Groups Projects
main_window.py 17.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andreas Bolin's avatar
    Andreas Bolin committed
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    """B-ASIC Scheduler-gui Module.
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    Contains the scheduler-gui class for scheduling operations in an SFG.
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    Start main-window with start_gui().
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    """
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    import os
    import sys
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from pathlib        import Path
    
    from types          import ModuleType
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from typing         import Any, Iterable, List
    
    from pprint         import pprint
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    #from matplotlib.pyplot import bar
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    #from diagram import *
    
    from importlib.machinery import SourceFileLoader
    import inspect
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy           import uic, QtCore, QtGui, QtWidgets
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtCore    import QCoreApplication, Qt, Slot, Signal, QSettings, QStandardPaths
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtGui     import QCloseEvent
    
    from qtpy.QtWidgets import (
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QApplication, QMainWindow, QMessageBox, QFileDialog, QInputDialog, QCheckBox, QAbstractButton,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QTableWidgetItem, QSizePolicy)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    # QGraphics and QPainter imports
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtCore    import (
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QRect, QRectF, QPoint, QSize, QByteArray, QMarginsF, QObject)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtGui     import (
        QPaintEvent, QPainter, QPainterPath, QColor, QBrush, QPen, QFont, QPolygon, QIcon, QPixmap,
        QLinearGradient)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtWidgets import (
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QGraphicsView, QGraphicsScene, QGraphicsWidget, QGraphicsScale,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QGraphicsLayout, QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayoutItem, QGraphicsAnchorLayout,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QGraphicsItem, QGraphicsItemGroup, QGraphicsRectItem)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    # B-ASIC
    import logger
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from b_asic.schedule            import Schedule
    from graphics_graph_item        import GraphicsGraphItem
    from graphics_axis_item         import GraphicsAxisItem
    from graphics_component_item    import GraphicsComponentItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    # if sys.version_info >= (3, 9):
    #     List = list
    #     #Dict = dict
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    log = logger.getLogger()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    sys.excepthook = logger.handle_exceptions
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    if __debug__:
        log.setLevel('DEBUG')
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    if __debug__:
    
        # Print some system version information
        QT_API = os.environ.get('QT_API')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        log.debug('Qt version (runtime):     {}'.format(QtCore.qVersion()))
        log.debug('Qt version (compiletime): {}'.format(QtCore.__version__))
        log.debug('QT_API:                   {}'.format(QT_API))
    
        if QT_API.lower().startswith('pyside'):
            import PySide2
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            log.debug('PySide version:           {}'.format(PySide2.__version__))
    
        if QT_API.lower().startswith('pyqt'):
    
            from qtpy.QtCore import PYQT_VERSION_STR
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            log.debug('PyQt version:             {}'.format(PYQT_VERSION_STR))
        log.debug('QtPy version:             {}'.format(qtpy.__version__))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
        # Autocompile the .ui form to a python file.
        try:                                        # PyQt5, try autocompile
            from qtpy.uic import compileUiDir
    
            uic.compileUiDir('.', map=(lambda dir,file: (dir, 'ui_' + file)))
    
        except:
            try:                                    # PySide2, try manual compile
                import subprocess
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                os_ = sys.platform
                if os_.startswith('linux'):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    cmds = ['pyside2-uic -o ui_main_window.py main_window.ui']
    
                    for cmd in cmds:
                        subprocess.call(cmd.split())
                else:
                    #TODO: Implement (startswith) 'win32', 'darwin' (MacOs)
                    raise SystemExit
            except:                                 # Compile failed, look for pre-compiled file
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                try:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    from ui_main_window import Ui_MainWindow
    
                except:                             # Everything failed, exit
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    log.exception("Could not import 'Ui_MainWindow'.")
                    log.exception("Can't autocompile under", QT_API, "eviroment. Try to manual compile 'main_window.ui' to 'ui/main_window_ui.py'")
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    sys.path.insert(0, 'icons/')                # Needed for the compiled '*_rc.py' files in 'ui_*.py' files
    from ui_main_window import Ui_MainWindow    # Only availible when the form (.ui) is compiled
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    # The folowing QCoreApplication values is used for QSettings among others
    QCoreApplication.setOrganizationName('Linöping University')
    QCoreApplication.setOrganizationDomain('liu.se')
    QCoreApplication.setApplicationName('B-ASIC Scheduler')
    #QCoreApplication.setApplicationVersion(__version__)     # TODO: read from packet __version__
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    class MainWindow(QMainWindow, Ui_MainWindow):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        """Schedule of an SFG with scheduled Operations."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _scene: QGraphicsScene
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _graph: GraphicsGraphItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _scale: float
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _debug_rects: QGraphicsItemGroup
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def __init__(self):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Initialize Schedule-gui."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            super().__init__()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._graph = None
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._open_file_dialog_opened = False
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._scale = 75
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._debug_rects = None
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            QIcon.setThemeName('breeze')
    
            log.debug('themeName: \'{}\''.format(QIcon.themeName()))
            log.debug('themeSearchPaths: {}'.format(QIcon.themeSearchPaths()))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._init_ui()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._init_graphics()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._read_settings()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def _init_ui(self) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Initialize the ui"""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.setupUi(self)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # Connect signals to slots
    
            self.menu_load_from_file.triggered      .connect(self._load_schedule_from_pyfile)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.menu_save          .triggered      .connect(self.save)
            self.menu_save_as       .triggered      .connect(self.save_as)
    
            self.menu_quit          .triggered      .connect(self.close)
            self.menu_node_info     .triggered      .connect(self.toggle_component_info)
    
            self.menu_exit_dialog   .triggered      .connect(self.toggle_exit_dialog)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.actionT            .triggered      .connect(self.actionTbtn)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.splitter           .splitterMoved  .connect(self._splitter_moved)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # Setup event member functions
            self.closeEvent = self._close_event
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # Setup info table
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.info_table.setHorizontalHeaderLabels(['Property','Value'])
            # test = '#b085b2'
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # self.info_table.setStyleSheet('alternate-background-color: lightGray;background-color: white;')
            self.info_table.setStyleSheet('alternate-background-color: #fadefb;background-color: #ebebeb;')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for i in range(10):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                self.info_table.insertRow(i)
                item = QTableWidgetItem('this is a very very very very long string that says abolutly nothing')
                self.info_table.setItem(i,0, QTableWidgetItem('property {}: '.format(i)))
                self.info_table.setItem(i,1,item)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
            # Init central-widget splitter
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.splitter.setStretchFactor(0, 1)
            self.splitter.setStretchFactor(1, 0)
            self.splitter.setCollapsible(0, False)
            self.splitter.setCollapsible(1, True)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def _init_graphics(self) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Initialize the QGraphics framework"""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._scene = QGraphicsScene()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.view.setScene(self._scene)
            self.view.scale(self._scale, self._scale)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            GraphicsComponentItem._scale = self._scale
            GraphicsAxisItem._scale = self._scale
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._scene.changed.connect(self.shrink_scene_to_min_size)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
        ###############
        #### Slots ####
        ###############
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @Slot()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def actionTbtn(self) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._graph.schedule.plot_schedule()
            print(f'filtersChildEvents(): {self._graph.filtersChildEvents()}')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # self.printButtonPressed('callback_pushButton()')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
        @Slot()
    
        def _load_schedule_from_pyfile(self) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            settings = QSettings()
            # open_dir = QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0] if not self._open_file_dialog_opened else ''
            last_file = settings.value('mainwindow/last_opened_file', QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0], str)
            if not os.path.exists(last_file):               # if filename does not exist
                last_file = os.path.dirname(last_file) + '/'
                if not os.path.exists(last_file):           # if path does not exist
                    last_file = QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0]
    
            abs_path_filename, _ = QFileDialog.getOpenFileName(self,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                                                            self.tr("Open python file"),
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                                                            last_file,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                                                            self.tr("Python Files (*.py *.py3)"))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
            if not abs_path_filename:       # return if empty filename (QFileDialog was canceled)
                return
            log.debug('abs_path_filename = {}.'.format(abs_path_filename))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._open_file_dialog_opened = True
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            module_name = inspect.getmodulename(abs_path_filename)
            if not module_name:             # return if empty module name
                log.error('Could not load module from file \'{}\'.'.format(abs_path_filename))
                return 
            
            try:
                module = SourceFileLoader(module_name, abs_path_filename).load_module()
            except:
                log.exception('Exception occurred. Could not load module from file \'{}\'.'.format(abs_path_filename))
                return
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            schedule_obj_list = dict(inspect.getmembers(module, (lambda x: isinstance(x, Schedule))))
    
            
            if not schedule_obj_list:       # return if no Schedule objects in script
                QMessageBox.warning(self,
                                    self.tr('File not found'),
                                    self.tr('Could not find any Schedule object in file \'{}\'.')
                                    .format(os.path.basename(abs_path_filename)))
                log.info('Could not find any Schedule object in file \'{}\'.'
                         .format(os.path.basename(abs_path_filename)))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                del module
    
                return
            
            ret_tuple = QInputDialog.getItem(self,
                                             self.tr('Load object'),
                                             self.tr('Found the following Schedule object(s) in file.)\n\n'
                                                     'Select an object to proceed:'),
                                             schedule_obj_list.keys(),0,False)
    
            if not ret_tuple[1]:                  # User canceled the operation
                log.debug('Load schedule operation: user canceled')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                del module
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.open(schedule_obj_list[ret_tuple[0]])
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            del module
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            settings.setValue("mainwindow/last_opened_file", abs_path_filename)
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
        
        #@Slot()
        def open(self, schedule: Schedule) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Takes in an Schedule and creates a GraphicsGraphItem object."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._graph = GraphicsGraphItem(schedule)
            self._scene.addItem(self._graph)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._graph.installSceneEventFilters()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
            # graph.prepareGeometryChange()
            # graph.setPos(200, 20)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # self._scene.setSceneRect(self._scene.itemsBoundingRect())   # Forces the scene to it's minimum size
            
            # # Debug rectangles
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # if __debug__:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     # self._scene.setSceneRect(self._scene.itemsBoundingRect())   # Forces the scene to it's minimum size
            #     m = QMarginsF(1/self._scale, 1/self._scale, 0, 0)
            #     m2 = QMarginsF(1, 1, 1, 1)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     pen = QPen(Qt.red)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     pen.setStyle(Qt.DotLine)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     pen.setCosmetic(True)
            #     for component in graph.items:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #         self._scene.addRect(component.mapRectToScene(component.boundingRect() - m), pen)
            #     pen.setColor(Qt.red)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     for axis in graph.axis.childItems():
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #         self._scene.addRect(axis.mapRectToScene(axis.boundingRect() - m), pen)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     pen.setColor(Qt.green)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #     # self._scene.addRect(self._scene.itemsBoundingRect() - m, pen)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                
            # self._scene.setSceneRect(self._scene.itemsBoundingRect())
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.update_statusbar(self.tr('Schedule loaded successfully'))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @Slot()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def save(self) -> None:
            """This method save an schedule."""
            #TODO: all
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.printButtonPressed('save_schedule()')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.update_statusbar(self.tr('Schedule saved successfully'))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
        @Slot()
        def save_as(self) -> None:
            """This method save as an schedule."""
            #TODO: all
            self.printButtonPressed('save_schedule()')
            self.update_statusbar(self.tr('Schedule saved successfully'))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @Slot(bool)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def toggle_component_info(self, checked: bool) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """This method toggles the right hand side info window."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # Note: splitter handler index 0 is a hidden splitter handle far most left, use index 1
            settings = QSettings()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            range = self.splitter.getRange(1)    # tuple(min, max)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if checked:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                self.splitter.restoreState(settings.value("mainwindow/splitter/last_state"))
                # self.splitter.restoreState(settings.value("splitterSizes"))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            else:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                settings.setValue("mainwindow/splitter/last_state", self.splitter.saveState())
                self.splitter.moveSplitter(range[1], 1)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def toggle_exit_dialog(self, checked: bool) -> None:
    
            s = QSettings()
            s.setValue("mainwindow/hide_exit_dialog", checked)
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @Slot(int, int)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def _splitter_moved(self, pos: int, index: int) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Callback method used to check if the right widget (info window) 
            has collapsed. Update the checkbutton accordingly."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # TODO: Custom move handler, save state on click-release?
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            widths: list[int, int] = list(self.splitter.sizes())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if widths[1] == 0:
                self.menu_node_info.setChecked(False)
            else:
                self.menu_node_info.setChecked(True)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @Slot('QList<QRectF>')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def shrink_scene_to_min_size(self, region: List[QRectF]) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._scene.setSceneRect(self._scene.itemsBoundingRect())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        ################
        #### Events ####
        ################
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def _close_event(self, event: QCloseEvent) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Replaces QMainWindow default closeEvent(QCloseEvent) event"""
            s = QSettings()
            hide_dialog = s.value('mainwindow/hide_exit_dialog', False, bool)
            ret = QMessageBox.StandardButton.Yes
            
            if not hide_dialog:
                box = QMessageBox(self)
                box.setWindowTitle(self.tr('Confirm Exit'))
                box.setText('<h3>' + self.tr('Confirm Exit') + '</h3><p><br>' +
                            self.tr('Are you sure you want to exit?') +
                            '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></p>')
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                box.setIcon(QMessageBox.Question)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
    
                buttons: list[QAbstractButton] = box.buttons()
                buttons[0].setText(self.tr('&Exit'))
                buttons[1].setText(self.tr('&Cancel'))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                checkbox = QCheckBox(self.tr('Don\'t ask again'))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                box.setCheckBox(checkbox)
                ret = box.exec_()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if ret == QMessageBox.StandardButton.Yes:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                if not hide_dialog:
                    s.setValue('mainwindow/hide_exit_dialog', checkbox.isChecked())
                self._write_settings()
    
                log.info('Exit: {}'.format(os.path.basename(__file__)))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                event.accept()
            else:
                event.ignore()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        #################################
        #### Helper member functions ####
        #################################
        def printButtonPressed(self, func_name: str) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            #TODO: remove        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
            alert = QMessageBox(self)
            alert.setText("Called from " + func_name + '!')
            alert.exec_()
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def update_statusbar(self, msg: str) -> None:
            """Write the given str to the statusbar with temporarily policy."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.statusbar.showMessage(msg)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
        def _write_settings(self) -> None:
            """Write settings from MainWindow to Settings."""
            s = QSettings()
    
            s.setValue('mainwindow/maximized',      self.isMaximized())     # window: maximized, in X11 - alwas False
            s.setValue('mainwindow/pos',            self.pos())             # window: pos
            s.setValue('mainwindow/size',           self.size())            # window: size
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            s.setValue('mainwindow/state',          self.saveState())       # toolbars, dockwidgets: pos, size
            s.setValue('mainwindow/menu/node_info', self.menu_node_info.isChecked())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            s.setValue('mainwindow/splitter/state', self.splitter.saveState())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
            if s.isWritable():
                log.debug('Settings written to \'{}\'.'.format(s.fileName()))
            else:
                log.warning('Settings cant be saved to file, read-only.')
        
        def _read_settings(self) -> None:
            """Read settings from Settings to MainWindow."""
            s = QSettings()
    
            if s.value('mainwindow/maximized', defaultValue=False, type=bool):
                self.showMaximized()
            else:
    
                self.move(                      s.value('mainwindow/pos', self.pos()))
                self.resize(                    s.value('mainwindow/size', self.size()))
            self.restoreState(                  s.value('mainwindow/state', QByteArray()))
            self.menu_node_info.setChecked(     s.value('mainwindow/menu/node_info', True, bool))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.splitter.restoreState(         s.value('mainwindow/splitter/state', QByteArray()))
            self.menu_exit_dialog.setChecked(   s.value('mainwindow/hide_exit_dialog', False, bool))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
            log.debug('Settings read from \'{}\'.'.format(s.fileName()))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    def start_gui():
        app = QApplication(sys.argv)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        window = MainWindow()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        window.show()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        sys.exit(app.exec_())
    
    if __name__ == "__main__":
        start_gui()