Example 3: Writing Your Own Dialog Script¶
This example shows how to create your own graphical dialogs with the application wxFormBuilder (wxFB) and to visualize the measurement values inside the dialog in real-time.
As this measurement script will need a considerable amount of time ( > 3s) for execution, the multi-threading template tmpusrscriptmthread.py will be used to build the application.
Note
Narda provides links to offers from third parties in this documentation. The providers of these pages are exclusively responsible for the contents and in particular any damages that may arise from the use or non-use of the information provided in this way. Narda only checks the pages at the time the link is created. All subsequent changes are the responsibility of the provider.
Step 1) Define the ‘Measurement Sequence’ and the ‘Dialog’¶
This example application is intended to measure level meter values and display them live in a dialog. The user should be able to end the measurement by clicking a dialog button.
To create this application, we can segregate the scripting steps for the measurement sequence and the dialog as follows:
a) Measurement Sequence¶
For the measurements, the scripts must perform the following steps:
Get measurement settings like detector and measurement time and setup the corresponding variables
Connect to the device
Add RT Spectrum Task and Level Meter View if they do not already exist
Setup the device with the measurement settings
Start the measurement and open the dialog
Acquire level meter values and send them to the dialog using the “update_progress” method
Display the level meter values in dialog until the “stopevent” flag is set
Disconnect from the device
b) Dialog Requirements¶
The dialogs must perform the following tasks to show the live spectrum measurements:
Show the real-time level values as a static text label inside the dialog
Show a progress bar indicating that the measurements are running
Show a status label and an icon
Show a “Cancel” button to end the dialog
This application requires the measurement sequence in step 1a) to interact with the dialogs defined in Step 1b) from the start of the measurements till the end of the measurements. Therefore, the scripts are created using the multithreading template tmpusrscriptmthread.py that is described in Example 2. The next steps of this example will explain how to modify the multithreading template to add a dialog and meet the requirements stated in step 1a) and 1b).
The “MeasDlgThread” is the base class for the measurement thread that keeps the graphical user interface (GUI) alive while the measurements are being performed. This base class offers a standard dialog to display control buttons, a status label, a progress bar and an icon. It also supports the use of a user-defined dialog based on the class “DataVisualizationDlg” for additional visualization of measurement results. It is important to note the following:
The dialog classes can be accessed within the “_run_script” of the tmpusrscriptmthread.py only.
It is not possible to access the controls of the dialog directly from within the measurement method “_run_measurement” of the tmpusrscriptmthread.py. To access these controls, please use the method “update_progress” to update the buttons, the status label, the icon or the progress bar and to send measurement data to the dialog.
More details on the MeasDlgThread and DataVisualizationDlg can be found in chapter Additional Multithreading Functions: in the Scripting Tutorial.
Step 2) Using a wxFB Base Dialog Template¶
The wxFormBuilder (wxFB) is an open source tool for creating graphical dialogs. It can be downloaded from a third party link here:
https://sourceforge.net/projects/wxformbuilder/
This step will help a user to create a dialog in the wxFB using a project template and will also help to generate scripts that can interact with the data visualization dialog class (DataVisualizationDlg).
a) Start with the Base Dialog Template¶
Predefined wxFB project template MyDialogs.fbp is available for dialog creation. It provides two base dialogs that can be modified by the user as per the requirements:
MySettingsBaseDlg (wxDialog) is a simple dialog
MyMeasGUIBaseDlg (DataVisualizationDlg) can be used together with the class “MeasDlgThread” to measure values in a separate thread and display them in the main GUI thread

The ‘dialog creation sequence’ in step 1b) requires progress update, therefore MyMeaseGUIBaseDlg will be used. The “MeasDlgThread” class will measure the values of the spectrum level in a separate thread and will allow the dialog to access the value for display. The MyMeasGUIBaseDlg contains the following widgets that are also supported by the base class “DataVisualizationDlg”:
wxStaticText: To visualize the level meter value in the form of text
wxGauge: To display the progress bar
wxStaticBitmap: To display icon
wxStdDialogButtonSizer: A standard button to stop measurements and close the dialog
For more complex dialogs, it is helpful to make a rough sketch of the dialog structure first, and add/delete the required widgets by modifying the provided template MyDialogs.fbp.
b) Adapt the Base Dialog Template¶
Adapt the template file according to the defined goal in step 1b). The template MyMeasGUIBaseDlg from the template file MyDialogs.fbp used for this example gives the following adapted dialog:

As the measurement template cannot access the GUI template directly, we use the data visualization class (DataVisualizationDlg) from measurement threading abstraction layer to update the progress bar within the dialog. For this purpose, DataVisualizationDlg must be defined as the subclass of the MyMeaseGUIBaseDlg in the properties tab of the dialog:

Note
Note: For other applications, more GUI objects can be added/removed within the project template MyDialogs.fbp as per the requirement. A number of widgets can be found in the component palette of wxFB:

For more details on the wxFB, the following external link can be used:
https://www.tutorialspoint.com/wxpython/wxpython_quick_guide.htm
c) Generate Script for the Base Dialog¶
The project template MyDialogs.fbp provides a graphical layout of the dialog only. Once the layout is created as per the user requirement, the dialog script can be created using the “Generate Code” button on the main palette of the wxFB application as shown in the figure below:

Before generating the code, the path of the wxFB project must be set to the project directory of the IDE containing the measurement scripts.
The object properties of the project template MyDialogs.fbp show several code generation options provided by wxFB.

We use Python as the code generation language. Once the generate button is pressed by the user, a script mydialogsbase.py is created with .py extension representing the python code. Hence, the template MyDialogs.fbp provides only the graphical layout of the dialogs in a wxFB project file, whereas the template mydialogsbase.py provides the python script generated for this wxFB project.
The naming conventions also need to be taken into account:
The project file name becomes the module name with .py extension
The dialog name becomes the class name
The object names become the class variables
The event names for each object become the class methods
Note
It is recommended to use an IDE such as PyCharm to create and edit a script. These offer the possibility of debugging the script execution in addition to autocompletion of code.
The Narda Script Launcher API uses four spaces per indentation. The type of indentation must not be changed within a script!
The script mydialogsbase.py generated with MyDialogs.fbp template imports all the relevant modules itself and declares two classes i.e. MySettingsBaseDlg for simple dialog, and MyMeasGUIBaseDlg for multithreading dialog derived from DataVisualizationDlg.
Do not edit the base dialog file mydialogsbase.py generated with wxFB.
Step 3) Derive from the classes created by the wxFB tool¶
a) Start with the Dialog Events Template¶
As mentioned in the previous step, we do not edit the base dialog script mydialogsbase.py generated by the wxFB.
Instead, we create a class mydialogs.py that is derived from the base dialog template mydialogsbase.py. Here we can override the virtual event handlers and add the actual functionality to our application.
b) Adapt the derived dialog class(es)¶
The derived classes from the base classes generated in Step 2) are:
MySettingsDlg(MySettingsBaseDlg): This class represents a simple wxDialog and derives from the base class MySettingsBaseDlg.
MyMeasGUIDlg(MyMeasGUIBaseDlg): This class is derived from the base class dialog created for the visualization of data. As DataVisualizationDlg is its parent class, it allows the use of the update_progress method that allows the progress bar to be updated and the level meter values to be passed to the dialog. The event handlers of MyMeasGUIBaseDlg are overridden by the MyMeasGUIDlg class.
For this application, we code the MyMeasGUIDlg class only. The MySettingsDlg remains unused for this example.
The data visualization class (DataVisualizationDlg) defines an abstract method “update_progress” that receives data from the thread and updates the progress bar and message text.
It is important to note that the base dialog (MyMeasGUIBaseDlg) derived from the data visualization class (DataVisualizationDlg) using wxFB does not override the update_progress function. Therefore, an event handling class (MyMeasGUIDlg) is derived from the base dialog to override this function.
A detailed description of the “update_progress” function can be found in chapter Additional Multithreading Functions: in the Scripting Tutorial.
Note
For other applications, if virtual event handlers are used in any of the base dialog classes (MySettingsBaseDlg or MyMeasGUIBaseDlg), over-ride them here in the derived class.
c) Add Packages to the Import Section of Module¶
In this example we derive the event class from the base class generated with wxFormBuilder.
To import the base dialog class in the event class, the following line of code is added to the import section besides the nardascripting base classes:
from .mydialogsbase import MySettingsBaseDlg, MyMeasGUIBaseDlg
1 2 3 4 5 6 | import wx
from nardascripting.base.signalsharkdev import *
from nardascripting.base.usrscriptbase import *
from nardascripting.base.measthread import *
from .mydialogsbase import MySettingsBaseDlg, MyMeasGUIBaseDlg
from . import scriptimages
|
Step 4) Using the Measurement Template¶
Predefined template files are available for various kind of applications.
For more information see: Script Templates
a) Start with the Multithreading Template¶
The ‘measurement sequence’ as described in ‘step 1a)’ takes an indefinite time. Therefore we have to use to use the template tmpusrscriptmthread.py. The main difference with the simple template is that it utilizes multithreading to keep the graphical user interface “alive”.
b) Adapt the Multithreading Template¶
In this step we have to adapt the template file tmpusrscriptmthread.py according to our defined goal. The adapted script for this example is provided as the template maxvaluemeas.py.
Note
It is recommended to use an IDE such as PyCharm to create and edit a script. These offer the possibility of debugging the script execution in addition to autocompletion of code.
The Narda Script Launcher API uses four spaces per indentation. The type of indentation must not be changed within a script!
Name the script and add a description.¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class MyMeasurement(UsrScriptBase):
"""User script class"""
def __init__(self, main_gui, dev=SignalSharkDev()):
"""Initialization. Please leave this line unchanged"""
super().__init__(main_gui, dev, __file__)
# Script settings.
# -------------------------------------------------------------------------------
# Please adapt the following lines of code according to your script.
self._tab_name = 'Examples'
self._scr_title = 'Examples03 - Maximum Value Measurement'
self._scr_description = 'Starts the measurement, displays value of each measurement in a dialog, ' \
'and outputs the maximum value. '
# self._icon_path = self.script_path.joinpath('NardaScriptLauncher_example01.png')
self._list_prio = 3
self._nsl_executed_behavior = NSL_Executed_Behaviors.SHOW_NSL
# Add class variables if needed
# -------------------------------------------------------------------------------
# self.my_variable = None
|
Setup the measurement thread¶
This part of the code deals with both scripting elements described in step 1a) and 1b), i.e. the measurement sequence and the dialog. Hence, the dialog and measurement thread, both need to be instantiated here.
To instantiate the dialog for data visualization, add the following line of code in the “_run_script” method:
meas_dlg = MyMeasGUIDlg(self.main_gui, “Title”)
This meas_dlg is then passed in the instantiation of the measurement thread base class as an optional parameter dlg, whereas the measurements are fetched with the callback parameter from the “_run_measurement” method:
- mthread = MeasDlgThread( main_gui=self.main_gui, dlg =meas_dlg,
callback =MyMeasurement._run_measurement, args=[self.signalshark.addr, measurement_steps] )
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 | def _run_script(self, args):
""" Script main function
This method is called when a user clicks on the corresponding script button.
Use the dialog classes only in this part of the code.
"""
# Optional: Prepare some parameters for the measurement.
measurement_steps = 10
# Instantiate dialog event class.
# -------------------------------------------------------------------------------
meas_dlg = MyMeasGUIDlg(self.main_gui, 'Title')
# Create an additional measurement thread to keep the GUI alive.
# Please change 'MyUserScript._run_measurement' according to your class name.
# With 'args=[param1, parm2, paramn]' you can pass some optional parameters to the measurement thread.
mthread = MeasDlgThread(main_gui=self.main_gui, dlg=meas_dlg,
callback=MyMeasurement._run_measurement,
args=[self.signalshark.addr, measurement_steps])
# Start the measurement thread and show a visualization (progress) dialog.
mthread.start_measurement()
# The lines of code below will be executed after the measurement thread has done it's job.
# ...
# Evaluate the result of the measurement thread (if any).
if mthread.result is not None:
self.MessageBoxModal('Max value: ' + str(mthread.result),
'Measurement Result', wx.OK | wx.ICON_INFORMATION)
|
Code the defined ‘Measurement Sequence’ in the Thread Function “_run_measurement“¶
This part of the code deals with the measurement thread only. The ‘measurement sequence’ defined in step 1a) is coded here using the multithreading template.
A detailed description of the functions “stopevent”, “update_progress” and “wait_for_stopevent” can be found in chapter Additional Multithreading Functions:.
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 | @staticmethod
def _run_measurement(stopevent, update_progress, wait_for_stopevent,
signalshark_addr: str, measurement_steps: int):
""" Main method of the measurement thread that handles a time consuming measurement.
Please do NOT use any wxPython elements like a MessageBox or a Dialog here!
They are only permitted in the GUI thread ('_run_script' method).
Please use the 'update_progress' method to display information!
:param stopevent: Thread flag that indicates whether the procedure should be finished/cancelled
:param update_progress: Delegate method to update a progress bar, message string and icon
:param wait_for_stopevent: Delegate method to wait until stopevent is raised with possibility to show a message.
:param signalshark_addr: Custom parameter to handover the ip address of the SignalShark
:param measurement_steps: Custom parameter
:return: Optional custom parameter
"""
# Initialize return value (if any)
max_value = -999.0
# Setup connection to the SignalShark and display error message if connection fails.
signalshark = SignalSharkDev(signalshark_addr)
if not signalshark.connect():
return wait_for_stopevent(msg='Cannot connect to the device!', icon_style=wx.ICON_ERROR) # Returns None
# Clear SCPI error queue
signalshark.scpi.check_error()
try:
# Prepare progress value calculation. Progress is a value from 0.0 to 1.0
progress_step = 1.0 / measurement_steps
act_progress = 0.0
# Setup measurement:
# Check if desired task exists.
# If task does not exist, add one and do initialization stuff.
if signalshark.scpi.check_add_task(TaskTypes.RT_SPECTRUM, 'MyMeas'):
pass
# Check if desired view exists and add one if it does not.
exists, index = signalshark.scpi.check_add_view(ViewTypes.LEVEL)
if not exists:
# return wait_for_stopevent('Please setup the newly added Level Meter View.')
return wait_for_stopevent('Level Meter View was added to the task. '
'Please setup Level Meter View and restart the script.')
# Update the dialog message text and show a progress bar
update_progress(msg='Starting measurement', show_progress=True)
# Loop, that simulates a time consuming measurement
scan_number = -1
last_scan_number = -1
loop_cnt = 0
while loop_cnt < measurement_steps:
if stopevent.is_set():
return None
# Example measurement:
# Wait until Scan Number has increased.
scan_number = signalshark.scpi.levelmeter.get_data_update(last_scan_number)
if scan_number < 0:
return wait_for_stopevent('Cannot acquire measurement data!', icon_style=wx.ICON_ERROR)
elif scan_number > last_scan_number:
last_scan_number = scan_number
else:
stopevent.wait(0.5)
continue
# Update maximum value if determined level meter value is greater.
det_val, det_minmax = signalshark.scpi.levelmeter.get_data_detector(1)
if det_val > max_value:
max_value = det_val
# Update the progress bar.
act_progress += progress_step
update_progress(progress=act_progress, data=det_val)
loop_cnt += 1
# Allow the thread to synchronize the stop event.
stopevent.wait(0.5)
# Return some optional measurement result (number, string, list, dict, object ...)
return max_value
except Exception as e:
# Do some error handling
return wait_for_stopevent(str(e), icon_style=wx.ICON_ERROR) # Returns None
finally:
# Close connection.
signalshark.disconnect()
|
Add Packages to the Import Section of the Module¶
If any of the used packages in the measurement script is not a part of the standard Python library, then import it before use.
In this example the dialog event class is instantiated for use in measurement thread. To import this class, add the following line of code to the import section:
from .gui.mydialogs import MyMeasGUIDlg
1 2 3 4 5 6 7 | from pathlib import Path
import wx
from nardascripting.base.usrscriptbase import *
from nardascripting.base.signalsharkdev import *
from nardascripting.base.measthread import MeasDlgThread
# Import the dialog event class
from .gui.mydialogs import MyMeasGUIDlg, MySettingsDlg
|
Step 4) Debug the Script¶
It is recommended to use an IDE such as PyCharm to create and edit a script. These offer the possibility of debugging the script execution in addition to autocompletion of code.
The application note “Developing Scripts for SignalShark with PyCharm.pdf”, which can be downloaded from the Narda website, describes how to use the Narda Script Launcher together with PyCharm.
Note
The Narda Script Launcher API uses four spaces per indentation. The type of indentation must not be changed within a script!
You can view and download the complete script example here example3.zip