Skip to content
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkWindow" id="db_window">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Database</property>
<property name="window_position">center-on-parent</property>
<property name="destroy_with_parent">True</property>
<child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="db_enable_checkbutton">
<property name="label" translatable="yes">Enable Database saving</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_db_enable_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="db_control_table">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Measurement ID</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Experiment ID</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="exp_id_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="measure_id_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="patient_id_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Patient ID</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="exp_id_autogen_button">
<property name="label" translatable="yes">Generate New</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_exp_id_autogen_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">DB Path</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="db_path_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="db_apply_button">
<property name="label" translatable="yes">Apply</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_db_apply_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Measurement Name</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="measure_name_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import logging
from uuid import uuid4
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
logger = logging.getLogger(__name__)
class DB_Window(GObject.GObject):
__gsignals__ = {
'db-reset' : (GObject.SIGNAL_RUN_LAST,
GObject.TYPE_NONE,
(GObject.TYPE_STRING,)
)
}
def __init__(self):
GObject.GObject.__init__(self)
self.builder = Gtk.Builder()
self.builder.add_from_file('interface/db.glade')
self.builder.connect_signals(self)
self.window = self.builder.get_object('db_window')
# Make sure window isn't destroyed if checkbox hit.
self.window.connect('delete-event', self.on_delete_event)
self.db_control_table = self.builder.get_object('db_control_table')
ui_keys = ['exp_id_entry',
'measure_id_entry',
'patient_id_entry',
'measure_name_entry',
'db_path_entry',
'db_apply_button',
'exp_id_autogen_button',
'db_enable_checkbutton'
]
self.persistent_keys = ['db_path_entry','db_enable_checkbutton']
self.ui = {}
for i in ui_keys:
self.ui[i] = self.builder.get_object(i)
self.metadata_map = [('experiment_uuid', 'exp_id_entry'),
('patient_id', 'patient_id_entry'),
('name', 'measure_name_entry')]
self._params = {}
@property
def persistent_params(self):
"""Dict of parameters that should be saved."""
self._get_params()
return {k:self._params[k] for k in self.persistent_keys}
@property
def params(self):
"""Dict of parameters."""
self._get_params()
return self._params
def _get_params(self):
"""Updates self._params from UI."""
for i in self.ui:
if i.endswith('checkbutton'):
self._params[i] = self.ui[i].get_active()
elif i.endswith('entry'):
text = self.ui[i].get_text()
if text == "":
text = None
self._params[i] = text
@params.setter
def params(self, params):
if self._params is {}:
self._params = dict.fromkeys(self.ui.keys())
for i in self._params:
if i in params:
self._params[i] = params[i]
self._set_params()
def _set_params(self):
"""Updates UI with new parameters."""
for i in self.ui:
if i.endswith('checkbutton'):
self.ui[i].set_active(self._params[i])
elif i.endswith('entry'):
if self._params[i] is not None:
self.ui[i].set_text(self._params[i])
else:
self.ui[i].set_text("")
def update_from_metadata(self, metadata):
params = {k : metadata[j] for j, k in self.metadata_map
if j in metadata}
self.params = params
def show(self):
self.window.show_all()
self.on_db_enable_checkbutton_toggled()
def on_db_enable_checkbutton_toggled(self, *args):
if self.ui['db_enable_checkbutton'].get_active():
self.db_control_table.show()
else:
self.db_control_table.hide()
def on_exp_id_autogen_button_clicked(self, *args):
self.ui['exp_id_entry'].set_text(uuid4().hex)
def on_db_apply_button_clicked(self, widget=None, *data):
self.emit('db-reset', self.params['db_path_entry'])
def on_delete_event(self, widget=None, *data):
widget.hide()
return True
GObject.type_register(DB_Window)
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<requires lib="gtk+" version="3.10"/>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="vbox1">
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
......@@ -33,54 +34,73 @@
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<object class="GtkTable" id="table1">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label3">
<object class="GtkEntry" id="clean_s">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Potential (mV)</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<object class="GtkEntry" id="dep_s">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Time (s)</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<object class="GtkEntry" id="dep_mV">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<object class="GtkEntry" id="clean_mV">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Cleaning</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -90,101 +110,51 @@
<property name="label" translatable="yes">Deposition</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="clean_mV">
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Cleaning</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="dep_mV">
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="dep_s">
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Time (s)</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="clean_s">
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Potential (mV)</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
......@@ -221,13 +191,11 @@
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<object class="GtkTable" id="table2">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">6</property>
<property name="n_columns">2</property>
<property name="column_spacing">10</property>
<property name="homogeneous">True</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
......@@ -235,7 +203,8 @@
<property name="label" translatable="yes">Start (mV)</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -245,9 +214,8 @@
<property name="label" translatable="yes">Stop (mV)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -257,9 +225,8 @@
<property name="label" translatable="yes">Step Size (mV)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -270,18 +237,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -292,20 +253,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing>
</child>
<child>
......@@ -316,20 +269,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing>
</child>
<child>
......@@ -339,9 +284,8 @@
<property name="label" translatable="yes">Pulse Height (mV)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -351,9 +295,8 @@
<property name="label" translatable="yes">Pulse Width (ms)</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
......@@ -364,20 +307,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing>
</child>
<child>
......@@ -388,20 +323,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
......@@ -412,8 +339,8 @@
<property name="label" translatable="yes">Pulse Period (ms)</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
......@@ -424,20 +351,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">4</property>
</packing>
</child>
</object>
......@@ -464,9 +383,10 @@
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Note: The ADC converts continuously during DPV measurements and has priority over DAC changes. ADC sample rate should be significantly larger than DPV frequency for accurate timing.</property>
<property name="justify">center</property>
<property name="label" translatable="yes">Note: ADC samples 1/(ADC Frequency) before end of pulse. 1/(ADC frequency) should be significantly shorter than pulse period to reduce capacitive current.</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="width_chars">43</property>
</object>
<packing>
<property name="expand">True</property>
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkListStore" id="ExpComboListStore">
<columns>
<!-- column-name index -->
<column type="guint"/>
<!-- column-name name -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0">0</col>
<col id="1" translatable="yes">Chronoamperometry</col>
</row>
<row>
<col id="0">1</col>
<col id="1" translatable="yes">Linear Sweep Voltammetry</col>
</row>
<row>
<col id="0">2</col>
<col id="1" translatable="yes">Cyclic Voltammetry</col>
</row>
<row>
<col id="0">3</col>
<col id="1" translatable="yes">Square Wave Voltammetry</col>
</row>
<row>
<col id="0">4</col>
<col id="1" translatable="yes">Differential Pulse Voltammetry</col>
</row>
<row>
<col id="0">5</col>
<col id="1" translatable="yes">AC Voltammetry</col>
</row>
<row>
<col id="0">6</col>
<col id="1" translatable="yes">Photodiode</col>
</row>
</data>
</object>
<requires lib="gtk+" version="3.10"/>
<object class="GtkAboutDialog" id="aboutdialog1">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="program_name">Dstat Interface</property>
<property name="version">0.1</property>
<property name="program_name">DStat-interface</property>
<property name="copyright" translatable="yes">© Michael Dryden 2014</property>
<property name="license" translatable="yes">GNU</property>
<property name="authors">Michael Dryden</property>
<property name="comments" translatable="yes">This software is licensed under the GNU GPL v3</property>
<property name="website">http://microfluidics.utoronto.ca/dstat</property>
<property name="website_label" translatable="yes">Wheeler Microfuidics Lab</property>
<property name="authors">Michael Dryden
Thanks to Christian Fobel for help with Dropbot Plugin</property>
<property name="documenters"/>
<property name="logo_icon_name">image-missing</property>
<property name="license_type">gpl-3-0</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1">
<object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area1">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
......@@ -77,18 +46,48 @@
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="yalign">0.47999998927116394</property>
<property name="halign">center</property>
<property name="stock">gtk-missing-image</property>
</object>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-save-as</property>
<property name="icon_name">image7</property>
</object>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-save</property>
<property name="stock">gtk-save-as</property>
</object>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-save-as</property>
</object>
<object class="GtkImage" id="image5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-open</property>
</object>
<object class="GtkImage" id="image6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-preferences</property>
</object>
<object class="GtkImage" id="image7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">image7</property>
</object>
<object class="GtkImage" id="image8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-save-as</property>
</object>
<object class="GtkImage" id="image9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">image7</property>
</object>
<object class="GtkListStore" id="serial_liststore">
<columns>
......@@ -104,9 +103,10 @@
<property name="default_height">800</property>
<signal name="destroy" handler="on_window1_destroy" swapped="no"/>
<child>
<object class="GtkVBox" id="vbox1">
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
......@@ -115,48 +115,49 @@
<object class="GtkMenuItem" id="menuitem1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<property name="label" translatable="yes">File</property>
<child type="submenu">
<object class="GtkMenu" id="menu1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="gtk_new">
<property name="label">gtk-new</property>
<object class="GtkImageMenuItem" id="file_save_exp">
<property name="label" translatable="yes">Save Experiment Data…</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<property name="image">image8</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_file_save_exp_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="gtk-open">
<property name="label">gtk-open</property>
<object class="GtkImageMenuItem" id="file_save_plot">
<property name="label" translatable="yes">Save Plot…</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<property name="image">image3</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_file_save_plot_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="file_save_exp">
<property name="label">Save current data...</property>
<object class="GtkImageMenuItem" id="file_save_params">
<property name="label" translatable="yes">Save Parameters…</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="image">image2</property>
<property name="image">image4</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_file_save_exp_activate" swapped="no"/>
<signal name="activate" handler="on_file_save_params_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="file_save_plot">
<property name="label">Save Plot</property>
<object class="GtkImageMenuItem" id="file_load_params">
<property name="label" translatable="yes">Load Parameters</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="image">image3</property>
<property name="image">image5</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_file_save_plot_activate" swapped="no"/>
<signal name="activate" handler="on_file_load_params_activate" swapped="no"/>
</object>
</child>
<child>
......@@ -173,12 +174,51 @@
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu_dstat">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">DStat</property>
<child type="submenu">
<object class="GtkMenu" id="menu5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="menu_dstat_info">
<property name="label">System Information</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="image">image7</property>
<property name="use_stock">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_dstat_fw">
<property name="label" translatable="yes">Firmware…</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="image">image2</property>
<property name="use_stock">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_dstat_reset">
<property name="label">Reset EEPROM</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="image">image9</property>
<property name="use_stock">False</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu_dropbot">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Dropbot</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu2">
<property name="visible">True</property>
......@@ -188,6 +228,7 @@
<property name="label">gtk-connect</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_menu_dropbot_connect_activate" swapped="no"/>
</object>
......@@ -198,6 +239,7 @@
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_menu_dropbot_disconnect_activate" swapped="no"/>
</object>
......@@ -207,37 +249,22 @@
</object>
</child>
<child>
<object class="GtkMenuItem" id="gtk_view">
<object class="GtkMenuItem" id="menu_analysis">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<property name="label" translatable="yes">Analysis</property>
<child type="submenu">
<object class="GtkMenu" id="menu5">
<object class="GtkMenu" id="menu4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="push_status">
<property name="label">Push Status</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_stock">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="pop_status">
<property name="label">Pop Status</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_stock">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="clear_status">
<property name="label">Clear Status</property>
<object class="GtkImageMenuItem" id="menu_analysis_options">
<property name="label" translatable="yes">Analysis Options…</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="image">image6</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menu_analysis_options_activate" swapped="no"/>
</object>
</child>
</object>
......@@ -248,8 +275,7 @@
<object class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<property name="label" translatable="yes">About</property>
<child type="submenu">
<object class="GtkMenu" id="menu3">
<property name="visible">True</property>
......@@ -276,31 +302,34 @@
</packing>
</child>
<child>
<object class="GtkHPaned" id="hpaned1">
<object class="GtkPaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">300</property>
<property name="position_set">True</property>
<property name="wide_handle">True</property>
<child>
<object class="GtkVBox" id="vbox2">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkVBox" id="vbox3">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkHBox" id="gain_adc_box">
<object class="GtkBox" id="gain_adc_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
......@@ -314,36 +343,38 @@
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">10</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="expcombobox">
<placeholder/>
</child>
<child>
<object class="GtkComboBoxText" id="expcombobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">ExpComboListStore</property>
<signal name="changed" handler="on_expcombobox_changed" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="exp_section_box">
<object class="GtkBox" id="exp_section_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
......@@ -355,7 +386,7 @@
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
......@@ -404,10 +435,25 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">5</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="exp_progressbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="show_text">True</property>
<property name="margin_top">10</property>
<property name="margin_bottom">15</property>
<property name="margin_right">5</property>
<property name="margin_left">2</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
......@@ -415,17 +461,50 @@
</packing>
</child>
<child>
<object class="GtkNotebook" id="notebook1">
<object class="GtkNotebook" id="main_notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="scrollable">True</property>
<child>
<object class="GtkVBox" id="vbox4">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkNotebook" id="plot_notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tab_pos">bottom</property>
<child>
<placeholder/>
</child>
<child type="tab">
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child type="tab">
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child type="tab">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">-1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<child>
<object class="GtkCheckButton" id="autosave_checkbutton">
<property name="label" translatable="yes">Autosave</property>
......@@ -444,6 +523,7 @@
<object class="GtkFileChooserButton" id="autosavedir_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="action">select-folder</property>
<property name="preview_widget_active">False</property>
<property name="title" translatable="yes">Select a Save Folder</property>
......@@ -459,16 +539,16 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_tooltip">True</property>
<property name="valign">start</property>
<property name="max_length">32</property>
<property name="invisible_char"></property>
<property name="text" translatable="yes">file</property>
<property name="text" translatable="yes">filename</property>
<property name="caps_lock_warning">False</property>
<property name="primary_icon_stock">gtk-file</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="primary_icon_tooltip_text" translatable="yes">File name</property>
<property name="primary_icon_tooltip_markup" translatable="yes">File name</property>
</object>
<packing>
<property name="expand">False</property>
......@@ -479,24 +559,10 @@
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="plotbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="tab">
......@@ -510,96 +576,116 @@
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<placeholder/>
</child>
<child type="tab">
<placeholder/>
</child>
<child>
<object class="GtkTextView" id="datatextview1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="left_margin">10</property>
<property name="right_margin">10</property>
<property name="buffer">databuffer1</property>
</object>
<placeholder/>
</child>
<child type="tab">
<placeholder/>
</child>
</object>
<packing>
<property name="position">1</property>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Raw Data</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="can_focus">False</property>
<child>
<object class="GtkTextView" id="datatextview2">
<object class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="left_margin">10</property>
<property name="right_margin">10</property>
<property name="buffer">databuffer2</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Serial Port:</property>
</object>
<packing>
<property name="position">2</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label5">
<child>
<object class="GtkComboBox" id="serial_combobox">
<property name="width_request">128</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Extra Data</property>
<property name="halign">start</property>
<property name="model">serial_liststore</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkButton" id="serial_connect">
<property name="label">gtk-connect</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_serial_connect_clicked" swapped="no"/>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="pmt_mode">
<property name="label" translatable="yes">Connect (PMT mode)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_serial_connect_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkComboBox" id="serial_combobox">
<object class="GtkButton" id="serial_disconnect">
<property name="label">gtk-disconnect</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">serial_liststore</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_serial_disconnect_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
<property name="position">5</property>
</packing>
</child>
<child>
......@@ -614,25 +700,28 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkStatusbar" id="statusbar">
<object class="GtkLabel" id="ocp_disp">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
<property name="halign">start</property>
<property name="label" translatable="yes">OCP:</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">3</property>
<property name="position">7</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
......
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, absolute_import, print_function, unicode_literals
import os
import sys
import logging
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
from ..dstat import comm, state
from ..experiments import (cal, chronoamp, cv, experiment_template,
idle, lsv, pot, swv)
import __main__
from ..errors import InputError, VarError
logger = logging.getLogger(__name__)
mod_dir = os.path.dirname(os.path.abspath(__file__))
class ExpInterface(GObject.Object):
"""Generic experiment interface class. Should be subclassed to implement
experiment interfaces by populating self.entry. Override class attributes
to set id and experiment class to run.
"""
__gsignals__ = {
b'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()),
b'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ())
}
id = None
experiment = None
def __init__(self, glade_path):
super(ExpInterface, self).__init__()
self.builder = Gtk.Builder()
self.builder.add_from_file(glade_path)
self.builder.connect_signals(self)
self.entry = {} # to be used only for str parameters
self._params = None
def get_experiment(self, parameters):
return self.__class__.experiment(parameters)
def _fill_params(self):
self._params = dict.fromkeys(self.entry.keys())
@property
def params(self):
"""Dict of parameters"""
if self._params is None:
self._fill_params()
self._get_params()
return self._params
def _get_params(self):
"""Updates self._params from UI."""
for i in self.entry:
self._params[i] = self.entry[i].get_text()
@params.setter
def params(self, params):
if self._params is None:
self._fill_params()
for i in self._params:
try:
self._params[i] = params[i]
except KeyError as e:
logger.warning("Invalid parameter key: %s" % e)
self._set_params()
def _set_params(self):
"""Updates UI with new parameters."""
for i in self.entry:
self.entry[i].set_text(self._params[i])
def get_window(self):
return self.builder.get_object('scrolledwindow1')
def on_run_utility(self, data=None):
self.emit('run_utility')
def on_done_utility(self, data=None):
self.emit('done_utility')
class Chronoamp(ExpInterface):
"""Experiment class for chronoamperometry. Extends ExpInterface class to
support treeview neeeded for CA.
Public methods:
on_add_button_clicked(self, widget)
on_remove_button_clicked(self, widget)
get_params(self)
"""
id = 'cae'
experiment = chronoamp.Chronoamp
def __init__(self):
"""Extends superclass method to support treeview."""
super(Chronoamp, self).__init__(os.path.join(mod_dir, 'chronoamp.glade'))
self.name = "Chronoamperometry"
self.statusbar = self.builder.get_object('statusbar')
self.model = self.builder.get_object('ca_list')
self.treeview = self.builder.get_object('treeview')
self.treeview.set_fixed_height_mode(False)
for i, column_title in enumerate(["Potential", "Time"]):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
column.set_expand(True)
self.treeview.append_column(column)
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.MULTIPLE)
def _fill_params(self):
super(Chronoamp, self)._fill_params()
self._params['potential'] = []
self._params['time'] = []
def on_add_button_clicked(self, widget):
"""Add current values in potential_entry and time_entry to model."""
self.statusbar.remove_all(0)
try:
potential = int(
self.builder.get_object('potential_entry').get_text())
time = int(self.builder.get_object('time_entry').get_text())
if not state.board_instance.test_mv(potential):
raise ValueError("Potential out of range")
if not state.board_instance.test_s(time):
raise ValueError("Time out of range")
self.model.append([potential, time])
except ValueError as err:
self.statusbar.push(0, str(err))
except TypeError as err:
self.statusbar.push(0, str(err))
def on_remove_button_clicked(self, widget):
"""Remove currently selected items from model."""
# returns 2-tuple: treemodel, list of paths of selected rows
selected_rows = list(self.selection.get_selected_rows()[1])
referencelist = []
for i in selected_rows:
referencelist.append(Gtk.TreeRowReference(self.model, i))
for i in referencelist:
self.model.remove(self.model.get_iter(i.get_path()))
def _get_params(self):
"""Updates self._params from UI. Overrides superclass method."""
self._params['potential'] = [int(r[0]) for r in self.model]
self._params['time'] = [int(r[1]) for r in self.model]
def _set_params(self):
"""Updates UI from self._params. Overrides superclass method."""
self.model.clear()
table = zip(self._params['potential'], self._params['time'])
for i in table:
self.model.append(i)
class LSV(ExpInterface):
"""Experiment class for LSV."""
id = 'lsv'
experiment = lsv.LSVExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(LSV, self).__init__(os.path.join(mod_dir, 'lsv.glade'))
self.name = "Linear Sweep Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s')
self.entry['dep_mV'] = self.builder.get_object('dep_mV')
self.entry['dep_s'] = self.builder.get_object('dep_s')
self.entry['start'] = self.builder.get_object('start_entry')
self.entry['stop'] = self.builder.get_object('stop_entry')
self.entry['slope'] = self.builder.get_object('slope_entry')
class CV(ExpInterface):
"""Experiment class for CV."""
id = 'cve'
experiment = cv.CVExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(CV, self).__init__(os.path.join(mod_dir, 'cv.glade'))
self.name = "Cyclic Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s')
self.entry['dep_mV'] = self.builder.get_object('dep_mV')
self.entry['dep_s'] = self.builder.get_object('dep_s')
self.entry['start'] = self.builder.get_object('start_entry')
self.entry['v1'] = self.builder.get_object('v1_entry')
self.entry['v2'] = self.builder.get_object('v2_entry')
self.entry['slope'] = self.builder.get_object('slope_entry')
self.entry['scans'] = self.builder.get_object('scans_entry')
class SWV(ExpInterface):
"""Experiment class for SWV."""
id = 'swv'
experiment = swv.SWVExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(SWV, self).__init__(os.path.join(mod_dir, 'swv.glade'))
self.name = "Square Wave Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s')
self.entry['dep_mV'] = self.builder.get_object('dep_mV')
self.entry['dep_s'] = self.builder.get_object('dep_s')
self.entry['start'] = self.builder.get_object('start_entry')
self.entry['stop'] = self.builder.get_object('stop_entry')
self.entry['step'] = self.builder.get_object('step_entry')
self.entry['pulse'] = self.builder.get_object('pulse_entry')
self.entry['freq'] = self.builder.get_object('freq_entry')
self.entry['scans'] = self.builder.get_object('scans_entry')
def _fill_params(self):
super(SWV, self)._fill_params()
self._params['cyclic_true'] = False
def _get_params(self):
"""Updates self._params from UI."""
super(SWV, self)._get_params()
self._params['cyclic_true'] = self.builder.get_object(
'cyclic_checkbutton').get_active()
def _set_params(self):
"""Updates UI with new parameters."""
super(SWV, self)._set_params()
self.builder.get_object('cyclic_checkbutton').set_active(
self._params['cyclic_true'])
class DPV(ExpInterface):
"""Experiment class for DPV."""
id = 'dpv'
experiment = swv.DPVExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(DPV, self).__init__(os.path.join(mod_dir, 'dpv.glade'))
self.name = "Differential Pulse Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s')
self.entry['dep_mV'] = self.builder.get_object('dep_mV')
self.entry['dep_s'] = self.builder.get_object('dep_s')
self.entry['start'] = self.builder.get_object('start_entry')
self.entry['stop'] = self.builder.get_object('stop_entry')
self.entry['step'] = self.builder.get_object('step_entry')
self.entry['pulse'] = self.builder.get_object('pulse_entry')
self.entry['period'] = self.builder.get_object('period_entry')
self.entry['width'] = self.builder.get_object('width_entry')
# class ACV(ExpInterface):
# """Experiment class for ACV."""
# id = 'acv'
# def __init__(self):
# """Adds entry listings to superclass's self.entry dict"""
# super(ACV, self).__init__('interface/acv.glade')
# self.name = "AC Voltammetry"
#
# self.entry['start'] = self.builder.get_object('start_entry')
# self.entry['stop'] = self.builder.get_object('stop_entry')
# self.entry['slope'] = self.builder.get_object('slope_entry')
# self.entry['amplitude'] = self.builder.get_object('amplitude_entry')
# self.entry['freq'] = self.builder.get_object('freq_entry')
class PD(ExpInterface):
"""Experiment class for PD."""
id = 'pde'
experiment = chronoamp.PDExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(PD, self).__init__(os.path.join(mod_dir, 'pd.glade'))
self.name = "Photodiode/PMT"
self.entry['time'] = self.builder.get_object('time_entry')
self.entry['sync_freq'] = self.builder.get_object('sync_freq')
self.entry['fft_start'] = self.builder.get_object('fft_entry')
self.entry['fft_int'] = self.builder.get_object('fft_int_entry')
self.buttons = map(self.builder.get_object,
['light_button', 'threshold_button'])
self.shutter_buttons = map(
self.builder.get_object,
['sync_button', 'sync_freq', 'fft_label', 'fft_entry', 'fft_label2',
'fft_int_entry']
)
bool_keys = ['interlock_true', 'shutter_true', 'sync_true']
bool_cont = map(self.builder.get_object,
['interlock_button',
'shutter_button',
'sync_button']
)
self.bool = dict(zip(bool_keys, bool_cont))
def _fill_params(self):
super(PD, self)._fill_params()
for i in self.bool:
self._params[i] = self.bool[i].get_active()
self._params['voltage'] = 0
def _get_params(self):
"""Updates self._params from UI."""
super(PD, self)._get_params()
for i in self.bool:
self._params[i] = self.bool[i].get_active()
self._params['voltage'] = self.builder.get_object(
'voltage_adjustment').get_value()
def _set_params(self):
"""Updates UI with new parameters."""
super(PD, self)._set_params()
for i in self.bool:
self.bool[i].set_active(self._params[i])
self.builder.get_object('voltage_adjustment').set_value(
self._params['voltage'])
def on_light_button_clicked(self, data=None):
__main__.MAIN.on_pot_stop_clicked()
__main__.MAIN.stop_ocp()
for i in self.buttons:
i.set_sensitive(False)
try:
self.builder.get_object('light_label').set_text(str(
dstat_comm.read_light_sensor()))
comm.read_settings()
state.settings['tcs_enabled'][1] = '1' # Make sure TCS enabled
comm.write_settings()
self.builder.get_object('threshold_entry').set_text(str(
state.settings['tcs_clear_threshold'][1]))
__main__.MAIN.start_ocp()
finally:
GObject.timeout_add(700, restore_buttons, self.buttons)
def on_threshold_button_clicked(self, data=None):
__main__.MAIN.on_pot_stop_clicked()
__main__.MAIN.stop_ocp()
for i in self.buttons:
i.set_sensitive(False)
try:
state.settings['tcs_clear_threshold'][1] = self.builder.get_object(
'threshold_entry').get_text()
comm.write_settings()
comm.read_settings()
self.builder.get_object('threshold_entry').set_text(
str(state.settings['tcs_clear_threshold'][1]))
__main__.MAIN.start_ocp()
finally:
GObject.timeout_add(700, restore_buttons, self.buttons)
def on_shutter_button_toggled(self, widget):
if self.bool['shutter_true'].get_active():
for i in self.shutter_buttons:
i.set_sensitive(True)
else:
for i in self.shutter_buttons:
i.set_sensitive(False)
class POT(ExpInterface):
"""Experiment class for Potentiometry."""
id = 'pot'
experiment = pot.PotExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(POT, self).__init__(os.path.join(mod_dir, 'potexp.glade'))
self.name = "Potentiometry"
self.entry['time'] = self.builder.get_object('time_entry')
class CAL(ExpInterface):
"""Experiment class for Calibrating gain."""
id = 'cal'
experiment = cal.CALExp
def __init__(self):
"""Adds entry listings to superclass's self.entry dict"""
super(CAL, self).__init__(os.path.join(mod_dir, 'calib.glade'))
self.name = "Calilbration"
self.entry['time'] = self.builder.get_object('time_entry')
self.entry['R100'] = self.builder.get_object('100_entry')
self.entry['R3k'] = self.builder.get_object('3k_entry')
self.entry['R30k'] = self.builder.get_object('30k_entry')
self.entry['R300k'] = self.builder.get_object('300k_entry')
self.entry['R3M'] = self.builder.get_object('3M_entry')
self.entry['R30M'] = self.builder.get_object('30M_entry')
self.entry['R100M'] = self.builder.get_object('100M_entry')
self.buttons = [self.builder.get_object('read_button'),
self.builder.get_object('write_button'),
self.builder.get_object('measure_button')]
def on_read_button_clicked(self, data=None):
for i in self.buttons:
i.set_sensitive(False)
try:
__main__.MAIN.on_pot_stop_clicked()
__main__.MAIN.stop_ocp()
comm.read_settings()
self.entry['R100'].set_text(str(
state.settings['r100_trim'][1]))
self.entry['R3k'].set_text(str(
state.settings['r3k_trim'][1]))
self.entry['R30k'].set_text(str(
state.settings['r30k_trim'][1]))
self.entry['R300k'].set_text(str(
state.settings['r300k_trim'][1]))
self.entry['R3M'].set_text(str(
state.settings['r3M_trim'][1]))
self.entry['R30M'].set_text(str(
state.settings['r30M_trim'][1]))
self.entry['R100M'].set_text(str(
state.settings['r100M_trim'][1]))
__main__.MAIN.start_ocp()
finally:
GObject.timeout_add(700, restore_buttons, self.buttons)
def on_write_button_clicked(self, data=None):
for i in self.buttons:
i.set_sensitive(False)
try:
__main__.MAIN.on_pot_stop_clicked()
__main__.MAIN.stop_ocp()
state.settings['r100_trim'][1] = self.entry['R100'].get_text()
state.settings['r3k_trim'][1] = self.entry['R3k'].get_text()
state.settings['r30k_trim'][1] = self.entry['R30k'].get_text()
state.settings['r300k_trim'][1] = self.entry['R300k'].get_text()
state.settings['r3M_trim'][1] = self.entry['R3M'].get_text()
state.settings['r30M_trim'][1] = self.entry['R30M'].get_text()
state.settings['r100M_trim'][1] = self.entry['R100M'].get_text()
comm.write_settings()
__main__.MAIN.start_ocp()
finally:
GObject.timeout_add(700, restore_buttons, self.buttons)
def on_measure_button_clicked(self, data=None):
if int(self.entry['time'].get_text()) <= 0 or int(self.entry['time'].get_text()) > 65535:
logger.error("ERR: Time out of range")
return
for i in self.buttons:
i.set_sensitive(False)
try:
__main__.MAIN.stop_ocp()
__main__.MAIN.spinner.start()
offset = cal.measure_offset(self.params['time'])
for i in offset:
logger.info("{} {}".format(i, str(-offset[i])))
state.settings[i][1] = str(-offset[i])
self.entry['R100'].set_text(str(
state.settings['r100_trim'][1]))
self.entry['R3k'].set_text(str(
state.settings['r3k_trim'][1]))
self.entry['R30k'].set_text(str(
state.settings['r30k_trim'][1]))
self.entry['R300k'].set_text(str(
state.settings['r300k_trim'][1]))
self.entry['R3M'].set_text(str(
state.settings['r3M_trim'][1]))
self.entry['R30M'].set_text(str(
state.settings['r30M_trim'][1]))
self.entry['R100M'].set_text(str(
state.settings['r100M_trim'][1]))
__main__.MAIN.start_ocp()
finally:
GObject.timeout_add(700, restore_buttons, self.buttons)
__main__.MAIN.spinner.stop()
def restore_buttons(buttons):
""" Should be called with GObject callback """
for i in buttons:
i.set_sensitive(True)
return False
\ No newline at end of file
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import inspect
import logging
from collections import OrderedDict
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
from . import exp_int
logger = logging.getLogger(__name__)
class Experiments(GObject.Object):
__gsignals__ = {
'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()),
'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self, builder):
super(Experiments,self).__init__()
self.builder = builder
self.builder.connect_signals(self)
# (experiment index in UI, experiment)
classes = {c.id : c() # Make class instances
for _, c in inspect.getmembers(exp_int, inspect.isclass)
if issubclass(c, exp_int.ExpInterface)
and c is not exp_int.ExpInterface
}
self.classes = OrderedDict(sorted(classes.items()))
# fill exp_section
exp_section = self.builder.get_object('exp_section_box')
self.containers = {}
for key, c in self.classes.items():
c.connect('run_utility', self.on_run_utility)
c.connect('done_utility', self.on_done_utility)
self.containers[key] = c.get_window()
for key in self.containers:
try:
self.containers[key].get_parent().remove(self.containers[key])
except AttributeError:
pass
exp_section.add(self.containers[key])
self.expcombobox = self.builder.get_object('expcombobox')
self.expcombobox.connect('changed', self.on_expcombobox_changed)
for c in self.classes.values():
self.expcombobox.append(id=c.id, text=c.name)
def on_run_utility(self, data=None):
self.emit('run_utility')
def on_done_utility(self, data=None):
self.emit('done_utility')
def on_expcombobox_changed(self, data=None):
"""Change the experiment window when experiment box changed."""
self.set_exp(self.expcombobox.get_active_id())
def setup_exp(self, parameters):
"""Takes parameters.
Returns experiment instance.
"""
exp = self.classes[self.expcombobox.get_active_id()]
try:
exp.param_test(parameters)
except AttributeError:
logger.warning(
"Experiment {} has no defined parameter test.".format(
exp.name)
)
return exp.get_experiment(parameters)
def hide_exps(self):
for key in self.containers:
self.containers[key].hide()
def set_exp(self, selection):
"""Changes parameter tab to selected experiment. Returns True if
successful, False if invalid selection received.
Arguments:
selection -- id string of experiment type
"""
self.hide_exps()
self.containers[selection].show()
self.selected_exp = selection
return True
def get_params(self, experiment):
return self.classes[experiment].params
def set_params(self, experiment, parameters):
try:
self.classes[experiment].params = parameters
except KeyError as e:
logger.warning("Tried to load inavlid experiment with id %s", e.args)
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from ..dstat import state, dfu
from ..dstat.comm import dstat_logger, exp_logger
import logging
import time
import serial
logger = logging.getLogger(__name__)
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
class InfoDialog(object):
def __init__(self, parent, connect, signal='activate'):
self.parent = parent
connect.connect(signal, self.activate)
def activate(self, object=None, data=None):
self.dialog = Gtk.MessageDialog(self.parent, 0, Gtk.MessageType.INFO,
Gtk.ButtonsType.OK, "DStat Info")
self.dialog.format_secondary_text(
"PCB Version: {}\n".format(state.dstat_version.base_version) +
"Firmware Version: {}".format(state.firmware_version)
)
self.dialog.connect('response', self.destroy)
self.dialog.show()
def destroy(self, object=None, data=None):
self.dialog.destroy()
class ResetDialog(object):
def __init__(self, parent, connect, stop_callback, disconnect_callback, signal='activate'):
self.parent = parent
self.stop = stop_callback
self.disconnect = disconnect_callback
connect.connect(signal, self.activate)
def activate(self, object=None, data=None):
dialog = Gtk.MessageDialog(self.parent, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK_CANCEL, "EEPROM Reset")
dialog.format_secondary_text("This will reset the DStat's EEPROM settings, then disconneect."
)
response = dialog.run()
if response == Gtk.ResponseType.OK:
self.dstat_reset_eeprom()
dialog.destroy()
def dstat_reset_eeprom(self):
"""Tries to contact DStat and resets EEPROM.
If no response, returns False, otherwise True.
"""
self.stop()
exp = EEPROMReset()
state.ser.start_exp(exp)
logger.info("Resetting DStat EEPROM…")
while True:
result = state.ser.get_proc(block=True)
if result in ('SERIAL_ERROR', 'DONE', 'ABORT'):
break
logger.info(result)
self.disconnect()
class EEPROMReset(object):
def __init__(self):
pass
def run(self, ser, ctrl_pipe, data_pipe):
status = None
try:
ser.write(b'!2\n')
exp_logger.info('!2')
for i in range(10):
if ser.readline().rstrip() == b"@ACK 2":
dstat_logger.info('@ACK 2')
ser.write(b'SD\n')
exp_logger.info('SD')
status = "DONE"
time.sleep(5)
break
else:
time.sleep(.5)
ser.reset_input_buffer()
ser.write(b'!2\n')
exp_logger.info('!2')
time.sleep(.1)
except UnboundLocalError as e:
status = "SERIAL_ERROR"
except serial.SerialException as e:
logger.error('SerialException: %s', e)
status = "SERIAL_ERROR"
finally:
return status
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<requires lib="gtk+" version="3.10"/>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="vbox1">
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
......@@ -33,11 +34,11 @@
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<object class="GtkTable" id="table1">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
......@@ -46,8 +47,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -58,8 +58,7 @@
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -68,7 +67,8 @@
<property name="can_focus">False</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -78,9 +78,8 @@
<property name="label" translatable="yes">Cleaning</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -90,9 +89,8 @@
<property name="label" translatable="yes">Deposition</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -103,19 +101,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -126,19 +117,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -149,19 +133,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -172,19 +149,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</object>
......@@ -221,13 +191,11 @@
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<object class="GtkTable" id="table2">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">10</property>
<property name="homogeneous">True</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
......@@ -235,7 +203,8 @@
<property name="label" translatable="yes">Start (mV)</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -245,9 +214,8 @@
<property name="label" translatable="yes">Stop (mV)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -257,9 +225,8 @@
<property name="label" translatable="yes">Slope (mV/s)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -270,18 +237,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -292,20 +253,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing>
</child>
<child>
......@@ -316,20 +269,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing>
</child>
</object>
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkAdjustment" id="voltage_adjustment">
<property name="upper">3000</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkEntry" id="time_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Bias Voltage (mV)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Measurement Time (s)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">4</property>
<property name="invisible_char"></property>
<property name="text" translatable="yes">0</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="adjustment">voltage_adjustment</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Ambient Light Interlock</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="interlock_button">
<property name="label" translatable="yes">Enable interlock</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="threshold_button">
<property name="label" translatable="yes">Set threshold</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<signal name="clicked" handler="on_threshold_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="threshold_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="light_button">
<property name="label" translatable="yes">Refresh light measurement</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<signal name="clicked" handler="on_light_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="light_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">0</property>
<property name="justify">center</property>
<property name="track_visited_links">False</property>
<attributes>
<attribute name="weight" value="medium"/>
</attributes>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Electromechanical Shutter</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sync_button">
<property name="label" translatable="yes">Synchronous Detection (Hz)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">center</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="sync_freq">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="shutter_button">
<property name="label" translatable="yes">Enable Shutter</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_shutter_button_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="fft_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Start FFT after (s)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="fft_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">2</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text">1</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="fft_label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">FFT Integral bandwidth (Hz)</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">10</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="fft_int_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">6</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text">1</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">10</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Creates data plot.
"""
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3agg \
import FigureCanvasGTK3Agg as FigureCanvas
from matplotlib.backends.backend_gtk3 \
import NavigationToolbar2GTK3 as NavigationToolbar
try:
import seaborn as sns
except ImportError:
pass
from numpy import sin, linspace, pi, mean, trapz
from scipy import fft, arange
def plotSpectrum(y,Fs):
"""
Plots a Single-Sided Amplitude Spectrum of y(t)
"""
y = y-mean(y)
n = len(y) # length of the signal
k = arange(n)
T = n/Fs
frq = k/T # two sides frequency range
frq = frq[range(n/2)] # one side frequency range
Y = fft(y)/n # fft computing and normalization
Y = abs(Y[range(n/2)])
return (frq, Y)
def integrateSpectrum(x, y, target, bandwidth):
"""
Returns integral of range of bandwidth centered on target (both in Hz).
"""
j = 0
k = len(x)
for i in range(len(x)):
if x[i] >= target-bandwidth/2:
j = i
break
for i in range(j,len(x)):
if x[i] >= target+bandwidth/2:
k = i
break
return trapz(y=y[j:k], x=x[j:k])
def findBounds(y):
start_index = 0;
stop_index = len(y)-1;
for i in range(len(y)):
if (y[i] <= mean(y) and y[i+1] > mean(y)):
start_index = i
break
for i in range(len(y)):
if (y[-(i+1)] <= mean(y) and y[-i] > mean(y)):
stop_index = len(y)-1-i # len(y) is last index + 1
break
return (start_index, stop_index)
import logging
logger = logging.getLogger(__name__)
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
from matplotlib.backends.backend_gtk3 \
import NavigationToolbar2GTK3 as NavigationToolbar
def clear_notebook(notebook):
for pages in range(notebook.get_n_pages()):
notebook.remove_page(0)
def add_exp_to_notebook(notebook, exp):
for plot in exp.plots:
label = Gtk.Label.new(plot.name)
notebook.append_page(plot.box, label)
plot.box.show_all()
def replace_notebook_exp(notebook, exp, window):
clear_notebook(notebook)
add_exp_to_notebook(notebook, exp)
add_navigation_toolbars(exp, window)
def add_navigation_toolbars(exp, window):
for plot in exp.plots:
toolbar = NavigationToolbar(plot.canvas, window)
plot.box.pack_start(toolbar, expand=False, fill=False, padding=0)
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<requires lib="gtk+" version="3.10"/>
<object class="GtkListStore" id="ca_list">
<columns>
<!-- column-name millivolts -->
<column type="gint"/>
<!-- column-name seconds -->
<column type="guint"/>
</columns>
</object>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<property name="default_width">300</property>
<property name="default_height">500</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="vbox1">
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkTable" id="table1">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkEntry" id="voltage_entry">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Time (s)</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -53,45 +52,17 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="width_chars">5</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="truncate_multiline">True</property>
<property name="caps_lock_warning">False</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Bias Voltage (mV)</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Measurement Time (s)</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
......@@ -102,19 +73,17 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="label13">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xpad">20</property>
<property name="ypad">20</property>
<property name="label" translatable="yes">Experiment not yet implemented</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<property name="label" translatable="yes">Connect the electrodes to the RE input and the W_SHIELD connectors.
The ADC's PGA can be used to amplify the input signal, but note that the plot's y-axis is only correct for PGA 2x.</property>
<property name="wrap">True</property>
<property name="width_chars">30</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, absolute_import, print_function, unicode_literals
import io
import os
import logging
logger = logging.getLogger(__name__)
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
import numpy as np
from ..errors import InputError, VarError
from ..params import save_params, load_params
def manSave(current_exp):
fcd = Gtk.FileChooserDialog("Save…", None, Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
filters = [Gtk.FileFilter()]
filters[0].set_name("Tab-separated Text (.txt)")
filters[0].add_pattern("*.txt")
fcd.set_do_overwrite_confirmation(True)
for i in filters:
fcd.add_filter(i)
response = fcd.run()
if response == Gtk.ResponseType.OK:
path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path)
filter_selection = fcd.get_filter().get_name().decode("utf-8")
if filter_selection.endswith("(.txt)"):
save_text(current_exp, path)
fcd.destroy()
elif response == Gtk.ResponseType.CANCEL:
fcd.destroy()
def plot_save_dialog(plots):
fcd = Gtk.FileChooserDialog("Save Plot…", None,
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
filters = [Gtk.FileFilter()]
filters[0].set_name("Portable Document Format (.pdf)")
filters[0].add_pattern("*.pdf")
filters.append(Gtk.FileFilter())
filters[1].set_name("Portable Network Graphics (.png)")
filters[1].add_pattern("*.png")
fcd.set_do_overwrite_confirmation(True)
for i in filters:
fcd.add_filter(i)
response = fcd.run()
if response == Gtk.ResponseType.OK:
path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %r", path)
filter_selection = fcd.get_filter().get_name().decode("utf-8")
if filter_selection.endswith("(.pdf)"):
if not path.endswith(".pdf"):
path += ".pdf"
elif filter_selection.endswith("(.png)"):
if not path.endswith(".png"):
path += ".png"
save_plot(plots, path)
fcd.destroy()
elif response == Gtk.ResponseType.CANCEL:
fcd.destroy()
def man_param_save(window):
fcd = Gtk.FileChooserDialog("Save Parameters…",
None,
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
filters = [Gtk.FileFilter()]
filters[0].set_name("Parameter File (.yml)")
filters[0].add_pattern("*.yml")
fcd.set_do_overwrite_confirmation(True)
for i in filters:
fcd.add_filter(i)
response = fcd.run()
if response == Gtk.ResponseType.OK:
path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path)
if not path.endswith(".yml"):
path += '.yml'
save_params(window, path)
fcd.destroy()
elif response == Gtk.ResponseType.CANCEL:
fcd.destroy()
def man_param_load(window):
fcd = Gtk.FileChooserDialog("Load Parameters…",
None,
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filters = [Gtk.FileFilter()]
filters[0].set_name("Parameter File (.yml)")
filters[0].add_pattern("*.yml")
for i in filters:
fcd.add_filter(i)
response = fcd.run()
if response == Gtk.ResponseType.OK:
path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path)
load_params(window, path)
fcd.destroy()
elif response == Gtk.ResponseType.CANCEL:
fcd.destroy()
def autoSave(exp, path, name):
if name == "":
name = "file"
path += '/'
path += name
save_text(exp, path)
def autoPlot(exp, path, name):
if name == "":
name = "file"
path += '/'
path += name
if not (path.endswith(".pdf") or path.endswith(".png")):
path += ".pdf"
save_plot(exp, path)
def save_text(exp, path):
savestrings = exp.get_save_strings()
path = path.rstrip('.txt')
num = ''
j = 0
for key, text in savestrings.items(): # Test for existing files of any kind
while os.path.exists("{}{}-{}.txt".format(path, num, key)):
j += 1
num = j
save_path = "{}{}".format(path, num)
for key, text in savestrings.items():
with open('{}-{}.txt'.format(save_path, key), 'w') as f:
f.write(text)
def save_plot(exp, path):
"""Saves everything in exp.plots to path. Appends a number for duplicates.
If no file extension or unknown, uses pdf.
"""
name, _sep, ext = path.rpartition('.')
if _sep == '':
name = ext
ext = 'pdf'
num = ''
j = 0
for i in exp.plots: # Test for any existing files
plot_type = '_'.join(i.name.lower().split())
while os.path.exists("{}{}-{}.{}".format(name, num, plot_type, ext)):
j += 1
num = j
for i in exp.plots: # save data
plot_type = '_'.join(i.name.lower().split())
i.figure.savefig("{}{}-{}.{}".format(name, num, plot_type, ext))
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<requires lib="gtk+" version="3.10"/>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="vbox1">
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
......@@ -33,11 +34,11 @@
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<object class="GtkTable" id="table1">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
......@@ -46,8 +47,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -58,8 +58,7 @@
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
......@@ -68,7 +67,8 @@
<property name="can_focus">False</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
......@@ -78,9 +78,8 @@
<property name="label" translatable="yes">Cleaning</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -90,9 +89,8 @@
<property name="label" translatable="yes">Deposition</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -103,19 +101,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -126,19 +117,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -149,19 +133,12 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
......@@ -172,21 +149,17 @@
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
......@@ -221,251 +194,198 @@
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<object class="GtkTable" id="table2">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">7</property>
<property name="n_columns">2</property>
<property name="column_spacing">10</property>
<property name="homogeneous">True</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel" id="label8">
<object class="GtkEntry" id="scans_entry">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Start (mV)</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label9">
<object class="GtkCheckButton" id="cyclic_checkbutton">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Stop (mV)</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="use_stock">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label10">
<object class="GtkLabel" id="label15">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Step Size (mV)</property>
<property name="label" translatable="yes">Scans</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="start_entry">
<object class="GtkLabel" id="label14">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Scan both forwards and backwards.</property>
<property name="label" translatable="yes">Cyclic Mode</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="stop_entry">
<object class="GtkEntry" id="freq_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="step_entry">
<object class="GtkEntry" id="pulse_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label11">
<object class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Pulse Height (mV)</property>
<property name="label" translatable="yes">Frequency (Hz)</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label12">
<object class="GtkLabel" id="label11">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Frequency (Hz)</property>
<property name="label" translatable="yes">Pulse Height (mV)</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options">GTK_FILL</property>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="pulse_entry">
<object class="GtkEntry" id="step_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="freq_entry">
<object class="GtkEntry" id="stop_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label14">
<object class="GtkEntry" id="start_entry">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Scan both forwards and backwards.</property>
<property name="label" translatable="yes">Cyclic Mode</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label15">
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Scans</property>
<property name="label" translatable="yes">Step Size (mV)</property>
</object>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="cyclic_checkbutton">
<object class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<property name="draw_indicator">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Stop (mV)</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options"/>
<property name="y_options"/>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="scans_entry">
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Start (mV)</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
......@@ -492,9 +412,10 @@
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Note: The ADC converts continuously during SWV measurements and has priority over DAC changes. ADC sample rate should be significantly larger than SWV frequency for accurate timing.</property>
<property name="justify">center</property>
<property name="label" translatable="yes">Note: ADC samples 1/(ADC Frequency) before end of pulse. ADC frequency should be significantly larger than SWV frequency to reduce capacitive current.</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="width_chars">43</property>
</object>
<packing>
<property name="expand">True</property>
......
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import zmq
#signals
CONREQ = "0"
CONREP = "1"
STARTEXP = "start"
START_REP = "started"
EXP_FINISH_REQ = "notify_completion"
EXPFINISHED = "completed"
INVAL_CMD = "99"
#States
RECV = 0
SEND = 1
class microdropConnection(object):
"""Manages microdrop connection over TCP with zmq"""
def __init__(self, port=6789):
"""Create zmq context and bind to port. Should be called manually
to reinitialize if reset is called.
Keyword arguments:
port -- the TCP to bind to on localhost
"""
self.port = port
self.connected = False
self.state = RECV
self.ctx = zmq.Context()
self.soc = zmq.Socket(self.ctx, zmq.REP)
self.soc.bind("".join(['tcp://*:', str(self.port)]))
def listen(self):
"""Perform non-blocking recv on zmq port. self.state must be RECV.
Returns a tuple:
[0] -- True if a message was received, False otherwise.
[1] -- The recieved message or "" if no message received.
"""
if self.state == SEND:
print "WAR: [uDrop-listen] Connection state invalid, resetting..."
# self.reset()
# self.__init__(self.port)
return (False, "")
try:
message = self.soc.recv(flags=zmq.NOBLOCK, copy=True)
self.state = SEND
return (True, message)
except zmq.Again:
return (False, "")
def reply(self, data):
"""Sends a reply on zmq port. self.state must be SEND.
Arguments:
data -- a str to be sent
"""
if self.state == RECV:
print "WAR: [uDrop-reply] Connection state invalid, resetting..."
self.reset()
self.__init__(self.port)
return False
self.state = RECV
self.soc.send(data)
return True
def reset(self):
"""Reset zmq interface. Must call __init__ again to reinitialize."""
self.soc.unbind("".join(['tcp://*:', str(self.port)]))
del self.soc
del self.ctx
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import yaml
from errors import InputError
logger = logging.getLogger(__name__)
def get_params(window):
"""Fetches and returns dict of all parameters for saving."""
selection = window.exp_window.expcombobox.get_active_id()
parameters = {'experiment_index' : selection}
try:
parameters['version'] = window.version
except AttributeError: # Will be thrown if not connected to DStat
pass
try:
parameters.update(window.adc_pot.params)
except InputError:
logger.info("No gain selected.")
parameters.update(window.exp_window.get_params(selection))
parameters.update(window.analysis_opt_window.params)
return parameters
def save_params(window, path):
"""Fetches current params and saves to path."""
logger.info("Save to: %s", path)
params = get_params(window)
with open(path, 'w') as f:
yaml.dump(params, f)
def load_params(window, path):
"""Loads params from a path into UI elements."""
try:
get_params(window)
except InputError: # Will be thrown because no experiment will be selected
pass
except KeyError: # Will be thrown because no experiment will be selected
pass
with open(path, 'r') as f:
params = yaml.load(f)
set_params(window, params)
def set_params(window, params):
window.adc_pot.params = params
if 'experiment_index' in params:
window.exp_window.expcombobox.set_active_id(params['experiment_index'])
window.exp_window.set_params(params['experiment_index'], params)
window.analysis_opt_window.params = params
window.params_loaded = True
# -*- coding: utf-8 -*-
import logging
from params import get_params, set_params, load_params, save_params
from interface.save import save_text, save_plot
from zmq_plugin.plugin import Plugin as ZmqPlugin
from zmq_plugin.schema import decode_content_data
import gtk
import zmq
logger = logging.getLogger(__name__)
def get_hub_uri(default='tcp://localhost:31000', parent=None):
message = 'Please enter 0MQ hub URI:'
d = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_MODAL |
gtk.DIALOG_DESTROY_WITH_PARENT,
type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK_CANCEL,
message_format=message)
entry = gtk.Entry()
entry.set_text(default)
d.vbox.pack_end(entry)
d.vbox.show_all()
entry.connect('activate', lambda _: d.response(gtk.RESPONSE_OK))
d.set_default_response(gtk.RESPONSE_OK)
r = d.run()
text = entry.get_text().decode('utf8')
d.destroy()
if r == gtk.RESPONSE_OK:
return text
else:
return None
class DstatPlugin(ZmqPlugin):
'''
Public 0MQ plugin API.
'''
def __init__(self, parent, *args, **kwargs):
self.parent = parent
super(DstatPlugin, self).__init__(*args, **kwargs)
def check_sockets(self):
'''
Check for messages on command and subscription sockets and process
any messages accordingly.
'''
try:
msg_frames = self.command_socket.recv_multipart(zmq.NOBLOCK)
except zmq.Again:
pass
else:
self.on_command_recv(msg_frames)
try:
msg_frames = self.subscribe_socket.recv_multipart(zmq.NOBLOCK)
source, target, msg_type, msg_json = msg_frames
self.most_recent = msg_json
except zmq.Again:
pass
except:
logger.error('Error processing message from subscription '
'socket.', exc_info=True)
return True
def on_execute__load_params(self, request):
'''
Args
----
params_path (str) : Path to file for parameters yaml file.
'''
data = decode_content_data(request)
load_params(self.parent, data['params_path'])
def on_execute__save_params(self, request):
'''
Args
----
params_path (str) : Path to file for parameters yaml file.
'''
data = decode_content_data(request)
save_params(self.parent, data['params_path'])
def on_execute__set_params(self, request):
'''
Args
----
(dict) : Parameters dictionary in format returned by `get_params`.
'''
data = decode_content_data(request)
set_params(self.parent, data['params'])
def on_execute__get_params(self, request):
return get_params(self.parent)
def on_execute__run_active_experiment(self, request):
data = decode_content_data(request)
self.parent.statusbar.push(self.parent.message_context_id, "µDrop "
"acquisition requested.")
return self.parent.run_active_experiment(
param_override=data.get('params'),
metadata=data.get('metadata')
)
def on_execute__set_metadata(self, request=None):
'''
Args
----
(dict) : Dictionary of metadata to be used in subsequent
experiments. Should include `device_id`, `patient_id`, and
`experiment_id`. Leave blank to reset all metadata fields or set
individual keys to `None` to reset individual values.
'''
data = decode_content_data(request)
self.parent.metadata = request
def on_execute__save_text(self, request):
'''
Args
----
save_data_path (str) : Path to file to save text data.
'''
data = decode_content_data(request)
save_text(self.parent.current_exp, data['save_data_path'])
def on_execute__save_plot(self, request):
'''
Args
----
save_plot_path (str) : Path to file to save plot.
'''
data = decode_content_data(request)
save_plot(self.parent.current_exp, data['save_plot_path'])
def on_execute__acquisition_complete(self, request):
'''
Args
----
Returns
-------
(datetime.datetime or None) : The completion time of the experiment
corresponding to the specified UUID.
'''
data = decode_content_data(request)
self.parent.statusbar.push(self.parent.message_context_id, "µDrop "
"notified of completed acquisition.")
if data['experiment_id'] in self.parent.completed_experiment_ids:
return self.parent.completed_experiment_ids[data['experiment_id']]
elif data['experiment_id'] == self.parent.active_experiment_id:
return None
else:
raise KeyError('Unknown experiment ID: %s' % data['experiment_id'])
# -*- coding: utf-8 -*-
"""Calculates the current version number.
If possible, uses output of “git describe” modified to conform to the
visioning scheme that setuptools uses (see PEP 386). Releases must be
labelled with annotated tags (signed tags are annotated) of the following
format:
v<num>(.<num>)+ [ {a|b|c|rc} <num> (.<num>)* ]
If “git describe” returns an error (likely because we're in an unpacked copy
of a release tarball, rather than a git working copy), or returns a tag that
does not match the above format, version is read from RELEASE-VERSION file.
To use this script, simply import it your setup.py file, and use the results
of getVersion() as your package version:
import version
setup(
version=version.getVersion(),
.
.
.
)
This will automatically update the RELEASE-VERSION file. The RELEASE-VERSION
file should *not* be checked into git but it *should* be included in sdist
tarballs (as should version.py file). To do this, run:
echo include RELEASE-VERSION version.py >>MANIFEST.in
echo RELEASE-VERSION >>.gitignore
With that setup, a new release can be labelled by simply invoking:
git tag -s v1.0
"""
__author__ = ('Douglas Creager <dcreager@dcreager.net>',
'Michal Nazarewicz <mina86@mina86.com>')
__license__ = 'This file is placed into the public domain.'
__maintainer__ = 'Michal Nazarewicz'
__email__ = 'mina86@mina86.com'
__all__ = ('getVersion')
import re
import subprocess
import sys
import os.path
import inspect
RELEASE_VERSION_FILE = '{}/RELEASE-VERSION'.format(
os.path.dirname(os.path.abspath(inspect.stack()[0][1])))
# http://www.python.org/dev/peps/pep-0386/
_PEP386_SHORT_VERSION_RE = r'\d+(?:\.\d+)+(?:(?:[abc]|rc)\d+(?:\.\d+)*)?'
_PEP386_VERSION_RE = r'^%s(?:\.post\d+)?(?:\.dev\d+)?$' % (
_PEP386_SHORT_VERSION_RE)
_GIT_DESCRIPTION_RE = r'^v(?P<ver>%s)-(?P<commits>\d+)-g(?P<sha>[\da-f]+)$' % (
_PEP386_SHORT_VERSION_RE)
def readGitVersion():
try:
proc = subprocess.Popen(('git', 'describe', '--long',
'--match', 'v[0-9]*.*'),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
data, _ = proc.communicate()
if proc.returncode:
return None
ver = data.splitlines()[0].strip()
proc = subprocess.Popen(('git', 'rev-parse', '--abbrev-ref', 'HEAD'),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
branch, _ = proc.communicate()
if proc.returncode:
return None
except:
return None
if not ver:
return None
m = re.search(_GIT_DESCRIPTION_RE, ver)
if not m:
sys.stderr.write('version: git description (%s) is invalid, '
'ignoring\n' % ver)
return None
commits = int(m.group('commits'))
if not commits:
version = m.group('ver')
else:
version = '%s.post%d' % (
m.group('ver'), commits)
if branch.strip() != 'master':
version += '.dev%d' % int(m.group('sha'), 16)
return version
def readReleaseVersion():
try:
fd = open(RELEASE_VERSION_FILE)
try:
ver = fd.readline().strip()
finally:
fd.close()
if not re.search(_PEP386_VERSION_RE, ver):
sys.stderr.write('version: release version (%s) is invalid, '
'will use it anyway\n' % ver)
return ver
except:
return None
def writeReleaseVersion(version):
fd = open(RELEASE_VERSION_FILE, 'w')
fd.write('%s\n' % version)
fd.close()
def getVersion():
release_version = readReleaseVersion()
version = readGitVersion() or release_version
if not version:
raise ValueError('Cannot find the version number')
if version != release_version:
writeReleaseVersion(version)
return version
if __name__ == '__main__':
print getVersion()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" GUI Interface for Wheeler Lab DStat """
from __future__ import division, absolute_import, print_function, unicode_literals
import sys
import os
import platform
import multiprocessing
import uuid
from collections import OrderedDict
from datetime import datetime
import logging
from pkg_resources import parse_version
from serial import SerialException
import zmq
from dstat_interface.core.utils.version import getVersion
from dstat_interface.core.experiments import idle, pot
from dstat_interface.core import params, analysis, dstat
from dstat_interface.core.dstat import boards
from dstat_interface.core.interface import (exp_window, adc_pot, plot_ui, data_view,
save, hw_info)
from dstat_interface.core.errors import InputError
from dstat_interface.core.plugin import DstatPlugin, get_hub_uri
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
mod_dir = os.path.dirname(os.path.abspath(__file__))
conf_path = os.path.join(os.path.expanduser("~"), '.dstat-interface')
# if __name__ == "__parents_main__": # Only runs for forking emulation on win
# sys.path.append(mod_dir)
# Setup Logging
logger = logging.getLogger(__name__)
core_logger = logging.getLogger("dstat_interface.core")
loggers = [logger, core_logger]
log_handler = logging.StreamHandler()
log_formatter = logging.Formatter(
fmt='%(asctime)s %(levelname)s: [%(name)s] %(message)s',
datefmt='%H:%M:%S'
)
log_handler.setFormatter(log_formatter)
for log in loggers:
log.setLevel(level=logging.INFO)
log.addHandler(log_handler)
class Main(GObject.Object):
"""Main program """
__gsignals__ = {
b'exp_start': (GObject.SIGNAL_RUN_FIRST, None, ()),
b'exp_stop': (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self):
super(Main, self).__init__()
self.builder = Gtk.Builder()
self.builder.add_from_file(
os.path.join(mod_dir, 'core/interface/dstatinterface.glade'))
self.builder.connect_signals(self)
self.cell = Gtk.CellRendererText()
# Create instance of interface components
self.statusbar = self.builder.get_object('statusbar')
self.ocp_disp = self.builder.get_object('ocp_disp')
self.window = self.builder.get_object('window1')
self.aboutdialog = self.builder.get_object('aboutdialog1')
self.stopbutton = self.builder.get_object('pot_stop')
self.startbutton = self.builder.get_object('pot_start')
self.startbutton.set_sensitive(False)
self.exp_progressbar = self.builder.get_object('exp_progressbar')
self.adc_pot = adc_pot.adc_pot()
self.error_context_id = self.statusbar.get_context_id("error")
self.message_context_id = self.statusbar.get_context_id("message")
self.exp_window = exp_window.Experiments(self.builder)
self.exp_window.connect('run_utility', self.stop_ocp)
self.exp_window.connect('done_utility', self.start_ocp)
self.analysis_opt_window = analysis.AnalysisOptions(self.builder)
# Setup Autosave
self.autosave_checkbox = self.builder.get_object('autosave_checkbutton')
self.autosavedir_button = self.builder.get_object('autosavedir_button')
self.autosavename = self.builder.get_object('autosavename')
# Setup Plots
self.plot_notebook = self.builder.get_object('plot_notebook')
self.main_notebook = self.builder.get_object('main_notebook')
self.data_view = data_view.DataPage(self.main_notebook)
self.info_page = data_view.InfoPage(self.main_notebook)
# fill adc_pot_box
self.adc_pot_box = self.builder.get_object('gain_adc_box')
self.adc_pot_container = self.adc_pot.builder.get_object('vbox1')
self.adc_pot_container.reparent(self.adc_pot_box)
# fill serial
self.serial_connect = self.builder.get_object('serial_connect')
self.serial_pmt_connect = self.builder.get_object('pmt_mode')
self.serial_disconnect = self.builder.get_object('serial_disconnect')
self.serial_disconnect.set_sensitive(False)
self.serial_combobox = self.builder.get_object('serial_combobox')
self.serial_combobox.pack_start(self.cell, True)
self.serial_combobox.add_attribute(self.cell, 'text', 0)
self.serial_liststore = self.builder.get_object('serial_liststore')
self.serial_devices = dstat.comm.SerialDevices()
for i in self.serial_devices.ports:
self.serial_liststore.append([i])
self.serial_combobox.set_active(0)
self.spinner = self.builder.get_object('spinner')
self.mainwindow = self.builder.get_object('window1')
self.menu_dstat_info = self.builder.get_object('menu_dstat_info')
self.menu_dstat_info.set_sensitive(False)
self.dstat_info_window = hw_info.InfoDialog(
parent=self.mainwindow,
connect=self.menu_dstat_info
)
self.menu_dstat_fw = self.builder.get_object('menu_dstat_fw')
self.menu_dstat_fw.set_sensitive(False)
self.dstat_fw_window = dstat.dfu.FWDialog(
parent=self.mainwindow,
connect=self.menu_dstat_fw,
stop_callback=self.stop_ocp,
disconnect_callback=self.on_serial_disconnect_clicked
)
self.menu_dstat_reset = self.builder.get_object('menu_dstat_reset')
self.menu_dstat_reset.set_sensitive(False)
self.menu_dstat_reset_window = hw_info.ResetDialog(
parent=self.mainwindow,
connect=self.menu_dstat_reset,
stop_callback=self.stop_ocp,
disconnect_callback=self.on_serial_disconnect_clicked
)
# Set Version Strings
try:
ver = getVersion()
except ValueError:
ver = "1.x"
logger.warning("Could not fetch version number")
self.mainwindow.set_title(" ".join(("DStat Interface", ver)))
self.aboutdialog.set_version(ver)
self.mainwindow.show_all()
self.exp_window.hide_exps()
self.expnumber = 0
self.connected = False
self.pmt_mode = False
self.menu_dropbot_connect = self.builder.get_object(
'menu_dropbot_connect')
self.menu_dropbot_disconnect = self.builder.get_object(
'menu_dropbot_disconnect')
self.dropbot_enabled = False
self.dropbot_triggered = False
self.metadata = None # Should only be added to by plugin interface
self.params_loaded = False
# Disable 0MQ plugin API by default.
self.plugin = None
self.plugin_timeout_id = None
# UUID for active experiment.
self.active_experiment_id = None
# UUIDs for completed experiments.
self.completed_experiment_ids = OrderedDict()
def on_window1_destroy(self, object, data=None):
""" Quit when main window closed."""
self.quit()
def on_gtk_quit_activate(self, menuitem, data=None):
"""Quit when Quit selected from menu."""
self.quit()
def quit(self):
"""Disconnect and save parameters on quit."""
try:
params.save_params(self, os.path.join(conf_path, 'last_params.yml'))
self.on_serial_disconnect_clicked()
except KeyError:
pass
mainloop.quit()
def on_gtk_about_activate(self, menuitem, data=None):
"""Display the about window."""
self.aboutdialog.run() # waits for user to click close
self.aboutdialog.hide()
def on_menu_analysis_options_activate(self, menuitem, data=None):
self.analysis_opt_window.show()
def on_serial_refresh_clicked(self, data=None):
"""Refresh list of serial devices."""
try:
self.serial_devices.refresh()
self.serial_liststore.clear()
except ValueError:
logger.warning("No DStats found")
for i in self.serial_devices.ports:
self.serial_liststore.append([i])
def on_serial_connect_clicked(self, widget):
"""Connect and retrieve DStat version."""
selection = self.serial_combobox.get_active_iter()
if selection is None:
return
if widget is self.serial_pmt_connect:
self.pmt_mode = True
self.adc_pot.ui['short_true'].set_active(True)
self.adc_pot.ui['short_true'].set_sensitive(False)
try:
self.serial_connect.set_sensitive(False)
self.serial_pmt_connect.set_sensitive(False)
dstat.comm.version_check(
self.serial_liststore.get_value(selection, 0)
)
dstat.state.board_instance = boards.find_board(
dstat.state.dstat_version)()
self.statusbar.remove_all(self.error_context_id)
self.adc_pot.set_version()
self.statusbar.push(
self.message_context_id,
"DStat version: {}".format(
dstat.state.dstat_version.base_version)
)
dstat.comm.read_settings()
try:
if dstat.state.settings['dac_units_true'][1] != b'1':
dstat.state.settings['dac_units_true'][1] = b'1'
dstat.comm.write_settings()
except KeyError:
logger.warning("Connected DStat does not support sending DAC units.")
dialog = Gtk.MessageDialog(
self.window, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK, "Connected DStat does not support sending DAC units." +
"Update firmware or set potentials will be incorrect!")
dialog.run()
dialog.destroy()
self.start_ocp()
self.connected = True
self.serial_disconnect.set_sensitive(True)
except:
self.serial_connect.set_sensitive(True)
self.serial_pmt_connect.set_sensitive(True)
self.adc_pot.ui['short_true'].set_sensitive(True)
raise
if self.params_loaded == False:
try:
try:
os.makedirs(conf_path)
except OSError:
if not os.path.isdir(conf_path):
raise
params.load_params(self,
os.path.join(conf_path, 'last_params.yml')
)
except IOError:
logger.info("No previous parameters found.")
def on_serial_disconnect_clicked(self, data=None):
"""Disconnect from DStat."""
if self.connected == False:
return
try:
if self.ocp_is_running:
self.stop_ocp()
else:
self.on_pot_stop_clicked()
dstat.state.ser.disconnect()
except AttributeError as err:
logger.warning("AttributeError: %s", err)
pass
if self.pmt_mode is True:
self.adc_pot.ui['short_true'].set_sensitive(True)
self.pmt_mode = False
self.connected = False
self.serial_connect.set_sensitive(True)
self.serial_pmt_connect.set_sensitive(True)
self.serial_disconnect.set_sensitive(False)
self.startbutton.set_sensitive(False)
self.stopbutton.set_sensitive(False)
self.menu_dstat_info.set_sensitive(False)
self.menu_dstat_fw.set_sensitive(False)
self.menu_dstat_reset.set_sensitive(False)
self.adc_pot.ui['short_true'].set_sensitive(True)
dstat.state.reset()
def start_ocp(self, data=None):
"""Start OCP measurements."""
if dstat.state.dstat_version >= parse_version('1.2'):
# Flush data pipe
dstat.state.ser.flush_data()
if self.pmt_mode is True:
logger.info("Start PMT idle mode")
dstat.state.ser.start_exp(idle.PMTIdle())
self.ocp_is_running = True
self.ocp_proc = (GObject.timeout_add(250, self.ocp_running_proc)
,
)
else:
logger.info("Start OCP")
dstat.state.ser.start_exp(idle.OCPExp())
self.ocp_proc = (GObject.timeout_add(300, self.ocp_running_data),
GObject.timeout_add(250, self.ocp_running_proc)
)
self.ocp_is_running = False
GObject.timeout_add(100, self.ocp_assert) # Check if getting data
else:
logger.info("OCP measurements not supported on v1.1 boards.")
return
def stop_ocp(self, data=None):
"""Stop OCP measurements."""
if dstat.state.dstat_version >= parse_version('1.2'):
if self.pmt_mode == True:
logger.info("Stop PMT idle mode")
else:
logger.info("Stop OCP")
dstat.state.ser.send_ctrl('a')
for i in self.ocp_proc:
GObject.source_remove(i)
while self.ocp_running_proc():
pass
self.ocp_disp.set_text("")
self.ocp_is_running = False
self.startbutton.set_sensitive(True)
self.menu_dstat_info.set_sensitive(True)
self.menu_dstat_fw.set_sensitive(True)
self.menu_dstat_reset.set_sensitive(True)
else:
logger.error("OCP measurements not supported on v1.1 boards.")
return
def ocp_assert(self):
if self.ocp_is_running:
self.startbutton.set_sensitive(True)
self.menu_dstat_info.set_sensitive(True)
self.menu_dstat_fw.set_sensitive(True)
self.menu_dstat_reset.set_sensitive(True)
return False
else:
return True
def ocp_running_data(self):
"""Receive OCP value from experiment process and update ocp_disp field
Returns:
True -- when experiment is continuing to keep function in GTK's queue.
False -- when experiment process signals EOFError or IOError to remove
function from GTK's queue.
"""
try:
incoming = dstat.state.ser.get_data()
while incoming is not None:
if isinstance(incoming, basestring): # test if incoming is str
self.on_serial_disconnect_clicked()
return False
try:
data = "".join(["OCP: ",
"{0:.3f}".format(incoming),
" V"])
self.ocp_disp.set_text(data)
self.ocp_is_running = True
except ValueError:
pass
incoming = dstat.state.ser.get_data()
return True
except EOFError:
return False
except IOError:
return False
def ocp_running_proc(self):
"""Handles signals on proc_pipe_p for OCP.
Returns:
True -- when experiment is continuing to keep function in GTK's queue.
False -- when experiment process signals EOFError or IOError to remove
function from GTK's queue.
"""
try:
proc_buffer = dstat.state.ser.get_proc()
while proc_buffer is not None:
logger.debug("ocp_running_proc: %s", proc_buffer)
if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]:
if proc_buffer == "SERIAL_ERROR":
self.on_serial_disconnect_clicked()
dstat.state.ser.flush_data()
return False
proc_buffer = dstat.state.ser.get_proc()
return True
except EOFError:
return False
except IOError:
return False
def on_pot_start_clicked(self, data=None):
try:
self.run_active_experiment()
except (ValueError, KeyError, InputError, SerialException,
AssertionError):
# Ignore expected exceptions when triggering experiment from UI.
pass
def run_active_experiment(self, param_override=None, metadata=None):
"""Run currently visible experiment."""
# Assign current experiment a unique identifier.
experiment_id = uuid.uuid4()
self.active_experiment_id = experiment_id
logger.info("Current measurement id: %s", experiment_id.hex)
self.metadata = metadata
if self.metadata is not None:
logger.info("Loading external metadata")
def exceptions():
""" Cleans up after errors """
self.spinner.stop()
self.startbutton.set_sensitive(True)
self.stopbutton.set_sensitive(False)
self.start_ocp()
self.stop_ocp()
self.statusbar.remove_all(self.error_context_id)
dstat.state.ser.flush_data()
parameters = {}
parameters['metadata'] = self.metadata
# Make sure these are defined
parameters['sync_true'] = False
parameters['shutter_true'] = False
try:
if param_override is not None:
params.set_params(self, param_override)
parameters.update(params.get_params(self))
self.line = 0
self.lastline = 0
self.lastdataline = 0
self.spinner.start()
self.startbutton.set_sensitive(False)
self.stopbutton.set_sensitive(True)
self.statusbar.remove_all(self.error_context_id)
try:
del self.current_exp
except AttributeError:
pass
callbacks = {'experiment_done' : self.experiment_done,
'progress_update' : self.progress_update}
self.current_exp = self.exp_window.setup_exp(parameters)
plot_ui.replace_notebook_exp(
self.plot_notebook, self.current_exp, self.window
)
self.data_view.clear_exps()
self.info_page.clear()
dstat.state.ser.start_exp(self.current_exp)
# Flush data pipe
dstat.state.ser.flush_data()
self.current_exp.setup_loops(callbacks)
return experiment_id
except ValueError as i:
logger.info("ValueError: %s",i)
self.statusbar.push(self.error_context_id,
"Experiment parameters must be integers.")
exceptions()
raise
except KeyError as i:
import traceback
traceback.print_exc()
logger.info("KeyError: %s", i)
self.statusbar.push(self.error_context_id,
"Experiment parameters must be integers.")
exceptions()
raise
except InputError as err:
logger.info("InputError: %s", err)
self.statusbar.push(self.error_context_id, err.msg)
exceptions()
raise
except SerialException as err:
logger.info("SerialException: %s", err)
self.statusbar.push(self.error_context_id,
"Could not establish serial connection.")
exceptions()
raise
except AssertionError as err:
logger.info("AssertionError: %s", err)
self.statusbar.push(self.error_context_id, str(err))
exceptions()
raise
def progress_update(self, widget, progress):
if 0 <= progress <= 1:
self.exp_progressbar.set_fraction(progress)
else:
self.exp_progressbar.pulse()
def experiment_running_data(self):
"""Receive data from experiment process and add to
current_exp.data['data].
Run in GTK main loop.
Returns:
True -- when experiment is continuing to keep function in GTK's queue.
False -- when experiment process signals EOFError or IOError to remove
function from GTK's queue.
"""
try:
incoming = dstat.state.ser.get_data()
while incoming is not None:
try:
self.line, data = incoming
if self.line > self.lastdataline:
newline = True
try:
logger.info("running scan_process()")
self.current_exp.scan_process(self.lastdataline)
except AttributeError:
pass
self.lastdataline = self.line
else:
newline = False
self.current_exp.store_data(incoming, newline)
if newline:
self.experiment_running_plot()
except TypeError:
pass
incoming = dstat.state.ser.get_data()
return True
except EOFError as err:
logger.error(err)
self.experiment_done()
return False
except IOError as err:
logger.error(err)
self.experiment_done()
return False
def experiment_running_proc(self):
"""Receive proc signals from experiment process.
Run in GTK main loop.
Returns:
True -- when experiment is continuing to keep function in GTK's queue.
False -- when experiment process signals EOFError or IOError to remove
function from GTK's queue.
"""
try:
ctrl_buffer = dstat.state.ser.get_ctrl()
try:
if ctrl_buffer is not None:
self.current_exp.ctrl_loop(ctrl_buffer)
except AttributeError:
pass
proc_buffer = dstat.state.ser.get_proc()
if proc_buffer is not None:
if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]:
self.experiment_done()
if proc_buffer == "SERIAL_ERROR":
self.on_serial_disconnect_clicked()
else:
logger.warning("Unrecognized experiment return code: %s",
proc_buffer)
return False
return True
except EOFError as err:
logger.warning("EOFError: %s", err)
self.experiment_done()
return False
except IOError as err:
logger.warning("IOError: %s", err)
self.experiment_done()
return False
def experiment_running_plot(self, force_refresh=False):
"""Plot all data in current_exp.data.
Run in GTK main loop. Always returns True so must be manually
removed from GTK's queue.
"""
for plot in self.current_exp.plots:
if (plot.scan_refresh and self.line > self.lastline):
while self.line > self.lastline:
# make sure all of last line is added
plot.updateline(self.current_exp, self.lastline)
self.lastline += 1
plot.updateline(self.current_exp, self.line)
plot.redraw()
else:
while self.line > self.lastline:
# make sure all of last line is added
plot.updateline(self.current_exp, self.lastline)
self.lastline += 1
plot.updateline(self.current_exp, self.line)
if plot.continuous_refresh is True or force_refresh is True:
plot.redraw()
return True
def experiment_done(self, widget=None):
"""Clean up after data acquisition is complete. Update plot and
copy data to raw data tab. Saves data if autosave enabled.
"""
try:
# Run Analysis
analysis.do_analysis(self.current_exp)
# Write DStat commands
self.info_page.set_text(self.current_exp.get_info_text())
try:
self.statusbar.push(
self.message_context_id,
"Integral: %s A" % self.current_exp.analysis['FT Integral'][0][1]
)
except KeyError:
pass
# Data Output
analysis_buffer = []
if self.current_exp.analysis != {}:
analysis_buffer.append("# ANALYSIS")
for key, value in self.current_exp.analysis.iteritems():
analysis_buffer.append("# %s:" % key)
for scan in value:
number, result = scan
analysis_buffer.append(
"# Scan %s -- %s" % (number, result)
)
for i in analysis_buffer:
self.info_page.add_line(i)
self.data_view.add_exp(self.current_exp)
# Autosaving
if self.autosave_checkbox.get_active():
save.autoSave(self.current_exp,
self.autosavedir_button.get_filename().decode('utf-8'),
self.autosavename.get_text()
)
save.autoPlot(self.current_exp,
self.autosavedir_button.get_filename().decode('utf-8'),
self.autosavename.get_text()
)
# uDrop
# UI stuff
finally:
self.metadata = None # Reset metadata
self.spinner.stop()
self.exp_progressbar.set_fraction(0)
self.stopbutton.set_sensitive(False)
self.start_ocp()
self.completed_experiment_ids[self.active_experiment_id] =\
datetime.utcnow()
# Temporary fix for weird drawing bug on windows Gtk+3
if platform.system() == 'Windows':
position = self.window.get_position()
self.window.hide()
self.window.show()
self.window.move(*position)
def on_pot_stop_clicked(self, data=None):
"""Stop current experiment. Signals experiment process to stop."""
try:
dstat.state.ser.stop_exp()
except AttributeError:
pass
except:
logger.warning(sys.exc_info())
def on_file_save_exp_activate(self, menuitem, data=None):
"""Activate dialogue to save current experiment data. """
try:
save.manSave(self.current_exp)
except AttributeError:
logger.warning("Tried to save with no experiment run")
def on_file_save_plot_activate(self, menuitem, data=None):
"""Activate dialogue to save current plot."""
try:
save.plot_save_dialog(self.current_exp)
except AttributeError:
logger.warning("Tried to save with no experiment run")
def on_file_save_params_activate(self, menuitem, data=None):
"""Activate dialogue to save current experiment parameters. """
save.man_param_save(self)
def on_file_load_params_activate(self, menuitem, data=None):
"""Activate dialogue to load experiment parameters from file. """
save.man_param_load(self)
def on_menu_dropbot_connect_activate(self, menuitem, data=None):
"""Listen for remote control connection from µDrop."""
# Prompt user for 0MQ plugin hub URI.
zmq_plugin_hub_uri = get_hub_uri(parent=self.window)
self.dropbot_enabled = True
self.menu_dropbot_connect.set_sensitive(False)
self.menu_dropbot_disconnect.set_sensitive(True)
self.statusbar.push(self.message_context_id,
"Waiting for µDrop to connect…")
self.enable_plugin(zmq_plugin_hub_uri)
def on_menu_dropbot_disconnect_activate(self, menuitem=None, data=None):
"""Disconnect µDrop connection and stop listening."""
self.cleanup_plugin()
self.dropbot_enabled = False
self.menu_dropbot_connect.set_sensitive(True)
self.menu_dropbot_disconnect.set_sensitive(False)
self.statusbar.push(self.message_context_id, "µDrop disconnected.")
def enable_plugin(self, hub_uri):
'''
Connect to 0MQ plugin hub to expose public D-Stat API.
Args
----
hub_uri (str) : URI for 0MQ plugin hub.
'''
self.cleanup_plugin()
# Initialize 0MQ hub plugin and subscribe to hub messages.
self.plugin = DstatPlugin(self, 'dstat-interface', hub_uri,
subscribe_options={zmq.SUBSCRIBE: ''})
# Initialize sockets.
self.plugin.reset()
# Periodically process outstanding message received on plugin sockets.
self.plugin_timeout_id = Gtk.timeout_add(500,
self.plugin.check_sockets)
def cleanup_plugin(self):
if self.plugin_timeout_id is not None:
GObject.source_remove(self.plugin_timeout_id)
if self.plugin is not None:
self.plugin = None
if __name__ == "__main__":
multiprocessing.freeze_support()
GObject.threads_init()
MAIN = Main()
mainloop = GObject.MainLoop()
try:
mainloop.run()
except KeyboardInterrupt:
logger.info('Ctrl+C hit, quitting')
MAIN.quit()