Skip to content
43 changes: 39 additions & 4 deletions lyse/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -96,6 +99,17 @@ def paintEvent(self, event):
self._previously_painted = True
self.firstPaint.emit()
return result

def changeEvent(self, event):

# theme update only for PySide6
if QT_ENV == 'PySide6' and event.type() == QtCore.QEvent.Type.ThemeChange:
for widget in self.findChildren(QtWidgets.QWidget):
# Complex widgets, like TreeView and TableView require triggering styleSheet and palette updates
widget.setStyleSheet(widget.styleSheet())
widget.setPalette(widget.palette())

return super().changeEvent(event)

class Lyse(object):

Expand All @@ -104,6 +118,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()
Expand All @@ -115,11 +132,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()

Expand All @@ -138,6 +157,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
Expand Down Expand Up @@ -188,6 +208,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()

Expand Down Expand Up @@ -284,12 +305,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

Expand Down Expand Up @@ -512,6 +533,20 @@ def delete_items(self, confirm):
qapplication = QtWidgets.QApplication(sys.argv)
qapplication.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus, False)

if QT_ENV == 'PySide6':
extra_styles = """
QTreeView:item:selected { color: palette(highlighted-text); }

QTreeView:item:hover { color: palette(highlighted-text); }

QTableView:item:selected { color: palette(highlighted-text); }

QTableView:item:hover { color: palette(highlighted-text); }
"""

current_style = qapplication.styleSheet()
qapplication.setStyleSheet(current_style + extra_styles)

app = Lyse(qapplication)

# Let the interpreter run every 500ms so it sees Ctrl-C interrupts:
Expand All @@ -522,7 +557,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()
15 changes: 13 additions & 2 deletions lyse/analysis_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import time
from types import ModuleType

from qtutils.qt import QtCore, QtGui, QtWidgets
from qtutils.qt import QtCore, QtGui, QtWidgets, QT_ENV
from qtutils.qt.QtCore import pyqtSignal as Signal

from qtutils import inmain, inmain_decorator, UiLoader
Expand Down Expand Up @@ -77,6 +77,17 @@ def closeEvent(self, event):
event.accept()
else:
event.ignore()

def changeEvent(self, event):

# theme update only for PySide6
if QT_ENV == 'PySide6' and event.type() == QtCore.QEvent.Type.ThemeChange:
for widget in self.findChildren(QtWidgets.QWidget):
# Complex widgets, like TreeView and TableView require triggering styleSheet and palette updates
widget.setStyleSheet(widget.styleSheet())
widget.setPalette(widget.palette())

return super().changeEvent(event)


class Plot(object):
Expand Down Expand Up @@ -475,5 +486,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()

4 changes: 2 additions & 2 deletions lyse/filebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 14 additions & 4 deletions lyse/routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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):
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
66 changes: 1 addition & 65 deletions lyse/user_interface/filebox.ui
Original file line number Diff line number Diff line change
Expand Up @@ -14,71 +14,7 @@
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">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
<string notr="true">QGroupBox
{
font-weight: bold;
}</string>
Expand Down
32 changes: 16 additions & 16 deletions lyse/user_interface/main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
}
</string>
Expand Down
Loading