Merge pull request #17 from nqrduck/3-display-multiple-measurements

3 display multiple measurements
This commit is contained in:
Julia P 2024-05-05 15:52:22 +02:00 committed by GitHub
commit 044b22393c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 225 additions and 12 deletions

View file

@ -1,5 +1,9 @@
# Changelog # Changelog
### Version 0.0.4 (05-05-2024)
- Added display of multiple measurements that can be displayed in th plot window.
- Now using the FileManager class instead of QFileManager
### Version 0.0.3 (26-04-2024) ### Version 0.0.3 (26-04-2024)
- Switched to new formbuilder. This should make implementation of signal processing methods more robust and easier. - Switched to new formbuilder. This should make implementation of signal processing methods more robust and easier.

View file

@ -7,7 +7,7 @@ allow-direct-references = true
[project] [project]
name = "nqrduck-measurement" name = "nqrduck-measurement"
version = "0.0.3" version = "0.0.4"
authors = [ authors = [
{ name="jupfi", email="support@nqrduck.cool" }, { name="jupfi", email="support@nqrduck.cool" },
] ]

View file

@ -230,3 +230,43 @@ class MeasurementController(ModuleController):
self.module.model.displayed_measurement = apodized_measurement self.module.model.displayed_measurement = apodized_measurement
self.module.model.add_measurement(apodized_measurement) self.module.model.add_measurement(apodized_measurement)
@pyqtSlot()
def change_displayed_measurement(self, measurement=None) -> None:
"""Change the displayed measurement."""
if not self.module.model.measurements:
logger.debug("No measurements to display.")
return
if not measurement:
index = self.module.view._ui_form.selectionBox.value()
self.module.model.displayed_measurement = self.module.model.measurements[
index
]
logger.debug(
f"Changing displayed measurement to {self.module.model.displayed_measurement.name}."
)
else:
logger.debug(f"Changing displayed measurement to {measurement.name}.")
self.module.model.displayed_measurement = measurement
@pyqtSlot(Measurement)
def delete_measurement(self, measurement: Measurement) -> None:
"""Delete a measurement.
The measurement is removed from the list of measurements. Then the displayed measurement is updated.
Args:
measurement (Measurement): The measurement to delete.
"""
logger.debug("Deleting measurement.")
self.module.model.remove_measurement(measurement)
if measurement == self.module.model.displayed_measurement:
if self.module.model.measurements:
self.module.model.displayed_measurement = (
self.module.model.measurements[-1]
)
else:
self.module.model.displayed_measurement = None

View file

@ -50,6 +50,7 @@ class MeasurementModel(ModuleModel):
displayed_measurement_changed = pyqtSignal(Measurement) displayed_measurement_changed = pyqtSignal(Measurement)
measurements_changed = pyqtSignal(list) measurements_changed = pyqtSignal(list)
view_mode_changed = pyqtSignal(str) view_mode_changed = pyqtSignal(str)
measurement_frequency_changed = pyqtSignal(float) measurement_frequency_changed = pyqtSignal(float)
@ -94,6 +95,14 @@ class MeasurementModel(ModuleModel):
def add_measurement(self, measurement: Measurement): def add_measurement(self, measurement: Measurement):
"""Add a measurement to the list of measurements.""" """Add a measurement to the list of measurements."""
self.measurements.append(measurement) self.measurements.append(measurement)
# Change the maximum value of the selectionBox.
self.measurements_changed.emit(self.measurements)
self.displayed_measurement_changed.emit(measurement)
def remove_measurement(self, measurement : Measurement):
"""Remove a measurement from the list of measurements."""
self.measurements.remove(measurement)
# Change the maximum value of the selectionBox.
self.measurements_changed.emit(self.measurements) self.measurements_changed.emit(self.measurements)
@property @property
@ -108,7 +117,12 @@ class MeasurementModel(ModuleModel):
@displayed_measurement.setter @displayed_measurement.setter
def displayed_measurement(self, value: Measurement): def displayed_measurement(self, value: Measurement):
self._displayed_measurement = value self._displayed_measurement = value
self.displayed_measurement_changed.emit(value) if value is not None:
logger.debug("Displayed measurement: " + value.name)
self.displayed_measurement_changed.emit(value)
else:
self.module.view.update_displayed_measurement()
@property @property
def measurement_frequency(self): def measurement_frequency(self):

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1920</width> <width>1807</width>
<height>1080</height> <height>1080</height>
</rect> </rect>
</property> </property>
@ -23,7 +23,7 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
<item> <item>
<layout class="QVBoxLayout" name="settingsLayout" stretch="0,0,0,0,0,0,0,0,0,0,0,0,0"> <layout class="QVBoxLayout" name="settingsLayout" stretch="0,0,0,0,0,0,0,0,0,0,0,0,0,0,0">
<item> <item>
<widget class="QLabel" name="titleLabel"> <widget class="QLabel" name="titleLabel">
<property name="font"> <property name="font">
@ -150,6 +150,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Measurements:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="measurementsList"/>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -184,7 +200,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="plotterLayout" stretch="0,1"> <layout class="QVBoxLayout" name="plotterLayout" stretch="0,0,1">
<item> <item>
<widget class="MplWidget" name="plotter" native="true"> <widget class="MplWidget" name="plotter" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
@ -195,6 +211,9 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4"/>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0,0"> <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0,0">
<item> <item>

View file

@ -2,7 +2,19 @@
import logging import logging
import numpy as np import numpy as np
from PyQt6.QtWidgets import QWidget, QDialog, QLabel, QVBoxLayout from functools import partial
from PyQt6.QtWidgets import (
QWidget,
QDialog,
QLabel,
QVBoxLayout,
QHBoxLayout,
QPushButton,
QListWidgetItem,
QSizePolicy,
QApplication,
)
from PyQt6.QtGui import QFontMetrics
from PyQt6.QtCore import pyqtSlot, Qt from PyQt6.QtCore import pyqtSlot, Qt
from nqrduck.module.module_view import ModuleView from nqrduck.module.module_view import ModuleView
from nqrduck.assets.icons import Logos from nqrduck.assets.icons import Logos
@ -50,6 +62,8 @@ class MeasurementView(ModuleView):
) )
self.module.model.view_mode_changed.connect(self.update_displayed_measurement) self.module.model.view_mode_changed.connect(self.update_displayed_measurement)
self.module.model.measurements_changed.connect(self.on_measurements_changed)
self._ui_form.buttonStart.clicked.connect( self._ui_form.buttonStart.clicked.connect(
self.on_measurement_start_button_clicked self.on_measurement_start_button_clicked
) )
@ -105,6 +119,11 @@ class MeasurementView(ModuleView):
self._ui_form.averagesEdit.set_min_value(1) self._ui_form.averagesEdit.set_min_value(1)
self._ui_form.averagesEdit.set_max_value(1e6) self._ui_form.averagesEdit.set_max_value(1e6)
# Connect selectionBox signal fors switching the displayed measurement
self._ui_form.selectionBox.valueChanged.connect(
self.module.controller.change_displayed_measurement
)
def init_plotter(self) -> None: def init_plotter(self) -> None:
"""Initialize plotter with the according units for time domain.""" """Initialize plotter with the according units for time domain."""
plotter = self._ui_form.plotter plotter = self._ui_form.plotter
@ -143,6 +162,18 @@ class MeasurementView(ModuleView):
plotter = self._ui_form.plotter plotter = self._ui_form.plotter
plotter.canvas.ax.clear() plotter.canvas.ax.clear()
try: try:
if self.module.model.displayed_measurement is None:
logger.debug("No measurement data to display. Clearing plotter.")
if self.module.model.view_mode == self.module.model.FFT_VIEW:
self.change_to_fft_view()
else:
self.change_to_time_view()
self._ui_form.plotter.canvas.draw()
return
if self.module.model.view_mode == self.module.model.FFT_VIEW: if self.module.model.view_mode == self.module.model.FFT_VIEW:
self.change_to_fft_view() self.change_to_fft_view()
y = self.module.model.displayed_measurement.fdy y = self.module.model.displayed_measurement.fdy
@ -173,6 +204,26 @@ class MeasurementView(ModuleView):
# Add legend # Add legend
self._ui_form.plotter.canvas.ax.legend() self._ui_form.plotter.canvas.ax.legend()
# Highlight the displayed measurement in the measurementsList
for i in range(self._ui_form.measurementsList.count()):
item = self._ui_form.measurementsList.item(i)
widget = self._ui_form.measurementsList.itemWidget(item)
button = widget.layout().itemAt(0).widget()
# Get the measurement by accessing measurement property
measurement = button.property("measurement")
if measurement == self.module.model.displayed_measurement:
item.setSelected(True)
else:
item.setSelected(False)
# Update the number of the selectionBox
for measurement in self.module.model.measurements:
if measurement.name == self.module.model.displayed_measurement.name:
self._ui_form.selectionBox.setValue(
self.module.model.measurements.index(measurement)
)
break
except AttributeError: except AttributeError:
logger.debug("No measurement data to display.") logger.debug("No measurement data to display.")
self._ui_form.plotter.canvas.draw() self._ui_form.plotter.canvas.draw()
@ -200,7 +251,7 @@ class MeasurementView(ModuleView):
"""Slot for when the measurement save button is clicked.""" """Slot for when the measurement save button is clicked."""
logger.debug("Measurement save button clicked.") logger.debug("Measurement save button clicked.")
file_manager = self.QFileManager( file_manager = self.FileManager(
self.module.model.FILE_EXTENSION, parent=self.widget self.module.model.FILE_EXTENSION, parent=self.widget
) )
file_name = file_manager.saveFileDialog() file_name = file_manager.saveFileDialog()
@ -212,13 +263,84 @@ class MeasurementView(ModuleView):
"""Slot for when the measurement load button is clicked.""" """Slot for when the measurement load button is clicked."""
logger.debug("Measurement load button clicked.") logger.debug("Measurement load button clicked.")
file_manager = self.QFileManager( file_manager = self.FileManager(
self.module.model.FILE_EXTENSION, parent=self.widget self.module.model.FILE_EXTENSION, parent=self.widget
) )
file_name = file_manager.loadFileDialog() file_name = file_manager.loadFileDialog()
if file_name: if file_name:
self.module.controller.load_measurement(file_name) self.module.controller.load_measurement(file_name)
@pyqtSlot()
def on_measurements_changed(self) -> None:
"""Slot for when a measurement is added."""
logger.debug("Measurement changed.")
if len(self.module.model.measurements) == 0:
self.module.view._ui_form.selectionBox.setMaximum(0)
self.module.view._ui_form.selectionBox.setValue(0)
else:
self.module.view._ui_form.selectionBox.setMaximum(
len(self.module.model.measurements) - 1
)
# Clear the measurements list
self._ui_form.measurementsList.clear()
for measurement in self.module.model.measurements:
measurement_widget = QWidget()
layout = QHBoxLayout()
measurement_widget.setLayout(layout)
delete_button = QPushButton()
delete_button.setIcon(Logos.Garbage12x12())
delete_button.setFixedWidth(delete_button.iconSize().width())
delete_button.clicked.connect(
lambda: self.module.controller.delete_measurement(measurement)
)
name_button = QPushButton()
name_button.clicked.connect(
partial(self.module.controller.change_displayed_measurement, measurement)
)
# Not sure if this is pretty
name_button.setProperty("measurement", measurement)
name_button.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred
) # Set size policy
layout.addWidget(name_button)
layout.addWidget(delete_button)
layout.addStretch() # Add stretch after delete button to ensure name button takes up space
item = QListWidgetItem()
item.setSizeHint(measurement_widget.sizeHint())
self._ui_form.measurementsList.addItem(item)
self._ui_form.measurementsList.setItemWidget(item, measurement_widget)
# Wait for the layout to be updated
QApplication.processEvents()
# Get the contents margins (left, top, right, bottom)
content_margins = layout.contentsMargins()
# Include the margins and spacing in the maxWidth calculation
maxWidth = (
self._ui_form.measurementsList.width()
- delete_button.width()
- content_margins.left()
- content_margins.right()
- layout.spacing()
)
fontMetrics = QFontMetrics(name_button.font())
elidedText = fontMetrics.elidedText(
measurement.name, Qt.TextElideMode.ElideRight, maxWidth
)
name_button.setText(elidedText)
name_button.setToolTip(measurement.name)
class MeasurementDialog(QDialog): class MeasurementDialog(QDialog):
"""This Dialog is shown when the measurement is started and therefore blocks the main window. """This Dialog is shown when the measurement is started and therefore blocks the main window.

View file

@ -1,6 +1,6 @@
# Form implementation generated from reading ui file '../nqrduck-measurement/src/nqrduck_measurement/resources/measurement_widget.ui' # Form implementation generated from reading ui file 'measurement_widget.ui'
# #
# Created by: PyQt6 UI code generator 6.5.1 # Created by: PyQt6 UI code generator 6.7.0
# #
# WARNING: Any manual changes made to this file will be lost when pyuic6 is # WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -12,7 +12,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_Form(object): class Ui_Form(object):
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") Form.setObjectName("Form")
Form.resize(1920, 1080) Form.resize(1807, 1080)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -83,6 +83,16 @@ class Ui_Form(object):
self.spsettingsButton = QtWidgets.QPushButton(parent=Form) self.spsettingsButton = QtWidgets.QPushButton(parent=Form)
self.spsettingsButton.setObjectName("spsettingsButton") self.spsettingsButton.setObjectName("spsettingsButton")
self.settingsLayout.addWidget(self.spsettingsButton) self.settingsLayout.addWidget(self.spsettingsButton)
self.label = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.label.setFont(font)
self.label.setObjectName("label")
self.settingsLayout.addWidget(self.label)
self.measurementsList = QtWidgets.QListWidget(parent=Form)
self.measurementsList.setObjectName("measurementsList")
self.settingsLayout.addWidget(self.measurementsList)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.settingsLayout.addItem(spacerItem1) self.settingsLayout.addItem(spacerItem1)
self.dataLayout = QtWidgets.QVBoxLayout() self.dataLayout = QtWidgets.QVBoxLayout()
@ -105,6 +115,9 @@ class Ui_Form(object):
self.plotter.setSizePolicy(sizePolicy) self.plotter.setSizePolicy(sizePolicy)
self.plotter.setObjectName("plotter") self.plotter.setObjectName("plotter")
self.plotterLayout.addWidget(self.plotter) self.plotterLayout.addWidget(self.plotter)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.plotterLayout.addLayout(self.horizontalLayout_4)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.horizontalLayout_3.setObjectName("horizontalLayout_3")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
@ -119,7 +132,7 @@ class Ui_Form(object):
self.horizontalLayout_3.addItem(spacerItem3) self.horizontalLayout_3.addItem(spacerItem3)
self.horizontalLayout_3.setStretch(1, 1) self.horizontalLayout_3.setStretch(1, 1)
self.plotterLayout.addLayout(self.horizontalLayout_3) self.plotterLayout.addLayout(self.horizontalLayout_3)
self.plotterLayout.setStretch(1, 1) self.plotterLayout.setStretch(2, 1)
self.horizontalLayout_2.addLayout(self.plotterLayout) self.horizontalLayout_2.addLayout(self.plotterLayout)
self.horizontalLayout_2.setStretch(1, 1) self.horizontalLayout_2.setStretch(1, 1)
self.horizontalLayout.addLayout(self.horizontalLayout_2) self.horizontalLayout.addLayout(self.horizontalLayout_2)
@ -142,6 +155,7 @@ class Ui_Form(object):
self.peakButton.setText(_translate("Form", "Peak-Picking")) self.peakButton.setText(_translate("Form", "Peak-Picking"))
self.fittingButton.setText(_translate("Form", "Fitting")) self.fittingButton.setText(_translate("Form", "Fitting"))
self.spsettingsButton.setText(_translate("Form", "Settings")) self.spsettingsButton.setText(_translate("Form", "Settings"))
self.label.setText(_translate("Form", "Measurements:"))
self.exportButton.setText(_translate("Form", "Export Measurement")) self.exportButton.setText(_translate("Form", "Export Measurement"))
self.importButton.setText(_translate("Form", "Import Measurement")) self.importButton.setText(_translate("Form", "Import Measurement"))
self.fftButton.setText(_translate("Form", "FFT")) self.fftButton.setText(_translate("Form", "FFT"))