Source code for mesmerize.common.qdialogs

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#@author: kushal

#Chatzigeorgiou Group
#Sars International Centre for Marine Molecular Biology

#GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007

from PyQt5.QtWidgets import QMessageBox, QFileDialog, QLabel, QWidget
import traceback
from functools import wraps
from . import get_project_manager, is_app
from typing import *


"""
Decorators for frequent Qt Dialog GUIs
"""


[docs]def present_exceptions(title: str = 'error', msg: str = 'The following error occurred.', help_func: callable = None): """ Use to catch exceptions and present them to the user in a QMessageBox warning dialog. The traceback from the exception is also shown. This decorator can be stacked on top of other decorators. Example: .. code-block: python @present_exceptions('Error loading file') @use_open_file_dialog('Choose file') def select_file(self, path: str, *args): pass :param title: Title of the dialog box :param msg: Message to display above the traceback in the dialog box :param help_func: A helper function which is called if the user clicked the "Help" button """ def catcher(func): @wraps(func) def fn(self, *args, **kwargs): if not is_app(): return func(self, *args, **kwargs) try: return func(self, *args, **kwargs) except Exception as e: tb = traceback.format_exc() args = (self, title, msg + f'\n\n{e.__class__.__name__}: {e}\n\n{tb}', QMessageBox.Ok) if help_func is not None and QMessageBox.warning(*args, QMessageBox.Help) == QMessageBox.Help: help_func(e, tb) else: QMessageBox.warning(*args) return fn return catcher
[docs]def exceptions_label(label: str, exception_holder: str = None, title: str = 'error', msg: str = 'The following error occured'): """ Use a label to display an exception instead of a QMessageBox :param label: name of a QLabel instance :param exception_holder: name of an exception_holder attribute where the exception message is stored. This can be used to view the whole exception when the label is clicked on for example. :param title: title supplied for the QMessageBox (if used later) :param msg: message supplied for the QMessageBox (if used later) """ def catcher(func): @wraps(func) def fn(self, *args, **kwargs): if not is_app(): func(self, *args, **kwargs) return fn try: return func(self, *args, **kwargs) except Exception as e: tb = traceback.format_exc() exc = f'{e.__class__.__name__}: {e}' qlabel = getattr(self, label) qlabel.setText(exc) qlabel.setStyleSheet("font-weight: bold; color: red") if exception_holder is not None: args = (title, msg + f'\n\n{exc}\n\n{tb}', QMessageBox.Ok) setattr(self, exception_holder, args) return fn return catcher
def _get_start_dir(start_dir) -> str: if start_dir is not None: return start_dir try: d = get_project_manager().root_dir except: return '' else: if d is None: return '' else: return d
[docs]def use_open_file_dialog(title: str = 'Choose file', start_dir: Union[str, None] = None, exts: List[str] = None): """ Use to pass a file path, for opening, into the decorated function using QFileDialog.getOpenFileName :param title: Title of the dialog box :param start_dir: Directory that is first shown in the dialog box. :param exts: List of file extensions to set the filter in the dialog box """ def wrapper(func): @wraps(func) def fn(self, *args, **kwargs): if 'qdialog' in kwargs.keys(): if not kwargs['qdialog']: func(self, *args, **kwargs) return fn if not is_app(): func(self, *args, **kwargs) return fn if exts is None: e = [] else: e = exts if isinstance(self, QWidget): parent = self else: parent = None path = QFileDialog.getOpenFileName(parent, title, _get_start_dir(start_dir), f'({" ".join(e)})') if not path[0]: return path = path[0] func(self, path, *args, **kwargs) return fn return wrapper
[docs]def use_save_file_dialog(title: str = 'Save file', start_dir: Union[str, None] = None, ext: str = None): """ Use to pass a file path, for saving, into the decorated function using QFileDialog.getSaveFileName :param title: Title of the dialog box :param start_dir: Directory that is first shown in the dialog box. :param exts: List of file extensions to set the filter in the dialog box """ def wrapper(func): @wraps(func) def fn(self, *args, **kwargs): if not is_app(): func(self, *args, **kwargs) return fn if ext is None: raise ValueError('Must specify extension') if ext.startswith('*'): ex = ext[1:] else: ex = ext if isinstance(self, QWidget): parent = self else: parent = None path = QFileDialog.getSaveFileName(parent, title, _get_start_dir(start_dir), f'(*{ex})') if not path[0]: return path = path[0] if not path.endswith(ex): path = f'{path}{ex}' func(self, path, *args, **kwargs) return fn return wrapper
[docs]def use_open_dir_dialog(title: str = 'Open directory', start_dir: Union[str, None] = None): """ Use to pass a dir path, to open, into the decorated function using QFileDialog.getExistingDirectory :param title: Title of the dialog box :param start_dir: Directory that is first shown in the dialog box. Example: .. code-block:: python @use_open_dir_dialog('Select Project Directory', '') def load_data(self, path, *args, **kwargs): my_func_to_do_stuff_and_load_data(path) """ def wrapper(func): @wraps(func) def fn(self, *args, **kwargs): if not is_app(): func(self, *args, **kwargs) return fn if isinstance(self, QWidget): parent = self else: parent = None path = QFileDialog.getExistingDirectory(parent, title, _get_start_dir(start_dir)) if not path: return func(self, path, *args, **kwargs) return fn return wrapper