Skip to content

Commit b7ea3f4

Browse files
authored
Merge pull request #129 from dihm/pyside6
Pyside6 compatibility
2 parents 831f64e + 6d7614a commit b7ea3f4

11 files changed

Lines changed: 125 additions & 182 deletions

File tree

lyse/__main__.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""
1515
import os
1616
import labscript_utils.excepthook
17+
import importlib.metadata
1718

1819
# Associate app windows with OS menu shortcuts, must be before any GUI calls, apparently
1920
import desktop_app
@@ -49,9 +50,11 @@
4950

5051
# qt imports
5152
splash.update_text('importing qt modules')
52-
from qtutils.qt import QtCore, QtWidgets
53+
from qtutils.qt import QtCore, QtWidgets, QT_ENV
5354
from qtutils.qt.QtCore import pyqtSignal as Signal
5455
from qtutils import UiLoader
56+
QT_VERSION_STR = QtCore.qVersion()
57+
PYQT_VERSION_STR = importlib.metadata.version(QT_ENV)
5558

5659
# needs to be present so that qtutils icons referenced in .ui files can be resolved. Since this is
5760
# magical is should not be implemented in this way.
@@ -96,6 +99,17 @@ def paintEvent(self, event):
9699
self._previously_painted = True
97100
self.firstPaint.emit()
98101
return result
102+
103+
def changeEvent(self, event):
104+
105+
# theme update only for PySide6/PyQt6
106+
if QT_ENV.endswith('6') and event.type() == QtCore.QEvent.Type.ThemeChange:
107+
for widget in self.findChildren(QtWidgets.QWidget):
108+
# Complex widgets, like TreeView and TableView require triggering styleSheet and palette updates
109+
widget.setStyleSheet(widget.styleSheet())
110+
widget.setPalette(widget.palette())
111+
112+
return super().changeEvent(event)
99113

100114
class Lyse(object):
101115

@@ -104,6 +118,9 @@ def __init__(self, qapplication):
104118
self.logger = setup_logging('lyse')
105119
labscript_utils.excepthook.set_logger(self.logger)
106120
self.logger.info('\n\n===============starting===============\n')
121+
self.logger.info(f'Qt Environment: {QT_ENV}')
122+
self.logger.info(f'PySide/PyQt version: {PYQT_VERSION_STR}')
123+
self.logger.info(f'Qt version: {QT_VERSION_STR}')
107124

108125
# Second: read lyse config
109126
self.setup_config()
@@ -115,11 +132,13 @@ def __init__(self, qapplication):
115132
# Forth: start remote communication server
116133
self.port = int(self.exp_config.get('ports', 'lyse'))
117134
self.server = lyse.communication.WebServer(self, self.port)
135+
self.logger.info(f'Started lyse server on port {self.port}')
118136

119137
# Last: UI setup
120138
self.qapplication = qapplication
121139
loader = UiLoader()
122140
self.ui = loader.load(os.path.join(lyse.utils.LYSE_DIR, 'user_interface/main.ui'), LyseMainWindow(self))
141+
self.logger.info('UI loaded')
123142

124143
self.connect_signals()
125144

@@ -138,6 +157,7 @@ def __init__(self, qapplication):
138157
self, to_multishot, from_multishot, self.output_box.port, multishot=True)
139158
self.filebox = lyse.filebox.FileBox(self, self.ui.verticalLayout_filebox, self.exp_config,
140159
to_singleshot, from_singleshot, to_multishot, from_multishot)
160+
self.logger.info('Boxes loaded')
141161

142162
self.last_save_config_file = None
143163
self.last_save_data = None
@@ -188,6 +208,7 @@ def load_the_config_file():
188208
# Success - skip loading window geometry in load_configuration:
189209
restore_window_geometry = False
190210
self.ui.firstPaint.connect(lambda: QtCore.QTimer.singleShot(50, load_the_config_file))
211+
self.logger.info('lyse configuration loaded')
191212

192213
self.ui.show()
193214

@@ -284,12 +305,12 @@ def get_save_data(self):
284305

285306
box = self.singleshot_routinebox
286307
save_data['singleshot'] = list(zip([routine.filepath for routine in box.routines],
287-
[box.model.item(row, box.COL_ACTIVE).checkState()
308+
[lyse.utils.gui.get_check_state(box.model.item(row, box.COL_ACTIVE))
288309
for row in range(box.model.rowCount())]))
289310
save_data['lastsingleshotfolder'] = box.last_opened_routine_folder
290311
box = self.multishot_routinebox
291312
save_data['multishot'] = list(zip([routine.filepath for routine in box.routines],
292-
[box.model.item(row, box.COL_ACTIVE).checkState()
313+
[lyse.utils.gui.get_check_state(box.model.item(row, box.COL_ACTIVE))
293314
for row in range(box.model.rowCount())]))
294315
save_data['lastmultishotfolder'] = box.last_opened_routine_folder
295316

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

536+
if QT_ENV.endswith('6'):
537+
extra_styles = """
538+
QTreeView:item:selected { color: palette(highlighted-text); }
539+
540+
QTreeView:item:hover { color: palette(highlighted-text); }
541+
542+
QTableView:item:selected { color: palette(highlighted-text); }
543+
544+
QTableView:item:hover { color: palette(highlighted-text); }
545+
"""
546+
547+
current_style = qapplication.styleSheet()
548+
qapplication.setStyleSheet(current_style + extra_styles)
549+
515550
app = Lyse(qapplication)
516551

517552
# Let the interpreter run every 500ms so it sees Ctrl-C interrupts:
@@ -522,7 +557,7 @@ def delete_items(self, confirm):
522557
signal.signal(signal.SIGINT, lambda *args: qapplication.exit())
523558

524559
splash.hide()
525-
qapplication.exec_()
560+
qapplication.exec()
526561

527562
# Shutdown the webserver.
528563
app.server.shutdown()

lyse/analysis_subprocess.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import time
2626
from types import ModuleType
2727

28-
from qtutils.qt import QtCore, QtGui, QtWidgets
28+
from qtutils.qt import QtCore, QtGui, QtWidgets, QT_ENV
2929
from qtutils.qt.QtCore import pyqtSignal as Signal
3030
from qtutils.qt.QtCore import QSettings, QByteArray
3131

@@ -102,6 +102,17 @@ def restore_geometry(self):
102102
geometry = self.settings.value(f"windowGeometry-{self.identifier:d}", QByteArray())
103103
if isinstance(geometry, QByteArray) and not geometry.isEmpty():
104104
self.restoreGeometry(geometry)
105+
106+
def changeEvent(self, event):
107+
108+
# theme update only for PySide6/PyQt6
109+
if QT_ENV.endswith('6') and event.type() == QtCore.QEvent.Type.ThemeChange:
110+
for widget in self.findChildren(QtWidgets.QWidget):
111+
# Complex widgets, like TreeView and TableView require triggering styleSheet and palette updates
112+
widget.setStyleSheet(widget.styleSheet())
113+
widget.setPalette(widget.palette())
114+
115+
return super().changeEvent(event)
105116

106117

107118
class Plot(object):
@@ -510,5 +521,5 @@ def reset_figs(self):
510521
if qapplication is None:
511522
qapplication = QtWidgets.QApplication(sys.argv)
512523
worker = AnalysisWorker(filepath, to_parent, from_parent)
513-
qapplication.exec_()
524+
qapplication.exec()
514525

lyse/filebox.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def on_treeView_context_menu_requested(self, point):
142142
menu = QtWidgets.QMenu(self.ui)
143143
menu.addAction(self.action_set_selected_visible)
144144
menu.addAction(self.action_set_selected_hidden)
145-
menu.exec_(QtGui.QCursor.pos())
145+
menu.exec(QtGui.QCursor.pos())
146146

147147
def on_set_selected_triggered(self, visible):
148148
selected_indexes = self.ui.treeView.selectedIndexes()
@@ -373,7 +373,7 @@ def mark_selection_not_done(self):
373373
def on_view_context_menu_requested(self, point):
374374
menu = QtWidgets.QMenu(self._view)
375375
menu.addAction(self.action_remove_selected)
376-
menu.exec_(QtGui.QCursor.pos())
376+
menu.exec(QtGui.QCursor.pos())
377377

378378
def on_double_click(self, index):
379379
filepath_item = self._model.item(index.row(), self.COL_FILEPATH)

lyse/routines.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ def __init__(self, app, container, exp_config, filebox, from_filebox, to_filebox
5454
self.to_filebox = to_filebox
5555
self.output_box_port = output_box_port
5656

57-
self.logger = logging.getLogger('lyse.RoutineBox.%s'%('multishot' if multishot else 'singleshot'))
57+
self.logger = logging.getLogger('lyse.RoutineBox.%s'%('multishot' if multishot else 'singleshot'))
58+
self.logger.info('starting')
5859

5960
loader = UiLoader()
6061
loader.registerCustomWidget(lyse.widgets.TreeView)
6162
self.ui = loader.load(os.path.join(lyse.utils.LYSE_DIR, 'user_interface/routinebox.ui'))
6263
container.addWidget(self.ui)
64+
self.logger.info('UI loaded')
6365

6466
if multishot:
6567
self.ui.groupBox.setTitle('Multishot routines')
@@ -110,6 +112,7 @@ def __init__(self, app, container, exp_config, filebox, from_filebox, to_filebox
110112

111113
self.connect_signals()
112114

115+
self.logger.info('starting analysis loop')
113116
self.analysis = threading.Thread(target = self.analysis_loop)
114117
self.analysis.daemon = True
115118
self.analysis.start()
@@ -170,7 +173,9 @@ def add_routines(self, routine_files, clear_existing=False):
170173
if filepath in [routine.filepath for routine in self.routines]:
171174
self.app.output_box.output('Warning: Ignoring duplicate analysis routine %s\n'%filepath, red=True)
172175
continue
173-
routine = AnalysisRoutine(self.app, filepath, self.model, self.output_box_port, checked)
176+
self.logger.info(f'adding routine for {filepath}')
177+
routine = AnalysisRoutine(self.app, filepath, self.model, self.output_box_port,
178+
QtCore.Qt.CheckState(checked))
174179
self.routines.append(routine)
175180
self.update_select_all_checkstate()
176181

@@ -215,6 +220,7 @@ def remove_selection(self, confirm=True):
215220
if routine.filepath in filepaths:
216221
routine.remove()
217222
self.routines.remove(routine)
223+
self.logger.info(f'removing routine for {routine.filepath}')
218224
self.update_select_all_checkstate()
219225

220226
def on_model_item_changed(self, item):
@@ -237,7 +243,7 @@ def on_treeView_context_menu_requested(self, point):
237243
menu.addAction(self.action_set_selected_inactive)
238244
menu.addAction(self.action_restart_selected)
239245
menu.addAction(self.action_remove_selected)
240-
menu.exec_(QtGui.QCursor.pos())
246+
menu.exec(QtGui.QCursor.pos())
241247

242248
def on_set_selected_triggered(self, active):
243249
selected_indexes = self.ui.treeView.selectedIndexes()
@@ -423,8 +429,12 @@ def __init__(self, app, filepath, model, output_box_port, checked=QtCore.Qt.Chec
423429

424430
self.error = False
425431
self.done = False
426-
432+
433+
self.logger = logging.getLogger(f'lyse.AnalysisRoutine.{self.shortname}')
434+
435+
self.logger.info('starting worker')
427436
self.to_worker, self.from_worker, self.worker = self.start_worker()
437+
self.logger.info('analysis_subprocess started')
428438

429439
# Make a row to put into the model:
430440
active_item = QtGui.QStandardItem()

lyse/user_interface/filebox.ui

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -14,71 +14,7 @@
1414
<string>Form</string>
1515
</property>
1616
<property name="styleSheet">
17-
<string notr="true">QPushButton {
18-
border: none;
19-
padding: 4px;
20-
}
21-
22-
QPushButton:hover {
23-
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
24-
stop: 0 #f6f7fa, stop: 1 #dadbde);
25-
border: 1px solid #8f8f91;
26-
border-radius: 3px;
27-
}
28-
29-
QPushButton:pressed {
30-
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
31-
stop: 0 #dadbde, stop: 1 #f6f7fa);
32-
border: 1px solid #8f8f91;
33-
border-radius: 3px;
34-
}
35-
36-
QPushButton:checked {
37-
background-color: #dadbde;
38-
border: 1px solid #8f8f91;
39-
border-radius: 3px;
40-
}
41-
42-
QPushButton:hover:checked {
43-
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
44-
stop: 0 #dadbde, stop: 1 #f6f7fa);
45-
border: 1px solid #8f8f91;
46-
border-radius: 3px;
47-
}
48-
49-
QToolButton {
50-
border: none;
51-
padding: 2px;
52-
}
53-
54-
QToolButton:hover {
55-
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
56-
stop: 0 #f6f7fa, stop: 1 #dadbde);
57-
border: 1px solid #8f8f91;
58-
border-radius: 3px;
59-
}
60-
61-
QToolButton:pressed {
62-
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
63-
stop: 0 #dadbde, stop: 1 #f6f7fa);
64-
border: 1px solid #8f8f91;
65-
border-radius: 3px;
66-
}
67-
68-
QToolButton:checked {
69-
background-color: #dadbde;
70-
border: 1px solid #8f8f91;
71-
border-radius: 3px;
72-
}
73-
74-
QToolButton:hover:checked {
75-
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
76-
stop: 0 #dadbde, stop: 1 #f6f7fa);
77-
border: 1px solid #8f8f91;
78-
border-radius: 3px;
79-
}
80-
81-
QGroupBox
17+
<string notr="true">QGroupBox
8218
{
8319
font-weight: bold;
8420
}</string>

lyse/user_interface/main.ui

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,28 @@
3232

3333
QPushButton:hover {
3434
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
35-
stop: 0 #f6f7fa, stop: 1 #dadbde);
36-
border: 1px solid #8f8f91;
35+
stop: 0 palette(light), stop: 1 palette(window));
36+
border: 1px solid palette(dark);
3737
border-radius: 3px;
3838
}
3939

4040
QPushButton:pressed {
4141
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
42-
stop: 0 #dadbde, stop: 1 #f6f7fa);
43-
border: 1px solid #8f8f91;
42+
stop: 0 palette(window), stop: 1 palette(light));
43+
border: 1px solid palette(dark);
4444
border-radius: 3px;
4545
}
4646

4747
QPushButton:checked {
48-
background-color: #dadbde;
49-
border: 1px solid #8f8f91;
48+
background-color: palette(window);
49+
border: 1px solid palette(dark);
5050
border-radius: 3px;
5151
}
5252

5353
QPushButton:hover:checked {
5454
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
55-
stop: 0 #dadbde, stop: 1 #f6f7fa);
56-
border: 1px solid #8f8f91;
55+
stop: 0 palette(window), stop: 1 palette(light));
56+
border: 1px solid palette(dark);
5757
border-radius: 3px;
5858
}
5959

@@ -64,28 +64,28 @@ QToolButton {
6464

6565
QToolButton:hover {
6666
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
67-
stop: 0 #f6f7fa, stop: 1 #dadbde);
68-
border: 1px solid #8f8f91;
67+
stop: 0 palette(light), stop: 1 palette(window));
68+
border: 1px solid palette(dark);
6969
border-radius: 3px;
7070
}
7171

7272
QToolButton:pressed {
7373
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
74-
stop: 0 #dadbde, stop: 1 #f6f7fa);
75-
border: 1px solid #8f8f91;
74+
stop: 0 palette(window), stop: 1 palette(light));
75+
border: 1px solid palette(dark);
7676
border-radius: 3px;
7777
}
7878

7979
QToolButton:checked {
80-
background-color: #dadbde;
81-
border: 1px solid #8f8f91;
80+
background-color: palette(window);
81+
border: 1px solid palette(dark);
8282
border-radius: 3px;
8383
}
8484

8585
QToolButton:hover:checked {
8686
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
87-
stop: 0 #dadbde, stop: 1 #f6f7fa);
88-
border: 1px solid #8f8f91;
87+
stop: 0 palette(window), stop: 1 palette(light));
88+
border: 1px solid palette(dark);
8989
border-radius: 3px;
9090
}
9191
</string>

0 commit comments

Comments
 (0)