diff --git a/lyse/__main__.py b/lyse/__main__.py index be6c02c..edb5037 100644 --- a/lyse/__main__.py +++ b/lyse/__main__.py @@ -14,6 +14,7 @@ """ import os import labscript_utils.excepthook +import importlib.metadata # Associate app windows with OS menu shortcuts, must be before any GUI calls, apparently import desktop_app @@ -49,9 +50,11 @@ # qt imports splash.update_text('importing qt modules') -from qtutils.qt import QtCore, QtWidgets +from qtutils.qt import QtCore, QtWidgets, QT_ENV from qtutils.qt.QtCore import pyqtSignal as Signal from qtutils import UiLoader +QT_VERSION_STR = QtCore.qVersion() +PYQT_VERSION_STR = importlib.metadata.version(QT_ENV) # needs to be present so that qtutils icons referenced in .ui files can be resolved. Since this is # magical is should not be implemented in this way. @@ -104,6 +107,9 @@ def __init__(self, qapplication): self.logger = setup_logging('lyse') labscript_utils.excepthook.set_logger(self.logger) self.logger.info('\n\n===============starting===============\n') + self.logger.info(f'Qt Environment: {QT_ENV}') + self.logger.info(f'PySide/PyQt version: {PYQT_VERSION_STR}') + self.logger.info(f'Qt version: {QT_VERSION_STR}') # Second: read lyse config self.setup_config() @@ -115,11 +121,13 @@ def __init__(self, qapplication): # Forth: start remote communication server self.port = int(self.exp_config.get('ports', 'lyse')) self.server = lyse.communication.WebServer(self, self.port) + self.logger.info(f'Started lyse server on port {self.port}') # Last: UI setup self.qapplication = qapplication loader = UiLoader() self.ui = loader.load(os.path.join(lyse.utils.LYSE_DIR, 'user_interface/main.ui'), LyseMainWindow(self)) + self.logger.info('UI loaded') self.connect_signals() @@ -138,6 +146,7 @@ def __init__(self, qapplication): self, to_multishot, from_multishot, self.output_box.port, multishot=True) self.filebox = lyse.filebox.FileBox(self, self.ui.verticalLayout_filebox, self.exp_config, to_singleshot, from_singleshot, to_multishot, from_multishot) + self.logger.info('Boxes loaded') self.last_save_config_file = None self.last_save_data = None @@ -188,6 +197,7 @@ def load_the_config_file(): # Success - skip loading window geometry in load_configuration: restore_window_geometry = False self.ui.firstPaint.connect(lambda: QtCore.QTimer.singleShot(50, load_the_config_file)) + self.logger.info('lyse configuration loaded') self.ui.show() @@ -284,12 +294,12 @@ def get_save_data(self): box = self.singleshot_routinebox save_data['singleshot'] = list(zip([routine.filepath for routine in box.routines], - [box.model.item(row, box.COL_ACTIVE).checkState() + [lyse.utils.gui.get_check_state(box.model.item(row, box.COL_ACTIVE)) for row in range(box.model.rowCount())])) save_data['lastsingleshotfolder'] = box.last_opened_routine_folder box = self.multishot_routinebox save_data['multishot'] = list(zip([routine.filepath for routine in box.routines], - [box.model.item(row, box.COL_ACTIVE).checkState() + [lyse.utils.gui.get_check_state(box.model.item(row, box.COL_ACTIVE)) for row in range(box.model.rowCount())])) save_data['lastmultishotfolder'] = box.last_opened_routine_folder @@ -522,7 +532,7 @@ def delete_items(self, confirm): signal.signal(signal.SIGINT, lambda *args: qapplication.exit()) splash.hide() - qapplication.exec_() + qapplication.exec() # Shutdown the webserver. app.server.shutdown() diff --git a/lyse/analysis_subprocess.py b/lyse/analysis_subprocess.py index 0a0f100..7021162 100644 --- a/lyse/analysis_subprocess.py +++ b/lyse/analysis_subprocess.py @@ -475,5 +475,5 @@ def reset_figs(self): if qapplication is None: qapplication = QtWidgets.QApplication(sys.argv) worker = AnalysisWorker(filepath, to_parent, from_parent) - qapplication.exec_() + qapplication.exec() diff --git a/lyse/filebox.py b/lyse/filebox.py index 8cf23d3..b2e135b 100644 --- a/lyse/filebox.py +++ b/lyse/filebox.py @@ -142,7 +142,7 @@ def on_treeView_context_menu_requested(self, point): menu = QtWidgets.QMenu(self.ui) menu.addAction(self.action_set_selected_visible) menu.addAction(self.action_set_selected_hidden) - menu.exec_(QtGui.QCursor.pos()) + menu.exec(QtGui.QCursor.pos()) def on_set_selected_triggered(self, visible): selected_indexes = self.ui.treeView.selectedIndexes() @@ -373,7 +373,7 @@ def mark_selection_not_done(self): def on_view_context_menu_requested(self, point): menu = QtWidgets.QMenu(self._view) menu.addAction(self.action_remove_selected) - menu.exec_(QtGui.QCursor.pos()) + menu.exec(QtGui.QCursor.pos()) def on_double_click(self, index): filepath_item = self._model.item(index.row(), self.COL_FILEPATH) diff --git a/lyse/routines.py b/lyse/routines.py index e638af3..1f4aa7a 100644 --- a/lyse/routines.py +++ b/lyse/routines.py @@ -54,12 +54,14 @@ def __init__(self, app, container, exp_config, filebox, from_filebox, to_filebox self.to_filebox = to_filebox self.output_box_port = output_box_port - self.logger = logging.getLogger('lyse.RoutineBox.%s'%('multishot' if multishot else 'singleshot')) + self.logger = logging.getLogger('lyse.RoutineBox.%s'%('multishot' if multishot else 'singleshot')) + self.logger.info('starting') loader = UiLoader() loader.registerCustomWidget(lyse.widgets.TreeView) self.ui = loader.load(os.path.join(lyse.utils.LYSE_DIR, 'user_interface/routinebox.ui')) container.addWidget(self.ui) + self.logger.info('UI loaded') if multishot: self.ui.groupBox.setTitle('Multishot routines') @@ -110,6 +112,7 @@ def __init__(self, app, container, exp_config, filebox, from_filebox, to_filebox self.connect_signals() + self.logger.info('starting analysis loop') self.analysis = threading.Thread(target = self.analysis_loop) self.analysis.daemon = True self.analysis.start() @@ -170,7 +173,9 @@ def add_routines(self, routine_files, clear_existing=False): if filepath in [routine.filepath for routine in self.routines]: self.app.output_box.output('Warning: Ignoring duplicate analysis routine %s\n'%filepath, red=True) continue - routine = AnalysisRoutine(self.app, filepath, self.model, self.output_box_port, checked) + self.logger.info(f'adding routine for {filepath}') + routine = AnalysisRoutine(self.app, filepath, self.model, self.output_box_port, + QtCore.Qt.CheckState(checked)) self.routines.append(routine) self.update_select_all_checkstate() @@ -215,6 +220,7 @@ def remove_selection(self, confirm=True): if routine.filepath in filepaths: routine.remove() self.routines.remove(routine) + self.logger.info(f'removing routine for {routine.filepath}') self.update_select_all_checkstate() def on_model_item_changed(self, item): @@ -237,7 +243,7 @@ def on_treeView_context_menu_requested(self, point): menu.addAction(self.action_set_selected_inactive) menu.addAction(self.action_restart_selected) menu.addAction(self.action_remove_selected) - menu.exec_(QtGui.QCursor.pos()) + menu.exec(QtGui.QCursor.pos()) def on_set_selected_triggered(self, active): selected_indexes = self.ui.treeView.selectedIndexes() @@ -423,8 +429,12 @@ def __init__(self, app, filepath, model, output_box_port, checked=QtCore.Qt.Chec self.error = False self.done = False - + + self.logger = logging.getLogger(f'lyse.AnalysisRoutine.{self.shortname}') + + self.logger.info('starting worker') self.to_worker, self.from_worker, self.worker = self.start_worker() + self.logger.info('analysis_subprocess started') # Make a row to put into the model: active_item = QtGui.QStandardItem() diff --git a/lyse/user_interface/filebox.ui b/lyse/user_interface/filebox.ui index d933201..93ecc98 100644 --- a/lyse/user_interface/filebox.ui +++ b/lyse/user_interface/filebox.ui @@ -14,71 +14,7 @@ Form - QPushButton { - border: none; - padding: 4px; - } - -QPushButton:hover { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; - border-radius: 3px; - } - - QPushButton:pressed { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QPushButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QPushButton:hover:checked { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QToolButton { - border: none; - padding: 2px; - } - -QToolButton:hover { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; - border-radius: 3px; - } - - QToolButton:pressed { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QToolButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QToolButton:hover:checked { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QGroupBox + QGroupBox { font-weight: bold; } diff --git a/lyse/user_interface/main.ui b/lyse/user_interface/main.ui index d984168..606ce4f 100644 --- a/lyse/user_interface/main.ui +++ b/lyse/user_interface/main.ui @@ -32,28 +32,28 @@ QPushButton:hover { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; + stop: 0 palette(light), stop: 1 palette(window)); + border: 1px solid palette(dark); border-radius: 3px; } QPushButton:pressed { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } QPushButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; + background-color: palette(window); + border: 1px solid palette(dark); border-radius: 3px; } QPushButton:hover:checked { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } @@ -64,28 +64,28 @@ QToolButton { QToolButton:hover { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; + stop: 0 palette(light), stop: 1 palette(window)); + border: 1px solid palette(dark); border-radius: 3px; } QToolButton:pressed { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } QToolButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; + background-color: palette(window); + border: 1px solid palette(dark); border-radius: 3px; } QToolButton:hover:checked { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } diff --git a/lyse/user_interface/plot_window.ui b/lyse/user_interface/plot_window.ui index 77ee24c..5b9f9c4 100644 --- a/lyse/user_interface/plot_window.ui +++ b/lyse/user_interface/plot_window.ui @@ -25,28 +25,28 @@ QPushButton:hover { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; + stop: 0 palette(light), stop: 1 palette(window)); + border: 1px solid palette(dark); border-radius: 3px; } QPushButton:pressed { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } QPushButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; + background-color: palette(window); + border: 1px solid palette(dark); border-radius: 3px; } QPushButton:hover:checked { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } @@ -57,28 +57,28 @@ QToolButton { QToolButton:hover { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; + stop: 0 palette(light), stop: 1 palette(window)); + border: 1px solid palette(dark); border-radius: 3px; } QToolButton:pressed { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } QToolButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; + background-color: palette(window); + border: 1px solid palette(dark); border-radius: 3px; } QToolButton:hover:checked { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; + stop: 0 palette(window), stop: 1 palette(light)); + border: 1px solid palette(dark); border-radius: 3px; } diff --git a/lyse/user_interface/routinebox.ui b/lyse/user_interface/routinebox.ui index 7f046f3..f147bb9 100644 --- a/lyse/user_interface/routinebox.ui +++ b/lyse/user_interface/routinebox.ui @@ -14,71 +14,7 @@ Form - QPushButton { - border: none; - padding: 4px; - } - -QPushButton:hover { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; - border-radius: 3px; - } - - QPushButton:pressed { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QPushButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QPushButton:hover:checked { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QToolButton { - border: none; - padding: 2px; - } - -QToolButton:hover { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #f6f7fa, stop: 1 #dadbde); - border: 1px solid #8f8f91; - border-radius: 3px; - } - - QToolButton:pressed { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QToolButton:checked { - background-color: #dadbde; - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QToolButton:hover:checked { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dadbde, stop: 1 #f6f7fa); - border: 1px solid #8f8f91; - border-radius: 3px; - } - -QGroupBox + QGroupBox { font-weight: bold; } diff --git a/lyse/utils/gui.py b/lyse/utils/gui.py index c52cc7b..2c66041 100644 --- a/lyse/utils/gui.py +++ b/lyse/utils/gui.py @@ -103,8 +103,23 @@ def get_screen_geometry(qapplication): """Return the a list of the geometries of each screen: each a tuple of left, top, width and height""" geoms = [] - desktop = qapplication.desktop() - for i in range(desktop.screenCount()): - sg = desktop.screenGeometry(i) - geoms.append((sg.left(), sg.top(), sg.width(), sg.height())) - return geoms \ No newline at end of file + screens = qapplication.screens() + for screen in screens: + sg = screen.geometry() + geoms.append((sg.x(), sg.y(), sg.width(), sg.height())) + return geoms + +def get_check_state(qcheckbox): + """Return check state of a QCheckBox as an integer + + Shim to handle differences between PyQt5 and PySide6 enum implementations + """ + + try: + # PySide6 uses proper python Enum, access by value attribute + return qcheckbox.checkState().value + except AttributeError: + # PyQt5 uses custom float class, cast to int directly + return int(qcheckbox.checkState()) + except Exception as e: + raise RuntimeError('Input is not a recognized QCheckBox type') from e \ No newline at end of file diff --git a/lyse/utils/tempfile2clipboard.py b/lyse/utils/tempfile2clipboard.py index 224f112..e50bc4b 100644 --- a/lyse/utils/tempfile2clipboard.py +++ b/lyse/utils/tempfile2clipboard.py @@ -65,7 +65,7 @@ def main(): # Keep running until the clipboard contents change to something else: app.clipboard().dataChanged.connect(app.quit) - app.exec_() + app.exec() if __name__ == '__main__': diff --git a/pyproject.toml b/pyproject.toml index 9a27b1d..79be505 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ "matplotlib", "numpy", "pandas>=1.5", - "qtutils>=2.2.2", + "qtutils>=4.0.0", "scipy", "tzlocal", "zprocess>=2.2.2",