From 7112ce7888a4e167829f4e0f3ae7c9e18504de92 Mon Sep 17 00:00:00 2001 From: "Michal Krenek (Mikos)" Date: Tue, 17 May 2016 02:37:06 +0200 Subject: [PATCH] Min. / Max. peak hold; recalculate curves from history; separate data backend; refactoring --- qspectrumanalyzer/__main__.py | 159 +++++++----- qspectrumanalyzer/backend.py | 8 +- qspectrumanalyzer/data.py | 233 ++++++++++++++++++ .../languages/qspectrumanalyzer_cs.ts | 118 +++++---- qspectrumanalyzer/plot.py | 200 +++++++++------ qspectrumanalyzer/qspectrumanalyzer.ui | 134 +++++----- qspectrumanalyzer/qspectrumanalyzer_colors.ui | 46 +++- qspectrumanalyzer/ui_qspectrumanalyzer.py | 75 +++--- .../ui_qspectrumanalyzer_colors.py | 45 ++-- 9 files changed, 695 insertions(+), 323 deletions(-) create mode 100644 qspectrumanalyzer/data.py diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index 87c7c21..5423f6f 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -6,6 +6,7 @@ from PyQt4 import QtCore, QtGui from qspectrumanalyzer.version import __version__ from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread +from qspectrumanalyzer.data import DataStorage from qspectrumanalyzer.plot import SpectrumPlotWidget, WaterfallPlotWidget from qspectrumanalyzer.utils import color_to_str, str_to_color @@ -124,7 +125,8 @@ class QSpectrumAnalyzerColors(QtGui.QDialog, Ui_QSpectrumAnalyzerColors): # Load settings settings = QtCore.QSettings() self.mainColorButton.setColor(str_to_color(settings.value("main_color", "255, 255, 0, 255"))) - self.peakHoldColorButton.setColor(str_to_color(settings.value("peak_hold_color", "255, 0, 0, 255"))) + self.peakHoldMaxColorButton.setColor(str_to_color(settings.value("peak_hold_max_color", "255, 0, 0, 255"))) + self.peakHoldMinColorButton.setColor(str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255"))) self.averageColorButton.setColor(str_to_color(settings.value("average_color", "0, 255, 255, 255"))) self.persistenceColorButton.setColor(str_to_color(settings.value("persistence_color", "0, 255, 0, 255"))) @@ -132,7 +134,8 @@ class QSpectrumAnalyzerColors(QtGui.QDialog, Ui_QSpectrumAnalyzerColors): """Save settings when dialog is accepted""" settings = QtCore.QSettings() settings.setValue("main_color", color_to_str(self.mainColorButton.color())) - settings.setValue("peak_hold_color", color_to_str(self.peakHoldColorButton.color())) + settings.setValue("peak_hold_max_color", color_to_str(self.peakHoldMaxColorButton.color())) + settings.setValue("peak_hold_min_color", color_to_str(self.peakHoldMinColorButton.color())) settings.setValue("average_color", color_to_str(self.averageColorButton.color())) settings.setValue("persistence_color", color_to_str(self.persistenceColorButton.color())) QtGui.QDialog.accept(self) @@ -145,11 +148,6 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin super().__init__(parent) self.setupUi(self) - # Setup rtl_power thread and connect signals - self.prev_data_timestamp = None - self.rtl_power_thread = None - self.setup_rtl_power_thread() - # Create plot widgets and update UI self.spectrumPlotWidget = SpectrumPlotWidget(self.mainPlotLayout) self.waterfallPlotWidget = WaterfallPlotWidget(self.waterfallPlotLayout, self.histogramPlotLayout) @@ -157,6 +155,12 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin # Link main spectrum plot to waterfall plot self.spectrumPlotWidget.plot.setXLink(self.waterfallPlotWidget.plot) + # Setup rtl_power thread and connect signals + self.prev_data_timestamp = None + self.data_storage = None + self.rtl_power_thread = None + self.setup_rtl_power_thread() + self.update_buttons() self.load_settings() @@ -166,13 +170,23 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin self.stop() settings = QtCore.QSettings() + self.data_storage = DataStorage(max_history_size=settings.value("waterfall_history_size", 100, int)) + self.data_storage.data_updated.connect(self.update_data) + self.data_storage.data_updated.connect(self.spectrumPlotWidget.update_plot) + self.data_storage.data_updated.connect(self.spectrumPlotWidget.update_persistence) + self.data_storage.data_recalculated.connect(self.spectrumPlotWidget.recalculate_plot) + self.data_storage.data_recalculated.connect(self.spectrumPlotWidget.recalculate_persistence) + self.data_storage.history_updated.connect(self.waterfallPlotWidget.update_plot) + self.data_storage.average_updated.connect(self.spectrumPlotWidget.update_average) + self.data_storage.peak_hold_max_updated.connect(self.spectrumPlotWidget.update_peak_hold_max) + self.data_storage.peak_hold_min_updated.connect(self.spectrumPlotWidget.update_peak_hold_min) + backend = settings.value("backend", "rtl_power") if backend == "rtl_power_fftw": - self.rtl_power_thread = RtlPowerFftwThread() + self.rtl_power_thread = RtlPowerFftwThread(self.data_storage) else: - self.rtl_power_thread = RtlPowerThread() + self.rtl_power_thread = RtlPowerThread(self.data_storage) - self.rtl_power_thread.dataUpdated.connect(self.update_data) self.rtl_power_thread.rtlPowerStarted.connect(self.update_buttons) self.rtl_power_thread.rtlPowerStopped.connect(self.update_buttons) @@ -212,7 +226,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin self.ppmSpinBox.setValue(settings.value("ppm", 0, int)) self.cropSpinBox.setValue(settings.value("crop", 0, int)) self.mainCurveCheckBox.setChecked(settings.value("main_curve", 1, int)) - self.peakHoldCheckBox.setChecked(settings.value("peak_hold", 0, int)) + self.peakHoldMaxCheckBox.setChecked(settings.value("peak_hold_max", 0, int)) + self.peakHoldMinCheckBox.setChecked(settings.value("peak_hold_min", 0, int)) self.averageCheckBox.setChecked(settings.value("average", 0, int)) self.smoothCheckBox.setChecked(settings.value("smooth", 0, int)) self.persistenceCheckBox.setChecked(settings.value("persistence", 0, int)) @@ -250,7 +265,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin settings.setValue("ppm", self.ppmSpinBox.value()) settings.setValue("crop", self.cropSpinBox.value()) settings.setValue("main_curve", int(self.mainCurveCheckBox.isChecked())) - settings.setValue("peak_hold", int(self.peakHoldCheckBox.isChecked())) + settings.setValue("peak_hold_max", int(self.peakHoldMaxCheckBox.isChecked())) + settings.setValue("peak_hold_min", int(self.peakHoldMinCheckBox.isChecked())) settings.setValue("average", int(self.averageCheckBox.isChecked())) settings.setValue("smooth", int(self.smoothCheckBox.isChecked())) settings.setValue("persistence", int(self.persistenceCheckBox.isChecked())) @@ -270,45 +286,55 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin self.singleShotButton.setEnabled(not self.rtl_power_thread.alive) self.stopButton.setEnabled(self.rtl_power_thread.alive) - def update_data(self, data): - """Update plots when new data is received""" - self.waterfallPlotWidget.update_plot(data) - self.spectrumPlotWidget.update_plot(data) - + def update_data(self, data_storage): + """Update GUI when new data is received""" # Show number of hops and how much time did the sweep really take timestamp = time.time() - self.show_status(self.tr("Frequency hops: {} Sweep time: {:.2f} s").format( - self.rtl_power_thread.params["hops"] or self.tr("N/A"), - timestamp - self.prev_data_timestamp - ), timeout=0) + sweep_time = timestamp - self.prev_data_timestamp self.prev_data_timestamp = timestamp + self.show_status( + self.tr("Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f}").format( + self.rtl_power_thread.params["hops"] or self.tr("N/A"), + sweep_time, + 1 / sweep_time + ), + timeout=0 + ) + def start(self, single_shot=False): """Start rtl_power thread""" settings = QtCore.QSettings() self.prev_data_timestamp = time.time() - self.waterfallPlotWidget.counter = 0 - self.waterfallPlotWidget.history_size = settings.value("waterfall_history_size", 100, int) + self.data_storage.reset() + self.data_storage.set_smooth( + bool(self.smoothCheckBox.isChecked()), + settings.value("smooth_length", 11, int), + settings.value("smooth_window", "hanning"), + recalculate=False + ) + + self.waterfallPlotWidget.history_size = settings.value("waterfall_history_size", 100, int) + self.waterfallPlotWidget.clear_plot() - self.spectrumPlotWidget.counter = 0 self.spectrumPlotWidget.main_curve = bool(self.mainCurveCheckBox.isChecked()) self.spectrumPlotWidget.main_color = str_to_color(settings.value("main_color", "255, 255, 0, 255")) - self.spectrumPlotWidget.peak_hold = bool(self.peakHoldCheckBox.isChecked()) - self.spectrumPlotWidget.peak_hold_color = str_to_color(settings.value("peak_hold_color", "255, 0, 0, 255")) + self.spectrumPlotWidget.peak_hold_max = bool(self.peakHoldMaxCheckBox.isChecked()) + self.spectrumPlotWidget.peak_hold_max_color = str_to_color(settings.value("peak_hold_max_color", "255, 0, 0, 255")) + self.spectrumPlotWidget.peak_hold_min = bool(self.peakHoldMinCheckBox.isChecked()) + self.spectrumPlotWidget.peak_hold_min_color = str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255")) self.spectrumPlotWidget.average = bool(self.averageCheckBox.isChecked()) self.spectrumPlotWidget.average_color = str_to_color(settings.value("average_color", "0, 255, 255, 255")) self.spectrumPlotWidget.persistence = bool(self.persistenceCheckBox.isChecked()) self.spectrumPlotWidget.persistence_length = settings.value("persistence_length", 5, int) self.spectrumPlotWidget.persistence_decay = settings.value("persistence_decay", "exponential") self.spectrumPlotWidget.persistence_color = str_to_color(settings.value("persistence_color", "0, 255, 0, 255")) - self.spectrumPlotWidget.smooth = bool(self.smoothCheckBox.isChecked()) - self.spectrumPlotWidget.smooth_length = settings.value("smooth_length", 11, int) - self.spectrumPlotWidget.smooth_window = settings.value("smooth_window", "hanning") - self.spectrumPlotWidget.main_clear() - self.spectrumPlotWidget.peak_hold_clear() - self.spectrumPlotWidget.average_clear() - self.spectrumPlotWidget.persistence_clear() + self.spectrumPlotWidget.clear_plot() + self.spectrumPlotWidget.clear_peak_hold_max() + self.spectrumPlotWidget.clear_peak_hold_min() + self.spectrumPlotWidget.clear_average() + self.spectrumPlotWidget.clear_persistence() if not self.rtl_power_thread.alive: self.rtl_power_thread.setup(float(self.startFreqSpinBox.value()), @@ -342,46 +368,60 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin @QtCore.pyqtSlot(bool) def on_mainCurveCheckBox_toggled(self, checked): self.spectrumPlotWidget.main_curve = checked - if not checked: - self.spectrumPlotWidget.main_clear() + if self.spectrumPlotWidget.curve.xData is None: + self.spectrumPlotWidget.update_plot(self.data_storage) + self.spectrumPlotWidget.curve.setVisible(checked) @QtCore.pyqtSlot(bool) - def on_peakHoldCheckBox_toggled(self, checked): - self.spectrumPlotWidget.peak_hold = checked - if not checked: - self.spectrumPlotWidget.peak_hold_clear() + def on_peakHoldMaxCheckBox_toggled(self, checked): + self.spectrumPlotWidget.peak_hold_max = checked + if self.spectrumPlotWidget.curve_peak_hold_max.xData is None: + self.spectrumPlotWidget.update_peak_hold_max(self.data_storage) + self.spectrumPlotWidget.curve_peak_hold_max.setVisible(checked) + + @QtCore.pyqtSlot(bool) + def on_peakHoldMinCheckBox_toggled(self, checked): + self.spectrumPlotWidget.peak_hold_min = checked + if self.spectrumPlotWidget.curve_peak_hold_min.xData is None: + self.spectrumPlotWidget.update_peak_hold_min(self.data_storage) + self.spectrumPlotWidget.curve_peak_hold_min.setVisible(checked) @QtCore.pyqtSlot(bool) def on_averageCheckBox_toggled(self, checked): self.spectrumPlotWidget.average = checked - if not checked: - self.spectrumPlotWidget.average_clear() - - @QtCore.pyqtSlot(bool) - def on_smoothCheckBox_toggled(self, checked): - self.spectrumPlotWidget.smooth = checked - self.spectrumPlotWidget.main_clear() - self.spectrumPlotWidget.peak_hold_clear() - self.spectrumPlotWidget.average_clear() - self.spectrumPlotWidget.persistence_clear() + if self.spectrumPlotWidget.curve_average.xData is None: + self.spectrumPlotWidget.update_average(self.data_storage) + self.spectrumPlotWidget.curve_average.setVisible(checked) @QtCore.pyqtSlot(bool) def on_persistenceCheckBox_toggled(self, checked): self.spectrumPlotWidget.persistence = checked - if not checked: - self.spectrumPlotWidget.persistence_clear() + if self.spectrumPlotWidget.persistence_curves[0].xData is None: + self.spectrumPlotWidget.recalculate_persistence(self.data_storage) + for curve in self.spectrumPlotWidget.persistence_curves: + curve.setVisible(checked) + + @QtCore.pyqtSlot(bool) + def on_smoothCheckBox_toggled(self, checked): + settings = QtCore.QSettings() + self.data_storage.set_smooth( + checked, + settings.value("smooth_length", 11, int), + settings.value("smooth_window", "hanning"), + recalculate=True + ) @QtCore.pyqtSlot() def on_smoothButton_clicked(self): dialog = QSpectrumAnalyzerSmooth(self) if dialog.exec_(): settings = QtCore.QSettings() - self.spectrumPlotWidget.smooth_length = settings.value("smooth_length", 11, int) - self.spectrumPlotWidget.smooth_window = settings.value("smooth_window", "hanning") - self.spectrumPlotWidget.main_clear() - self.spectrumPlotWidget.peak_hold_clear() - self.spectrumPlotWidget.average_clear() - self.spectrumPlotWidget.persistence_clear() + self.data_storage.set_smooth( + bool(self.smoothCheckBox.isChecked()), + settings.value("smooth_length", 11, int), + settings.value("smooth_window", "hanning"), + recalculate=True + ) @QtCore.pyqtSlot() def on_persistenceButton_clicked(self): @@ -397,7 +437,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin if persistence_length == prev_persistence_length: self.spectrumPlotWidget.set_colors() else: - self.spectrumPlotWidget.persistence_clear() + self.spectrumPlotWidget.recalculate_persistence(self.data_storage) @QtCore.pyqtSlot() def on_colorsButton_clicked(self): @@ -405,7 +445,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin if dialog.exec_(): settings = QtCore.QSettings() self.spectrumPlotWidget.main_color = str_to_color(settings.value("main_color", "255, 255, 0, 255")) - self.spectrumPlotWidget.peak_hold_color = str_to_color(settings.value("peak_hold_color", "255, 0, 0, 255")) + self.spectrumPlotWidget.peak_hold_max_color = str_to_color(settings.value("peak_hold_max_color", "255, 0, 0, 255")) + self.spectrumPlotWidget.peak_hold_min_color = str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255")) self.spectrumPlotWidget.average_color = str_to_color(settings.value("average_color", "0, 255, 255, 255")) self.spectrumPlotWidget.persistence_color = str_to_color(settings.value("persistence_color", "0, 255, 0, 255")) self.spectrumPlotWidget.set_colors() diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 69e883e..7a8492b 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -6,12 +6,12 @@ from PyQt4 import QtCore class RtlPowerBaseThread(QtCore.QThread): """Thread which runs rtl_power process""" - dataUpdated = QtCore.pyqtSignal(object) rtlPowerStarted = QtCore.pyqtSignal() rtlPowerStopped = QtCore.pyqtSignal() - def __init__(self, parent=None): + def __init__(self, data_storage, parent=None): super().__init__(parent) + self.data_storage = data_storage self.alive = False self.process = None @@ -137,7 +137,7 @@ class RtlPowerThread(RtlPowerBaseThread): # This have to be stupid like this to be compatible with old broken version of rtl_power. Right way is: # if stop_freq == self.params["stop_freq"] * 1e6: if stop_freq > (self.params["stop_freq"] * 1e6) - step: - self.dataUpdated.emit(self.databuffer) + self.data_storage.update(self.databuffer) class RtlPowerFftwThread(RtlPowerBaseThread): @@ -227,7 +227,7 @@ class RtlPowerFftwThread(RtlPowerBaseThread): # Two empty lines => new set elif not line and not self.prev_line: self.hop = 0 - self.dataUpdated.emit(self.databuffer) + self.data_storage.update(self.databuffer) self.databuffer = {"timestamp": [], "x": [], "y": []} # Get timestamp for new hop and set diff --git a/qspectrumanalyzer/data.py b/qspectrumanalyzer/data.py new file mode 100644 index 0000000..0f600e6 --- /dev/null +++ b/qspectrumanalyzer/data.py @@ -0,0 +1,233 @@ +import time, sys + +from PyQt4 import QtCore +import numpy as np + +from qspectrumanalyzer.utils import smooth + + +class HistoryBuffer: + """Fixed-size NumPy array ring buffer""" + def __init__(self, data_size, max_history_size, dtype=float): + self.data_size = data_size + self.max_history_size = max_history_size + self.history_size = 0 + self.counter = 0 + self.buffer = np.empty(shape=(max_history_size, data_size), dtype=dtype) + + def append(self, data): + """Append new data to ring buffer""" + self.counter += 1 + if self.history_size < self.max_history_size: + self.history_size += 1 + self.buffer = np.roll(self.buffer, -1, axis=0) + self.buffer[-1] = data + + def get_buffer(self): + """Return buffer stripped to size of actual data""" + if self.history_size < self.max_history_size: + return self.buffer[-self.history_size:] + else: + return self.buffer + + def __getitem__(self, key): + return self.buffer[key] + + +class TaskSignals(QtCore.QObject): + """Task signals emitter""" + result = QtCore.pyqtSignal(object) + + +class Task(QtCore.QRunnable): + """Threaded task (run it with QThreadPool worker threads)""" + def __init__(self, task, *args, **kwargs): + super().__init__() + self.task = task + self.args = args + self.kwargs = kwargs + self.signals = TaskSignals() + + def run(self): + """Run task in worker thread and emit signal with result""" + #print('Running', self.task, 'in thread', QtCore.QThread.currentThreadId()) + result = self.task(*self.args, **self.kwargs) + self.signals.result.emit(result) + + +class DataStorage(QtCore.QObject): + """Data storage for spectrum measurements""" + history_updated = QtCore.pyqtSignal(object) + data_updated = QtCore.pyqtSignal(object) + data_recalculated = QtCore.pyqtSignal(object) + average_updated = QtCore.pyqtSignal(object) + peak_hold_max_updated = QtCore.pyqtSignal(object) + peak_hold_min_updated = QtCore.pyqtSignal(object) + + def __init__(self, max_history_size=100, parent=None): + super().__init__(parent) + self.max_history_size = max_history_size + self.smooth = False + self.smooth_length = 11 + self.smooth_window = "hanning" + + # Use only one worker thread because it is not faster + # with more threads (and memory consumption is much higher) + self.threadpool = QtCore.QThreadPool() + self.threadpool.setMaxThreadCount(1) + + self.reset() + + def reset(self): + """Reset all data""" + self.wait() + self.x = None + self.history = None + self.reset_data() + + def reset_data(self): + """Reset current data""" + self.wait() + self.y = None + self.average_counter = 0 + self.average = None + self.peak_hold_max = None + self.peak_hold_min = None + + def start_task(self, fn, *args, **kwargs): + """Run function asynchronously in worker thread""" + task = Task(fn, *args, **kwargs) + self.threadpool.start(task) + + def wait(self): + """Wait for worker threads to complete all running tasks""" + self.threadpool.waitForDone() + + def update(self, data): + """Update data storage""" + self.average_counter += 1 + + if self.x is None: + self.x = data["x"] + + self.start_task(self.update_history, data.copy()) + self.start_task(self.update_data, data) + + def update_data(self, data): + """Update main spectrum data (and possibly apply smoothing)""" + if self.smooth: + data["y"] = self.smooth_data(data["y"]) + + self.y = data["y"] + self.data_updated.emit(self) + + self.start_task(self.update_average, data) + self.start_task(self.update_peak_hold_max, data) + self.start_task(self.update_peak_hold_min, data) + + def update_history(self, data): + """Update spectrum measurements history""" + if self.history is None: + self.history = HistoryBuffer(len(data["y"]), self.max_history_size) + + self.history.append(data["y"]) + self.history_updated.emit(self) + + def update_average(self, data): + """Update average data""" + if self.average is None: + self.average = data["y"].copy() + else: + self.average = np.average((self.average, data["y"]), axis=0, weights=(self.average_counter - 1, 1)) + self.average_updated.emit(self) + + def update_peak_hold_max(self, data): + """Update max. peak hold data""" + if self.peak_hold_max is None: + self.peak_hold_max = data["y"].copy() + else: + self.peak_hold_max = np.maximum(self.peak_hold_max, data["y"]) + self.peak_hold_max_updated.emit(self) + + def update_peak_hold_min(self, data): + """Update min. peak hold data""" + if self.peak_hold_min is None: + self.peak_hold_min = data["y"].copy() + else: + self.peak_hold_min = np.minimum(self.peak_hold_min, data["y"]) + self.peak_hold_min_updated.emit(self) + + def smooth_data(self, y): + """Apply smoothing function to data""" + return smooth(y, window_len=self.smooth_length, window=self.smooth_window) + + def set_smooth(self, toggle, length=11, window="hanning", recalculate=False): + """Toggle smoothing and set smoothing params""" + if toggle != self.smooth or length != self.smooth_length or window != self.smooth_window: + self.smooth = toggle + self.smooth_length = length + self.smooth_window = window + if recalculate: + self.start_task(self.recalculate_data) + else: + self.reset_data() + + def recalculate_data(self): + """Recalculate current data from history""" + if self.history is None: + return + + history = self.history.get_buffer() + if self.smooth: + self.y = self.smooth_data(history[-1]) + self.average_counter = 0 + self.average = self.y.copy() + self.peak_hold_max = self.y.copy() + self.peak_hold_min = self.y.copy() + for y in history[:-1]: + self.average_counter += 1 + y = self.smooth_data(y) + self.average = np.average((self.average, y), axis=0, weights=(self.average_counter - 1, 1)) + self.peak_hold_max = np.maximum(self.peak_hold_max, y) + self.peak_hold_min = np.minimum(self.peak_hold_min, y) + else: + self.y = history[-1] + self.average_counter = self.history.history_size + self.average = np.average(history, axis=0) + self.peak_hold_max = history.max(axis=0) + self.peak_hold_min = history.min(axis=0) + + self.data_recalculated.emit(self) + #self.data_updated.emit({"x": self.x, "y": self.y}) + #self.average_updated.emit({"x": self.x, "y": self.average}) + #self.peak_hold_max_updated.emit({"x": self.x, "y": self.peak_hold_max}) + #self.peak_hold_min_updated.emit({"x": self.x, "y": self.peak_hold_min}) + + +class Test: + """Test data storage performance""" + def __init__(self, data_size=100000, max_history_size=100): + self.data_size = data_size + self.data = {"x": np.arange(data_size), + "y": None} + self.datastorage = DataStorage(max_history_size) + + def run_one(self): + """Generate random data and update data storage""" + self.data["y"] = np.random.normal(size=self.data_size) + self.datastorage.update(self.data) + + def run(self, runs=1000): + """Run performance test""" + t = time.time() + for i in range(runs): + self.run_one() + self.datastorage.wait() + total_time = time.time() - t + print("Total time:", total_time) + print("FPS:", runs / total_time) + + +if __name__ == "__main__": + test = Test(int(sys.argv[1]), int(sys.argv[2])) + test.run(int(sys.argv[3])) diff --git a/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts b/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts index 96cfa2e..642818a 100644 --- a/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts +++ b/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts @@ -3,213 +3,223 @@ QSpectrumAnalyzerColors - + Colors - QSpectrumAnalyzer - + Main curve color: - + ... - - Peak hold color: - - - - + Average color: - + Persistence color: + + + Max. peak hold color: + + + + + Min. peak hold color: + + QSpectrumAnalyzerMainWindow - + QSpectrumAnalyzer - + Settings - + MHz - + kHz - + auto - + Frequency - + Controls - + Levels - + QSpectrumAnalyzer {} - + &File - + &Help - + &Start - + S&top - + Si&ngle shot - + &Settings... - + &Quit - + Ctrl+Q - + &About - + Interval [s]: - + Gain [dB]: - + Corr. [ppm]: - + Crop [%]: - + Start: - + Stop: - + Bin size: - - Peak hold - - - - + Smoothing - + ... - + N/A - - Frequency hops: {} Sweep time: {:.2f} s - - - - + About - QSpectrumAnalyzer - + Persistence - + Average - + Colors... - + Main curve + + + Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f} + + + + + Max. hold + + + + + Min. hold + + QSpectrumAnalyzerPersistence @@ -277,7 +287,7 @@ - + Select executable - QSpectrumAnalyzer diff --git a/qspectrumanalyzer/plot.py b/qspectrumanalyzer/plot.py index 57adaf0..e9fbd55 100644 --- a/qspectrumanalyzer/plot.py +++ b/qspectrumanalyzer/plot.py @@ -1,10 +1,8 @@ import collections, math -import numpy as np +from PyQt4 import QtCore import pyqtgraph as pg -from qspectrumanalyzer.utils import smooth - # Basic PyQtGraph settings pg.setConfigOptions(antialias=True) @@ -23,19 +21,14 @@ class SpectrumPlotWidget: self.persistence_length = 5 self.persistence_decay = "exponential" self.persistence_color = pg.mkColor("g") - self.peak_hold = False - self.peak_hold_color = pg.mkColor("r") - self.average = False - self.average_color = pg.mkColor("c") - self.smooth = False - self.smooth_length = 11 - self.smooth_window = "hanning" - - self.counter = 0 - self.peak_hold_data = None - self.average_data = None self.persistence_data = None self.persistence_curves = None + self.peak_hold_max = False + self.peak_hold_max_color = pg.mkColor("r") + self.peak_hold_min = False + self.peak_hold_min_color = pg.mkColor("b") + self.average = False + self.average_color = pg.mkColor("c") self.create_plot() @@ -51,8 +44,9 @@ class SpectrumPlotWidget: self.create_persistence_curves() self.create_average_curve() + self.create_peak_hold_min_curve() + self.create_peak_hold_max_curve() self.create_main_curve() - self.create_peak_hold_curve() # Create crosshair self.vLine = pg.InfiniteLine(angle=90, movable=False) @@ -64,15 +58,20 @@ class SpectrumPlotWidget: self.mouseProxy = pg.SignalProxy(self.plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouse_moved) - def create_peak_hold_curve(self): - """Create peak hold curve""" - self.curve_peak_hold = self.plot.plot(pen=self.peak_hold_color) - self.curve_peak_hold.setZValue(900) - def create_main_curve(self): """Create main spectrum curve""" self.curve = self.plot.plot(pen=self.main_color) - self.curve.setZValue(800) + self.curve.setZValue(900) + + def create_peak_hold_max_curve(self): + """Create max. peak hold curve""" + self.curve_peak_hold_max = self.plot.plot(pen=self.peak_hold_max_color) + self.curve_peak_hold_max.setZValue(800) + + def create_peak_hold_min_curve(self): + """Create min. peak hold curve""" + self.curve_peak_hold_min = self.plot.plot(pen=self.peak_hold_min_color) + self.curve_peak_hold_min.setZValue(800) def create_average_curve(self): """Create average curve""" @@ -94,7 +93,8 @@ class SpectrumPlotWidget: def set_colors(self): """Set colors of all curves""" self.curve.setPen(self.main_color) - self.curve_peak_hold.setPen(self.peak_hold_color) + self.curve_peak_hold_max.setPen(self.peak_hold_max_color) + self.curve_peak_hold_min.setPen(self.peak_hold_min_color) self.curve_average.setPen(self.average_color) decay = self.get_decay() @@ -118,47 +118,85 @@ class SpectrumPlotWidget: else: return self.decay_linear - def update_plot(self, data): - """Update main spectrum plot""" - self.counter += 1 + def update_plot(self, data_storage, force=False): + """Update main spectrum curve""" + if data_storage.x is None: + return - # Apply smoothing to data - if self.smooth: - data["y"] = smooth(data["y"], - window_len=self.smooth_length, - window=self.smooth_window) + if self.main_curve or force: + self.curve.setData(data_storage.x, data_storage.y) + if force: + self.curve.setVisible(self.main_curve) - # Draw main curve - if self.main_curve: - self.curve.setData(data["x"], data["y"]) + def update_peak_hold_max(self, data_storage, force=False): + """Update max. peak hold curve""" + if data_storage.x is None: + return - # Update peak hold data and draw peak hold curve - if self.peak_hold: - if self.peak_hold_data is None: - self.peak_hold_data = data["y"].copy() - else: - for i, y in enumerate(data["y"]): - if y > self.peak_hold_data[i]: - self.peak_hold_data[i] = y - self.curve_peak_hold.setData(data["x"], self.peak_hold_data) + if self.peak_hold_max or force: + self.curve_peak_hold_max.setData(data_storage.x, data_storage.peak_hold_max) + if force: + self.curve_peak_hold_max.setVisible(self.peak_hold_max) - # Update average data and draw average curve - if self.average: - if self.average_data is None: - self.average_data = data["y"].copy() - else: - for i, y in enumerate(data["y"]): - self.average_data[i] = (self.counter * self.average_data[i] + y) / (self.counter + 1) - self.curve_average.setData(data["x"], self.average_data) + def update_peak_hold_min(self, data_storage, force=False): + """Update min. peak hold curve""" + if data_storage.x is None: + return - # Draw persistence curves - if self.persistence: + if self.peak_hold_min or force: + self.curve_peak_hold_min.setData(data_storage.x, data_storage.peak_hold_min) + if force: + self.curve_peak_hold_min.setVisible(self.peak_hold_min) + + def update_average(self, data_storage, force=False): + """Update average curve""" + if data_storage.x is None: + return + + if self.average or force: + self.curve_average.setData(data_storage.x, data_storage.average) + if force: + self.curve_average.setVisible(self.average) + + def update_persistence(self, data_storage, force=False): + """Update persistence curves""" + if data_storage.x is None: + return + + if self.persistence or force: if self.persistence_data is None: self.persistence_data = collections.deque(maxlen=self.persistence_length) else: for i, y in enumerate(self.persistence_data): - self.persistence_curves[i].setData(data["x"], y) - self.persistence_data.appendleft(data["y"].copy()) + curve = self.persistence_curves[i] + curve.setData(data_storage.x, y) + if force: + curve.setVisible(self.persistence) + self.persistence_data.appendleft(data_storage.y) + + def recalculate_plot(self, data_storage): + """Recalculate plot from history""" + if data_storage.x is None: + return + + QtCore.QTimer.singleShot(0, lambda: self.update_plot(data_storage, force=True)) + QtCore.QTimer.singleShot(0, lambda: self.update_average(data_storage, force=True)) + QtCore.QTimer.singleShot(0, lambda: self.update_peak_hold_max(data_storage, force=True)) + QtCore.QTimer.singleShot(0, lambda: self.update_peak_hold_min(data_storage, force=True)) + + def recalculate_persistence(self, data_storage): + """Recalculate persistence data and update persistence curves""" + if data_storage.x is None: + return + + self.clear_persistence() + self.persistence_data = collections.deque(maxlen=self.persistence_length) + for i in range(min(self.persistence_length, data_storage.history.history_size - 1)): + data = data_storage.history[-i - 2] + if data_storage.smooth: + data = data_storage.smooth_data(data) + self.persistence_data.append(data) + QtCore.QTimer.singleShot(0, lambda: self.update_persistence(data_storage, force=True)) def mouse_moved(self, evt): """Update crosshair when mouse is moved""" @@ -166,34 +204,31 @@ class SpectrumPlotWidget: if self.plot.sceneBoundingRect().contains(pos): mousePoint = self.plot.vb.mapSceneToView(pos) self.posLabel.setText( - "f={:0.3f} MHz, P={:0.3f} dBm".format(mousePoint.x() / 1e6, - mousePoint.y()) + "f={:0.3f} MHz, P={:0.3f} dBm".format( + mousePoint.x() / 1e6, + mousePoint.y() + ) ) self.vLine.setPos(mousePoint.x()) self.hLine.setPos(mousePoint.y()) - def main_clear(self): + def clear_plot(self): """Clear main spectrum curve""" self.curve.clear() - self.plot.removeItem(self.curve) - self.create_main_curve() - def peak_hold_clear(self): - """Clear peak hold curve""" - self.peak_hold_data = None - self.curve_peak_hold.clear() - self.plot.removeItem(self.curve_peak_hold) - self.create_peak_hold_curve() + def clear_peak_hold_max(self): + """Clear max. peak hold curve""" + self.curve_peak_hold_max.clear() - def average_clear(self): + def clear_peak_hold_min(self): + """Clear min. peak hold curve""" + self.curve_peak_hold_min.clear() + + def clear_average(self): """Clear average curve""" - self.counter = 0 - self.average_data = None self.curve_average.clear() - self.plot.removeItem(self.curve_average) - self.create_average_curve() - def persistence_clear(self): + def clear_persistence(self): """Clear spectrum persistence curves""" self.persistence_data = None for curve in self.persistence_curves: @@ -201,7 +236,6 @@ class SpectrumPlotWidget: self.plot.removeItem(curve) self.create_persistence_curves() - class WaterfallPlotWidget: """Waterfall plot""" def __init__(self, layout, histogram_layout=None): @@ -240,30 +274,32 @@ class WaterfallPlotWidget: #self.histogram.setHistogramRange(-50, 0) #self.histogram.setLevels(-50, 0) - def update_plot(self, data): + def update_plot(self, data_storage): """Update waterfall plot""" self.counter += 1 - # Create waterfall data array and waterfall image on first run + # Create waterfall image on first run if self.counter == 1: - self.waterfallImgArray = np.zeros((self.history_size, len(data["x"]))) self.waterfallImg = pg.ImageItem() - self.waterfallImg.scale((data["x"][-1] - data["x"][0]) / len(data["x"]), 1) + self.waterfallImg.scale((data_storage.x[-1] - data_storage.x[0]) / len(data_storage.x), 1) self.plot.clear() self.plot.addItem(self.waterfallImg) # Roll down one and replace leading edge with new data - self.waterfallImgArray = np.roll(self.waterfallImgArray, -1, axis=0) - self.waterfallImgArray[-1] = data["y"] - self.waterfallImg.setImage(self.waterfallImgArray[-self.counter:].T, + self.waterfallImg.setImage(data_storage.history.buffer[-self.counter:].T, autoLevels=False, autoRange=False) # Move waterfall image to always start at 0 - self.waterfallImg.setPos(data["x"][0], - -self.counter if self.counter < self.history_size - else -self.history_size) + self.waterfallImg.setPos( + data_storage.x[0], + -self.counter if self.counter < self.history_size else -self.history_size + ) # Link histogram widget to waterfall image on first run # (must be done after first data is received or else levels would be wrong) if self.counter == 1 and self.histogram_layout: self.histogram.setImageItem(self.waterfallImg) + + def clear_plot(self): + """Clear waterfall plot""" + self.counter = 0 diff --git a/qspectrumanalyzer/qspectrumanalyzer.ui b/qspectrumanalyzer/qspectrumanalyzer.ui index 0a410fc..63467ba 100644 --- a/qspectrumanalyzer/qspectrumanalyzer.ui +++ b/qspectrumanalyzer/qspectrumanalyzer.ui @@ -82,7 +82,7 @@ - 190 + 10 10 @@ -143,7 +143,7 @@ - 225 + 10 10 @@ -339,6 +339,25 @@ + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + auto + + + -1 + + + 49 + + + -1 + + + @@ -359,19 +378,6 @@ - - - - Qt::Vertical - - - - 20 - 1 - - - - @@ -395,10 +401,31 @@ - - + + - Peak hold + Colors... + + + + + + + Max. hold + + + + + + + Min. hold + + + + + + + Average @@ -409,46 +436,6 @@ - - - - Persistence - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - auto - - - -1 - - - 49 - - - -1 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Colors... - - - @@ -459,6 +446,13 @@ + + + + Persistence + + + @@ -469,10 +463,23 @@ - - - - Average + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -549,7 +556,8 @@ cropSpinBox mainCurveCheckBox colorsButton - peakHoldCheckBox + peakHoldMaxCheckBox + peakHoldMinCheckBox averageCheckBox smoothCheckBox smoothButton diff --git a/qspectrumanalyzer/qspectrumanalyzer_colors.ui b/qspectrumanalyzer/qspectrumanalyzer_colors.ui index e2039cc..5802a86 100644 --- a/qspectrumanalyzer/qspectrumanalyzer_colors.ui +++ b/qspectrumanalyzer/qspectrumanalyzer_colors.ui @@ -6,8 +6,8 @@ 0 0 - 202 - 214 + 232 + 260 @@ -42,15 +42,15 @@ - Peak hold color: + Max. peak hold color: - peakHoldColorButton + peakHoldMaxColorButton - + 0 @@ -63,17 +63,17 @@ - + - Average color: + Min. peak hold color: - averageColorButton + peakHoldMinColorButton - + 0 @@ -86,6 +86,29 @@ + + + Average color: + + + averageColorButton + + + + + + + + 0 + 0 + + + + ... + + + + Persistence color: @@ -95,7 +118,7 @@ - + @@ -144,7 +167,8 @@ mainColorButton - peakHoldColorButton + peakHoldMaxColorButton + peakHoldMinColorButton averageColorButton persistenceColorButton buttonBox diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer.py b/qspectrumanalyzer/ui_qspectrumanalyzer.py index 27e5880..db0ed27 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer.py @@ -71,7 +71,7 @@ class Ui_QSpectrumAnalyzerMainWindow(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.controlsDockWidget.sizePolicy().hasHeightForWidth()) self.controlsDockWidget.setSizePolicy(sizePolicy) - self.controlsDockWidget.setMinimumSize(QtCore.QSize(190, 10)) + self.controlsDockWidget.setMinimumSize(QtCore.QSize(10, 10)) self.controlsDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable) self.controlsDockWidget.setObjectName(_fromUtf8("controlsDockWidget")) self.controlsDockWidgetContents = QtGui.QWidget() @@ -97,7 +97,7 @@ class Ui_QSpectrumAnalyzerMainWindow(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frequencyDockWidget.sizePolicy().hasHeightForWidth()) self.frequencyDockWidget.setSizePolicy(sizePolicy) - self.frequencyDockWidget.setMinimumSize(QtCore.QSize(225, 10)) + self.frequencyDockWidget.setMinimumSize(QtCore.QSize(10, 10)) self.frequencyDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable) self.frequencyDockWidget.setObjectName(_fromUtf8("frequencyDockWidget")) self.frequencyDockWidgetContents = QtGui.QWidget() @@ -180,14 +180,19 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.intervalSpinBox.setProperty("value", 1.0) self.intervalSpinBox.setObjectName(_fromUtf8("intervalSpinBox")) self.gridLayout.addWidget(self.intervalSpinBox, 1, 0, 1, 1) + self.gainSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents) + self.gainSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.gainSpinBox.setMinimum(-1) + self.gainSpinBox.setMaximum(49) + self.gainSpinBox.setProperty("value", -1) + self.gainSpinBox.setObjectName(_fromUtf8("gainSpinBox")) + self.gridLayout.addWidget(self.gainSpinBox, 1, 1, 1, 2) self.label_5 = QtGui.QLabel(self.settingsDockWidgetContents) self.label_5.setObjectName(_fromUtf8("label_5")) self.gridLayout.addWidget(self.label_5, 2, 0, 1, 1) self.label_7 = QtGui.QLabel(self.settingsDockWidgetContents) self.label_7.setObjectName(_fromUtf8("label_7")) self.gridLayout.addWidget(self.label_7, 2, 1, 1, 1) - spacerItem2 = QtGui.QSpacerItem(20, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem2, 9, 0, 1, 1) self.ppmSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents) self.ppmSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.ppmSpinBox.setMinimum(-999) @@ -198,40 +203,38 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.mainCurveCheckBox.setChecked(True) self.mainCurveCheckBox.setObjectName(_fromUtf8("mainCurveCheckBox")) self.gridLayout.addWidget(self.mainCurveCheckBox, 4, 0, 1, 1) - self.peakHoldCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) - self.peakHoldCheckBox.setObjectName(_fromUtf8("peakHoldCheckBox")) - self.gridLayout.addWidget(self.peakHoldCheckBox, 5, 0, 1, 1) - self.smoothCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) - self.smoothCheckBox.setObjectName(_fromUtf8("smoothCheckBox")) - self.gridLayout.addWidget(self.smoothCheckBox, 7, 0, 1, 1) - self.persistenceCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) - self.persistenceCheckBox.setObjectName(_fromUtf8("persistenceCheckBox")) - self.gridLayout.addWidget(self.persistenceCheckBox, 8, 0, 1, 1) - self.gainSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents) - self.gainSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.gainSpinBox.setMinimum(-1) - self.gainSpinBox.setMaximum(49) - self.gainSpinBox.setProperty("value", -1) - self.gainSpinBox.setObjectName(_fromUtf8("gainSpinBox")) - self.gridLayout.addWidget(self.gainSpinBox, 1, 1, 1, 2) - self.cropSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents) - self.cropSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.cropSpinBox.setObjectName(_fromUtf8("cropSpinBox")) - self.gridLayout.addWidget(self.cropSpinBox, 3, 1, 1, 2) self.colorsButton = QtGui.QPushButton(self.settingsDockWidgetContents) self.colorsButton.setObjectName(_fromUtf8("colorsButton")) self.gridLayout.addWidget(self.colorsButton, 4, 1, 1, 2) + self.peakHoldMaxCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) + self.peakHoldMaxCheckBox.setObjectName(_fromUtf8("peakHoldMaxCheckBox")) + self.gridLayout.addWidget(self.peakHoldMaxCheckBox, 5, 0, 1, 1) + self.peakHoldMinCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) + self.peakHoldMinCheckBox.setObjectName(_fromUtf8("peakHoldMinCheckBox")) + self.gridLayout.addWidget(self.peakHoldMinCheckBox, 5, 1, 1, 2) + self.averageCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) + self.averageCheckBox.setObjectName(_fromUtf8("averageCheckBox")) + self.gridLayout.addWidget(self.averageCheckBox, 6, 0, 1, 1) + self.smoothCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) + self.smoothCheckBox.setObjectName(_fromUtf8("smoothCheckBox")) + self.gridLayout.addWidget(self.smoothCheckBox, 7, 0, 1, 1) self.smoothButton = QtGui.QToolButton(self.settingsDockWidgetContents) self.smoothButton.setAutoRaise(False) self.smoothButton.setObjectName(_fromUtf8("smoothButton")) self.gridLayout.addWidget(self.smoothButton, 7, 2, 1, 1) + self.persistenceCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) + self.persistenceCheckBox.setObjectName(_fromUtf8("persistenceCheckBox")) + self.gridLayout.addWidget(self.persistenceCheckBox, 8, 0, 1, 1) self.persistenceButton = QtGui.QToolButton(self.settingsDockWidgetContents) self.persistenceButton.setAutoRaise(False) self.persistenceButton.setObjectName(_fromUtf8("persistenceButton")) self.gridLayout.addWidget(self.persistenceButton, 8, 2, 1, 1) - self.averageCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) - self.averageCheckBox.setObjectName(_fromUtf8("averageCheckBox")) - self.gridLayout.addWidget(self.averageCheckBox, 6, 0, 1, 1) + spacerItem2 = QtGui.QSpacerItem(20, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout.addItem(spacerItem2, 9, 0, 1, 1) + self.cropSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents) + self.cropSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.cropSpinBox.setObjectName(_fromUtf8("cropSpinBox")) + self.gridLayout.addWidget(self.cropSpinBox, 3, 1, 1, 2) self.settingsDockWidget.setWidget(self.settingsDockWidgetContents) QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.settingsDockWidget) self.levelsDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow) @@ -289,8 +292,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object): QSpectrumAnalyzerMainWindow.setTabOrder(self.ppmSpinBox, self.cropSpinBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.cropSpinBox, self.mainCurveCheckBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.mainCurveCheckBox, self.colorsButton) - QSpectrumAnalyzerMainWindow.setTabOrder(self.colorsButton, self.peakHoldCheckBox) - QSpectrumAnalyzerMainWindow.setTabOrder(self.peakHoldCheckBox, self.averageCheckBox) + QSpectrumAnalyzerMainWindow.setTabOrder(self.colorsButton, self.peakHoldMaxCheckBox) + QSpectrumAnalyzerMainWindow.setTabOrder(self.peakHoldMaxCheckBox, self.peakHoldMinCheckBox) + QSpectrumAnalyzerMainWindow.setTabOrder(self.peakHoldMinCheckBox, self.averageCheckBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.averageCheckBox, self.smoothCheckBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothCheckBox, self.smoothButton) QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothButton, self.persistenceCheckBox) @@ -317,17 +321,18 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.settingsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Settings", None)) self.label_4.setText(_translate("QSpectrumAnalyzerMainWindow", "Interval [s]:", None)) self.label_6.setText(_translate("QSpectrumAnalyzerMainWindow", "Gain [dB]:", None)) + self.gainSpinBox.setSpecialValueText(_translate("QSpectrumAnalyzerMainWindow", "auto", None)) self.label_5.setText(_translate("QSpectrumAnalyzerMainWindow", "Corr. [ppm]:", None)) self.label_7.setText(_translate("QSpectrumAnalyzerMainWindow", "Crop [%]:", None)) self.mainCurveCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Main curve", None)) - self.peakHoldCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Peak hold", None)) - self.smoothCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Smoothing", None)) - self.persistenceCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Persistence", None)) - self.gainSpinBox.setSpecialValueText(_translate("QSpectrumAnalyzerMainWindow", "auto", None)) self.colorsButton.setText(_translate("QSpectrumAnalyzerMainWindow", "Colors...", None)) - self.smoothButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None)) - self.persistenceButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None)) + self.peakHoldMaxCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Max. hold", None)) + self.peakHoldMinCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Min. hold", None)) self.averageCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Average", None)) + self.smoothCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Smoothing", None)) + self.smoothButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None)) + self.persistenceCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Persistence", None)) + self.persistenceButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None)) self.levelsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Levels", None)) self.action_Settings.setText(_translate("QSpectrumAnalyzerMainWindow", "&Settings...", None)) self.action_Quit.setText(_translate("QSpectrumAnalyzerMainWindow", "&Quit", None)) diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer_colors.py b/qspectrumanalyzer/ui_qspectrumanalyzer_colors.py index f4aa3f4..4c691e1 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer_colors.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer_colors.py @@ -25,7 +25,7 @@ except AttributeError: class Ui_QSpectrumAnalyzerColors(object): def setupUi(self, QSpectrumAnalyzerColors): QSpectrumAnalyzerColors.setObjectName(_fromUtf8("QSpectrumAnalyzerColors")) - QSpectrumAnalyzerColors.resize(202, 214) + QSpectrumAnalyzerColors.resize(232, 260) self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerColors) self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) self.formLayout = QtGui.QFormLayout() @@ -44,17 +44,28 @@ class Ui_QSpectrumAnalyzerColors(object): self.label_4 = QtGui.QLabel(QSpectrumAnalyzerColors) self.label_4.setObjectName(_fromUtf8("label_4")) self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_4) - self.peakHoldColorButton = ColorButton(QSpectrumAnalyzerColors) + self.peakHoldMaxColorButton = ColorButton(QSpectrumAnalyzerColors) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.peakHoldColorButton.sizePolicy().hasHeightForWidth()) - self.peakHoldColorButton.setSizePolicy(sizePolicy) - self.peakHoldColorButton.setObjectName(_fromUtf8("peakHoldColorButton")) - self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.peakHoldColorButton) + sizePolicy.setHeightForWidth(self.peakHoldMaxColorButton.sizePolicy().hasHeightForWidth()) + self.peakHoldMaxColorButton.setSizePolicy(sizePolicy) + self.peakHoldMaxColorButton.setObjectName(_fromUtf8("peakHoldMaxColorButton")) + self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.peakHoldMaxColorButton) + self.label_6 = QtGui.QLabel(QSpectrumAnalyzerColors) + self.label_6.setObjectName(_fromUtf8("label_6")) + self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_6) + self.peakHoldMinColorButton = ColorButton(QSpectrumAnalyzerColors) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.peakHoldMinColorButton.sizePolicy().hasHeightForWidth()) + self.peakHoldMinColorButton.setSizePolicy(sizePolicy) + self.peakHoldMinColorButton.setObjectName(_fromUtf8("peakHoldMinColorButton")) + self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.peakHoldMinColorButton) self.label_5 = QtGui.QLabel(QSpectrumAnalyzerColors) self.label_5.setObjectName(_fromUtf8("label_5")) - self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_5) + self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_5) self.averageColorButton = ColorButton(QSpectrumAnalyzerColors) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -62,10 +73,10 @@ class Ui_QSpectrumAnalyzerColors(object): sizePolicy.setHeightForWidth(self.averageColorButton.sizePolicy().hasHeightForWidth()) self.averageColorButton.setSizePolicy(sizePolicy) self.averageColorButton.setObjectName(_fromUtf8("averageColorButton")) - self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.averageColorButton) + self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.averageColorButton) self.label_3 = QtGui.QLabel(QSpectrumAnalyzerColors) self.label_3.setObjectName(_fromUtf8("label_3")) - self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_3) + self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.label_3) self.persistenceColorButton = ColorButton(QSpectrumAnalyzerColors) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -73,7 +84,7 @@ class Ui_QSpectrumAnalyzerColors(object): sizePolicy.setHeightForWidth(self.persistenceColorButton.sizePolicy().hasHeightForWidth()) self.persistenceColorButton.setSizePolicy(sizePolicy) self.persistenceColorButton.setObjectName(_fromUtf8("persistenceColorButton")) - self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.persistenceColorButton) + self.formLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.persistenceColorButton) self.verticalLayout.addLayout(self.formLayout) spacerItem = QtGui.QSpacerItem(20, 2, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.verticalLayout.addItem(spacerItem) @@ -83,7 +94,8 @@ class Ui_QSpectrumAnalyzerColors(object): self.buttonBox.setObjectName(_fromUtf8("buttonBox")) self.verticalLayout.addWidget(self.buttonBox) self.label_2.setBuddy(self.mainColorButton) - self.label_4.setBuddy(self.peakHoldColorButton) + self.label_4.setBuddy(self.peakHoldMaxColorButton) + self.label_6.setBuddy(self.peakHoldMinColorButton) self.label_5.setBuddy(self.averageColorButton) self.label_3.setBuddy(self.persistenceColorButton) @@ -91,8 +103,9 @@ class Ui_QSpectrumAnalyzerColors(object): QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerColors.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerColors.reject) QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerColors) - QSpectrumAnalyzerColors.setTabOrder(self.mainColorButton, self.peakHoldColorButton) - QSpectrumAnalyzerColors.setTabOrder(self.peakHoldColorButton, self.averageColorButton) + QSpectrumAnalyzerColors.setTabOrder(self.mainColorButton, self.peakHoldMaxColorButton) + QSpectrumAnalyzerColors.setTabOrder(self.peakHoldMaxColorButton, self.peakHoldMinColorButton) + QSpectrumAnalyzerColors.setTabOrder(self.peakHoldMinColorButton, self.averageColorButton) QSpectrumAnalyzerColors.setTabOrder(self.averageColorButton, self.persistenceColorButton) QSpectrumAnalyzerColors.setTabOrder(self.persistenceColorButton, self.buttonBox) @@ -100,8 +113,10 @@ class Ui_QSpectrumAnalyzerColors(object): QSpectrumAnalyzerColors.setWindowTitle(_translate("QSpectrumAnalyzerColors", "Colors - QSpectrumAnalyzer", None)) self.label_2.setText(_translate("QSpectrumAnalyzerColors", "Main curve color:", None)) self.mainColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) - self.label_4.setText(_translate("QSpectrumAnalyzerColors", "Peak hold color:", None)) - self.peakHoldColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) + self.label_4.setText(_translate("QSpectrumAnalyzerColors", "Max. peak hold color:", None)) + self.peakHoldMaxColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) + self.label_6.setText(_translate("QSpectrumAnalyzerColors", "Min. peak hold color:", None)) + self.peakHoldMinColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) self.label_5.setText(_translate("QSpectrumAnalyzerColors", "Average color:", None)) self.averageColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) self.label_3.setText(_translate("QSpectrumAnalyzerColors", "Persistence color:", None))