CustomFunction: Difference between revisions
Franz Hempel (talk | contribs) (→main) |
|||
(39 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
The CustomFunction module is designed to allow users to | The CustomFunction module is designed to allow users to run their own Python functions within SweepMe!. These Python functions can be used to receive data from multiple modules to analyze data or calculate new parameters. | ||
== Features == | |||
* Access to measured data from all other modules. | |||
* Integration with external/custom Python scripts. | |||
* Generate simulated data for testing devices and adjusting parameters. | |||
* Automate manipulating or copying data files post-measurement, including saving in preferred formats. | |||
* Develop input and output GUI widgets to customize control and display. | |||
== Usage == | |||
version: 2022-08-17 or later | |||
The position of the CustomFunction module in the [[Sequencer]] is essential. It determines the amount of data that is handed over to the CustomFunction function. | The position of the CustomFunction module in the [[Sequencer]] is essential. It determines the amount of data that is handed over to the CustomFunction function. | ||
Line 9: | Line 20: | ||
# Add a CustomFunction module to the sequencer | # Add a CustomFunction module to the sequencer | ||
# Go to the tab of the CustomFunction module | # Go to the tab of the CustomFunction module | ||
# Press " | # Press "Copy selected function" and enter the name of the new function, it will be a copy of the example function. | ||
# You can modify your new function using 'Open/Modify' | # You can modify your new function using 'Open/Modify' | ||
# Your new function can be found in the public folder '.\DataModules\CustomFunction\Functions'. This folder can be found | # Your new function can be found in the public folder '.\DataModules\CustomFunction\Functions'. This folder can also be found by using the button "Open folder". | ||
# Edit the arguments of the function 'main' to add or remove further GUI items that are displayed to the user. | |||
# Change the lists 'variables' and 'units' and return the corresponding values at the end of the function 'main'. | |||
# Use the button 'Reload' to refresh the CustomFunction module after you did changed to your function. | |||
=== Editing a function === | |||
A CustomFunction script is basically a python file that must contain a class 'Main' with a function 'main'. | |||
Each class muss contain the static variables 'variables' and 'units' | |||
* '''variables''': a list of strings defining the names of the values that are returned by the function 'main' | |||
* '''units''': a list of strings defining the names of the units corresponding to variables | |||
* '''variables''' and '''units''' need to have the same length as the number of returned values of your function 'main' | |||
* A function 'main' must be added that returns as many values as defined by the length of '''variables''' and '''units'''. | |||
There are two ways to define arguments that are processed by the function 'main': | |||
* New style: The arguments are defined by the static variable 'arguments' using a dictionary | |||
* Old style: The arguments of the function 'main' are automatically interpreted to create the Arguments box in the user interface of the CustomFunction module | |||
This is a minimal working example for the new style: | |||
{{syntaxhighlight|lang=python|code= | |||
class Main(): | |||
""" here you can add some description of your script by using html. | |||
This description will be shown in the description box of the CustomFunction module | |||
""" | |||
variables = ["Variable1", "Variable2"] # add as many return variables as you like as strings | |||
units = ["unit1", "unit2"] # add units as strings in the same number as you have defined variables | |||
arguments = {"Argument 1": "some text", # a string | |||
"Argument 2": 42, # an integer | |||
"Argument 3": 3.14, # a float | |||
} | |||
def main(self, **kwargs): | |||
print(kwargs) # Now you can dow some processing with the arguments. kwargs is a dictionary containing the value for each argument name. | |||
var1 = 0 | |||
var2 = "another text" | |||
return variable1, variable2 # make sure you return an as many values as you have defined variables | |||
}} | |||
The arguments are defined by a static attribute 'arguments' whose keys are the argument names as presented to the user and the values are the default values after loading the script. The arguments are received by the function 'main' using **kwargs that can be used to receive an arbitrary number of keyword arguments as a dictionary. The advantage of the new style is that the arguments can have friendly names because space characters are allowed. Furthermore, the number of arguments can be dynamically adjusted. | |||
This is a minimal working example of the old style to define arguments: | |||
{{syntaxhighlight|lang=python|code= | |||
class Main(): | |||
""" here you can add some description of your script by using html. | |||
This description will be shown in the description box of the CustomFunction module | |||
""" | |||
variables = ["Variable1", "Variable2"] # add as many return variables as you like as strings | |||
units = ["unit1", "unit2"] # add units as strings in the same number as you have defined variables | |||
def main(self, argument1="some text", argument2=42, argument3=3.14): | |||
print("Arguments:", argument1, argument2, argument3) | |||
var1 = 0 | |||
var2 = "another text" | |||
return variable1, variable2 # make sure you return an as many values as you have defined variables | |||
}} | |||
Arguments are automatically defined by the arguments of the function 'main'. Whenever 'main' is called by SweepMe!, the current argument values are handed over and can be processed then. | |||
If you need to know the values of the arguments very early at the beginning of a run even before the 'main' function is called, you can use the variable 'self.main_arguments' that is handed over to the class instance of the script during "initialize" | |||
{{syntaxhighlight|lang=python|code= | |||
def initialize(self): | |||
print(self.main_arguments) # prints the dictionary with the argument names being the keys and the values being the user selection | |||
}} | |||
Don't forget to use the 'Reload' button to update changes to user interface if you have changed the number or the names of the arguments. | |||
=== Arguments/Input parameters === | === Arguments/Input parameters === | ||
* Data from SweepMe!: tuple -> () ''' Please note: data is always handed over as numpy array, | * Data from SweepMe!: tuple -> () ''' Please note: data is always handed over as numpy array, independent whether is is just a single value or a list. Please make sure you process the data correctly before you return it.''' | ||
* Integer: int -> any integer number which will be the preset value | * Integer: int -> any integer number which will be the preset value | ||
* Float: float -> any float value which will be the preset value | * Float: float -> any float value which will be the preset value | ||
Line 31: | Line 112: | ||
* List: list -> any list of strings which will be presented in ComboBox for selection by the user | * List: list -> any list of strings which will be presented in ComboBox for selection by the user | ||
* Bool: bool -> set to True or False; the user can later select using a CheckBox | * Bool: bool -> set to True or False; the user can later select using a CheckBox | ||
* | * Directory: pathlib.Path(<directory>) -> an empty pathlib.Path object or a non-empty pathlib.Path object with a given directory <directory>. The user will see a QFileDialog to choose a folder. | ||
* File: pathlib.Path(<file>) -> a non-empty pathlib.Path object with a given file <file>. The user will see a QFileDialog to choose a file. | |||
=== Procedure === | === Procedure === | ||
Whenever CustomFunction is part of an | Whenever CustomFunction is part of an iteration in a [[branch]] of the sequencer, the defined function 'main' is called with the given parameters and the defined variables must be returned. SweepMe! measurement values that are requested by using '''a tuple () as argument are automatically handed over as an array''' of the values that have been acquired since the last call CustomFunction. For example to get a single number from this array, one can take the item at the first index, e.g 'val[0]' assuming that 'val' is the list that has been handed over. Please also take about care changing to the correct type beforehand. | ||
=== Function calls === | |||
==== main ==== | |||
The function 'main' can define the arguments (old style) and returns as many values as defined by the static attributes 'variables' and 'units'. It is called in [[process()]] during the [[Sequencer procedure]]. | |||
It is possible to advance the call of the 'main' function to the beginning of the measurement by adding the 'execution' property to the CustomFunction class: | |||
{{syntaxhighlight|lang=python|code= | |||
class Main: | |||
execution = "transfer" | |||
}} | |||
Following execution options are available (requires SweepMe! 1.5.6.17 or newer and CustomFunction 2024-06-11 or newer): | |||
{| class="wikitable" | |||
|+ Execution Options for CustomFunction | |||
|- | |||
! Option Value !! Meaning | |||
|- | |||
| <code>transfer</code> || The main function is executed when the new set values are transferred to the modules. The CustomFunction may only depend on set values of modules higher up in the sequencer, and only modules further down in the sequencer can use the CustomFunction result for their configuration or set value. [[transfer()]] is called pair-wise with [[signin()]], meaning that a module's `transfer` is performed after its `signin`, but before the `signin` of the next child modules in the sequencer. After all transfers are done, the [[Sequencer procedure]] continues with [[start()]]. | |||
|- | |||
| <code>start</code> || The main function is executed during the [[start()]] phase of the [[Sequencer procedure]]. At this point the CustomFunction can access the set values of all modules, but in general the return value will not be available to other modules in the same measurement point. | |||
|- | |||
| <code>process</code> || The main function is executed during the [[process()]] phase of the [[Sequencer procedure]]. At this point the CustomFunction can access the processed results of modules that are higher up in the sequencer. This execution option is the default when <code>execution</code> is not defined. | |||
|} | |||
==== prepare_run / prepare_stop ==== | |||
You can add [[prepare_run]] and [[prepare_stop]] to your script. These functions have no arguments and return no values. They are called before and after the measurement inside the GUI thread. | |||
==== initialize / deinitialize ==== | |||
You can add [[initialize()]] and [[deinitialize()]] to your script. These functions have no arguments and return no values. They are called according to the [[Sequencer procedure]] at the start and at the end of the run, respectively. | |||
==== configure / unconfigure ==== | |||
You can add [[configure()]] and [[unconfigure()]] to your script. These functions have no arguments and return no values. They are called according to the [[Sequencer procedure]] when the module is in the new branch or not anymore in the next branch, respectively. | |||
==== signin / signout ==== | |||
You can add [[signin()]] and [[signout()]] to your script. These functions have no arguments and return no values. They are called according to the [[Sequencer procedure]] when a new setvalue in a module above your CustomFunction module is started or finished. | |||
=== GUI mode === | |||
Creating GUI widgets, e.g. by using PySide2 package, leads to a program crash as CustomFunction is running in the measurement thread of SweepMe! while GUI widgets must run in the main GUI thread of the program. For that reason, CustomFunction provides a GUI mode that can be enabled by using the function 'renew_widget' that takes care about exchanging Qwidget objects with the CustomFunction module. The function must be defined in class 'Main'. Please, use the function 'Example_GUI' to see how to use it. | |||
{{syntaxhighlight|lang=python|code= | |||
def renew_widget(self, widget = None): | |||
""" gets the widget from the module and returns the same widget or creates a new one""" | |||
if widget is None: | |||
# if the widget has not been created so far, we create it now and store it as self.widget | |||
self.widget = Widget() | |||
else: | |||
# the second time a run is started, we can use the widget, that is handed over to renwe_widget, to store is as self.widget | |||
self.widget = widget | |||
# return the actual widget to inform the module which one has to be inserted into the DockWidget of the Dashboard | |||
return self.widget | |||
}} | |||
As shown in the code snippet above, 'renew_widget' expects an argument 'widget' that is None at the very first call. Thus, one has to create a new QWidget-type object called here 'Widget()' that is set to 'self.widget' and returned at the end of the function. The second time 'renew_widget' is called, e.g. when a measurement is started again, the module CustomFunction hands over the last known Qwidget to 'renew_widget' and the widget can be set to 'self.widget' in order to make it available to other function of the class 'Main'. | |||
Qwidget objects that are returned by 'renew_widget' are automatically placed into a QDockWidget that can be placed by the user into the dashboard. 'renew_widget' is called before every measurement and whenever the selected function is changed. Thus, it is also possible to store the layout of the dock widget to the setting. However, values that are entered in such custom widgets are not automatically saved to the setting file. | |||
=== Importing packages === | |||
Importing external python packages that are not shipped with SweepMe! just works like for [[Driver Programming]]. You can find a guide here [[External libraries and dependencies]]. | |||
=== Applications === | === Applications === | ||
Line 44: | Line 194: | ||
* pre-evaluation of the data | * pre-evaluation of the data | ||
* using SweepMe! as a frontend for simulations | * using SweepMe! as a frontend for simulations | ||
* Control loops, e.g. a PID controller | |||
* creating output GUI widgets to display data | |||
* creating input GUI widgets to let the user change a parameter | |||
* Manipulating or Copying data files after a run | |||
=== Examples === | === Examples === | ||
* | * Characterization of field-effect transistors: Transfer characteristics are measured in the linear regime. Current and voltage measurement data is handed over to the Evaluation module where charge carrier mobility and threshold voltage is extracted. | ||
* Characterization of LEDs: Spectra and current-voltage characteristics can be handed over to the Evaluation module to calculate several device efficiency parameters. | * Characterization of LEDs: Spectra and current-voltage characteristics can be handed over to the Evaluation module to calculate several device efficiency parameters. | ||
* Calculation of peak values, zeroing, derivation, integral, mean, standard deviation, ... | * Calculation of peak values, zeroing, derivation, integral, mean, standard deviation, ... | ||
* Creating custom GUI widgets including several slider and input fields to control a certain process. | |||
* Displaying several values in a human-machine-interface (HMI) like way. | |||
[[Category:Modules]] | [[Category:Modules]] | ||
[[Category:Add-On Modules]] | [[Category:Add-On Modules]] |
Latest revision as of 14:24, 5 August 2024
The CustomFunction module is designed to allow users to run their own Python functions within SweepMe!. These Python functions can be used to receive data from multiple modules to analyze data or calculate new parameters.
Features
- Access to measured data from all other modules.
- Integration with external/custom Python scripts.
- Generate simulated data for testing devices and adjusting parameters.
- Automate manipulating or copying data files post-measurement, including saving in preferred formats.
- Develop input and output GUI widgets to customize control and display.
Usage
version: 2022-08-17 or later
The position of the CustomFunction module in the Sequencer is essential. It determines the amount of data that is handed over to the CustomFunction function.
A simplified rule is: Whenever a function of the CustomFunction module is called, all values generated since the last call of this function are handed over.
Adding a new function
- Add a CustomFunction module to the sequencer
- Go to the tab of the CustomFunction module
- Press "Copy selected function" and enter the name of the new function, it will be a copy of the example function.
- You can modify your new function using 'Open/Modify'
- Your new function can be found in the public folder '.\DataModules\CustomFunction\Functions'. This folder can also be found by using the button "Open folder".
- Edit the arguments of the function 'main' to add or remove further GUI items that are displayed to the user.
- Change the lists 'variables' and 'units' and return the corresponding values at the end of the function 'main'.
- Use the button 'Reload' to refresh the CustomFunction module after you did changed to your function.
Editing a function
A CustomFunction script is basically a python file that must contain a class 'Main' with a function 'main'.
Each class muss contain the static variables 'variables' and 'units'
- variables: a list of strings defining the names of the values that are returned by the function 'main'
- units: a list of strings defining the names of the units corresponding to variables
- variables and units need to have the same length as the number of returned values of your function 'main'
- A function 'main' must be added that returns as many values as defined by the length of variables and units.
There are two ways to define arguments that are processed by the function 'main':
- New style: The arguments are defined by the static variable 'arguments' using a dictionary
- Old style: The arguments of the function 'main' are automatically interpreted to create the Arguments box in the user interface of the CustomFunction module
This is a minimal working example for the new style:
class Main():
""" here you can add some description of your script by using html.
This description will be shown in the description box of the CustomFunction module
"""
variables = ["Variable1", "Variable2"] # add as many return variables as you like as strings
units = ["unit1", "unit2"] # add units as strings in the same number as you have defined variables
arguments = {"Argument 1": "some text", # a string
"Argument 2": 42, # an integer
"Argument 3": 3.14, # a float
}
def main(self, **kwargs):
print(kwargs) # Now you can dow some processing with the arguments. kwargs is a dictionary containing the value for each argument name.
var1 = 0
var2 = "another text"
return variable1, variable2 # make sure you return an as many values as you have defined variables
The arguments are defined by a static attribute 'arguments' whose keys are the argument names as presented to the user and the values are the default values after loading the script. The arguments are received by the function 'main' using **kwargs that can be used to receive an arbitrary number of keyword arguments as a dictionary. The advantage of the new style is that the arguments can have friendly names because space characters are allowed. Furthermore, the number of arguments can be dynamically adjusted.
This is a minimal working example of the old style to define arguments:
class Main():
""" here you can add some description of your script by using html.
This description will be shown in the description box of the CustomFunction module
"""
variables = ["Variable1", "Variable2"] # add as many return variables as you like as strings
units = ["unit1", "unit2"] # add units as strings in the same number as you have defined variables
def main(self, argument1="some text", argument2=42, argument3=3.14):
print("Arguments:", argument1, argument2, argument3)
var1 = 0
var2 = "another text"
return variable1, variable2 # make sure you return an as many values as you have defined variables
Arguments are automatically defined by the arguments of the function 'main'. Whenever 'main' is called by SweepMe!, the current argument values are handed over and can be processed then.
If you need to know the values of the arguments very early at the beginning of a run even before the 'main' function is called, you can use the variable 'self.main_arguments' that is handed over to the class instance of the script during "initialize"
def initialize(self):
print(self.main_arguments) # prints the dictionary with the argument names being the keys and the values being the user selection
Don't forget to use the 'Reload' button to update changes to user interface if you have changed the number or the names of the arguments.
Arguments/Input parameters
- Data from SweepMe!: tuple -> () Please note: data is always handed over as numpy array, independent whether is is just a single value or a list. Please make sure you process the data correctly before you return it.
- Integer: int -> any integer number which will be the preset value
- Float: float -> any float value which will be the preset value
- String: str -> any string which will be the preset value
- List: list -> any list of strings which will be presented in ComboBox for selection by the user
- Bool: bool -> set to True or False; the user can later select using a CheckBox
- Directory: pathlib.Path(<directory>) -> an empty pathlib.Path object or a non-empty pathlib.Path object with a given directory <directory>. The user will see a QFileDialog to choose a folder.
- File: pathlib.Path(<file>) -> a non-empty pathlib.Path object with a given file <file>. The user will see a QFileDialog to choose a file.
Procedure
Whenever CustomFunction is part of an iteration in a branch of the sequencer, the defined function 'main' is called with the given parameters and the defined variables must be returned. SweepMe! measurement values that are requested by using a tuple () as argument are automatically handed over as an array of the values that have been acquired since the last call CustomFunction. For example to get a single number from this array, one can take the item at the first index, e.g 'val[0]' assuming that 'val' is the list that has been handed over. Please also take about care changing to the correct type beforehand.
Function calls
main
The function 'main' can define the arguments (old style) and returns as many values as defined by the static attributes 'variables' and 'units'. It is called in process() during the Sequencer procedure.
It is possible to advance the call of the 'main' function to the beginning of the measurement by adding the 'execution' property to the CustomFunction class:
class Main:
execution = "transfer"
Following execution options are available (requires SweepMe! 1.5.6.17 or newer and CustomFunction 2024-06-11 or newer):
Option Value | Meaning |
---|---|
transfer |
The main function is executed when the new set values are transferred to the modules. The CustomFunction may only depend on set values of modules higher up in the sequencer, and only modules further down in the sequencer can use the CustomFunction result for their configuration or set value. transfer() is called pair-wise with signin(), meaning that a module's `transfer` is performed after its `signin`, but before the `signin` of the next child modules in the sequencer. After all transfers are done, the Sequencer procedure continues with start(). |
start |
The main function is executed during the start() phase of the Sequencer procedure. At this point the CustomFunction can access the set values of all modules, but in general the return value will not be available to other modules in the same measurement point. |
process |
The main function is executed during the process() phase of the Sequencer procedure. At this point the CustomFunction can access the processed results of modules that are higher up in the sequencer. This execution option is the default when execution is not defined.
|
prepare_run / prepare_stop
You can add prepare_run and prepare_stop to your script. These functions have no arguments and return no values. They are called before and after the measurement inside the GUI thread.
initialize / deinitialize
You can add initialize() and deinitialize() to your script. These functions have no arguments and return no values. They are called according to the Sequencer procedure at the start and at the end of the run, respectively.
configure / unconfigure
You can add configure() and unconfigure() to your script. These functions have no arguments and return no values. They are called according to the Sequencer procedure when the module is in the new branch or not anymore in the next branch, respectively.
signin / signout
You can add signin() and signout() to your script. These functions have no arguments and return no values. They are called according to the Sequencer procedure when a new setvalue in a module above your CustomFunction module is started or finished.
GUI mode
Creating GUI widgets, e.g. by using PySide2 package, leads to a program crash as CustomFunction is running in the measurement thread of SweepMe! while GUI widgets must run in the main GUI thread of the program. For that reason, CustomFunction provides a GUI mode that can be enabled by using the function 'renew_widget' that takes care about exchanging Qwidget objects with the CustomFunction module. The function must be defined in class 'Main'. Please, use the function 'Example_GUI' to see how to use it.
def renew_widget(self, widget = None):
""" gets the widget from the module and returns the same widget or creates a new one"""
if widget is None:
# if the widget has not been created so far, we create it now and store it as self.widget
self.widget = Widget()
else:
# the second time a run is started, we can use the widget, that is handed over to renwe_widget, to store is as self.widget
self.widget = widget
# return the actual widget to inform the module which one has to be inserted into the DockWidget of the Dashboard
return self.widget
As shown in the code snippet above, 'renew_widget' expects an argument 'widget' that is None at the very first call. Thus, one has to create a new QWidget-type object called here 'Widget()' that is set to 'self.widget' and returned at the end of the function. The second time 'renew_widget' is called, e.g. when a measurement is started again, the module CustomFunction hands over the last known Qwidget to 'renew_widget' and the widget can be set to 'self.widget' in order to make it available to other function of the class 'Main'.
Qwidget objects that are returned by 'renew_widget' are automatically placed into a QDockWidget that can be placed by the user into the dashboard. 'renew_widget' is called before every measurement and whenever the selected function is changed. Thus, it is also possible to store the layout of the dock widget to the setting. However, values that are entered in such custom widgets are not automatically saved to the setting file.
Importing packages
Importing external python packages that are not shipped with SweepMe! just works like for Driver Programming. You can find a guide here External libraries and dependencies.
Applications
- Data smoothing
- Repetitive curve fitting and extraction of fit parameters
- Parameter extraction
- Specializing a measurement setup to certain needs
- pre-evaluation of the data
- using SweepMe! as a frontend for simulations
- Control loops, e.g. a PID controller
- creating output GUI widgets to display data
- creating input GUI widgets to let the user change a parameter
- Manipulating or Copying data files after a run
Examples
- Characterization of field-effect transistors: Transfer characteristics are measured in the linear regime. Current and voltage measurement data is handed over to the Evaluation module where charge carrier mobility and threshold voltage is extracted.
- Characterization of LEDs: Spectra and current-voltage characteristics can be handed over to the Evaluation module to calculate several device efficiency parameters.
- Calculation of peak values, zeroing, derivation, integral, mean, standard deviation, ...
- Creating custom GUI widgets including several slider and input fields to control a certain process.
- Displaying several values in a human-machine-interface (HMI) like way.