243 lines
8.4 KiB
Python
243 lines
8.4 KiB
Python
import logging
|
|
from typing import List, Tuple, Dict
|
|
from uuid import uuid1
|
|
|
|
from PyQt6.QtCore import Qt
|
|
from PyQt6.QtGui import QDoubleValidator
|
|
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QWidget, QCheckBox, QButtonGroup, \
|
|
QRadioButton, QSplitter, QLineEdit, QGroupBox
|
|
|
|
from detektor_region import DetektorRegion, DetektorRegionState
|
|
from detektor_data import DetektorContainer
|
|
from widgets import RoundedColorRectangleWidget
|
|
|
|
|
|
class ChannelCalibrationDialog(QDialog):
|
|
|
|
_only_region = None
|
|
|
|
_radio_map: Dict[uuid1, QRadioButton] = {}
|
|
|
|
def __init__(self, region: DetektorRegion):
|
|
super().__init__()
|
|
self.region = region
|
|
|
|
self.setWindowTitle("Kalibrace kanálu")
|
|
self.setModal(True) # Set the dialog as modal (blocks main window)
|
|
self.resize(300, 150)
|
|
|
|
# Main layout
|
|
main_layout = QVBoxLayout()
|
|
self.setLayout(main_layout)
|
|
|
|
# Splitter for two columns
|
|
splitter = QSplitter()
|
|
splitter.setOrientation(Qt.Orientation.Horizontal) # Horizontal splitter for left/right columns
|
|
|
|
# Left GroupBox (Channels)
|
|
left_group = QGroupBox("Vyberte kanál")
|
|
left_layout = QVBoxLayout()
|
|
left_group.setLayout(left_layout)
|
|
|
|
# Right GroupBox (Settings)
|
|
right_group = QGroupBox("Nastavení kanálu")
|
|
right_layout = QVBoxLayout()
|
|
right_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
|
right_group.setLayout(right_layout)
|
|
|
|
splitter.addWidget(left_group)
|
|
splitter.addWidget(right_group)
|
|
|
|
splitter.setStretchFactor(0, 1) # 50%
|
|
splitter.setStretchFactor(1, 1) # 50%
|
|
|
|
main_layout.addWidget(splitter)
|
|
|
|
# Create a button group to ensure only one channel is active at a time
|
|
self.radio_group = QButtonGroup()
|
|
|
|
for ch in DetektorContainer().get().channels:
|
|
channel_layout = QHBoxLayout()
|
|
channel_layout.setContentsMargins(0, 0, 0, 0)
|
|
channel_widget = QWidget()
|
|
channel_widget.setLayout(channel_layout)
|
|
left_layout.addWidget(channel_widget)
|
|
|
|
channel_radio = QRadioButton(f"{ch.name}")
|
|
self._radio_map[ch.id] = channel_radio
|
|
|
|
self.radio_group.addButton(channel_radio) # Add to button group
|
|
channel_layout.addWidget(channel_radio)
|
|
|
|
channel_layout.addWidget(RoundedColorRectangleWidget(ch.color))
|
|
|
|
from PyQt6.QtCore import QLocale
|
|
float_validator = CustomDoubleValidator()
|
|
float_validator.setLocale(QLocale(QLocale.Language.C))
|
|
|
|
# Přičíst input
|
|
add_layout = QVBoxLayout()
|
|
add_label = QLabel("Přičíst hodnotu ke kanálu")
|
|
self.add_input = QLineEdit()
|
|
self.add_input.setValidator(float_validator)
|
|
self.add_input.setText('0')
|
|
add_layout.addWidget(add_label)
|
|
add_layout.addWidget(self.add_input)
|
|
|
|
# Vynásobit input
|
|
multiply_layout = QVBoxLayout()
|
|
multiply_label = QLabel("Vynásobit data kanálu hodnotou")
|
|
self.multiply_input = QLineEdit()
|
|
self.multiply_input.setValidator(float_validator)
|
|
self.multiply_input.setText('1')
|
|
multiply_layout.addWidget(multiply_label)
|
|
multiply_layout.addWidget(self.multiply_input)
|
|
|
|
only_region_checkbox = QCheckBox(f"Kalibrovat pouze výběr")
|
|
# initial state is taken from region state
|
|
self._only_region = (self.region.state != DetektorRegionState.UNSET)
|
|
only_region_checkbox.setEnabled(self._only_region)
|
|
only_region_checkbox.setChecked(self._only_region)
|
|
# on click, intert the property state
|
|
only_region_checkbox.stateChanged.connect(self.toggle_only_region)
|
|
|
|
|
|
# Add inputs to the right group box
|
|
right_layout.addLayout(add_layout)
|
|
right_layout.addLayout(multiply_layout)
|
|
right_layout.addWidget(only_region_checkbox)
|
|
|
|
# Buttons layout (aligned at the bottom, next to each other)
|
|
button_layout = QHBoxLayout()
|
|
|
|
calibrate_button = QPushButton("Kalibrovat")
|
|
calibrate_button.clicked.connect(self.accept) # Close the dialog when the button is clicked
|
|
button_layout.addWidget(calibrate_button)
|
|
|
|
cancel_button = QPushButton("Zrušit")
|
|
cancel_button.clicked.connect(self.close) # Close the dialog when the button is clicked
|
|
button_layout.addWidget(cancel_button)
|
|
|
|
# Add buttons to the main layout below both columns
|
|
main_layout.addLayout(button_layout)
|
|
|
|
# Show the dialog
|
|
self.exec()
|
|
|
|
def toggle_only_region(self):
|
|
self._only_region = not self._only_region
|
|
|
|
def accept(self):
|
|
for channel_id, radio in self._radio_map.items():
|
|
if radio.isChecked():
|
|
DetektorContainer().duplicate()
|
|
|
|
has_channel = False
|
|
for channel_id, radio in self._radio_map.items():
|
|
|
|
if radio.isChecked():
|
|
# get the current channel object of the current (duplicated dataset)
|
|
channel = DetektorContainer().get().get_channel_by_uuid(channel_id)
|
|
|
|
try:
|
|
# validate inputs
|
|
offset = float(self.add_input.text().replace(',', '.'))
|
|
multiple = float(self.multiply_input.text().replace(',', '.'))
|
|
except ValueError:
|
|
WrongDataDialog()
|
|
return
|
|
|
|
|
|
logging.debug(f'Calibrating channel {channel.name} +{offset} x{multiple}')
|
|
|
|
if self._only_region:
|
|
start, end = self.region.get_safe_region()
|
|
logging.debug(f'Calibrating only region {start} - {end}')
|
|
|
|
channel.calibrate_data(
|
|
offset=offset,
|
|
multiple=multiple,
|
|
start=start,
|
|
end=end
|
|
)
|
|
|
|
self.region.unset()
|
|
else:
|
|
channel.calibrate_data(
|
|
offset=offset,
|
|
multiple=multiple
|
|
)
|
|
|
|
has_channel = True
|
|
break
|
|
|
|
# if we got here, no channel has been selected
|
|
if not has_channel:
|
|
# TODO: revert the duplication, since nothing has changed
|
|
|
|
MissingChannelDialog()
|
|
else:
|
|
super().accept()
|
|
|
|
|
|
class MissingChannelDialog(QDialog):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.setWindowTitle("Vyberte kanál")
|
|
self.setModal(True) # Set the dialog as modal (blocks main window)
|
|
self.resize(400, 75)
|
|
|
|
# Main layout
|
|
main_layout = QVBoxLayout()
|
|
self.setLayout(main_layout)
|
|
label = QLabel("Vyberte kanál, na kterém chcete provést kalibraci")
|
|
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
main_layout.addWidget(label)
|
|
|
|
cancel_button = QPushButton("OK")
|
|
cancel_button.clicked.connect(self.close)
|
|
main_layout.addWidget(cancel_button)
|
|
|
|
# Show the dialog
|
|
self.exec()
|
|
|
|
|
|
class WrongDataDialog(QDialog):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.setWindowTitle("Špatná data")
|
|
self.setModal(True) # Set the dialog as modal (blocks main window)
|
|
self.resize(400, 75)
|
|
|
|
# Main layout
|
|
main_layout = QVBoxLayout()
|
|
self.setLayout(main_layout)
|
|
label = QLabel("Zadaná data nejsou platná čísla.")
|
|
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
main_layout.addWidget(label)
|
|
|
|
cancel_button = QPushButton("OK")
|
|
cancel_button.clicked.connect(self.close)
|
|
main_layout.addWidget(cancel_button)
|
|
|
|
# Show the dialog
|
|
self.exec()
|
|
|
|
class CustomDoubleValidator(QDoubleValidator):
|
|
|
|
def validate(self, input_str, pos):
|
|
# Accept both comma and dot as decimal separator for validation
|
|
if ',' in input_str:
|
|
# Allow comma as intermediate state so user can type it
|
|
test_str = input_str.replace(',', '.')
|
|
state, _, _ = super().validate(test_str, pos)
|
|
if state == QDoubleValidator.State.Acceptable:
|
|
return (QDoubleValidator.State.Intermediate, input_str, pos)
|
|
return (QDoubleValidator.State.Intermediate, input_str, pos)
|
|
return super().validate(input_str, pos)
|
|
|
|
def fixup(self, input_str):
|
|
# Actually replace comma with dot in the QLineEdit
|
|
return input_str.replace(',', '.') |