diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index ea3d528..87c7c21 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -7,9 +7,12 @@ from PyQt4 import QtCore, QtGui from qspectrumanalyzer.version import __version__ from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread from qspectrumanalyzer.plot import SpectrumPlotWidget, WaterfallPlotWidget +from qspectrumanalyzer.utils import color_to_str, str_to_color from qspectrumanalyzer.ui_qspectrumanalyzer_settings import Ui_QSpectrumAnalyzerSettings from qspectrumanalyzer.ui_qspectrumanalyzer_smooth import Ui_QSpectrumAnalyzerSmooth +from qspectrumanalyzer.ui_qspectrumanalyzer_persistence import Ui_QSpectrumAnalyzerPersistence +from qspectrumanalyzer.ui_qspectrumanalyzer_colors import Ui_QSpectrumAnalyzerColors from qspectrumanalyzer.ui_qspectrumanalyzer import Ui_QSpectrumAnalyzerMainWindow # Allow CTRL+C and/or SIGTERM to kill us (PyQt blocks it otherwise) @@ -26,11 +29,11 @@ class QSpectrumAnalyzerSettings(QtGui.QDialog, Ui_QSpectrumAnalyzerSettings): # Load settings settings = QtCore.QSettings() - self.executableEdit.setText(str(settings.value("rtl_power_executable") or "rtl_power")) - self.waterfallHistorySizeSpinBox.setValue(int(settings.value("waterfall_history_size") or 100)) - self.sampleRateSpinBox.setValue(int(settings.value("sample_rate") or 2560000)) + self.executableEdit.setText(settings.value("rtl_power_executable", "rtl_power")) + self.waterfallHistorySizeSpinBox.setValue(settings.value("waterfall_history_size", 100, int)) + self.sampleRateSpinBox.setValue(settings.value("sample_rate", 2560000, int)) - backend = str(settings.value("backend") or "rtl_power") + backend = settings.value("backend", "rtl_power") i = self.backendComboBox.findText(backend) if i == -1: self.backendComboBox.setCurrentIndex(0) @@ -60,7 +63,7 @@ class QSpectrumAnalyzerSettings(QtGui.QDialog, Ui_QSpectrumAnalyzerSettings): class QSpectrumAnalyzerSmooth(QtGui.QDialog, Ui_QSpectrumAnalyzerSmooth): - """QSpectrumAnalyzer smoothing dialog""" + """QSpectrumAnalyzer spectrum smoothing dialog""" def __init__(self, parent=None): # Initialize UI super().__init__(parent) @@ -68,9 +71,9 @@ class QSpectrumAnalyzerSmooth(QtGui.QDialog, Ui_QSpectrumAnalyzerSmooth): # Load settings settings = QtCore.QSettings() - self.windowLengthSpinBox.setValue(int(settings.value("smooth_length") or 11)) + self.windowLengthSpinBox.setValue(settings.value("smooth_length", 11, int)) - window_function = str(settings.value("smooth_window") or "hanning") + window_function = settings.value("smooth_window", "hanning") i = self.windowFunctionComboBox.findText(window_function) if i == -1: self.windowFunctionComboBox.setCurrentIndex(0) @@ -85,6 +88,56 @@ class QSpectrumAnalyzerSmooth(QtGui.QDialog, Ui_QSpectrumAnalyzerSmooth): QtGui.QDialog.accept(self) +class QSpectrumAnalyzerPersistence(QtGui.QDialog, Ui_QSpectrumAnalyzerPersistence): + """QSpectrumAnalyzer spectrum persistence dialog""" + def __init__(self, parent=None): + # Initialize UI + super().__init__(parent) + self.setupUi(self) + + # Load settings + settings = QtCore.QSettings() + self.persistenceLengthSpinBox.setValue(settings.value("persistence_length", 5, int)) + + decay_function = settings.value("persistence_decay", "exponential") + i = self.decayFunctionComboBox.findText(decay_function) + if i == -1: + self.decayFunctionComboBox.setCurrentIndex(0) + else: + self.decayFunctionComboBox.setCurrentIndex(i) + + def accept(self): + """Save settings when dialog is accepted""" + settings = QtCore.QSettings() + settings.setValue("persistence_length", self.persistenceLengthSpinBox.value()) + settings.setValue("persistence_decay", self.decayFunctionComboBox.currentText()) + QtGui.QDialog.accept(self) + + +class QSpectrumAnalyzerColors(QtGui.QDialog, Ui_QSpectrumAnalyzerColors): + """QSpectrumAnalyzer colors dialog""" + def __init__(self, parent=None): + # Initialize UI + super().__init__(parent) + self.setupUi(self) + + # 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.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"))) + + def accept(self): + """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("average_color", color_to_str(self.averageColorButton.color())) + settings.setValue("persistence_color", color_to_str(self.persistenceColorButton.color())) + QtGui.QDialog.accept(self) + + class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWindow): """QSpectrumAnalyzer main window""" def __init__(self, parent=None): @@ -113,7 +166,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin self.stop() settings = QtCore.QSettings() - backend = str(settings.value("backend") or "rtl_power") + backend = settings.value("backend", "rtl_power") if backend == "rtl_power_fftw": self.rtl_power_thread = RtlPowerFftwThread() else: @@ -151,24 +204,27 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin def load_settings(self): """Restore spectrum analyzer settings and window geometry""" settings = QtCore.QSettings() - self.startFreqSpinBox.setValue(float(settings.value("start_freq") or 87.0)) - self.stopFreqSpinBox.setValue(float(settings.value("stop_freq") or 108.0)) - self.binSizeSpinBox.setValue(float(settings.value("bin_size") or 10.0)) - self.intervalSpinBox.setValue(float(settings.value("interval") or 10.0)) - self.gainSpinBox.setValue(int(settings.value("gain") or 0)) - self.ppmSpinBox.setValue(int(settings.value("ppm") or 0)) - self.cropSpinBox.setValue(int(settings.value("crop") or 0)) - self.peakHoldCheckBox.setChecked(int(settings.value("peak_hold") or 0)) - self.smoothCheckBox.setChecked(int(settings.value("smooth") or 0)) + self.startFreqSpinBox.setValue(settings.value("start_freq", 87.0, float)) + self.stopFreqSpinBox.setValue(settings.value("stop_freq", 108.0, float)) + self.binSizeSpinBox.setValue(settings.value("bin_size", 10.0, float)) + self.intervalSpinBox.setValue(settings.value("interval", 10.0, float)) + self.gainSpinBox.setValue(settings.value("gain", 0, int)) + 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.averageCheckBox.setChecked(settings.value("average", 0, int)) + self.smoothCheckBox.setChecked(settings.value("smooth", 0, int)) + self.persistenceCheckBox.setChecked(settings.value("persistence", 0, int)) - # Restore window geometry + # Restore window state if settings.value("window_state"): self.restoreState(settings.value("window_state")) if settings.value("plotsplitter_state"): self.plotSplitter.restoreState(settings.value("plotsplitter_state")) # Migration from older version of config file - if int(settings.value("config_version") or 1) < 2: + if settings.value("config_version", 1, int) < 2: # Make tabs from docks when started for first time self.tabifyDockWidget(self.settingsDockWidget, self.levelsDockWidget) self.settingsDockWidget.raise_() @@ -186,17 +242,20 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin def save_settings(self): """Save spectrum analyzer settings and window geometry""" settings = QtCore.QSettings() - settings.setValue("start_freq", float(self.startFreqSpinBox.value())) - settings.setValue("stop_freq", float(self.stopFreqSpinBox.value())) - settings.setValue("bin_size", float(self.binSizeSpinBox.value())) - settings.setValue("interval", float(self.intervalSpinBox.value())) - settings.setValue("gain", int(self.gainSpinBox.value())) - settings.setValue("ppm", int(self.ppmSpinBox.value())) - settings.setValue("crop", int(self.cropSpinBox.value())) + settings.setValue("start_freq", self.startFreqSpinBox.value()) + settings.setValue("stop_freq", self.stopFreqSpinBox.value()) + settings.setValue("bin_size", self.binSizeSpinBox.value()) + settings.setValue("interval", self.intervalSpinBox.value()) + settings.setValue("gain", self.gainSpinBox.value()) + 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("average", int(self.averageCheckBox.isChecked())) settings.setValue("smooth", int(self.smoothCheckBox.isChecked())) + settings.setValue("persistence", int(self.persistenceCheckBox.isChecked())) - # Save window geometry + # Save window state and geometry settings.setValue("window_geometry", self.saveGeometry()) settings.setValue("window_state", self.saveState()) settings.setValue("plotsplitter_state", self.plotSplitter.saveState()) @@ -228,13 +287,29 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin """Start rtl_power thread""" settings = QtCore.QSettings() self.prev_data_timestamp = time.time() - self.waterfallPlotWidget.history_size = int(settings.value("waterfall_history_size") or 100) + self.waterfallPlotWidget.counter = 0 + self.waterfallPlotWidget.history_size = settings.value("waterfall_history_size", 100, int) + + 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_clear() + self.spectrumPlotWidget.peak_hold_color = str_to_color(settings.value("peak_hold_color", "255, 0, 0, 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 = int(settings.value("smooth_length") or 11) - self.spectrumPlotWidget.smooth_window = str(settings.value("smooth_window") or "hanning") + 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() + if not self.rtl_power_thread.alive: self.rtl_power_thread.setup(float(self.startFreqSpinBox.value()), float(self.stopFreqSpinBox.value()), @@ -244,7 +319,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin ppm=int(self.ppmSpinBox.value()), crop=int(self.cropSpinBox.value()) / 100.0, single_shot=single_shot, - sample_rate=int(settings.value("sample_rate") or 2560000)) + sample_rate=settings.value("sample_rate", 2560000, int)) self.rtl_power_thread.start() def stop(self): @@ -264,26 +339,76 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin def on_stopButton_clicked(self): self.stop() + @QtCore.pyqtSlot(bool) + def on_mainCurveCheckBox_toggled(self, checked): + self.spectrumPlotWidget.main_curve = checked + if not checked: + self.spectrumPlotWidget.main_clear() + @QtCore.pyqtSlot(bool) def on_peakHoldCheckBox_toggled(self, checked): self.spectrumPlotWidget.peak_hold = checked if not checked: self.spectrumPlotWidget.peak_hold_clear() + @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 - if self.spectrumPlotWidget.peak_hold: - self.spectrumPlotWidget.peak_hold_clear() + self.spectrumPlotWidget.main_clear() + self.spectrumPlotWidget.peak_hold_clear() + self.spectrumPlotWidget.average_clear() + self.spectrumPlotWidget.persistence_clear() + + @QtCore.pyqtSlot(bool) + def on_persistenceCheckBox_toggled(self, checked): + self.spectrumPlotWidget.persistence = checked + if not checked: + self.spectrumPlotWidget.persistence_clear() @QtCore.pyqtSlot() def on_smoothButton_clicked(self): dialog = QSpectrumAnalyzerSmooth(self) if dialog.exec_(): settings = QtCore.QSettings() - self.spectrumPlotWidget.smooth_length = int(settings.value("smooth_length") or 11) - self.spectrumPlotWidget.smooth_window = str(settings.value("smooth_window") or "hanning") + 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() + + @QtCore.pyqtSlot() + def on_persistenceButton_clicked(self): + prev_persistence_length = self.spectrumPlotWidget.persistence_length + dialog = QSpectrumAnalyzerPersistence(self) + if dialog.exec_(): + settings = QtCore.QSettings() + persistence_length = settings.value("persistence_length", 5, int) + self.spectrumPlotWidget.persistence_length = persistence_length + self.spectrumPlotWidget.persistence_decay = settings.value("persistence_decay", "exponential") + + # If only decay function has been changed, just reset colors + if persistence_length == prev_persistence_length: + self.spectrumPlotWidget.set_colors() + else: + self.spectrumPlotWidget.persistence_clear() + + @QtCore.pyqtSlot() + def on_colorsButton_clicked(self): + dialog = QSpectrumAnalyzerColors(self) + 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.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() @QtCore.pyqtSlot() def on_action_Settings_triggered(self): diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 11dca20..69e883e 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -88,7 +88,7 @@ class RtlPowerThread(RtlPowerBaseThread): if not self.process and self.params: settings = QtCore.QSettings() cmdline = [ - str(settings.value("rtl_power_executable") or "rtl_power"), + settings.value("rtl_power_executable", "rtl_power"), "-f", "{}M:{}M:{}k".format(self.params["start_freq"], self.params["stop_freq"], self.params["bin_size"]), @@ -194,7 +194,7 @@ class RtlPowerFftwThread(RtlPowerBaseThread): if not self.process and self.params: settings = QtCore.QSettings() cmdline = [ - str(settings.value("rtl_power_executable") or "rtl_power_fftw"), + settings.value("rtl_power_executable", "rtl_power_fftw"), "-f", "{}M:{}M".format(self.params["start_freq"], self.params["stop_freq"]), "-b", "{}".format(self.params["bins"]), diff --git a/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts b/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts index e3c4c8b..96cfa2e 100644 --- a/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts +++ b/qspectrumanalyzer/languages/qspectrumanalyzer_cs.ts @@ -1,162 +1,243 @@ - QSpectrumAnalyzerMainWindow + QSpectrumAnalyzerColors - - QSpectrumAnalyzer + + Colors - QSpectrumAnalyzer - - Settings + + Main curve color: - - 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 - - - - + ... - + + Peak hold color: + + + + + Average color: + + + + + Persistence 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 + + + + + QSpectrumAnalyzerPersistence + + + Persistence - QSpectrumAnalyzer + + + + + Persistence length: + + + + + Decay function: + + + + + linear + + + + + exponential + + QSpectrumAnalyzerSettings @@ -196,7 +277,7 @@ - + Select executable - QSpectrumAnalyzer @@ -209,42 +290,42 @@ QSpectrumAnalyzerSmooth - + &Window function: - + rectangular - + hanning - + hamming - + bartlett - + blackman - + Window len&gth: - + Smoothing - QSpectrumAnalyzer diff --git a/qspectrumanalyzer/plot.py b/qspectrumanalyzer/plot.py index 5991208..57adaf0 100644 --- a/qspectrumanalyzer/plot.py +++ b/qspectrumanalyzer/plot.py @@ -1,3 +1,5 @@ +import collections, math + import numpy as np import pyqtgraph as pg @@ -11,14 +13,29 @@ class SpectrumPlotWidget: """Main spectrum plot""" def __init__(self, layout): if not isinstance(layout, pg.GraphicsLayoutWidget): - raise ValueError('layout must be instance of pyqtgraph.GraphicsLayoutWidget') + raise ValueError("layout must be instance of pyqtgraph.GraphicsLayoutWidget") self.layout = layout - self.peak_hold_data = None + + self.main_curve = True + self.main_color = pg.mkColor("y") + self.persistence = False + 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.smooth_window = "hanning" + + self.counter = 0 + self.peak_hold_data = None + self.average_data = None + self.persistence_data = None + self.persistence_curves = None self.create_plot() @@ -32,41 +49,116 @@ class SpectrumPlotWidget: self.plot.setLimits(xMin=0) self.plot.showButtons() - # Create spectrum curve - self.curve = self.plot.plot() - - # Create peak hold curve - self.curve_peak_hold = self.plot.plot(pen='r') + self.create_persistence_curves() + self.create_average_curve() + self.create_main_curve() + self.create_peak_hold_curve() # Create crosshair self.vLine = pg.InfiniteLine(angle=90, movable=False) + self.vLine.setZValue(1000) self.hLine = pg.InfiniteLine(angle=0, movable=False) + self.vLine.setZValue(1000) self.plot.addItem(self.vLine, ignoreBounds=True) self.plot.addItem(self.hLine, ignoreBounds=True) 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) + + def create_average_curve(self): + """Create average curve""" + self.curve_average = self.plot.plot(pen=self.average_color) + self.curve_average.setZValue(700) + + def create_persistence_curves(self): + """Create spectrum persistence curves""" + z_index_base = 600 + decay = self.get_decay() + self.persistence_curves = [] + for i in range(self.persistence_length): + alpha = 255 * decay(i + 1, self.persistence_length + 1) + color = self.persistence_color + curve = self.plot.plot(pen=(color.red(), color.green(), color.blue(), alpha)) + curve.setZValue(z_index_base - i) + self.persistence_curves.append(curve) + + 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_average.setPen(self.average_color) + + decay = self.get_decay() + for i, curve in enumerate(self.persistence_curves): + alpha = 255 * decay(i + 1, self.persistence_length + 1) + color = self.persistence_color + curve.setPen((color.red(), color.green(), color.blue(), alpha)) + + def decay_linear(self, x, length): + """Get alpha value for persistence curve (linear decay)""" + return (-x / length) + 1 + + def decay_exponential(self, x, length, const=1/3): + """Get alpha value for persistence curve (exponential decay)""" + return math.e**(-x / (length * const)) + + def get_decay(self): + """Get decay function""" + if self.persistence_decay == 'exponential': + return self.decay_exponential + else: + return self.decay_linear + def update_plot(self, data): """Update main spectrum plot""" + self.counter += 1 + # Apply smoothing to data if self.smooth: data["y"] = smooth(data["y"], window_len=self.smooth_length, window=self.smooth_window) + # Draw main curve + if self.main_curve: + self.curve.setData(data["x"], data["y"]) + # 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"] + 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) - # Draw main curve - self.curve.setData(data["x"], data["y"]) + # 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) + + # Draw persistence curves + if self.persistence: + 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()) def mouse_moved(self, evt): """Update crosshair when mouse is moved""" @@ -80,23 +172,48 @@ class SpectrumPlotWidget: self.vLine.setPos(mousePoint.x()) self.hLine.setPos(mousePoint.y()) + def main_clear(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 average_clear(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): + """Clear spectrum persistence curves""" + self.persistence_data = None + for curve in self.persistence_curves: + curve.clear() + self.plot.removeItem(curve) + self.create_persistence_curves() class WaterfallPlotWidget: """Waterfall plot""" def __init__(self, layout, histogram_layout=None): if not isinstance(layout, pg.GraphicsLayoutWidget): - raise ValueError('layout must be instance of pyqtgraph.GraphicsLayoutWidget') + raise ValueError("layout must be instance of pyqtgraph.GraphicsLayoutWidget") if histogram_layout and not isinstance(histogram_layout, pg.GraphicsLayoutWidget): - raise ValueError('histogram_layout must be instance of pyqtgraph.GraphicsLayoutWidget') + raise ValueError("histogram_layout must be instance of pyqtgraph.GraphicsLayoutWidget") self.layout = layout self.histogram_layout = histogram_layout + self.history_size = 100 self.counter = 0 diff --git a/qspectrumanalyzer/qspectrumanalyzer.ui b/qspectrumanalyzer/qspectrumanalyzer.ui index 376dc7f..0a410fc 100644 --- a/qspectrumanalyzer/qspectrumanalyzer.ui +++ b/qspectrumanalyzer/qspectrumanalyzer.ui @@ -6,8 +6,8 @@ 0 0 - 1080 - 776 + 1200 + 810 @@ -51,7 +51,7 @@ 0 0 - 1080 + 1200 30 @@ -83,7 +83,7 @@ 190 - 126 + 10 @@ -143,8 +143,8 @@ - 156 - 232 + 225 + 10 @@ -157,8 +157,11 @@ 2 - - + + + QFormLayout::ExpandingFieldsGrow + + Start: @@ -168,8 +171,14 @@ - + + + + 0 + 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -190,7 +199,7 @@ - + Stop: @@ -200,8 +209,14 @@ - + + + + 0 + 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -222,7 +237,7 @@ - + Bin size: @@ -232,8 +247,14 @@ - + + + + 0 + 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -251,7 +272,7 @@ - + Qt::Vertical @@ -285,43 +306,26 @@ - - + + - Smoothing - - - - - - - Corr. [ppm]: + Interval [s]: - ppmSpinBox + intervalSpinBox - - + + - Peak hold + Gain [dB]: + + + gainSpinBox - - - - Qt::Vertical - - - - 20 - 1 - - - - @@ -335,13 +339,80 @@ - - + + - ... + Corr. [ppm]: - - false + + ppmSpinBox + + + + + + + Crop [%]: + + + cropSpinBox + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + -999 + + + 999 + + + + + + + Main curve + + + true + + + + + + + Peak hold + + + + + + + Smoothing + + + + + + + Persistence @@ -364,39 +435,6 @@ - - - - Crop [%]: - - - cropSpinBox - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - -999 - - - 999 - - - - - - - Gain [dB]: - - - gainSpinBox - - - @@ -404,13 +442,37 @@ - - + + - Interval [s]: + Colors... - - intervalSpinBox + + + + + + ... + + + false + + + + + + + ... + + + false + + + + + + + Average @@ -485,9 +547,14 @@ gainSpinBox ppmSpinBox cropSpinBox + mainCurveCheckBox + colorsButton peakHoldCheckBox + averageCheckBox smoothCheckBox smoothButton + persistenceCheckBox + persistenceButton histogramPlotLayout mainPlotLayout waterfallPlotLayout diff --git a/qspectrumanalyzer/qspectrumanalyzer_colors.ui b/qspectrumanalyzer/qspectrumanalyzer_colors.ui new file mode 100644 index 0000000..e2039cc --- /dev/null +++ b/qspectrumanalyzer/qspectrumanalyzer_colors.ui @@ -0,0 +1,187 @@ + + + QSpectrumAnalyzerColors + + + + 0 + 0 + 202 + 214 + + + + Colors - QSpectrumAnalyzer + + + + + + + + Main curve color: + + + mainColorButton + + + + + + + + 0 + 0 + + + + ... + + + + + + + Peak hold color: + + + peakHoldColorButton + + + + + + + + 0 + 0 + + + + ... + + + + + + + Average color: + + + averageColorButton + + + + + + + + 0 + 0 + + + + ... + + + + + + + Persistence color: + + + persistenceColorButton + + + + + + + + 0 + 0 + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 2 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + ColorButton + QPushButton +
pyqtgraph
+
+
+ + mainColorButton + peakHoldColorButton + averageColorButton + persistenceColorButton + buttonBox + + + + + buttonBox + accepted() + QSpectrumAnalyzerColors + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QSpectrumAnalyzerColors + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/qspectrumanalyzer/qspectrumanalyzer_persistence.ui b/qspectrumanalyzer/qspectrumanalyzer_persistence.ui new file mode 100644 index 0000000..7398f29 --- /dev/null +++ b/qspectrumanalyzer/qspectrumanalyzer_persistence.ui @@ -0,0 +1,130 @@ + + + QSpectrumAnalyzerPersistence + + + + 0 + 0 + 250 + 130 + + + + Persistence - QSpectrumAnalyzer + + + + + + + + Decay function: + + + decayFunctionComboBox + + + + + + + 1 + + + + linear + + + + + exponential + + + + + + + + Persistence length: + + + persistenceLengthSpinBox + + + + + + + 5 + + + + + + + + + Qt::Vertical + + + + 20 + 5 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + decayFunctionComboBox + persistenceLengthSpinBox + buttonBox + + + + + buttonBox + accepted() + QSpectrumAnalyzerPersistence + accept() + + + 243 + 123 + + + 157 + 129 + + + + + buttonBox + rejected() + QSpectrumAnalyzerPersistence + reject() + + + 243 + 123 + + + 249 + 129 + + + + + diff --git a/qspectrumanalyzer/qspectrumanalyzer_smooth.ui b/qspectrumanalyzer/qspectrumanalyzer_smooth.ui index 36c1dfa..69804a0 100644 --- a/qspectrumanalyzer/qspectrumanalyzer_smooth.ui +++ b/qspectrumanalyzer/qspectrumanalyzer_smooth.ui @@ -29,7 +29,7 @@ - 0 + 1 @@ -108,6 +108,11 @@ + + windowFunctionComboBox + windowLengthSpinBox + buttonBox + diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer.py b/qspectrumanalyzer/ui_qspectrumanalyzer.py index 463d73b..27e5880 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer.py @@ -25,7 +25,7 @@ except AttributeError: class Ui_QSpectrumAnalyzerMainWindow(object): def setupUi(self, QSpectrumAnalyzerMainWindow): QSpectrumAnalyzerMainWindow.setObjectName(_fromUtf8("QSpectrumAnalyzerMainWindow")) - QSpectrumAnalyzerMainWindow.resize(1080, 776) + QSpectrumAnalyzerMainWindow.resize(1200, 810) self.centralwidget = QtGui.QWidget(QSpectrumAnalyzerMainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget) @@ -55,7 +55,7 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.horizontalLayout.addWidget(self.plotSplitter) QSpectrumAnalyzerMainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(QSpectrumAnalyzerMainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1080, 30)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1200, 30)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menu_File = QtGui.QMenu(self.menubar) self.menu_File.setObjectName(_fromUtf8("menu_File")) @@ -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, 126)) + self.controlsDockWidget.setMinimumSize(QtCore.QSize(190, 10)) self.controlsDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable) self.controlsDockWidget.setObjectName(_fromUtf8("controlsDockWidget")) self.controlsDockWidgetContents = QtGui.QWidget() @@ -97,47 +97,63 @@ class Ui_QSpectrumAnalyzerMainWindow(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frequencyDockWidget.sizePolicy().hasHeightForWidth()) self.frequencyDockWidget.setSizePolicy(sizePolicy) - self.frequencyDockWidget.setMinimumSize(QtCore.QSize(156, 232)) + self.frequencyDockWidget.setMinimumSize(QtCore.QSize(225, 10)) self.frequencyDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable) self.frequencyDockWidget.setObjectName(_fromUtf8("frequencyDockWidget")) self.frequencyDockWidgetContents = QtGui.QWidget() self.frequencyDockWidgetContents.setObjectName(_fromUtf8("frequencyDockWidgetContents")) - self.verticalLayout = QtGui.QVBoxLayout(self.frequencyDockWidgetContents) - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.formLayout = QtGui.QFormLayout(self.frequencyDockWidgetContents) + self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) + self.formLayout.setObjectName(_fromUtf8("formLayout")) self.label_2 = QtGui.QLabel(self.frequencyDockWidgetContents) self.label_2.setObjectName(_fromUtf8("label_2")) - self.verticalLayout.addWidget(self.label_2) + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2) self.startFreqSpinBox = QtGui.QDoubleSpinBox(self.frequencyDockWidgetContents) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.startFreqSpinBox.sizePolicy().hasHeightForWidth()) + self.startFreqSpinBox.setSizePolicy(sizePolicy) self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.startFreqSpinBox.setDecimals(3) self.startFreqSpinBox.setMinimum(24.0) self.startFreqSpinBox.setMaximum(1766.0) self.startFreqSpinBox.setProperty("value", 87.0) self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox")) - self.verticalLayout.addWidget(self.startFreqSpinBox) + self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox) self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents) self.label_3.setObjectName(_fromUtf8("label_3")) - self.verticalLayout.addWidget(self.label_3) + self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_3) self.stopFreqSpinBox = QtGui.QDoubleSpinBox(self.frequencyDockWidgetContents) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.stopFreqSpinBox.sizePolicy().hasHeightForWidth()) + self.stopFreqSpinBox.setSizePolicy(sizePolicy) self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.stopFreqSpinBox.setDecimals(3) self.stopFreqSpinBox.setMinimum(24.0) self.stopFreqSpinBox.setMaximum(1766.0) self.stopFreqSpinBox.setProperty("value", 108.0) self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox")) - self.verticalLayout.addWidget(self.stopFreqSpinBox) + self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox) self.label = QtGui.QLabel(self.frequencyDockWidgetContents) self.label.setObjectName(_fromUtf8("label")) - self.verticalLayout.addWidget(self.label) + self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label) self.binSizeSpinBox = QtGui.QDoubleSpinBox(self.frequencyDockWidgetContents) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.binSizeSpinBox.sizePolicy().hasHeightForWidth()) + self.binSizeSpinBox.setSizePolicy(sizePolicy) self.binSizeSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.binSizeSpinBox.setDecimals(3) self.binSizeSpinBox.setMaximum(2800.0) self.binSizeSpinBox.setProperty("value", 10.0) self.binSizeSpinBox.setObjectName(_fromUtf8("binSizeSpinBox")) - self.verticalLayout.addWidget(self.binSizeSpinBox) + self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.binSizeSpinBox) spacerItem1 = QtGui.QSpacerItem(20, 0, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem1) + self.formLayout.setItem(3, QtGui.QFormLayout.SpanningRole, spacerItem1) self.frequencyDockWidget.setWidget(self.frequencyDockWidgetContents) QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.frequencyDockWidget) self.settingsDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow) @@ -152,27 +168,45 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.settingsDockWidgetContents.setObjectName(_fromUtf8("settingsDockWidgetContents")) self.gridLayout = QtGui.QGridLayout(self.settingsDockWidgetContents) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.smoothCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) - self.smoothCheckBox.setObjectName(_fromUtf8("smoothCheckBox")) - self.gridLayout.addWidget(self.smoothCheckBox, 5, 0, 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.peakHoldCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) - self.peakHoldCheckBox.setObjectName(_fromUtf8("peakHoldCheckBox")) - self.gridLayout.addWidget(self.peakHoldCheckBox, 4, 0, 1, 3) - spacerItem2 = QtGui.QSpacerItem(20, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem2, 6, 0, 1, 1) + self.label_4 = QtGui.QLabel(self.settingsDockWidgetContents) + self.label_4.setObjectName(_fromUtf8("label_4")) + self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) + self.label_6 = QtGui.QLabel(self.settingsDockWidgetContents) + self.label_6.setObjectName(_fromUtf8("label_6")) + self.gridLayout.addWidget(self.label_6, 0, 1, 1, 1) self.intervalSpinBox = QtGui.QDoubleSpinBox(self.settingsDockWidgetContents) self.intervalSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.intervalSpinBox.setMaximum(999.0) self.intervalSpinBox.setProperty("value", 1.0) self.intervalSpinBox.setObjectName(_fromUtf8("intervalSpinBox")) self.gridLayout.addWidget(self.intervalSpinBox, 1, 0, 1, 1) - self.smoothButton = QtGui.QToolButton(self.settingsDockWidgetContents) - self.smoothButton.setAutoRaise(False) - self.smoothButton.setObjectName(_fromUtf8("smoothButton")) - self.gridLayout.addWidget(self.smoothButton, 5, 2, 1, 1) + 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) + self.ppmSpinBox.setMaximum(999) + self.ppmSpinBox.setObjectName(_fromUtf8("ppmSpinBox")) + self.gridLayout.addWidget(self.ppmSpinBox, 3, 0, 1, 1) + self.mainCurveCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents) + 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) @@ -180,25 +214,24 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.gainSpinBox.setProperty("value", -1) self.gainSpinBox.setObjectName(_fromUtf8("gainSpinBox")) self.gridLayout.addWidget(self.gainSpinBox, 1, 1, 1, 2) - self.label_7 = QtGui.QLabel(self.settingsDockWidgetContents) - self.label_7.setObjectName(_fromUtf8("label_7")) - self.gridLayout.addWidget(self.label_7, 2, 1, 1, 2) - self.ppmSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents) - self.ppmSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.ppmSpinBox.setMinimum(-999) - self.ppmSpinBox.setMaximum(999) - self.ppmSpinBox.setObjectName(_fromUtf8("ppmSpinBox")) - self.gridLayout.addWidget(self.ppmSpinBox, 3, 0, 1, 1) - self.label_6 = QtGui.QLabel(self.settingsDockWidgetContents) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout.addWidget(self.label_6, 0, 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.label_4 = QtGui.QLabel(self.settingsDockWidgetContents) - self.label_4.setObjectName(_fromUtf8("label_4")) - self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) + self.colorsButton = QtGui.QPushButton(self.settingsDockWidgetContents) + self.colorsButton.setObjectName(_fromUtf8("colorsButton")) + self.gridLayout.addWidget(self.colorsButton, 4, 1, 1, 2) + 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.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) self.settingsDockWidget.setWidget(self.settingsDockWidgetContents) QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.settingsDockWidget) self.levelsDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow) @@ -238,10 +271,10 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.label_2.setBuddy(self.startFreqSpinBox) self.label_3.setBuddy(self.stopFreqSpinBox) self.label.setBuddy(self.binSizeSpinBox) + self.label_4.setBuddy(self.intervalSpinBox) + self.label_6.setBuddy(self.gainSpinBox) self.label_5.setBuddy(self.ppmSpinBox) self.label_7.setBuddy(self.cropSpinBox) - self.label_6.setBuddy(self.gainSpinBox) - self.label_4.setBuddy(self.intervalSpinBox) self.retranslateUi(QSpectrumAnalyzerMainWindow) QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerMainWindow) @@ -254,10 +287,15 @@ class Ui_QSpectrumAnalyzerMainWindow(object): QSpectrumAnalyzerMainWindow.setTabOrder(self.intervalSpinBox, self.gainSpinBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.gainSpinBox, self.ppmSpinBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.ppmSpinBox, self.cropSpinBox) - QSpectrumAnalyzerMainWindow.setTabOrder(self.cropSpinBox, self.peakHoldCheckBox) - QSpectrumAnalyzerMainWindow.setTabOrder(self.peakHoldCheckBox, self.smoothCheckBox) + 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.averageCheckBox, self.smoothCheckBox) QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothCheckBox, self.smoothButton) - QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothButton, self.histogramPlotLayout) + QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothButton, self.persistenceCheckBox) + QSpectrumAnalyzerMainWindow.setTabOrder(self.persistenceCheckBox, self.persistenceButton) + QSpectrumAnalyzerMainWindow.setTabOrder(self.persistenceButton, self.histogramPlotLayout) QSpectrumAnalyzerMainWindow.setTabOrder(self.histogramPlotLayout, self.mainPlotLayout) QSpectrumAnalyzerMainWindow.setTabOrder(self.mainPlotLayout, self.waterfallPlotLayout) @@ -277,14 +315,19 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.label.setText(_translate("QSpectrumAnalyzerMainWindow", "Bin size:", None)) self.binSizeSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " kHz", None)) self.settingsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Settings", None)) - self.smoothCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Smoothing", None)) - self.label_5.setText(_translate("QSpectrumAnalyzerMainWindow", "Corr. [ppm]:", None)) - self.peakHoldCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Peak hold", None)) - self.smoothButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None)) - self.gainSpinBox.setSpecialValueText(_translate("QSpectrumAnalyzerMainWindow", "auto", None)) - self.label_7.setText(_translate("QSpectrumAnalyzerMainWindow", "Crop [%]:", None)) - self.label_6.setText(_translate("QSpectrumAnalyzerMainWindow", "Gain [dB]:", None)) self.label_4.setText(_translate("QSpectrumAnalyzerMainWindow", "Interval [s]:", None)) + self.label_6.setText(_translate("QSpectrumAnalyzerMainWindow", "Gain [dB]:", 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.averageCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Average", 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 new file mode 100644 index 0000000..f4aa3f4 --- /dev/null +++ b/qspectrumanalyzer/ui_qspectrumanalyzer_colors.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_colors.ui' +# +# Created by: PyQt4 UI code generator 4.11.4 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + +class Ui_QSpectrumAnalyzerColors(object): + def setupUi(self, QSpectrumAnalyzerColors): + QSpectrumAnalyzerColors.setObjectName(_fromUtf8("QSpectrumAnalyzerColors")) + QSpectrumAnalyzerColors.resize(202, 214) + self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerColors) + self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.formLayout = QtGui.QFormLayout() + self.formLayout.setObjectName(_fromUtf8("formLayout")) + self.label_2 = QtGui.QLabel(QSpectrumAnalyzerColors) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2) + self.mainColorButton = ColorButton(QSpectrumAnalyzerColors) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainColorButton.sizePolicy().hasHeightForWidth()) + self.mainColorButton.setSizePolicy(sizePolicy) + self.mainColorButton.setObjectName(_fromUtf8("mainColorButton")) + self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.mainColorButton) + 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) + 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) + self.label_5 = QtGui.QLabel(QSpectrumAnalyzerColors) + self.label_5.setObjectName(_fromUtf8("label_5")) + self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_5) + self.averageColorButton = ColorButton(QSpectrumAnalyzerColors) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + 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.label_3 = QtGui.QLabel(QSpectrumAnalyzerColors) + self.label_3.setObjectName(_fromUtf8("label_3")) + self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_3) + self.persistenceColorButton = ColorButton(QSpectrumAnalyzerColors) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + 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.verticalLayout.addLayout(self.formLayout) + spacerItem = QtGui.QSpacerItem(20, 2, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem) + self.buttonBox = QtGui.QDialogButtonBox(QSpectrumAnalyzerColors) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(_fromUtf8("buttonBox")) + self.verticalLayout.addWidget(self.buttonBox) + self.label_2.setBuddy(self.mainColorButton) + self.label_4.setBuddy(self.peakHoldColorButton) + self.label_5.setBuddy(self.averageColorButton) + self.label_3.setBuddy(self.persistenceColorButton) + + self.retranslateUi(QSpectrumAnalyzerColors) + 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.averageColorButton, self.persistenceColorButton) + QSpectrumAnalyzerColors.setTabOrder(self.persistenceColorButton, self.buttonBox) + + def retranslateUi(self, QSpectrumAnalyzerColors): + 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_5.setText(_translate("QSpectrumAnalyzerColors", "Average color:", None)) + self.averageColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) + self.label_3.setText(_translate("QSpectrumAnalyzerColors", "Persistence color:", None)) + self.persistenceColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None)) + +from pyqtgraph import ColorButton diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer_persistence.py b/qspectrumanalyzer/ui_qspectrumanalyzer_persistence.py new file mode 100644 index 0000000..38fb932 --- /dev/null +++ b/qspectrumanalyzer/ui_qspectrumanalyzer_persistence.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_persistence.ui' +# +# Created by: PyQt4 UI code generator 4.11.4 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + +class Ui_QSpectrumAnalyzerPersistence(object): + def setupUi(self, QSpectrumAnalyzerPersistence): + QSpectrumAnalyzerPersistence.setObjectName(_fromUtf8("QSpectrumAnalyzerPersistence")) + QSpectrumAnalyzerPersistence.resize(250, 130) + self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerPersistence) + self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.formLayout = QtGui.QFormLayout() + self.formLayout.setObjectName(_fromUtf8("formLayout")) + self.label_2 = QtGui.QLabel(QSpectrumAnalyzerPersistence) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2) + self.decayFunctionComboBox = QtGui.QComboBox(QSpectrumAnalyzerPersistence) + self.decayFunctionComboBox.setObjectName(_fromUtf8("decayFunctionComboBox")) + self.decayFunctionComboBox.addItem(_fromUtf8("")) + self.decayFunctionComboBox.addItem(_fromUtf8("")) + self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.decayFunctionComboBox) + self.label = QtGui.QLabel(QSpectrumAnalyzerPersistence) + self.label.setObjectName(_fromUtf8("label")) + self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label) + self.persistenceLengthSpinBox = QtGui.QSpinBox(QSpectrumAnalyzerPersistence) + self.persistenceLengthSpinBox.setProperty("value", 5) + self.persistenceLengthSpinBox.setObjectName(_fromUtf8("persistenceLengthSpinBox")) + self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.persistenceLengthSpinBox) + self.verticalLayout.addLayout(self.formLayout) + spacerItem = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem) + self.buttonBox = QtGui.QDialogButtonBox(QSpectrumAnalyzerPersistence) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(_fromUtf8("buttonBox")) + self.verticalLayout.addWidget(self.buttonBox) + self.label_2.setBuddy(self.decayFunctionComboBox) + self.label.setBuddy(self.persistenceLengthSpinBox) + + self.retranslateUi(QSpectrumAnalyzerPersistence) + self.decayFunctionComboBox.setCurrentIndex(1) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerPersistence.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerPersistence.reject) + QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerPersistence) + QSpectrumAnalyzerPersistence.setTabOrder(self.decayFunctionComboBox, self.persistenceLengthSpinBox) + QSpectrumAnalyzerPersistence.setTabOrder(self.persistenceLengthSpinBox, self.buttonBox) + + def retranslateUi(self, QSpectrumAnalyzerPersistence): + QSpectrumAnalyzerPersistence.setWindowTitle(_translate("QSpectrumAnalyzerPersistence", "Persistence - QSpectrumAnalyzer", None)) + self.label_2.setText(_translate("QSpectrumAnalyzerPersistence", "Decay function:", None)) + self.decayFunctionComboBox.setItemText(0, _translate("QSpectrumAnalyzerPersistence", "linear", None)) + self.decayFunctionComboBox.setItemText(1, _translate("QSpectrumAnalyzerPersistence", "exponential", None)) + self.label.setText(_translate("QSpectrumAnalyzerPersistence", "Persistence length:", None)) + diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer_smooth.py b/qspectrumanalyzer/ui_qspectrumanalyzer_smooth.py index 031a14a..dceb797 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer_smooth.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer_smooth.py @@ -62,10 +62,12 @@ class Ui_QSpectrumAnalyzerSmooth(object): self.label_2.setBuddy(self.windowLengthSpinBox) self.retranslateUi(QSpectrumAnalyzerSmooth) - self.windowFunctionComboBox.setCurrentIndex(0) + self.windowFunctionComboBox.setCurrentIndex(1) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerSmooth.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerSmooth.reject) QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerSmooth) + QSpectrumAnalyzerSmooth.setTabOrder(self.windowFunctionComboBox, self.windowLengthSpinBox) + QSpectrumAnalyzerSmooth.setTabOrder(self.windowLengthSpinBox, self.buttonBox) def retranslateUi(self, QSpectrumAnalyzerSmooth): QSpectrumAnalyzerSmooth.setWindowTitle(_translate("QSpectrumAnalyzerSmooth", "Smoothing - QSpectrumAnalyzer", None)) diff --git a/qspectrumanalyzer/utils.py b/qspectrumanalyzer/utils.py index b9879f5..18f67d3 100644 --- a/qspectrumanalyzer/utils.py +++ b/qspectrumanalyzer/utils.py @@ -1,5 +1,7 @@ import numpy as np +from PyQt4 import QtGui + def smooth(x, window_len=11, window='hanning'): """Smooth 1D signal using specified window with given size""" @@ -23,3 +25,13 @@ def smooth(x, window_len=11, window='hanning'): y = np.convolve(w / w.sum(), s, mode='same') return y[window_len - 1:-window_len + 1] + + +def str_to_color(color_string): + """Create QColor from comma sepparated RGBA string""" + return QtGui.QColor(*[int(c.strip()) for c in color_string.split(',')]) + + +def color_to_str(color): + """Create comma separated RGBA string from QColor""" + return ", ".join([str(color.red()), str(color.green()), str(color.blue()), str(color.alpha())])