mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat: added key bindings and help dialog
This commit is contained in:
@ -1,22 +1,26 @@
|
|||||||
import csv
|
import json
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
import h5py
|
import h5py
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import zmq
|
import zmq
|
||||||
from PyQt5.QtCore import pyqtSignal, Qt
|
from PyQt5.QtCore import pyqtSignal
|
||||||
from PyQt5.QtCore import pyqtSlot
|
from PyQt5.QtCore import pyqtSlot
|
||||||
|
from PyQt5.QtGui import QKeySequence
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget,
|
QWidget,
|
||||||
QFileDialog,
|
QFileDialog,
|
||||||
|
QShortcut,
|
||||||
|
QDialog,
|
||||||
|
QVBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QFrame,
|
||||||
)
|
)
|
||||||
from pyqtgraph.Qt import QtWidgets, uic
|
from pyqtgraph.Qt import uic
|
||||||
|
|
||||||
from scipy.stats import multivariate_normal
|
from scipy.stats import multivariate_normal
|
||||||
|
|
||||||
|
|
||||||
@ -30,6 +34,9 @@ class EigerPlot(QWidget):
|
|||||||
current_path = os.path.dirname(__file__)
|
current_path = os.path.dirname(__file__)
|
||||||
uic.loadUi(os.path.join(current_path, "eiger_plot.ui"), self)
|
uic.loadUi(os.path.join(current_path, "eiger_plot.ui"), self)
|
||||||
|
|
||||||
|
# Set widow name
|
||||||
|
self.setWindowTitle("Eiger Plot")
|
||||||
|
|
||||||
self.hist_lims = None
|
self.hist_lims = None
|
||||||
self.mask = None
|
self.mask = None
|
||||||
self.image = None
|
self.image = None
|
||||||
@ -37,6 +44,7 @@ class EigerPlot(QWidget):
|
|||||||
# UI
|
# UI
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
self.hook_signals()
|
self.hook_signals()
|
||||||
|
self.key_bindings()
|
||||||
|
|
||||||
# ZMQ Consumer
|
# ZMQ Consumer
|
||||||
self.start_zmq_consumer()
|
self.start_zmq_consumer()
|
||||||
@ -63,6 +71,7 @@ class EigerPlot(QWidget):
|
|||||||
self.pushButton_test.clicked.connect(self.start_sim_stream)
|
self.pushButton_test.clicked.connect(self.start_sim_stream)
|
||||||
self.pushButton_mask.clicked.connect(self.load_mask_dialog)
|
self.pushButton_mask.clicked.connect(self.load_mask_dialog)
|
||||||
self.pushButton_delete_mask.clicked.connect(self.delete_mask)
|
self.pushButton_delete_mask.clicked.connect(self.delete_mask)
|
||||||
|
self.pushButton_help.clicked.connect(self.show_help_dialog)
|
||||||
|
|
||||||
# SpinBoxes
|
# SpinBoxes
|
||||||
self.doubleSpinBox_hist_min.valueChanged.connect(self.update_hist)
|
self.doubleSpinBox_hist_min.valueChanged.connect(self.update_hist)
|
||||||
@ -71,6 +80,47 @@ class EigerPlot(QWidget):
|
|||||||
# Signal/Slots
|
# Signal/Slots
|
||||||
self.update_signal.connect(self.on_image_update)
|
self.update_signal.connect(self.on_image_update)
|
||||||
|
|
||||||
|
def key_bindings(self):
|
||||||
|
# Key bindings for rotation
|
||||||
|
rotate_plus = QShortcut(QKeySequence("Ctrl+A"), self)
|
||||||
|
rotate_minus = QShortcut(QKeySequence("Ctrl+Z"), self)
|
||||||
|
self.comboBox_rotation.setToolTip("Increase rotation: Ctrl+A\nDecrease rotation: Ctrl+Z")
|
||||||
|
self.checkBox_transpose.setToolTip("Toggle transpose: Ctrl+T")
|
||||||
|
|
||||||
|
max_index = self.comboBox_rotation.count() - 1 # Maximum valid index
|
||||||
|
|
||||||
|
rotate_plus.activated.connect(
|
||||||
|
lambda: self.comboBox_rotation.setCurrentIndex(
|
||||||
|
min(self.comboBox_rotation.currentIndex() + 1, max_index)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rotate_minus.activated.connect(
|
||||||
|
lambda: self.comboBox_rotation.setCurrentIndex(
|
||||||
|
max(self.comboBox_rotation.currentIndex() - 1, 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Key bindings for transpose
|
||||||
|
transpose = QShortcut(QKeySequence("Ctrl+T"), self)
|
||||||
|
transpose.activated.connect(self.checkBox_transpose.toggle)
|
||||||
|
|
||||||
|
FFT = QShortcut(QKeySequence("Ctrl+F"), self)
|
||||||
|
FFT.activated.connect(self.checkBox_FFT.toggle)
|
||||||
|
self.checkBox_FFT.setToolTip("Toggle FFT: Ctrl+F")
|
||||||
|
|
||||||
|
log = QShortcut(QKeySequence("Ctrl+L"), self)
|
||||||
|
log.activated.connect(self.checkBox_log.toggle)
|
||||||
|
self.checkBox_log.setToolTip("Toggle log: Ctrl+L")
|
||||||
|
|
||||||
|
mask = QShortcut(QKeySequence("Ctrl+M"), self)
|
||||||
|
mask.activated.connect(self.pushButton_mask.click)
|
||||||
|
self.pushButton_mask.setToolTip("Load mask: Ctrl+M")
|
||||||
|
|
||||||
|
delete_mask = QShortcut(QKeySequence("Ctrl+D"), self)
|
||||||
|
delete_mask.activated.connect(self.pushButton_delete_mask.click)
|
||||||
|
self.pushButton_delete_mask.setToolTip("Delete mask: Ctrl+D")
|
||||||
|
|
||||||
def update_hist(self):
|
def update_hist(self):
|
||||||
self.hist_levels = [
|
self.hist_levels = [
|
||||||
self.doubleSpinBox_hist_min.value(),
|
self.doubleSpinBox_hist_min.value(),
|
||||||
@ -92,11 +142,19 @@ class EigerPlot(QWidget):
|
|||||||
self.load_mask(file_name)
|
self.load_mask(file_name)
|
||||||
|
|
||||||
def load_mask(self, path):
|
def load_mask(self, path):
|
||||||
with h5py.File(path, "r") as f:
|
try:
|
||||||
self.mask = f["data"][...]
|
with h5py.File(path, "r") as f:
|
||||||
|
self.mask = f["data"][...]
|
||||||
|
if self.mask is not None:
|
||||||
|
# Set label to mask name without path
|
||||||
|
self.label_mask.setText(os.path.basename(path))
|
||||||
|
except KeyError as e:
|
||||||
|
# Update GUI with the error message
|
||||||
|
print(f"Error: {str(e)}")
|
||||||
|
|
||||||
def delete_mask(self):
|
def delete_mask(self):
|
||||||
self.mask = None
|
self.mask = None
|
||||||
|
self.label_mask.setText("No Mask")
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_image_update(self):
|
def on_image_update(self):
|
||||||
@ -144,6 +202,63 @@ class EigerPlot(QWidget):
|
|||||||
receiver.disconnect(live_stream_url)
|
receiver.disconnect(live_stream_url)
|
||||||
receiver.context.term()
|
receiver.context.term()
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# just simulations from here
|
||||||
|
###############################
|
||||||
|
|
||||||
|
def show_help_dialog(self):
|
||||||
|
dialog = QDialog(self)
|
||||||
|
dialog.setWindowTitle("Help")
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Key bindings section
|
||||||
|
layout.addWidget(QLabel("Keyboard Shortcuts:"))
|
||||||
|
|
||||||
|
key_bindings = [
|
||||||
|
("Ctrl+A", "Increase rotation"),
|
||||||
|
("Ctrl+Z", "Decrease rotation"),
|
||||||
|
("Ctrl+T", "Toggle transpose"),
|
||||||
|
("Ctrl+F", "Toggle FFT"),
|
||||||
|
("Ctrl+L", "Toggle log scale"),
|
||||||
|
("Ctrl+M", "Load mask"),
|
||||||
|
("Ctrl+D", "Delete mask"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for keys, action in key_bindings:
|
||||||
|
layout.addWidget(QLabel(f"{keys} - {action}"))
|
||||||
|
|
||||||
|
# Separator
|
||||||
|
separator = QFrame()
|
||||||
|
separator.setFrameShape(QFrame.HLine)
|
||||||
|
separator.setFrameShadow(QFrame.Sunken)
|
||||||
|
layout.addWidget(separator)
|
||||||
|
|
||||||
|
# Histogram section
|
||||||
|
layout.addWidget(QLabel("Histogram:"))
|
||||||
|
layout.addWidget(
|
||||||
|
QLabel(
|
||||||
|
"Use the Double Spin Boxes to adjust the minimum and maximum values of the histogram."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Another Separator
|
||||||
|
another_separator = QFrame()
|
||||||
|
another_separator.setFrameShape(QFrame.HLine)
|
||||||
|
another_separator.setFrameShadow(QFrame.Sunken)
|
||||||
|
layout.addWidget(another_separator)
|
||||||
|
|
||||||
|
# Mask section
|
||||||
|
layout.addWidget(QLabel("Mask:"))
|
||||||
|
layout.addWidget(
|
||||||
|
QLabel(
|
||||||
|
"Use 'Load Mask' to load a mask from an H5 file. 'Delete Mask' removes the current mask."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
dialog.setLayout(layout)
|
||||||
|
dialog.exec_()
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# just simulations from here
|
# just simulations from here
|
||||||
###############################
|
###############################
|
||||||
|
@ -150,10 +150,36 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pushButton_test">
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>stream test</string>
|
<string>Help</string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_mask">
|
||||||
|
<property name="text">
|
||||||
|
<string>No Mask</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_help">
|
||||||
|
<property name="text">
|
||||||
|
<string>Help</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_test">
|
||||||
|
<property name="text">
|
||||||
|
<string>Simulated Stream</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -10,6 +10,5 @@ plot_motors:
|
|||||||
extra_columns:
|
extra_columns:
|
||||||
- sample name: "sample 1"
|
- sample name: "sample 1"
|
||||||
- Step [x]: 1
|
- Step [x]: 1
|
||||||
# - Step [y]: 1
|
- Step [y]: 1
|
||||||
# - Exposure time [s]: 1
|
- Exposure time [s]: 1
|
||||||
- Temperature [K]: 270
|
|
Reference in New Issue
Block a user