Driver Programming: Difference between revisions

From SweepMe! Wiki
Jump to navigation Jump to search
Line 100: Line 100:
                 "MyOwnKeyForBool"      : True,                    # define a GUI element to use CheckBox
                 "MyOwnKeyForBool"      : True,                    # define a GUI element to use CheckBox
                 "MyOwnKeyForSeparator" : None,                    # define a GUI element that just displays the key with bold font (from SweepMe! v1.5.4)
                 "MyOwnKeyForSeparator" : None,                    # define a GUI element that just displays the key with bold font (from SweepMe! v1.5.4)
                 "MyOwnKeyForFolder"    : pathlib.Path(<folder>)  # define a GUI element that displays a button to select a folder (from 1.5.4.25)
                 "MyOwnKeyForFolder"    : pathlib.Path(<folder>)  # define a GUI element that displays a button to select a folder (from SweepMe! v1.5.4.25)
                 "MyOwnKeyForFile"      : pathlib.Path(<file>)    # define a GUI element that displays a button to select a file (from 1.5.4.25)
                 "MyOwnKeyForFile"      : pathlib.Path(<file>)    # define a GUI element that displays a button to select a file (from SweepMe! v1.5.4.25)
               }
               }
}}
}}

Revision as of 23:31, 11 February 2020

Minimal working example

A Device Class is a main.py file in which a python class-Object is inherited from a parent class called EmptyDeviceClass.

The following four lines of codes are always needed:

from EmptyDeviceClass import EmptyDevice  # Loading the EmptyDevice Class

class Device(EmptyDevice):            # Creating a new Device Class by inheriting from EmptyDevice
    def __init__(self):               # The python class object need to be initialized
       EmptyDevice.__init__(self)     # Finally, the initialization of EmptyDevice has to be done

Basic principle

SweepMe! runs a couple of pre-defined function that are equal across all modules that can load Device classes. These functions are listed in our sequencer procedure. Add these functions to your Device class to run them during the measurement. Leave them out if you do not need them.

Importing python packages

All packages which come along with SweepMe! can be imported as usual at the beginning of the file. If you need to import packages which are not yet included to your SweepMe! installation, you can use the LibraryBuilder to ship the python package with your DeviceClass.

Defining return variables

Each Device Class can return an arbitrary number of variables that are subsequently available for plotting, displaying them in a monitor widget, or saving them to the measurement data file.

In the function __init__ of your Device class you have to define following objects:

self.variables = ["Variable1", "Variable2", "Variable3"]
self.units = ["s", "m", ""]

In this example, three variables are defined, but you can also add more. Please make sure that you have the same number of units which are defined as seconds, meters, and no unit (empty string).

self.plottype = [True, True, False]
self.savetype = [True, False, False]

Additionally, you can define 'self.plottype' and 'self.savetype' which again need to have the same length as 'self.variables'. If you do not define them, they will be always True for each variable. If the plottype of a variable is True, you can select this variable in the plot. If the savetype of a variable is True, the data of this variable is saved to the measurement file.

In order to return your measured data to SweepMe! use the call function and return as many values as you have defined variables.

GUI interaction

The interaction with grahical user interface (GUI) is realized with the two function get_GUIparameter and set_GUIparameter. The function get_GUIparameter receives a dictionary with all parameters of the GUI. The function set_GUIparameter has to return a dictionary that tells SweepMe! which GUI elements should be enabled (active) and which options or default values should be displayed.

Getting GUI parameter

If you need to know what the user configuration of the Module GUI, insert the following function:

def get_GUIparameter(self, parameter):
    print(parameter)

The dictionary parameter is used to hand over all GUI settings to the Device Class. The print command can be used to see the content and which keys are accessible. These keys of the dictionary can vary between each Module.

In order to load a single parameter, for example the Sweep mode, use:

self.sweepmode = self.parameter["SweepMode"]


Do not forget to change the type from string to whatever you need by using int() or float(), etc.

Setting GUI parameter

GUI elements of the Module for which you implement your Device Class must be activated. For that reason, put the following function into your DeviceClass

def set_GUIparameter(self):
    GUIparameter = {}
    return GUIparameter

Now, you can fill the dictionary GUIparameter with keys and values. Each key represents a certain GUI item of the Module.

GUIparameter = {
                "SweepMode" : ["Current in A", "Voltage in V"],  # define a list 
               }

Here, "SweepMode" represents the ComboBox of the Module that presents the possible modes to vary set values. To get all possible keys that can be used with set_GUIparameter, you can use the function get_GUIparameter.

The Modules Logger and Switch provide the possibility to generate GUI items dynamically. Just add your own keys to the dictionary and based on the type of the default value, the corresponding GUI element will be created for you.

GUIparameter = {
                "MyOwnKeyForInteger"   : 1,                       # define a GUI element to enter an integer
                "MyOwnKeyForFloat"     : 1.23,                    # define a GUI element to enter a float
                "MyOwnKeyForString"    : "SomeText",              # define a GUI element to enter a string
                "MyOwnKeyForSelection" : ["Choice1", "Choice2"],  # define a GUI element to select from a ComboBox
                "MyOwnKeyForBool"      : True,                    # define a GUI element to use CheckBox
                "MyOwnKeyForSeparator" : None,                    # define a GUI element that just displays the key with bold font (from SweepMe! v1.5.4)
                "MyOwnKeyForFolder"    : pathlib.Path(<folder>)   # define a GUI element that displays a button to select a folder (from SweepMe! v1.5.4.25)
                "MyOwnKeyForFile"      : pathlib.Path(<file>)     # define a GUI element that displays a button to select a file (from SweepMe! v1.5.4.25)
               }

The keys you use will be returned then by get_GUIparameter with the selected values of the user. Please make sure that you do not use keys that are provided by the Module. See the description of get_GUIparameter to see which keys already exist.

Description

Device Classes that are programmed for the modules Logger or Switch can have a description that is displayed in the description box when the corresponding Device Class is selected.

To enable this feature, add a static variable 'description' to your class, e.g.

class Device(EmptyDevice):
    description = "here you describe how your Device Class should be used" # a static variable

    def __init__(self):
       ...

The string will be interpreted like html, so that headings, enumerations, etc. are possible.

Multichannel support

Some measurement equipment has two channels but only one communications port, e.g. some Source-Measuring-Units or Paramter Analyzers have multiple channels to independently source voltages or currents, but everything is controlled via one port.

Of course, one could implement a Device Class for each channel of the device but in case of changes multiple files have to be revised. In order to unify the device handling, you can define multiple channels in your Device Class by adding a static variable:

class Device(EmptyDevice):
    multichannel = ["CH1", "CH2", "CH3", "CH4"] # a static variable

    def __init__(self):
       ...

In that case, the user will see four Device Classes in the Device list of the Module with the string defined above at the end.

To figure out which channel is chosen by the user, use:

def get_GUIparameter(self, parameter):
    self.device = self.parameter["Device"]
    self.channel = int(self.device[-1])

The above example will first read out the name of the chosen Device Class. Assuming that the last character of the string is the number of the selected channel. Of course, you can modify the above example to your needs.

Stop a measurement

You can set the variable self.stopMeasurement, e.g.

self.stopMeasurement = "text-to-be-displayed-in-a-message-box-to-inform-user"

If SweepMe! detects a non-empty string it will stop the measurement after the current function returns. Use "return False" to let the current function return immediately.

Alternatively, you can use the following function

self.stop_Measurement("text-to-be-displayed-in-a-message-box-to-inform-user")

In that case, SweepMe! will immediately stop the measurement after the MessageBox has been confirmed by the user.

Communication with the user

If you like to display a message in the info box of the "Measurement" tab, you can use:

self.message_Info("text-to-be-displayed-in-the-info-box")

If you like to inform the user with a message box, use

self.message_Box("text-to-be-displayed-in-the-message-box")

Please note that the message box is non-blocking and the measurement will continue, even if the the message is not confirmed by the user.

Log messages to a file

You can easily write messages to a file using

self.write_Log("message-to-be-saved")

The message will be saved in the file temp_logbook.txt within the temp-folder. When saving data the leading "temp" of the file will be automatically renamed by the given file name.


Inter Device Class communication

Starting with the function connect, each Device Class has the dictionary self.device_communication. One can store information using a key string. This key string should be as unique as possible to prevent accidential overwriting by another Device Class. Inter Device Class communication might be required if the access to a certain physical device can be generated only once, but needs to be known by multiple Device Classes. For example, a Device Class which opens an own port object to communicate with a spectrometer will have exclusive access to that port. In order to allow multiple Spectrometer modules in the sequencer to access a single port object it must be made available to all of their generated Device Classes.