plotdialogs.pyΒΆ
This is the event handling script derived from the base dialog script plotdialogsbase.py generated using wxFormBuilder template PlotDialogs.fbp.
The event handling template file allows to override any virtual event handler used in the base script and to define new classes to cater for the needs of the application.
It uses the matplotlib to integrate plots in the dialog panel. For detailed description see: Integrating matplotlib with wxPython Dialogs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | #!/usr/bin/env python
import wx
import matplotlib as mpl
mpl.use('WXAgg') # switch backend (case insensitive)
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from operator import itemgetter, attrgetter
import numpy as np
from dataclasses import dataclass
from nardascripting.base.signalsharkdev import *
from nardascripting.base.usrscriptbase import *
from .plotdialogsbase import PlotSettingsBaseDlg, PlotBaseDlg
from nardascripting.base.measthread import *
class PlotSettingsDlg(PlotSettingsBaseDlg):
"""Settings dialog"""
def __init__(self, parent, msg: str):
"""Initialization"""
super().__init__(parent)
self.lbl_my_label.SetLabelText(msg)
# Code here ---------------------------------------------------------------------------------------
# Add more methods here if required
# If virtual event handlers are used in MySettingsBaseDlg, over-ride them here in the derived class
# -------------------------------------------------------------------------------------------------
def on_my_button_clicked(self, event):
# self.lbl_my_label.SetLabelText('clicked')
event.Skip()
def on_cancel(self, event):
event.Skip()
def on_ok(self, event):
event.Skip()
@dataclass
class SpectrumSettings:
"""Class for keeping track of measurement parameters."""
# General settings
valid: bool = False
meas_time:float = 0.0
comment: str = ''
# Amplitude settings
trace_list: [] = None
trace_3rd_detector: Spec3rdDetectorModes = Spec3rdDetectorModes.UNKNOWN
trace_infinite: bool = False
input: int = 1
atten: float = 0.0
unit: LevelUnits = LevelUnits.UNKNOWN
lmax: float = 0.0
lrange: float = 0.0
# Frequency settings
fstart: float = 0.0
fstep: float = 0.0
bin_count: int = 0
rbw:float = 0.0
rbw_filter_type:RbwFilterTypes = RbwFilterTypes.UNKNOWN
@property
def fstop(self):
"""Calculates the stop frequency"""
return self.fstart + (self.fstep*self.bin_count)
def get_flist(self, divider:float = 1.0):
""" Calculates the x-axis frequencies
:param divider: Factor by which the frequency is divided (e.g. 1.0e6 to represent MHz)
:type divider: float
:return:
"""
retvalue = []
if self.bin_count > 0 and divider != 0.0:
freq = self.fstart / divider
freq_step = self.fstep / divider
retvalue.append(freq)
for x in range(self.bin_count-1):
freq += freq_step
retvalue.append(freq)
return retvalue
@property
def lmin(self):
"""Calculates the y-axis minimum level"""
return self.lmax - self.lrange
def clone(self):
"""Clones the current data class"""
retvalue = None
try:
retvalue = SpectrumSettings()
retvalue.trace_list = self.trace_list
retvalue.trace_3rd_detector = self.trace_3rd_detector
retvalue.trace_infinite = self.trace_infinite
retvalue.input = self.input
retvalue.atten = self.atten
retvalue.unit = self.unit
retvalue.lmax = self.lmax
retvalue.lrange = self.lrange
retvalue.fstart = self.fstart
retvalue.fstep = self.fstep
retvalue.bin_count = self.bin_count
retvalue.rbw = self.rbw
retvalue.rbw_filter_type = self.rbw_filter_type
retvalue.meas_time = self.meas_time
retvalue.comment = self.comment
except:
retvalue = None
return retvalue
@staticmethod
def from_device(dev: SignalSharkDev):
""" Creates a SpectrumSettings object and reads data from device
:param dev: The SignalShark device
:type dev: SignalSharkDev
:return: SpectrumSettings
:rtype: SpectrumSettings
"""
retvalue = None
try:
retvalue = SpectrumSettings()
if dev.connect():
dev.scpi.check_error()
retvalue.trace_list = dev.scpi.spectrum.get_trace_list()
retvalue.trace_3rd_detector = dev.scpi.spectrum.get_trace_3rd_detector()
retvalue.trace_infinite = dev.scpi.spectrum.get_trace_infinite()
retvalue.input = dev.scpi.sense.get_input()
retvalue.atten = dev.scpi.sense.get_attenuator()
retvalue.unit = dev.scpi.display.get_unit()
retvalue.lmax = dev.scpi.display.get_spectrum_lmax()
retvalue.lrange = dev.scpi.display.get_spectrum_lrange()
retvalue.fstart = dev.scpi.spectrum.get_data_frequency_start()
retvalue.fstep = dev.scpi.spectrum.get_data_frequency_step()
retvalue.bin_count = dev.scpi.spectrum.get_data_count()
retvalue.rbw = dev.scpi.spectrum.get_rbw()
retvalue.rbw_filter_type = dev.scpi.spectrum.get_rbw_filter_type()
retvalue.meas_time = dev.scpi.spectrum.get_measurement_time()
retvalue.valid = dev.scpi.check_no_error()
except:
retvalue = None
return retvalue
class TracePlotCanvasPnl(wx.Panel):
"""wxWidget plot panel based on matplotlib plot"""
def __init__(self, parent, tag: str = ''):
""" wxWidget plot panel based on matplotlib plot
:param parent: The parent widget.
:type parent: wx.Widget
:param tag: Optional string tag.
:type tag: str
"""
super(TracePlotCanvasPnl, self).__init__(parent, size=(450, 360))
self.tag = tag
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
# Workaround for size issue
self.canvas.SetMinSize(wx.Size(10, 10))
# self.toolbar = NavigationToolbar(self.canvas)
# self.toolbar.Realize()
self.x_values = []
self.lines = {}
self.ty = []
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, proportion=1, flag=wx.ALL | wx.GROW)
# self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
self.SetSizer(self.sizer)
self.Fit()
self.trace_colors = {
'': '#000000',
'UNKNOWN': '#000000',
'RMS': '#00C000',
'PPk': '#FF0000',
'MPk': '#0000FF',
'Avg': '#0000FF',
'Smp': '#0000FF',
'MnR': '#003955',
'AvR': '#FF00FF',
'MxR': '#553900',
'MxP': '#AA0000',
'MnP': '#0000AA',
'MxA': '#0000AA',
'MxS': '#0000AA'
}
def clear_memory(self):
"""Clears the actual traces"""
self.lines = {}
for i, line in enumerate(self.axes.lines):
self.axes.lines.pop(i)
line.remove()
self.x_values = []
def update_settings(self, spectrum_settings: SpectrumSettings, title:str = ''):
"""
:param spectrum_settings: Measurement settings of the Spectrum View.
:type spectrum_settings: SpectrumSettings
:param title: Title of the plot panel.
:type title: str
"""
self.clear_memory()
self.axes.set_ylabel('Power/{}'.format(spectrum_settings.unit))
self.axes.set_xlabel('Frequency/MHz')
self.axes.set_title(title)
self.axes.set_autoscale_on(False)
self.axes.set_autoscalex_on(False)
self.axes.set_autoscaley_on(False)
self.axes.grid(True)
self.axes.set_xbound(spectrum_settings.fstart / 1e6, spectrum_settings.fstop / 1e6)
self.axes.set_ybound(spectrum_settings.lmin, spectrum_settings.lmax)
self.figure.subplots_adjust(left=0.2, right=0.9, bottom=0.2, top=0.9)
self.x_values = spectrum_settings.get_flist(1e6)
# self.ty = [float(self.user_selected_value)] * spectrum_settings.bin_count
# self.axes.plot(self.x_values, self.ty)
for trace in spectrum_settings.trace_list:
trace_name = str(trace)
y_values = [0.0]*spectrum_settings.bin_count
self.lines[str(trace)] = self.axes.plot(self.x_values, y_values,
color=self.trace_colors.get(trace_name, '#000000'),
label=trace_name)
self.axes.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc='lower left',
ncol=4, mode='expand', borderaxespad=0.)
def update_y_values(self, trace: str, y_values: list):
""" Updates a trace with new y data.
:param trace: Trace name
:type trace: str
:param y_values: List of level values.
:type y_values: list of float
"""
line = self.lines.get(trace)
if not line:
return
line[0].set_ydata(y_values)
def update_plot(self):
self.canvas.draw()
class SpectrumPlotDlg(PlotBaseDlg):
"""Custom dialog class for data visualization."""
def __init__(self, parent, title: str, spectrum_settings: SpectrumSettings):
"""Dialog class for data visualization.
:param parent: The parent window as wx.Window.
:param title: The dialog caption.
:param spectrum_settings: The spectrum settings.
"""
super().__init__(parent=parent)
self.init_progress_bar(self.pgb_progress)
self.init_dlg_buttons(self.sdbs_buttons, wx.OK)
self.btn_ok.SetLabel('Quit')
self.SetTitle(title)
self.spectrum_settings = spectrum_settings
# self.Maximize(True)
# Add plot area:
# ---------------------------------------------------------------------
self.plt = TracePlotCanvasPnl(self.pnl_plot_container)
self.plt.update_settings(spectrum_settings)
self.szr_plot.Add(self.plt, 1, wx.EXPAND | wx.ALL, 5)
def update_progress(self, evt: ProgressEvent):
"""Receives data from thread and updates progressbar and message text
:param evt: Event of type ProgressEvent.
"""
# Handle progress
self.update_progress_bar(evt, self.pgb_progress)
# Handle message
if evt.msg is not None:
self.lbl_status.SetLabelText(evt.msg)
# Handle data:
if evt.data is not None:
for trace_data in evt.data:
self.plt.update_y_values(trace_data[0], trace_data[1])
self.plt.update_plot()
# Handle buttons:
if evt.btn_style:
self.update_btn_style(evt.btn_style)
# Handle icons:
if evt.icon_style:
self.update_icon_style(self.bmp_icon, evt.icon_style)
# Handle help text:
if evt.help_text:
self.help_text = evt.help_text
self.Layout()
self.Refresh()
self.Update()
# Code here --------------------------------------------------------------------------------------
# Add more methods here if required
# If virtual event handlers are used in MyMeasGUIBaseDlg, over-ride them here in the derived class
# ------------------------------------------------------------------------------------------------
# This is just a python module containing helper classes/functions:
if __name__ == '__main__':
pass
|