from .window_ui import Ui_window
from .pages import Pages
from ..logger import logs_folder
from .. import parameters
from .. import meta
from PySide6.QtWidgets import QMainWindow
from PySide6.QtWidgets import QMessageBox
from PySide6.QtWidgets import QWidget
from PySide6.QtGui import QAction
from PySide6.QtGui import QShortcut
from PySide6.QtGui import QKeySequence
from PySide6.QtCore import QEvent
from PySide6.QtCore import Qt
from webbrowser import open as browse
from platform import system
from subprocess import run
from pathlib import Path
import os
[docs]
class Window(QMainWindow):
"""Main window of the application"""
def __init__(self, parent: QWidget = None):
# Load user interface design created in Qt Designer.
super().__init__(parent)
self.ui = Ui_window()
self.ui.setupUi(self)
self.setFocusPolicy(Qt.ClickFocus)
self.setWindowTitle(meta.title)
# We only have one widget, which manages the various pages.
self.pages = Pages()
self.setCentralWidget(self.pages)
# Make sure we get notified of status updates.
self.pages.status_update.connect(self.ui.status.showMessage)
self.pages.status_clear.connect(self.ui.status.clearMessage)
# Add keyboard shortcuts.
self.keys = []
key = QShortcut(QKeySequence('Esc'), self)
key.activated.connect(self.close)
self.keys.append(key)
# Display all actions in the right-click context menu.
self.setContextMenuPolicy(Qt.ActionsContextMenu)
# Add context menu entries.
action = QAction('About…', self)
action.triggered.connect(self.show_about)
action.setShortcut('Alt+A')
self.addAction(action)
action = QAction('Documentation…', self)
action.triggered.connect(self.show_docs)
action.setShortcut('F1')
self.addAction(action)
action = QAction('Edit parameters…', self)
action.triggered.connect(self.edit_parameters)
action.setShortcut('Alt+P')
self.addAction(action)
action = QAction('Edit defaults…', self)
action.triggered.connect(self.edit_defaults)
action.setShortcut('Alt+D')
self.addAction(action)
action = QAction('Debug logs…', self)
action.triggered.connect(self.show_logs)
action.setShortcut('Alt+L')
self.addAction(action)
[docs]
def show_about(self):
"""Displays the About dialog."""
text = (f'{meta.title} {meta.version}\n\n'
f'{meta.synopsis}\n\n'
f'Developed by Scientific IT Services of ETH Zurich.\n\n'
f'© {meta.copyright}\n')
QMessageBox.about(self, 'About', text)
[docs]
def show_docs(self):
"""
Opens the documentation in the web browser.
We prefer to show the documentation distributed with the packaged
application, as it should then refer to the same version. But if
for some unforeseen reason we cannot find that, we direct the
user to the online version.
"""
here = Path(__file__).parent.resolve()
files = (
here.parent.parent.parent/'docs'/'index.html',
here.parent.parent/'build'/'docs'/'index.html',
)
for file in files:
if file.exists():
if system() == 'Darwin':
run(f'open "{file}"', shell=True)
else:
browse(str(file))
# On Windows, we could pass the Path object directly.
# But that didn't work on Linux Mint, hence the
# explicit string conversion here.
break
else:
browse(r'https://spinningdiskanalyzer.ethz.ch/')
[docs]
def edit_parameters(self):
"""Opens the file with the processing parameters in an editor."""
if parameters.file:
self.open_item(parameters.file)
else:
QMessageBox.critical(self, 'Error',
'The parameters have not been initialized yet.\n\n'
'A "parameters.yaml" file is created (in an "analysis" '
'subfolder) once a valid input folder has been selected.')
[docs]
def edit_defaults(self):
"""Opens the config file with the parameter defaults in an editor."""
self.open_item(parameters.defaults_file())
[docs]
def show_logs(self):
"""Opens the folder with the log files."""
self.open_item(logs_folder())
[docs]
def open_item(self, item: Path):
"""Opens a file or folder using the default application."""
if system() == 'Windows':
os.startfile(item)
elif system() == 'Linux':
run(f'xdg-open "{item}"', shell=True)
elif system() == 'Darwin':
run(f'open "{item}"', shell=True)
else:
noun = 'files' if item.is_file() else 'folders'
QMessageBox.information(self, 'Information',
f'Opening {noun} not supported on this platform.\n\n'
f'Open your file manager and navigate to this location:\n'
f'\t{item}')
[docs]
def closeEvent(self, event: QEvent):
"""
Called when user closes the window.
Qt does not propagate the "close" event to child widgets. That
is, they are not explicitly closed, by calling `.close()` on
them, so don't get a say in whether terminating the application
is a good idea right now. We do want that though, so we can
determine that within the context of the pages, one of which
might be running a background task. Therefore we implement the
mechanism ourselves, asking each page to confirm.
"""
if self.pages.confirm_close():
event.accept()
else:
event.ignore()