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
### 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)
- 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]
name = "nqrduck-measurement"
version = "0.0.3"
version = "0.0.4"
authors = [
{ 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.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)
measurements_changed = pyqtSignal(list)
view_mode_changed = pyqtSignal(str)
measurement_frequency_changed = pyqtSignal(float)
@ -94,6 +95,14 @@ class MeasurementModel(ModuleModel):
def add_measurement(self, measurement: Measurement):
"""Add a measurement to the list of measurements."""
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)
@property
@ -108,7 +117,12 @@ class MeasurementModel(ModuleModel):
@displayed_measurement.setter
def displayed_measurement(self, value: Measurement):
self._displayed_measurement = 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
def measurement_frequency(self):

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1920</width>
<width>1807</width>
<height>1080</height>
</rect>
</property>
@ -23,7 +23,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
<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>
<widget class="QLabel" name="titleLabel">
<property name="font">
@ -150,6 +150,22 @@
</property>
</widget>
</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>
<spacer name="verticalSpacer">
<property name="orientation">
@ -184,7 +200,7 @@
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="plotterLayout" stretch="0,1">
<layout class="QVBoxLayout" name="plotterLayout" stretch="0,0,1">
<item>
<widget class="MplWidget" name="plotter" native="true">
<property name="sizePolicy">
@ -195,6 +211,9 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0,0">
<item>

View file

@ -2,7 +2,19 @@
import logging
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 nqrduck.module.module_view import ModuleView
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.measurements_changed.connect(self.on_measurements_changed)
self._ui_form.buttonStart.clicked.connect(
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_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:
"""Initialize plotter with the according units for time domain."""
plotter = self._ui_form.plotter
@ -143,6 +162,18 @@ class MeasurementView(ModuleView):
plotter = self._ui_form.plotter
plotter.canvas.ax.clear()
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:
self.change_to_fft_view()
y = self.module.model.displayed_measurement.fdy
@ -173,6 +204,26 @@ class MeasurementView(ModuleView):
# Add 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:
logger.debug("No measurement data to display.")
self._ui_form.plotter.canvas.draw()
@ -200,7 +251,7 @@ class MeasurementView(ModuleView):
"""Slot for when the measurement save button is clicked."""
logger.debug("Measurement save button clicked.")
file_manager = self.QFileManager(
file_manager = self.FileManager(
self.module.model.FILE_EXTENSION, parent=self.widget
)
file_name = file_manager.saveFileDialog()
@ -212,13 +263,84 @@ class MeasurementView(ModuleView):
"""Slot for when the measurement load button is clicked."""
logger.debug("Measurement load button clicked.")
file_manager = self.QFileManager(
file_manager = self.FileManager(
self.module.model.FILE_EXTENSION, parent=self.widget
)
file_name = file_manager.loadFileDialog()
if 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):
"""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
# 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):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(1920, 1080)
Form.resize(1807, 1080)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -83,6 +83,16 @@ class Ui_Form(object):
self.spsettingsButton = QtWidgets.QPushButton(parent=Form)
self.spsettingsButton.setObjectName("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)
self.settingsLayout.addItem(spacerItem1)
self.dataLayout = QtWidgets.QVBoxLayout()
@ -105,6 +115,9 @@ class Ui_Form(object):
self.plotter.setSizePolicy(sizePolicy)
self.plotter.setObjectName("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.setObjectName("horizontalLayout_3")
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.setStretch(1, 1)
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.setStretch(1, 1)
self.horizontalLayout.addLayout(self.horizontalLayout_2)
@ -142,6 +155,7 @@ class Ui_Form(object):
self.peakButton.setText(_translate("Form", "Peak-Picking"))
self.fittingButton.setText(_translate("Form", "Fitting"))
self.spsettingsButton.setText(_translate("Form", "Settings"))
self.label.setText(_translate("Form", "Measurements:"))
self.exportButton.setText(_translate("Form", "Export Measurement"))
self.importButton.setText(_translate("Form", "Import Measurement"))
self.fftButton.setText(_translate("Form", "FFT"))