Documentation
The ‘’PyOpticonDashboard’’ class
- class pyopticon.dashboard.PyOpticonDashboard(dashboard_name, **kwargs)
A Dashboard is our term for a GUI window containing various ‘widgets’. A standalone program should initialize, configure, and run each dashboard. One dashboard may contain many widgets, each representing a physical device or some other functionality.
See the tutorials for examples of initializing and populating a dashboard. Generally, the workflow is to:
Initialize a dashboard
Initialize widgets and add them to the dashboard
Define any desired interlocks and add them to the dashboard
Launch the dashboard
- Parameters:
dashboard_name (str) – The name of the dashboard, which appears in any data logging files that the dashboard creates.
offline_mode (bool, optional) – Defaults to False. If True, doesn’t attempt to build any serial.Serial objects, and widgets may also check to behave differently.
polling_interval_ms (int, optional) – The interval for polling all connected devices, in milliseconds. Defaults to 1000. You may want to use a larger interval if certain devices are slow to poll, or polling them involves blocking code (not recommended).
window_resizeable (bool, optional) – Whether or not you can manually resize the dashboard by dragging and dropping the corner. Defaults to false. If True, the window is resizeable, but the widgets don’t scale or center themselves.
persistent_console_logfile (bool, optional) – Whether or not to log console events to a persistent file (same throughout multiple dashboard relaunches) in the same directory as the dashboard initialization script.
print_stacktraces (bool, optional) – If true, exception stack traces are printed to console; if false, only to the logfile. Exception names are printed regardless.
x_pad (int, optional) – The horizontal pad between widgets, in pixels.
y_pad (int, optional) – The vertical pad between widgets, in pixels.
socket_ports (list, optional) – A list of integer ports on which to open sockets for client connections. Defaults to [12345].
include_auto_widget (bool, optional) – Whether or not to display an automation widget on the dashboard.
include_socket_widget (bool, optional) – Whether or not to display a socket widget on the dashboard.
- add_widget(widget, row, column)
Add a widget to the dashboard at the specified row and column, each indexed from 0. Note that rows 0-3 in column 0 are reserved for the four dashboard control widgets. If the specified grid coordinates are already occupied, add_widget hides the widget that was there before. It warns to console if the widget added shares a name or nickname with an existing widget.
- Parameters:
widget (pyopticon.generic_widget.GenericWidget or pyopticon.minimal_widget.MinimalWidget) – The widget to add to the dashboard
row (int) – The row in the dashboard’s Tkinter grid at which to place the widget, indexed from 0
column (int) – The column in the dashboard’s Tkinter grid at which to place the widget, indexed from 0
- add_interlock(fn)
Add an interlock function that will be called once every polling cycle.
You’ll probably want to define such a function in the same file where the dashboard is constructed. You can use the dashboard’s get_field and/or get_widgets_by_nickname methods to check whether the system state violates a certain interlock condition (e.g., a certain temperature reading is too high), and then respond accordingly (e.g., use the dashboard’s set_field method to shut off the flow of reactive gases, or use the gmail_helper module to email or text the operator that something has gone wrong).
- Parameters:
fn (function) – The interlock function to add. Should take no arguments and return nothing.
- start()
Launch the dashboard, including all necessary threads.
- exc_handler(exc, source='system', widget=None)
Handle an exception according to the protocol configured when the dashboard was launched. Generate a message about what subprocess raised the exception.
- Parameters:
exc (Exception) – The Exception being raised
source (str, optional) – The source of the exception according to a scheme outlined in the if-statement in the function definition. Defaults to ‘system’.
widget (str, optional) – The nickname of the widget that raised the exception, if applicable
- check_offline_mode()
Check whether dashboard is in offline mode.
- Returns:
Whether the dashboard is in offline mode.
- Return type:
bool
return self.offline_mode
- check_serial_connected()
Check whether serial is currently connected
- Returns:
Whether the dashboard’s serial is connected.
- Return type:
bool
- get_field(target_widget_nickname, target_field)
Get the current value of a certain field of a certain widget. The field must have been created with the add_field method of the GenericWidget class. To access an instance variable of a widget, use get_widget_by_nickname instead. To see a list of widgets’ nicknames and fields, run the dashboard and use the ‘automation help’ button.
- Parameters:
target_widget_nickname (str) – The nickname of the widget
target_field (str) – The name of the field to read
- Returns:
The value of the field that you queried
- Return type:
str
- set_field(target_widget_nickname, target_field, new_value, confirm=True)
Set the value of a certain field of a certain widget and, optionally, execute the widget’s confirm function. The field must have been created with the add_field method of the GenericWidget class. To modify an instance variable of a widget, use get_widget_by_nickname instead. To see a list of widgets’ nicknames and fields, run the dashboard and use the ‘automation help’ button.
- Parameters:
target_widget_nickname (str) – The nickname of the widget
target_field (str) – The name of the field to modify
new_value (str) – The new value for the field. Fields’ values are always stored as strings, even if they represent numbers.
confirm (bool) – Whether or not to execute the widget’s confirm function, which usually sends a command to the physical device based on the newly updated field.
- get_widget_by_nickname(nickname)
Get a certain widget based on its nickname. To see a list of widgets’ nicknames and fields, run the dashboard and use the ‘automation help’ button.
- Parameters:
target_widget_nickname (str) – The nickname of the widget
- Returns:
The corresponding widget
- Return type:
pyopticon.generic_widget.GenericWidget or pyopticon.minimal_widget.MinimalWidget
- get_widgets_by_nickname()
Get a dict that maps widgets’ nicknames to the corresponding widget objects. Widgets that were added with no or None nicknames are excluded.
- Returns:
A dict mapping nicknames (str) to widgets (GenericWidget)
- Return type:
dict
- get_tkinter_object()
Get the dashboard’s Tkinter frame object, through which Tkinter functions like after() can be accessed.
- Returns:
The dashboard’s Tkinter frame object.
- Return type:
tkinter.Tk
- scale_all_text(scale_factor)
Go through all the widgets and scale the font on any tkinter Label, Text, OptionMenu, or Button objects. Meant as a quick fix for using dashboards on smaller or larger screems.
- Parameters:
scale_factor (float) – A factor by which to scale text, e.g. 1.2. Values are calculated in font units and are rounded to the nearest int.
Superclasses for creating new widgets
Most widgets should be created using the GenericWidget superclass, but we include the MinimalWidget class for the edge case of widgets that are purely cosmetic and don’t reflect physical instruments (e.g., the built-in ‘’TitleWidget’’ class).
The ‘’GenericWidget’’ class
- class pyopticon.generic_widget.GenericWidget(parent_dashboard, name, nickname, color, **kwargs)
This is the superclass for all widgets representing physical devices. It contains a lot of the machinery for generating GUI elements, setting up a serial connection, and logging data, so that subclass implementation is mostly defining the input/output fields and the serial communication protocol for a given instrument.
- Parameters:
parent_dashboard (pyopticon.dashboard.PyOpticonDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
color (str) – The color of the widget’s frame, as a RGB hex string, e.g. ‘#00FF00’
use_serial (bool, optional) – True if this widget needs to have a serial connection; False otherwise. Defaults to True.
default_serial_port (str, optional) – The name of the default selected serial port, e.g. ‘COM9’
default_serial_port – The name of the default selected serial port, e.g. ‘COM9’. Required unless no_serial is True.
baudrate (int, optional) – The baud rate of the serial connection, as an integer, e.g. 19200. Required unless no_serial is True or build_serial_object is overridden.
widget_to_share_thread_with (pyopticon.generic_widget.GenericWidget, optional) – A widget whose thread this widget will share, rather than creating its own. If use_serial = True, it’s assumed the widget will share the serial.Serial object.
update_every_n_cycles – Set the widget to poll its serial connection for updates every n cycles. Useful for instruments that poll slowly for some reason, or whose state changes infrequently. Defaults to 1.
- on_failed_serial_open()
This function is called when the Dashboard attempts to open a Serial port and it fails for some reason. It can be used to set the readout fields to ‘None’ or something, if desired.
- on_handshake()
This function gets called whenever the widget is initialized. If the widget uses a Serial connection, you can assume that the serial connection was already initialized successfully. If not, you’ll need to initialize whatever objects are needed to update the widget in this method (say, an OEM Python driver).
By default, it just calls on_update(), assuming that the handshake was successful if (and only if) no exception was raised.
- on_update()
This function gets called once every polling interval when the dashboard prompts each device to update itself. It should be overridden in a subclass implementation; if not, it prints a warning.
- on_confirm()
This function gets called whenever a widget’s ‘confirm’ button is pressed, which should result in a command (reflecting the latest entries in user input fields) getting sent through the serial connection. This method should be overridden in a subclass implementation, unless the widget has no user input fields. If it’s called without being implemented in the subclass, a warning is printed.
- on_serial_close()
This function gets called whenever serial connections are closed. It should be overridden in a subclass implementation. Usually, this function sets readout fields to something like ‘no reading’ after serial communications are closed. Note that while the other user-defined methods run in the widget’s thread, this method runs immediately in the main GUI thread, ensuring that the serial connection closure happens immediately.
- add_field(field_type, name, label, default_value=None, **kwargs)
Adds a field (i.e., a text entry box, a dropdown menu, or a text display) to the widget.
Adding a field is like making an instance variable for the widget, except 1) the GUI elements get autogenerated for you and 2) fields’ values are, by default, logged whenever the dashboard’s data logging is active. This method is meant to streamline adding input and output fields, though you can of course define your own instance variables, configure data logging, and add GUI elements by hand to the tkinter frame from widget.get_frame() if you want more granular control. Underlying each field is a tkinter StringVar bound to some tkinter GUI element.
If you add the first input field to a widget, a ‘Confirm’ button will also automatically be generated and placed. Use the move_confirm_button method to change its location.
- Parameters:
field_type (str) – Valid options are ‘text output’, ‘text input’, ‘dropdown’, or ‘button’
name (str) – The name of the field, which will be used to identify it for automation and for data logging
label (str) – The text label that will appear to the left of the field. This may differ from the name if you want to include units or abbreviate the label; e.g., the name might be ‘Temperature’ and the label might be ‘Temp. (C)’. If this argument is ‘’ (an empty string), no label is added.
default_value (str) – The starting value that appears in the field
options (list, optional) – The options in the dropdown option menu. Required if field_type is ‘dropdown’, ignored otherwise.
log (bool, optional) – Whether or not to log this field’s contents when the dashboard’s data logging is active. Defaults to True.
custom_stringvar (tkinter.StringVar, optional) – If you want to pass a pre-existing tkinter StringVar to be bound to the field’s GUI element, rather than letting this method initialize a new one.
- do_threadsafe(to_do)
Feeds the specified function to tkinter’s after() method with a delay of 0, so that it will be executed in a thread-safe way.
- Parameters:
to_do (function) – The function to execute.
- get_field(which_field)
Get the current value of the specified field.
- Parameters:
which_field (str) – The name of the field whose value to get.
- Returns:
The current value of the specified field
- Return type:
str
- set_field(which_field, new_value, hush_warning=False)
Set the value of the specified field to a specified value.
- Parameters:
which_field (str) – The name of the field whose value to set.
new_value (str) – The value to which to set the specified field.
hush_warning (True) – Silence the warning when you set a field while a widget’s serial isn’t connected.
- log_data()
Generate a dict of data that is sent to the dashboard’s data logging script. The dict contains the current values of all fields that were created using add_field with the option log=True. This method may be overridden in a subclass if you would like to do some kind of preprocessing on data before it’s logged, e.g. stripping out units or typecasting to int or float.
- Returns:
A dict of the widget’s loggable fields and their current values
- Return type:
dict
- disable_field(which_field)
Grey out an input field so that it can’t be interacted with.
- Parameters:
which_field (str) – The name of the field that will be greyed out.
- enable_field(which_field)
Un-grey out an input field that had previously been greyed out, allowing it to be interacted with again.
- Parameters:
which_field (Str) – The name of the field to re-enable.
- move_confirm_button(row, column)
Move the confirm button, which is automatically placed when using the add_field method to add an input field.
- Parameters:
row (int) – The row at which to place the confirm button, indexed from 0
column (int) – The column at which to place the confirm button, indexed from 0
- override_color(new_color)
Manually change the color of a widget’s frame to something besides its default defined in its constructor.
- Parameters:
new_color (str) – The new color, in hex, e.g. ‘#FF00FF’
- _run_thread()
Launch a thread to process commands from the widget’s queue. The thread just keeps checking for new commands in its queue forever until the close flag is set. Valid commands are ‘UPDATE’, ‘HANDSHAKE’, and ‘CONFIRM’.
- _shutdown_thread()
Shutdown the widget’s thread once the GUI is closed.
- _build_serial_object()
This function just calls get_serial_object and assigns its value to self.serial_object
- confirm()
Method executed when the Confirm button is pressed. Checks whether serial is connected and unfocuses any input field that’s focused, then calls the on_confirm method that is hopefully defined in a subclass.
- _update()
Executes every time the widget is prompted to update. Checks whether to update this cycle, checks whether serial is connected, and then calls the on_update method that is hopefully defined in a subclass implementation.
- _handshake()
Builds the serial object, if needed, and prompts the widget to handshake with the device, handling errors as needed.
- close_serial()
Closes the serial object, if needed, and returns the GUI fields to their default non-connected states. Executes on_serial_close, which is hopefully implemented in a subclass.
- get_serial_object()
Get the serial object that this widget is using
- Returns:
This widget’s serial object, which is probably a Pyserial Serial object.
- Return type:
serial.Serial
- show_serial()
Show the serial port selector and status GUI elements, if they were hidden
- hide_serial()
Hide the serial port selector and status GUI elements
- update_serial_ports(new_serial_options)
Update the dropdown list of available serial ports
- Parameters:
new_serial_options (list) – The new list of available serial ports
- get_frame()
Get the tkinter frame on which this object is drawn.
- Returns:
The widget’s tkinter frame
- Return type:
tkinter.Frame
The ‘’MinimalWidget’’ class
- class pyopticon.minimal_widget.MinimalWidget(parent_dashboard)
Superclass for creating widgets entirely from scratch, without any of the automation or data logging machinery in the GenericWidget class. This is mostly useful for creating widgets that are entirely cosmetic, e.g. the TitleWidget class. This superclass implements all functions required to interact with a Dashboard, but none of them do anything.
- Parameters:
parent_dashboard (pyopticon.dashboard.PyOpticonDashboard) – The dashboard object to which this device will be added
- on_handshake()
Runs when serial is connected. Does nothing unless overridden.
- on_update()
Runs every update cycle. Does nothing unless overridden.
- on_serial_close()
Runs on serial close. Does nothing unless overridden.
- on_confirm()
Runs on ‘confirm’, though these widgets generally don’t have confirm buttons unless you add one. Does nothing unless overridden.
- _run_thread()
Launch a thread to process commands from the widget’s queue.
- _shutdown_thread()
Shutdown the widget’s thread once the GUI is closed.
- get_frame()
Get the widget’s Tkinter frame object.
- show_serial()
This method must be implemented in all widgets; in this case, it is empty.
- hide_serial()
This method must be implemented in all widgets; in this case, it is empty.
- update_serial_ports(new_com_options)
This method must be implemented in all widgets; in this case, it is empty.
- Parameters:
new_com_options (list) – A list containing the names (strings) of the available serial ports.
The ‘’utilities’’ package
The ‘’scan_serial_ports’’ function
- pyopticon.utilities.scan_serial_ports()
Enter an infinite loop in which anytime a serial port appears or disappears, its name is printed to the console. This is useful for figuring out which physical devices are represented by which serial ports. This function should be invoked from some kind of Python shell like IDLE so you can see its printed outputs.
The ‘’GmailHelper’’ class
- class pyopticon.utilities.gmail_helper.GmailHelper(gmail_address, auth_string, destination_emails)
This widget allows your Python code to send emails and, by extension, texts. It’s mainly useful for sending notifications after an interlock is tripped, e.g. if the dashboard detects that an instrument went offline partway through an automation script.
You’ll need to follow an online tutorial to get an ‘app password’ for a gmail account to allow Python to send emails from that account. You can test that it worked by constructing a GmailHelper object in a shell like IDLE and trying to send yourself emails from it. If you’d like to send texts, send an email to the appropriate address at the cell carrier’s SMS gateway, e.g. xxx-xxx-xxxx@vtext.com for Verizon.
- Parameters:
gmail_address (str) – The gmail address from which you want the notification to be sent.
auth_string (str) – An app password (or equivalent) for the gmail account.
destination_emails (str) – A list of the email addresses to which you want to send the notifications.
- send_email(subject, message_body)
Send an email with the specified subject and message to the email addresses specified when this GmailHelper was created.
- Parameters:
subject (str) – The subject of the email. Note that if you’re using this to send texts through an SMS gateway, different carriers treat the subject in different ways.
message_body (str) – The body text of the email message to send.
The ‘’socket_client’’ package
The ‘’PyOpticonSocketClient’’ class
- class pyopticon.socket_client.PyOpticonSocketClient(**kwargs)
Class representing a client-side socket connection to a PyOpticon dashboard. Can be used to send various commands and queries to the dashboard from a separate Python script.
- Parameters:
socket_number (int, optional) – The port on which to open the socket connection. Defaults to 12345.
handle_errors (str, optional) – How to handle errors reported by the dashboard when attempting to execute a socket command. ‘none’ does nothing, ‘print’ prints a warning to console but continues executing, ‘exception’ raises an exception. Defaults to ‘none’.
- _check_errors(source, result)
Checks a string returned by the dashboard for whether it’s an error message (beginning with ‘Error: ‘) and, if so, processes it.
- Parameters:
source (str) – The command, e.g. ‘get’, that the dashboard was trying to execute.
result (str) – The string that the dashboard sent through the socket as its reply, which may or may not be an error message.
- _query_socket(to_send)
Converts a dict to a JSON string and sends it through the socket.
- Parameters:
to_send (dict) – The dict to send to the dashboard.
- get_field(widget_nickname, field_name, printout=True)
Gets the current value of a field from the dashboard via the socket.
- Parameters:
widget_nickname (str) – The nickname of the widget to query
field_name (str) – The field to query
printout (bool, optional) – Whether the dashboard should print to its own console a record that the socket command was received. Defaults to True.
- Returns:
The current value of the specified field.
- Return type:
str
- set_field(widget_nickname, field_name, new_value, printout=True)
Sets the value of a field to a specified value via the socket.
- Parameters:
widget_nickname (str) – The nickname of the widget whose field to set
field_name (str) – The field to set
printout (bool, optional) – Whether the dashboard should print to its own console a record that the socket command was received. Defaults to True.
- do_confirm(widget_nickname, printout=True)
Executes a widget’s ‘confirm’ method via the socket.
- Parameters:
widget_nickname (str) – The nickname of the widget to confirm
printout (bool, optional) – Whether the dashboard should print to its own console a record that the socket command was received. Defaults to True.
- do_eval(expression, printout=True)
Tells the dashboard to evaluate an expression and return the result. Eval is run in a namespace containing the methods get_dashboard(), which returns a dashboard object, and do_threadsafe(f), which executes a function f in the main GUI thread.
- Parameters:
expression – The expression to evaluate
printout (bool, optional) – Whether the dashboard should print to its own console a record that the socket command was received. Defaults to True.
- do_exec(fn, printout=True)
Tells the dashboard to execute a given function. Exec is run in a namespace containing the methods get_dashboard(), which returns a dashboard object, and do_threadsafe(f), which executes a function f in the main GUI thread.
The code to execute should be supplied as a function. The object uses the ‘inspect’ module to get the function’s source as a string and pass it through the socket. This lets you benefit from normal syntax highlighting in writing your function, rather than having to define it as a string with a bunch of escaped newline and tab characters in it. Due to the ‘inspect’ module’s limitations, the function must be defined separately and then passed to do_exec, rather than being defined inline with the do_exec call using a lambda function. E.g., s.do_exec(lambda: print(“Hi”)) is invalid. See the tutorial.
- Parameters:
expression – The code to run
printout (bool, optional) – Whether the dashboard should print to its own console a record that the socket command was received. Defaults to True.
- close()
Send a message via the socket telling the Dashboard to close the socket on its end. Close the socket connection on the client end.
Internal PyOpticon widget classes
These widgets are initialized once per dashboard by the dashboard’s constructor. They control and manage all of the dashboard’s high-level functions.
The ‘’SerialWidget’’ class
- class pyopticon._system._serial_widget.SerialWidget(parent_dashboard, polling_interval)
This widget is responsible for prompting all of the widgets to open serial connections, query them regularly, check them for responses, and closing the connections when needed. It also has the button to update the list of serial ports.
- Parameters:
parent (pyopticon.dashboard.Dashboard) – The dashboard to which this widget will be added.
- get_frame()
Get the tkinter frame on which this object is drawn.
- Returns:
The widget’s tkinter frame
- Return type:
tkinter.Frame
- _toggle_serial_connected()
Toggle whether serial communications are active. Prompt all widgets to open or close serial connections. Grey out and change text on the appropriate GUI buttons while serial communications are active.
- _update_widgets()
So long as serial communications are active, poll all widgets. Also check interlocks
- _read_serial_ports()
Prompt every widget to read the lastest responses from its serial port and update its display accordingly.
- _poll_interlocks()
Execute every interlock function that has been added to the dashboard.
- _update_serial_ports()
Update the drop-down menu of available serial ports, accounting for any ports that have appeared/disappeared since this method was last called.
The ‘’ShowHideWidget’’ class
- class pyopticon._system._show_hide_widget.ShowHideWidget(parent_dashboard)
This widget contains buttons to show/hide the console (on PC’s), show/hide all the other widgets’ serial controls, print automation help to the console, and open help/tutorials.
- Parameters:
parent (pyopticon.dashboard.Dashboard) – The dashboard to which this widget will be added.
- get_frame()
Get the tkinter frame on which this object is drawn.
- Returns:
The widget’s tkinter frame
- Return type:
tkinter.Frame
- _toggle_console()
On a PC, toggles whether the console is visible; on a Mac, opens a message box with instructions to open a console manually.
- _toggle_serial_shown()
Toggles whether the serial port dropdowns and serial statuses are visible on all widgets that have them.
- _documentation()
Open a more comprehensive document/site for support using this package.
- _print_automation_help()
Print a crash course in the syntax for automation scripts, as well as a list of widgets’ nicknames and automatable fields.
The ‘’DataLoggingWidget’’ class
- class pyopticon._system._data_logging_widget.DataLoggingWidget(parent_dashboard)
This widget allows a user to choose a data logging location and to start and stop data logging
- Parameters:
parent (pyopticon.dashboard.Dashboard) – The dashboard to which this widget will be added.
- get_frame()
Get the tkinter frame on which this object is drawn.
- Returns:
The widget’s tkinter frame
- Return type:
tkinter.Frame
- _toggle_logging()
Toggle whether data logging is active. Do the actual logging at regular intervals. Enable/disable buttons accordingly. Some machinery to manage the corner case where you stop logging and then restart logging within a shorter timespan than the data logging interval.
- _flip_interlock()
Necessary for toggle_logging to handle a corner case when logging is flipped on and off in quick succession.
- _poll_loggable_data()
Prompt every widget to return all of the data that it wants logged. Format that data into .csv append it to the log file.
- _choose_destination()
Open a dialog window to let the user select where the logfile should be saved.
The ‘’AutomationWidget’’ class
- class pyopticon._system._automation_widget.AutomationWidget(parent_dashboard)
This widget contains buttons to show/hide the console (on PC’s), show/hide all the other widgets’ serial controls, print automation help to the console, and open help/tutorials.
- Parameters:
parent (pyopticon.dashboard.Dashboard) – The dashboard to which this widget will be added.
- get_frame()
Get the tkinter frame on which this object is drawn.
- Returns:
The widget’s tkinter frame
- Return type:
tkinter.Frame
- get_parent_dashboard()
Get the automation widget’s parent Dashboard object.
- Returns:
The Dashboard to which this widget was added
- Return type:
pyopticon.dashboard.Dashboard
- log_data()
The automation widget will optionally log whatever step of the automation it’s currently on, in order to make splitting up time series data easier in post-experiment analysis.
- Returns:
A dict of the widget’s loggable fields and their current values
- Return type:
dict
- schedule_delay(delay)
This function is meant to be called in automation scripts. It causes a delay before a subsequent call to schedule_function or schedule_action is executed. It fills a role in an automation script similar to time.sleep() in a Python program.
In the automation script, you can just say ‘schedule_delay(…)’; you don’t need to say ‘dashboard.automation_widget.schedule_delay(…)’ or similar.
- Parameters:
delay (str) – The delay in hh:mm:ss format
- schedule_function(function)
This function is meant to be called in automation scripts. It executes whatever function is passed after any delay that has been caused by preceding calls to schedule_delay. If you are trying to change a widget field and then confirm the change, it’s better to use schedule_action – this method is meant to let you execute arbitrary functions at a scheduled time within an automation script, e.g. printing a widget’s instance variable to the console.
In the automation script, you can just say ‘schedule_function(…)’; you don’t need to say ‘dashboard.automation_widget.schedule_function(…)’ or similar.
- Parameters:
function (function) – The function to be executed at the scheduled time
- schedule_action(target_widget_nickname, target_field_name, new_target_value, confirm=True)
This function is meant to be called in automation scripts. It changes the target field in the target widget to a certain value, then optionally executes that widget’s confirm function. It’s useful for scheduling most simple automation tasks, e.g., flipping a valve at a certain time.
If you want to change multiple fields on a widget, make several consecutive calls to schedule_action, with confirm=False on all but the last and confirm=True on the last. That way, the widget’s confirm function is only executed once all its fields have been set properly.
In the automation script, you can just say ‘schedule_action(…)’; you don’t need to say ‘dashboard.automation_widget.schedule_action(…)’ or similar.
- Parameters:
target_widget_nickname (str) – The nickname of the widget whose field you want to change.
target_field_name (str) – The name of the widget field that you want to change.
new_target_value (str) – The new value to which the target field should be set. Numbers should be typecast to str first.
confirm (bool, optional) – True if the target widget’s confirm method should be called after updating the field; False if not.
- _schedule_helper(target_widget, field_name, new_target_value, confirm_function)
This is used to generate function objects that will be executed at a future time as a result of the schedule_action method
- Parameters:
target_widget (pyopticon.generic_widget.GenericWidget) – The widget whose field you want to change.
field_name (str) – The name of the widget field that you want to change.
new_target_value (str) – The new value to which the target field should be set. Numbers should be typecast to str first.
confirm_function (function) – The widget’s confirm function, or None if no confirm function should be executed.
- schedule_await_condition(condition, console_message='(No summary provided)')
This function schedules the script to ‘await’ a certain condition, such as a thermocouple temperature dropping below a certain value, before allowing the automation script to proceed. Data logging and GUI functions proceed in the meantime. While the script is awaiting the condition, a ‘Skip’ button also becomes available in the automation control widget.
- Parameters:
condition – A function that takes a Dashboard object as its only argument and returns True if the condition to proceed is met and False if not. See tutorials for an example.
console_message – A message that’s printed to the console as user-legible shorthand for the condition being awaited, e.g. ‘Temperature < 100C’
- _schedule_await_condition_helper(condition, console_message)
This is a function whose execution is scheduled by schedule_await_condition. It checks whether the awaited condition is met. If so, it lets the automation script proceed. If not, or if an error is raised, it causes the automation panel to check again during the next automation update cycle (usually 1 second later). It does various cosmetic things to manage the ‘time to next step’ and ‘time remaining’ readouts, as well as showing and hiding the ‘skip’ button.
- Parameters:
condition – A function that takes a Dashboard object as its only argument and returns True if the condition to proceed is met and False if not.
console_message – A message that’s printed to the console as user-legible shorthand for the condition being awaited, e.g. ‘Temperature < 100C’
- _skip_await()
This function gets called when the ‘skip await’ button gets pressed. It just sets a flag that _schedule_await_condition_helper reads.
- _update_tasks()
This function gets called every polling interval and checks whether it’s time to execute the next task(s) in the automation queue. There’s some other machinery to handle pausing the script and update GUI components like the ‘n out of m steps done’ and ‘xx:xx:xx to go’ readouts.
- _start_automated_tasks()
Start an automation script. Generates a list of absolute times at which each action in the automation queue will be executed, and starts calling _update_tasks every second.
- _load_automation_file()
Load an automation script from a file. Generates a list of functions and a list of the delays before each one will be executed after starting the script. Sets GUI elements like ‘0/n steps done’ and ‘xx:xx:xx remaining’.
Note that this method loads the contents of the automation file and calls exec() on it. This is obviously not secure; only use automation scripts whose authors you trues. exec() is called in a namespace with schedule_delay, schedule_action, schedule_function, and schedule_await_condition already defined as local variables for you to use.
- _pause_automated_tasks()
Pause the automation script
- _stop_automated_tasks()
Stop the automation script and go back to the beginning
- _buttons_running_mode()
Disable ‘start’ and ‘load’ buttons, turn frame green, add ‘running’ label.
- _buttons_paused_mode()
Disable ‘pause’ and ‘load’ buttons, turn frame yellow, add ‘paused’ label.
- _buttons_stopped_mode()
Disable ‘pause’ and ‘stop’ buttons, turn frame red, add ‘inactive’ label.
The ‘’SocketWidget’’ class
- class pyopticon._system._socket_widget.SocketWidget(parent_dashboard, port_numbers)
This widget manages socket connections from external Python programs. It displays how many sockets are connected and has a few other useful functions.
- Parameters:
parent (pyopticon.dashboard.PyOpticonDashboard) – The dashboard to which this widget will be added.
port_numbers (list) – A list of int ports on which to open socket server threads.
- _change_socket_counter(change)
Changes the socket counter displayed on the screen and updates the display and frame color accordingly.
- Parameters:
change (int) – Value to add to the socket counter (+1 or -1)
- _increment_socket_counter()
Increments the socket connection counter in a thread-safe way.
- _decrement_socket_counter()
Decrements the socket connection counter in a thread-safe way.
- get_frame()
Get the tkinter frame on which this object is drawn.
- Returns:
The widget’s tkinter frame
- Return type:
tkinter.Frame
- _shutdown_threads()
Sets a flag that tells the socket processing threads to close themselves at program shutdown.
- _run_one_thread(which_port)
Launch a thread that listens on a port for socket connections, handles the various types of commands that may be received on that socket, and shuts down the socket and thread on program close. Also handles ports that are ‘left hanging’ when a client crashes or disconnects without sending a ‘Close’ method.
- Parameters:
which_port (int) – The port on which this thread will listen.
- _force_disconnect()
Force any connected sockets to disconnect, resulting in broken pipe exceptions on the client side.
The ‘’built_in_widgets’’ package
The ‘’TitleWidget’’ class
- class pyopticon.built_in_widgets.title_widget.TitleWidget(parent_dashboard, title, font_size)
Bases:
MinimalWidgetA simple widget containing only text, intended for making a big-text title for a dashboard. Uses the MinimalWidget superclass, since all of the GenericWidget machinery is unnecessary.
- Parameters:
parent_dashboard (pyopticon.dashboard.PyOpticonDashboard) – The dashboard object to which this device will be added
title (str) – The text to be displayed within this widget, called ‘title’ because it’s likely to be the title of the entire dashboard.
font_size (int) – The size of font to be used in the text, as an integer.
The ‘’SpicinessWidget’’ class
- class pyopticon.built_in_widgets.spiciness_widget.SpicinessWidget(parent_dashboard, name, nickname)
Bases:
GenericWidgetThis is a silly demonstration of extending the GenericWidget class to make a widget that has no serial connection and only updates every few seconds. The superclass constructor is called with no_serial=True and update_every_n_cycles=3. The widget simply reports how spicy it’s feeling with a value randomly selected from a list.
The use_serial=False option is intended to allow the creation of widgets that do something besides poll a serial connection to update their information, but still have access to the data-logging and other machinery of the GenericWidget class. You might use this to:
Make a widget that communicates with a physical device through some means other than a Pyserial serial connection, e.g. a Python package provided by the instrument vendor.
Make a widget that reads the latest values from some instrument’s logfile on the computer. This can simplify post-experiment data fusion even if the entire instrument (e.g., a gas chromatograph) is far too complex to configure and run with a PyOpticon interface alone.
Make a standalone widget, e.g. a handy calculator, that has GUI elements but doesn’t interface with any physical devices.
The update_every_n_cycles option is meant to help interface with instruments that update less than once per second or take a long time to query. For example, a gas chromatograph will only log new concentration data every few minutes, while reading its logfile may be a slow operation, so polling it every 10 or 20 seconds is plenty and avoids gumming up the program with unnecessary reads. Similarly, if a device for some reason required serial queries to be spaced 200ms apart, and one needed to make 6 queries to extract all the data one wanted from it, beginning a sequence of 6 queries every second would overwhelm the instrument. Using update_every_n_cycles to start a sequence of 6 queries every other or every third second would avoid that issue.
- Parameters:
parent_dashboard (pyopticon.dashboard.PyOpticonDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
- on_failed_serial_open(success)
Set readout to ‘no reading’ if initialization failed.
- on_update()
Update the device by polling the serial connection.
- on_serial_query()
“Nothing is done on a serial query for this device.
- on_serial_read()
Updates the readout with a randomly selected level of spiciness. Returns True if this process was successful and False otherwise.
- Returns:
True if the device updated itself successfully, False otherwise.
- Return type:
bool
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
The ‘’majumdar_lab_widgets’’ package
These widgets were developed for the devices present in Prof. Arun Majumdar’s lab at Stanford University.
The ‘’AalborgDPCWidget’’ class
- class pyopticon.majumdar_lab_widgets.aalborg_dpc_widget.AalborgDPCWidget(parent_dashboard, name, nickname, default_serial_port, default_gas='Ar', **kwargs)
Bases:
GenericWidgetWidget for an Aalborg DPC mass flow controller (MFC). This widget controls a single MFC via a serial port.
By default, the gas selection dropdown includes a few gases that the author happened to use. Aalborg has many, many gas options in its user manual. You can configure the gas options in the constructor.
In practice, the MFC’s sometimes bug out when serial commands are sent directly back-to-back, so we use a short delay between queries/commands that are sent.
One can add a manual calibration curve, though these devices tend to be pretty accurate. This is done by tabulating the commanded flow (i.e., the value sent to the MFC) and the actual flow (according to an external flow meter) at various flow conditions, then feeding the resulting tuples of flows to the constructor for this class.
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
default_serial_port (str) – The name of the default selected serial port, e.g. ‘COM9’
default_gas (str, optional) – The default gas that’s selected in the dropdown, defaults to ‘Ar’ for Argon
gas_options (list, optional) – The list of gas names (strings) that can be selected. Defaults to argon, hydrogen, and methane.
gas_numbers (list, optional) – The list of gas numbers (integers) corresponding to gas_options according to the Aalborg DPC handbook. Defaults to indices for Ar, H2, and CH4.
calibration (tuple, optional) – a tuple containing two tuples of ints or floats with the results of calibrating the MFC. The first should contain a range of flow commands sent to the MFC. The second should contain the result flows according to an external flow meter. (0,0) should be included, as should a value above the highest flow you expect to use. For example, ( (0,10,20,30), (0,11.5,20.7,33.4) ).
- on_failed_serial_open()
If serial failed to open, set the readouts to ‘no reading’.
- on_handshake()
Conduct a handshake and populate the default values.
- on_update()
Send four queries to the serial device asking for the gas selection, mode, setpoint, and actual flow rate. Mode refers to open, closed, or setpoint. Read and process the responses.
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
- on_confirm()
When ‘confirm’ is pressed, send the appropriate commands to the MFC. Prints warnings to console if the entered parameters are invalid.
The ‘’MksMFCWidget’’ class
- class pyopticon.majumdar_lab_widgets.mks_mfc_widget.MksMFCWidget(parent_dashboard, name, nickname, channel, **kwargs)
Bases:
GenericWidgetWidget to control MKS ‘mass flo controllers’ (MFCs), which are themselves controlled by an MKS ‘vacuum controller’.
A control box like an MKS ‘946 Vacuum System Controller’ converts digital signals or manual inputs into the actual voltage and/or current signals that control MKS mass flo controllers. One 946 control box can control up to 6 mass flo controllers at once, with some trickery required (described below) to let multiple widgets share a single serial connection.
Each control box has a 3-digit ID number, e.g. 001, which can be configured on the box. Each MFC on the box has a channel, which is one of {A1, A2, B1, B2, C1, C2}. These are fixed when you initialize a widget and can’t be changed from the GUI.
Each mass flo controller on the same vacuum controller gets its own widget, even though they all share the same control box and hence the same serial connection. The first MFC widget is initialized normally, and then for every subsequent widget representing an MFC on the same control box, the constructor is called with the first widget passed as the keyword argument ‘widget_to_share_serial_with’. The later widgets then know to share the serial connection with the first widget, rather than trying to initialize a new one, which would fail because that serial port is already in use by the first widget.
One can also set a ‘scale factor,’ which adjusts for different gas types (e.g. air is usually a conversion factor of 1.0). Refer to the MKS MFC documentation for what scale factor to use for a particular gas. One can optionally lock the scale factor for a certain widget.
Finally, one can add a manual calibration curve independent of the scale factor. This is done by tabulating the commanded flow (i.e., the value sent to the MFC) and the actual flow (according to an external flow meter) at various flow conditions, then feeding the resulting tuples of flows to the constructor for this class.
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
channel (str) – A string representing which channel on the control box the MFC is connected to. One of (A1,A2,B1,B2,C1,C2).
widget_to_share_serial_with (richardview.majumdar_lab_widgets.mks_mfc_widget.MksMFCWidget, optional) – If this is the 2nd-6th MFC sharing the same control box, pass the widget for the first MFC on that control box as this argument. If not, the arguments device_ID and default_serial_port are required.
device_ID (str, optional) – The ID of the MKS control box / vacuum system controller, which is a string of a 3-digit number, e.g. ‘001’. Can be set on the control box.
default_serial_port (str, optional) – The name of the default selected serial port, e.g. ‘COM9’
force_scale_factor (float, optional) – Optionally set a certain scale factor and disable adjusting the scale factor via the interface. See the MKS manual to select factors for different gases.
calibration (tuple, optional) – a tuple containing two tuples of ints or floats with the results of calibrating the MFC. The first should contain a range of flow commands sent to the MFC. The second should contain the result flows according to an external flow meter. (0,0) should be included, as should a value above the highest flow you expect to use. For example, ( (0,10,20,30), (0,11.5,20.7,33.4) ).
- on_failed_serial_open()
If serial opened unsuccessfully, set readouts to ‘No Reading’
- on_handshake()
This function gets called whenever the widget is initialized. If the widget uses a Serial connection, you can assume that the serial connection was already initialized successfully. If not, you’ll need to initialize whatever objects are needed to update the widget in this method (say, an OEM Python driver).
By default, it just calls on_update(), assuming that the handshake was successful if (and only if) no exception was raised.
- on_update()
Send four queries to the serial device asking for the gas scale factor, mode, setpoint, and actual flow rate. Mode refers to open, closed, or setpoint. Process the results.
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
- on_confirm()
When ‘confirm’ is pressed, send the appropriate commands to the MFC. Prints warnings to console if the entered parameters are invalid.
The ‘’MKSFTIRWidget’’ class
- class pyopticon.majumdar_lab_widgets.mks_ftir_widget.MKSFTIRWidget(parent_dashboard, name, nickname, gas_labels, gas_columns, calibration_functions=None, default_logfile_path=None)
Bases:
GenericWidgetA widget representing an MKS multi-gas 2000 FTIR.
This widget doesn’t communicate with the FTIR via serial. The FTIR is controlled by MKS software, which logs data to a .prn (tab-delimited) file. This widget watches one of those files and reads the gas concentrations in the last line in the file.
One can make this widget extract several gases’ data from the logfile. The gases’ names are passed as one argument. A .prn contains many columns, so you pass the respective column indices as another argument.
You can pass in a list of calibration functions for the respective gases to adjust the FTIR’s raw readings. It probably makes sense to define the calibration functions using Numpy’s interp function to interpolate between a range of calibration points, e.g. ‘ch4_cal_function_lo = lambda x: np.interp(x,[0,514,1024,1430],[0,20,40,60])’.
An example initialization is as follows: ftir1 = mlw.MKSFTIRWidget(parent_dashboard=dashboard,name=’MKS FTIR’,nickname=’FTIR’,
gas_labels=(‘CH4 (ppm)’,’CO2 (ppm)’,’H2O (%)’,’CO (ppm)’), gas_columns=(3,15,30,12))
dashboard.add_widget(ftir1,row=2,column=1)
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
gas_labels (list) – The labels of the different gases to be logged. You may want to include units, e.g. ‘CO2 (ppm)’
gas_columns (list) – The indices of the columns corresponding to the gases’ concentrations in the logfile
calibration_functions (list) – A list specifying calibration functions for each gas; see the description above. If None, just keeps the values in the file.
default_logfile_path (str, optional) – The default path of the FTIR logfile. Can be used to avoid clicking through the file location choosing dialog every time.
- on_failed_serial_open()
If the device initialized unsuccessfully, set its readout to ‘No Reading’
- on_handshake()
This function gets called whenever the widget is initialized. If the widget uses a Serial connection, you can assume that the serial connection was already initialized successfully. If not, you’ll need to initialize whatever objects are needed to update the widget in this method (say, an OEM Python driver).
By default, it just calls on_update(), assuming that the handshake was successful if (and only if) no exception was raised.
- on_update()
“Read from the file and populate the fields.
- on_serial_close()
When serial is closed, set all readouts to ‘No Reading’.
- _update_file_to_watch()
Prompt the user to select a new file to watch.
- construct_serial_emulator()
No serial emulator is needed for this device, since its normal operation doesn’t assume any hardware is present. Returns None.
- Returns:
None
- Return type:
NoneType
The ‘’IotRelayWidget’’ class
- class pyopticon.majumdar_lab_widgets.iot_relay_widget.IotRelayWidget(parent_dashboard, name, nickname, default_serial_port)
Bases:
GenericWidgetWidget for using an Arduino to control a Digital Loggers Internet of Things (IoT) Relay, like this: https://www.digital-loggers.com/iot2.html . This can be used for on/off control of pretty much any AC-powered device like a light, fan, or pump.
The arduino is expected to control the IoT relay with a digital output pin and to read serial commands using its built-in USB connection. An arduino nano works well; these typically use mini-B USB connections. The arduino ground and digital output pin get connected to the green connector on the side of the IoT relay. Commands to the arduino are broken up by carriage return and newline characters. The arduino should turn on the IoT relay when the command ‘1’ is received and turn it off when the command ‘0’ is received. Additionally, when the command ‘Q’ for query is received, it should reply with its status (‘1’ or ‘0’) followed by a newline or carriage return character.
A suitable arduino sketch (program) to control the IoT relay can quickly be written by analogy to this Arduino forum post: https://forum.arduino.cc/t/serial-commands-to-activate-a-digital-output/49036/3 A working .ino sketch is also available in the majumdar_lab_widgets source code file on this project’s Github.
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
default_serial_port (str) – The name of the default selected serial port, e.g. ‘COM9’
- on_failed_serial_open()
If serial failed, set readout to ‘No Reading’
- on_handshake()
Handshake with the Arduino.
- on_update()
Query the device and update the display based on the reply.
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
- on_confirm()
When ‘confirm’ is pressed, send the appropriate commands to the arduino.
The ‘’OmegaUSBUTCWidget’’ class
- class pyopticon.majumdar_lab_widgets.omega_usb_utc_widget.OmegaUSBUTCWidget(parent_dashboard, name, nickname, default_serial_port)
Bases:
GenericWidgetWidget for an Omega USB-UTC thermocouple reader. A USB-UTC converts a single thermocouple (using the usual 2-prong thermocouple connection) into a USB signal. The Omega thermocouple reader desktop app can be used to set what type of thermocouple (K-type, etc.) is assumed.
- Parameters:
parent_dashboard (pyopticon.dashboard.PyOpticonDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
default_serial_port (str) – The name of the default selected serial port, e.g. ‘COM9’
- on_failed_serial_open()
If serial opened unsuccessfully, set readouts to ‘No Reading’
- on_update()
Update the device by polling the serial connection.
- on_serial_query()
Send a query to the serial device asking for the temperature.
- on_serial_read()
Parse the responses from the previous serial query and update the display. Return True if valid and and error string if not.
- Returns:
True if the response was of the expected format, an error string otherwise.
- Return type:
bool or str
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
The ‘’PicarroCRDWidget’’ class
- class pyopticon.majumdar_lab_widgets.picarro_crd_widget.PicarroCRDWidget(parent_dashboard, name, nickname, default_serial_port)
Bases:
GenericWidgetWidget for a Picarro GG201-i isotopic analyzer that measures 0-30 ppm CH4, 200-2000+ ppm CO2, and 0-100% relative humidity.
Refer to the Picarro manual to configure one of its extra serial ports for data logging to an external device. Two modes are possible. In one, the Picarro listens for a query and replies with its latest measurements. In the other, the Picarro sends its latest measurements every second. We chose to use the ‘send measurements every second’ option. This means that the Picarro widget only listens for measurements, and doesn’t ever send any queries via the serial line. There wasn’t a super strong reason for choosing one over the other, except for slightly more resilience to the Picarro ‘lagging’ for a few seconds when it receives a methane concentration above its 30 ppm ‘limit’.
The Picarro needs to be set to send (in this order) the CH4 concentration in ppm, the water concentration in volume percent, and the CO2 concentration in ppm. This is done using the Picarro’s own monitor and interface, as described in its manual – contact the manufacturer if your manual doesn’t tell you how to do this.
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
default_serial_port (str) – The name of the default selected serial port, e.g. ‘COM9’
- on_failed_serial_open()
If serial opened unsuccessfully, set readouts to ‘No Reading’
- on_update()
Parse the latest message from the Picarro and update the display.
Note that sometimes we get unlucky with the timing and a valid Picarro message gets chopped off halfway through and fails to parse. So occasionally we get a ‘read error’ when the instrument is behaving just fine. This is easy to fix in data post-processing, but we might also consider fixing it by switching to a query-response setup.
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
The ‘’Valco2WayValveWidget’’ class
- class pyopticon.majumdar_lab_widgets.valco_2_way_valve_widget.Valco2WayValveWidget(parent_dashboard, name, nickname, default_serial_port, valve_positions, valve_id='1')
Bases:
GenericWidgetWidget for a VICI Valco 2-position valve, like this: https://www.vici.com/vval/vval_2pos.php . These valves have two positions that are internally referred to as ‘A’ and ‘B’. In the widget, the positions can be labeled whatever you want.
Valco produces many other valves, e.g. 9-way selector valves. To control one of them, you could probably copy-paste the source code of this module and make pretty minor modifications to on_serial_query, on_serial_read, on_confirm, and the serial emulator class. Refer to the valve’s documentation and/or mess around manually with a serial connection (Pyserial in a shell like IDLE is probably easiest) to figure out the serial protocol for controlling a different type of Valco valve – e.g., valves with more than 2 positions may label the positions with numbers rather than letters in the serial protocol.
- Parameters:
parent_dashboard (pyopticon.dashboard.PyOpticonDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
default_serial_port (str) – The name of the default selected serial port, e.g. ‘COM9’
valve_positions (list) – A list of strings with which to label the valve positions. For a 2-way valve, this should be a 2-element list with the labels for valve positions A and B respectively.
valve_id – A string representing the ID of the valve, which goes at the beginning of each command. This seems to always be ‘1’. However, if there are issues, going into a serial shell
(e.g. Pyserial in IDLE) and sending the message b’*IDr’ to the valve should cause it to respond with its ID.
- on_failed_serial_open(success)
Set fields to no reading if serial failed to open.
- on_update()
Update the widget by querying and reading the serial port.
- on_serial_query()
Send a query to the valve asking for its current position.
- on_serial_read()
Parse the responses from the previous serial query and update the display. Return True if the response is valid and an error string if not.
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
- on_confirm()
When ‘confirm’ is pressed, send the appropriate commands to the valve.
The ‘’SRIGasChromatographFIDWidget’’ class
- class pyopticon.majumdar_lab_widgets.sri_gc_fid_widget.SRIGasChromatographFIDWidget(parent_dashboard, name, nickname, gas_labels, gas_columns, calibration_functions, default_logfile_path=None)
Bases:
GenericWidgetA widget representing an SRI gas chromatograph’s flame ionization detector (FID). We use an SRI 8610c GC.
This widget doesn’t communicate with the GC via serial. The GC should be controlled using SRI’s PeakSimple application. PeakSimple can be programmed to log GC FID data to a ‘.res’ file, with a row appended to the file every time a new GC scan is completed. This widget watches that file and displays the results. The benefit is that the GC data then ends up in the same RichardView file as all the MFC and other data, simplifying postprocessing.
One can make this widget extract several gases’ data from the logfile. The gases’ names are passed as one argument. A .res files contains a bunch of columns of data, so you pass this constructor the indices of the columns that correspond to the peak areas for those gases.
The logfile just contains peak areas, but you can pass in calibration functions that map peak areas to concentrations according to some calibration curve. Since the GC FID has different sensitivity settings (low, medium, and high), you can pass a set of calibration functions for each, and select which to use with a dropdown. The calibration functions are passed using a dict whose keys are the labels (probably a subset of ‘Low’,’Medium’,and ‘High’) and whose values are a tuple of calibration functions for the respective gases. For example, {‘Low’:(ch4_cal_function_lo,co2_cal_function_lo), ‘Medium’:(ch4_cal_function_med,co2_cal_function_med)}. It probably makes sense to define the calibration functions using Numpy’s interp function to interpolate between a range of calibration points, e.g. ‘ch4_cal_function_lo = lambda x: np.interp(x,[0,514,1024,1430],[0,20,40,60])’.
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
gas_labels (list) – The labels of the different gases to be logged. You may want to include units, e.g. ‘CO2 (ppm)’
gas_columns (list) – The indices of the columns corresponding to the gases’ respective peak areas in the GC FID logfile format
calibration_functions (dict) – A dict specifying calibration functions for each gas at one or more sensitivity settings; see the description above.
default_logfile_path (str, optional) – The default path of the GC FID logfile. Can be used to avoid clicking through the file location choosing dialog every time.
- on_serial_open(success)
If the device initialized unsuccessfully, set its readout to ‘No Reading’
- disable_button()
Disable the file dialog button.
- on_handshake()
“On handshake, disable the file chooser button and run an update.
- on_update()
Poll the GC logfile and updates the readout with the latest values.
- on_serial_close()
When serial is closed, set all readouts to ‘No Reading’.
- _update_file_to_watch()
Prompt the user to select a new file to watch.
The ‘’ThorlabsLightMeterWidget’’ class
- class pyopticon.majumdar_lab_widgets.thorlabs_light_meter_widget.ThorlabsLightMeterWidget(parent_dashboard, name, nickname, wavelength, scale_factor, device_index=0)
Bases:
GenericWidgetThis widget represents a Thorlabs optical power meter, e.g. a PM100D. The Thorlabs driver library (one .py and two .dll’s) needs to be in a folder labeled ‘thorlabs’ next to the widget. You may have to go into TLPM.py and edit the .dll file paths (listed as strings) if it’s having trouble loading them.
- Parameters:
parent_dashboard (richardview.dashboard.RichardViewDashboard) – The dashboard object to which this device will be added
name (str) – The name that the widget will be labeled with, and under which its data will be logged, e.g. “Methane Mass Flow Controller”
nickname (str) – A shortened nickname that can be used to identify the widget in automation scripts, e.g. “CH4 MFC”
wavelength (int) – The wavelength of light assumed, in nm. Relevant for photodiode sensors.
scale_factor (float) – A float constant by which to multiply the measured power to get an irradiance - usually 1/(collector area).
device_index (int) – The index of the Thorlabs device this widget represents, according to the Thorlabs driver’s list of compatible devices. Defaults to 0, but you may set to 1, 2, etc. if you have more than one Thorlabs power meter connected to your computer. With multiple devices, find the indices by trial and error.
- on_failed_serial_open()
If serial open failed, set readouts to ‘No Reading’
- on_handshake()
Connect to the power meter and conduct a handshake.
- on_update()
Query the device and update the fields with the responses.
- on_serial_close()
When serial is closed, set all readouts to ‘None’.
The ‘’run_demo_dashboard’’ function
- pyopticon.demo_dashboard.run_demo_dashboard()
Run a demo dashboard, configured for offline use via serial emulators, including widgets for a thermocouple, two MKS MFC’s, an Aalborg MFC, an Picarro spectrometer, and an ultraviolet light controlled via an IoT relay. Intended for early learning of how to use a PyOpticon dashboard.