Source code for PyICe.lab_gui

'''Graphical Interface to Channel Objects'''

import lab_core
from lab_utils import column_formatter
import xml.etree.ElementTree as ET
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4 import Qt
from PyQt4.QtCore import pyqtSlot,SIGNAL,SLOT
import Queue
import datetime
import collections
import time
import traceback

MAX_CAT_ROWS = 3

[docs]class data_store(): ''' this is a very simple hierarchy of key/value pairs. Each can have an ordered list of children. Only int/float/str should be stored each pair must have a name but a value is optional''' def __init__(self,name=None,value=None,parent=None): self._children = [] self.set_name(name) self.set_value(value) if parent != None: parent.add_child(self) def _check_value_ok(self,value): if value == None: return if isinstance(value,str): return if isinstance(value,int): return if isinstance(value,float): return raise DataStoreException('Bad Value') def _check_name_ok(self,name): if value == None: raise DataStoreException('Bad Name {}'.format(name)) try: self._check_value_ok(name) except DataStoreException as e: raise DataStoreException('Bad Name {}'.format(name)) def set_name(self,name): self._check_value_ok(name) self._name = name def __getitem__(self,key): for child in self._children: if child.get_name() == key: return child.get_value() return None def __in__(self,key): for child in self._children: if child.get_name() == key: return True return False def __iter__(self): for child in self._children: yield child def __setitem__(self, key, value): for child in self._children: if child.get_name() == key: child.set_value(value) return data_store(key,value,self) def get_name(self): return self._name def set_value(self,value): self._check_value_ok(value) self._value = value def get_value(self): return self._value def add_child(self,data_store_object): if not isinstance(data_store_object,data_store): raise DataStoreException('Addind a non-data_store as a child') self._children.append(data_store_object) def get_child(self,child_name): for child in self._children: if child.get_name() == child_name: return child def _to_xml(self,parent_xml=None): if parent_xml == None: element = ET.Element('pair') else: element = ET.SubElement(parent_xml,'pair') element.attrib['name'] = self._name if self._value: element.attrib['value'] = self._value for child in self._children: child._to_xml(element) return element def _from_xml(self,element): if 'name' in element.attrib.keys(): self.set_name(element.attrib['name']) if 'value' in element.attrib.keys(): self.set_value(element.attrib['value']) for child_xml in element: child = data_store() child._from_xml(child_xml) self.add_child(child) def save(self,filename): root = self._to_xml() tree = ET.ElementTree(root) tree.write(filename) def load(self,filename): tree = ET.parse(filename) root = tree.getroot() data_store.__init__(self) #clear all data self._from_xml(root)
class DataStoreException(Exception): pass class channel_wrapper(object): def __init__(self,channel): self._channel = channel self._name = self._channel.get_name() def get_default_format(self): if self.get_formats(): return self._channel.get_format() else: return None def get_channel(self): return self._channel def get_formats(self): try: return self._channel.get_formats() except AttributeError: return [] def get_presets(self): try: return self._channel.get_presets() except AttributeError: return [] def get_presets_dict(self): try: return self._channel.get_presets_dict() except AttributeError: return {} def get_category(self): try: return self._channel.get_category() except AttributeError: return None def get_name(self): return self._name def get_description(self): return self._channel.get_description() def get_attribute(self,attribute_name): return self._channel.get_attribute(attribute_name) def get_attributes(self): return self._channel.get_attributes() def get_units(self, format): if format is not None: return self._channel.get_units(format) return '' def format(self,data,format,use_presets): if data == None: return 'None' if data == 'READ_ERROR': return 'READ_ERROR' if hasattr(self._channel, 'format'): data = self._channel.format(data,format,use_presets) return data def unformat(self,data,format,use_presets): if data == "None": return None if hasattr(self._channel, 'unformat'): data = self._channel.unformat(data,format,use_presets) else: #convert string to number if underlying channel doesn't do format/unformat try: data = float(data) if data == int(data): data = int(data) except ValueError: try: data = str(data) except: pass return data class display_item(QtGui.QLabel,channel_wrapper): def __init__(self,channel_object): QtGui.QLabel.__init__(self) self._alpha = 0 #https://en.wikipedia.org/wiki/Web_colors self.highlight_color_unfilt = (180,255,0) #Safety self.highlight_color_filt = (100,255,100) #Nuclear self.highlight_color_avg = (255,150,255) #Light Magenta #self.highlight_color_trans = (255,150,150) #Now animated # [255,182,192] #LightPink # [255,162,172] #LT # [180,180,255] #ADI self.palette = QtGui.QPalette() self.setAutoFillBackground(True) self.setPalette(self.palette) self.highlight = QtCore.QPropertyAnimation( self,"alpha") self.highlight.setDuration(3500) #msecs self.highlight.setStartValue(255) self.highlight.setEasingCurve(QtCore.QEasingCurve.OutCirc) # self.highlight.setEndValue(80) # green self.highlight.setEndValue(40) # safety yellow is more visible, so can be faded further channel_wrapper.__init__(self,channel_object) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect(self,SIGNAL("customContextMenuRequested(QPoint)"),self, SLOT("contextMenuRequested(QPoint)")) self._use_presets_read = False self._use_presets_write = True self._current_raw_data = None self._prev_raw_data = None self.set_iir("Disabled") self._iir_rampto = self._iir_setting self._iir_data = None self._format = self.get_default_format() self._displayed2 = False self._wcd = None self._flash = True self._data_changed = False self._history_length = 20 self._history = collections.deque(maxlen=self._history_length + 1) ttip = u"<p><b>{}</b></p>".format(self.get_name()) if self.get_description(): ttip += u"<p>{}</p>".format(QtCore.Qt.escape(self.get_description())) #put a paragraph in to make it rich text and get the wrapping if len(self.get_attributes()) != 0: ttip += u'<p><table border="0.2">' ttip += u'<tr><th>Attribute Name:</th><th>Attribute Value:</th></tr>' for attribute in self.get_attributes(): ttip += u'<tr><td>{}</td><td>{}</td></tr>'.format(attribute, self.get_attribute(attribute)) ttip += u'</table></p>' if self.get_presets(): ttip += u'<p><table border="0.2">' ttip += u'<tr><th>Preset Name:</th><th>Preset Value:</th></tr>' for preset, value in self.get_presets_dict().iteritems(): ttip += u'<tr><td>{}</td><td>{}</td></tr>'.format(preset, value) ttip += u'</table></p>' ttip += u'<p><table border="0.2"><tr><th>Category:</th><td>{}</td></tr></table></p>'.format(self.get_category()) self.setToolTip(ttip) self.update_display(force = True) def _get_alpha(self): return self._alpha def _set_alpha(self, value): self._alpha = value if self._iir_setting == "Disabled": color = self.highlight_color_unfilt elif self._iir_setting != self._iir_rampto: #color = self.highlight_color_trans try: color = [255, min(int(round(255.0 * self._iir_rampto / self._iir_setting)),255), #Fade red to yellow. 0 ] except TypeError: color = self.highlight_color_avg #color[0] = int(round(100.0 * (self._iir_setting - self._iir_rampto) / self._iir_setting)) + 155 #sweep red to indicate settling #color[1] = 150 else: color = self.highlight_color_filt self.palette.setColor(self.backgroundRole(), QtGui.QColor(*color, alpha=self._alpha) ) self.setPalette(self.palette) alpha = QtCore.pyqtProperty(int, _get_alpha, _set_alpha) def get_formatted_data(self, raw_data=None): formatted_data = self.format(self._current_raw_data if raw_data is None else raw_data, format=self._format, use_presets=self._use_presets_read ) if not isinstance(formatted_data, str): try: formatted_data = self._channel.format_display(formatted_data) except Exception as e: print e print traceback.format_exc() return u'{}{}'.format(formatted_data, self.get_units(self._format)) def set_format(self,new_format): self._format = new_format self.update_display() def set_iir(self,iir): self._iir_setting = iir def update_display(self,force=False): if self.displayed() or force or True: txt = u"{}: {}".format(self.get_name(),self.get_formatted_data()) if self._data_changed and self._flash: self.highlight.stop() self.highlight.start(QtCore.QPropertyAnimation.KeepWhenStopped) else: if self.highlight.state() == QtCore.QAbstractAnimation.Stopped: self.alpha = 0 self.setText(txt) def update_from_dict(self,data_dict): if self.get_name() in data_dict.keys(): data = data_dict[self.get_name()] if hasattr(self._channel,"format") and data_dict[self.get_name()] != 'READ_ERROR': #channel read data may be pre-formatted by underlying channel. #need to un-do before reformatting with GUI settings raw_data = self._channel.unformat(data_dict[self.get_name()],format=self._channel.get_format(),use_presets=self._channel.using_presets_read()) else: raw_data = data_dict[self.get_name()] if raw_data != self._prev_raw_data: self._data_changed = True else: self._data_changed = False self._prev_raw_data = raw_data if self._data_changed: self._history.append([datetime.datetime.utcnow(), raw_data, 1]) else: try: self._history[-1][2] += 1 #incremement value-unchanged read count except IndexError: #if first channel read is None, _data_changed detection doesn't worker self._history.append([datetime.datetime.utcnow(), raw_data, 1]) try: if self._iir_setting == "Disabled": self._current_raw_data = raw_data self._iir_data = None self._iir_rampto = 1 elif self._iir_data is None: self._current_raw_data = raw_data self._iir_data = float(raw_data) self._iir_rampto = 1 else: self._current_raw_data = raw_data + (1 - 1.0/self._iir_rampto) * (self._iir_data - raw_data) self._iir_data = self._current_raw_data if self._iir_setting == "Average" or self._iir_setting > self._iir_rampto: self._iir_rampto += 1 elif self._iir_setting < self._iir_rampto: self._iir_rampto = self._iir_setting except (TypeError, ValueError): self._current_raw_data = raw_data self._iir_data = None except Exception as e: print e print traceback.format_exc() self._current_raw_data = raw_data self._iir_data = None self.update_display() def display(self,state): self._displayed2 = state def displayed(self): return self._displayed2 @pyqtSlot(QtCore.QPoint) def contextMenuRequested(self, point): menu = QtGui.QMenu() # presets sub menu if len(self.get_presets() ) and self._channel.is_writeable(): write_preset_menu = QtGui.QMenu("Write Preset") presets = self.get_presets() presets.sort(key=lambda s: str(s).upper()) for preset in presets: action_write_preset = QtGui.QAction(preset,write_preset_menu) action_write_preset.connect(action_write_preset,SIGNAL("triggered()"),lambda preset=preset: self.write_preset(preset)) write_preset_menu.addAction(action_write_preset) menu.addMenu(write_preset_menu) # write menu item if self._channel.is_writeable(): action_write = QtGui.QAction("Write...",menu) self.connect(action_write,SIGNAL("triggered()"),self,SLOT("display_write_dialog()")) menu.addAction(action_write) # read menu item action_read = QtGui.QAction("Read",menu) self.connect(action_read,SIGNAL("triggered()"),self,SLOT("read_now()")) menu.addAction(action_read) # copy menu item action_copy = QtGui.QAction("Copy",menu) self.connect(action_copy,SIGNAL("triggered()"),self.copy_clipboard) menu.addAction(action_copy) # history menu item action_print_history = QtGui.QAction("Print History",menu) self.connect(action_print_history,SIGNAL("triggered()"),self.print_history) menu.addAction(action_print_history) # formats sub menu if len(self.get_formats()): format_menu = QtGui.QMenu("Select Format") action_set_format = QtGui.QAction("None",format_menu,checkable=True) if self._format == None: action_set_format.setChecked(True) self.connect(action_set_format,SIGNAL("triggered()"), lambda : self.set_format(None)) format_menu.addAction(action_set_format) for reg_format in self.get_formats(): action_set_format = QtGui.QAction(reg_format,format_menu,checkable=True) if reg_format == self._format: action_set_format.setChecked(True) action_set_format.connect(action_set_format,SIGNAL("triggered()"),lambda reg_format=reg_format: self.set_format(reg_format)) format_menu.addAction(action_set_format) menu.addMenu(format_menu) # iir filter sub menu iir_menu = QtGui.QMenu("IIR Filter") for iir in ["Disabled",2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,"Average"]: action_set_iir = QtGui.QAction(str(iir),iir_menu,checkable=True) if iir == self._iir_setting: action_set_iir.setChecked(True) action_set_iir.connect(action_set_iir,SIGNAL("triggered()"),lambda iir=iir: self.set_iir(iir)) iir_menu.addAction(action_set_iir) menu.addMenu(iir_menu) # use presets read menu item action_use_read_presets = QtGui.QAction("Use Presets (Read)",menu,checkable=True) menu.addAction(action_use_read_presets) if self._use_presets_read: action_use_read_presets.setChecked(True) self.connect(action_use_read_presets,SIGNAL("toggled(bool)"),self,SLOT("setUseReadPresets(bool)")) # use presets write menu item if self._channel.is_writeable(): action_use_write_presets = QtGui.QAction("Use Presets (Write)",menu,checkable=True) menu.addAction(action_use_write_presets) if self._use_presets_write: action_use_write_presets.setChecked(True) self.connect(action_use_write_presets,SIGNAL("toggled(bool)"),self,SLOT("setUseWritePresets(bool)")) # flash on change menu item action_flash_on_change = QtGui.QAction("Flash on Change",menu,checkable=True) menu.addAction(action_flash_on_change) if self._flash: action_flash_on_change.setChecked(True) self.connect(action_flash_on_change,SIGNAL("toggled(bool)"),self,SLOT("setFlash(bool)")) menu.exec_(self.mapToGlobal(point)) def copy_clipboard(self): app = self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() app.cb.setText(unicode(self.get_formatted_data())) def print_history(self): request_time = datetime.datetime.utcnow() if not len(self._history): print "{} has no change history at {}".format(self.get_name(), request_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')) return print "{} change history at {}".format(self.get_name(), request_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')) row_col_data = [["ID:","TIME_REL_NOW","DURATION","FMT","RAW","COUNT"]] def hms(seconds): minutes = int(seconds / 60) seconds = seconds - 60 * minutes hours = int(minutes / 60) minutes = minutes - 60 * hours return {"hours" : hours, "minutes" : minutes, "seconds" : seconds } time_fmt = "{prefix}{hours:02d}:{minutes:02d}:{seconds:06.3f}{postfix}" for idx, [timestamp, raw_data, read_count] in enumerate(self._history): prefix = "" if len(self._history) != self._history_length + 1: #dequeue not yet filled if idx == 0: #Indicate measurment time is after signal change time by uncertain period. prefix = "+" else: if idx == 0: #Skip oldest reading with uncertain transisition time after dequeue fills. continue t_rel = time_fmt.format(prefix="-",postfix="",**hms((request_time-timestamp).total_seconds())) try: t_duration = time_fmt.format(prefix=prefix,postfix=" ",**hms((self._history[idx+1][0]-timestamp).total_seconds())) except IndexError: #t_duration = " --:--:--.---" t_duration = time_fmt.format(prefix=prefix,postfix="+",**hms((request_time-timestamp).total_seconds())) row_col_data.append([u"{:02d}:".format(idx), t_rel, t_duration, self.get_formatted_data(raw_data), raw_data, read_count]) print column_formatter(row_col_data,padding=2,justification="right") @pyqtSlot() def display_write_dialog(self): if not self._wcd: #not yet created self._create_write_dialog() self._wcd.move(300, 150) self._wcd.update_format(self._format) self._wcd.set_value(self.format(self._current_raw_data,format=self._format,use_presets=self._use_presets_read)) else: #already exists if self._wcd.isHidden(): #window was created, then closed. Re-sync with display_item settings self._wcd.update_format(self._format) self._wcd.set_value(self.format(self._current_raw_data,format=self._format,use_presets=self._use_presets_read)) self._wcd.show() self._wcd.activateWindow() self._wcd.raise_() def _create_write_dialog(self): self._wcd = write_channel_dialog(self._channel,self._current_raw_data,self._format,self._use_presets_write) self.connect(self._wcd,SIGNAL('request_write_channel_list(PyQt_PyObject)'),self.write) def mouseDoubleClickEvent(self, event): self.read_now() @pyqtSlot() def read_now(self): self.emit(SIGNAL("request_read_channel_list(PyQt_PyObject)"), [self.get_name()] ) @pyqtSlot(bool) def setUseReadPresets(self,state): self._use_presets_read = state self.update_display() @pyqtSlot(bool) def setUseWritePresets(self,state): self._use_presets_write = state @pyqtSlot(bool) def setFlash(self,state): self._flash = state def write_preset(self,preset): data = self.unformat(preset,format=None,use_presets=True) self.write( [(self.get_name(),data)] ) def write(self,data): self.emit(SIGNAL("request_write_channel_list(PyQt_PyObject)"),data) self.emit(SIGNAL("request_read_channel_list(PyQt_PyObject)"),[data_pair[0] for data_pair in data]) def save(self,ds_parent): di_ds = data_store('display_item',self.get_name(),ds_parent) di_ds['use_presets_read'] = str(self._use_presets_read) di_ds['use_presets_write'] = str(self._use_presets_write) if self._format: di_ds['format'] = str(self._format) di_ds['flash'] = str(self._flash) if self._wcd: di_ds['write_dialog_container'] = 'True' self._wcd.save( di_ds.get_child('write_dialog_container') ) di_ds['iir_setting'] = str(self._iir_setting) def load(self,ds_parent): di_ds = ds_parent.get_child('display_item') if di_ds: assert di_ds.get_value() == self.get_name() self._use_presets_read = di_ds['use_presets_read'] == 'True' self._use_presets_write = di_ds['use_presets_write'] == 'True' if di_ds['format'] in self.get_formats(): self._format = di_ds['format'] self._flash = di_ds['flash'] == 'True' if di_ds['write_dialog_container']: self._create_write_dialog() self._wcd.load( di_ds.get_child('write_dialog_container') ) if di_ds['iir_setting']: try: self._iir_setting = int(di_ds['iir_setting']) except ValueError: self._iir_setting = di_ds['iir_setting'] class write_channel_dialog(QtGui.QDialog,channel_wrapper): def __init__(self,channel_object,current_raw_data,format,use_presets_write): QtGui.QDialog.__init__(self) channel_wrapper.__init__(self,channel_object) self.setWindowTitle(u"Write {}".format(self.get_name())) self._use_presets_write = use_presets_write self._format = format self._current_raw_data = current_raw_data self.init_interface() def init_interface(self): layout = QtGui.QVBoxLayout() # value box self.value_text_box = QtGui.QLineEdit(unicode(self.format(self._current_raw_data, self._format, self._use_presets_write))) self.value_text_box.setFocus() layout.addWidget( QtGui.QLabel("Value:")) layout.addWidget(self.value_text_box) # formats if len(self.get_formats()): layout.addWidget( QtGui.QLabel("Format:")) self.formats_combo = QtGui.QComboBox() self.formats_combo.addItem("None") for format in self.get_formats(): self.formats_combo.addItem(format) self.formats_combo.setCurrentIndex(self.formats_combo.findText("None" if self._format is None else self._format)) layout.addWidget(self.formats_combo) self.connect(self.formats_combo,SIGNAL("currentIndexChanged (QString)"),self.update_format) self.link_format_checkbox = QtGui.QCheckBox('Link Format',self) layout.addWidget(self.link_format_checkbox) # presets if len(self.get_presets()): presest_checkbox = QtGui.QCheckBox('Use Presets',self) if self._use_presets_write: presest_checkbox.setCheckState( 2 ) self.connect(presest_checkbox,SIGNAL("stateChanged(int)"),self.update_use_presets_write) layout.addWidget(presest_checkbox) # increment layout.addWidget( QtGui.QLabel("Increment Value:")) self.increment_text_box = QtGui.QLineEdit("1") layout.addWidget(self.increment_text_box) increment_widget = QtGui.QWidget() increment_layout = QtGui.QHBoxLayout() dec_button = QtGui.QPushButton("-") dec_button.setDefault(False) dec_button.setAutoDefault(False) self.connect(dec_button,SIGNAL('clicked()'),self.decrement) increment_layout.addWidget( dec_button ) inc_button = QtGui.QPushButton("+") inc_button.setDefault(False) inc_button.setAutoDefault(False) self.connect(inc_button,SIGNAL('clicked()'),self.increment) increment_layout.addWidget(inc_button) increment_widget.setLayout(increment_layout) layout.addWidget(increment_widget) # write close write_close_widget = QtGui.QWidget() write_close_layout = QtGui.QHBoxLayout() write_button = QtGui.QPushButton("Write") write_button.setDefault(True) write_button.setAutoDefault(True) self.connect(write_button,SIGNAL("clicked()"),self.write) write_close_layout.addWidget( write_button ) write_close_button = QtGui.QPushButton("Write + Close") write_close_button.setDefault(False) write_close_button.setAutoDefault(False) self.connect(write_close_button,SIGNAL("clicked()"),self.write_close) write_close_layout.addWidget(write_close_button) write_close_widget.setLayout(write_close_layout) layout.addWidget(write_close_widget) self.setLayout(layout) def get_value(self): return str(self.value_text_box.displayText()) def get_increment(self): return str(self.increment_text_box.displayText()) def set_increment(self,text): self.increment_text_box.setText(text) def set_value(self,value): self.value_text_box.setText(str(value)) def increment(self): if self.get_value() == "None": value = 0 elif self.get_value() == "True": raise ValueError("Can't increment 'True'.") elif self.get_value() == "False": self.set_value(True) self.write() return elif self._format in ('hex','bin'): #special handling for default formats. others should tolerate float conversion. value = self.unformat(self.get_value(), self._format, self._use_presets_write) else: value = float(self.get_value()) result = float(value + float(self.get_increment())) if result == int(result): result = int(result) if self._format in ('hex','bin'): #special handling for default formats. others should tolerate float conversion. try: result = self.format(result, self._format, self._use_presets_write) except Exception as e: print e print traceback.format_exc() return self.set_value(result) self.write() def decrement(self): if self.get_value() == "None": value = 0 elif self.get_value() == "True": self.set_value(False) self.write() return elif self.get_value() == "False": raise ValueError("Can't decrement 'False'.") elif self._format in ('hex','bin'): #special handling for default formats. others should tolerate float conversion. value = self.unformat(self.get_value(), self._format, self._use_presets_write) else: value = float(self.get_value()) result = float(value - float(self.get_increment())) if result == int(result): result = int(result) if self._format in ('hex','bin'): #special handling for default formats. others should tolerate float conversion. try: result = self.format(result, self._format, self._use_presets_write) except Exception as e: print e print traceback.format_exc() return self.set_value(result) self.write() def write_close(self): self.write() self.close() def write(self): data = self.unformat(self.get_value(),self._format,self._use_presets_write) self.emit(SIGNAL("request_write_channel_list(PyQt_PyObject)"),[(self.get_name(), data)] ) def update_use_presets_write(self,state): unformatted_data = self.unformat(self.get_value(), self._format, self._use_presets_write) self._use_presets_write = state self.set_value(str(self.format(unformatted_data, self._format, self._use_presets_write))) def update_format(self,format): format = str(format) #dump QString if format == "None": format = None try: if hasattr(self,'link_format_checkbox') and self.link_format_checkbox.checkState(): old_value = self.unformat(self.get_value(),self._format,self._use_presets_write) new_text = self.format(old_value,format,self._use_presets_write) self.set_value(new_text) except ValueError as e: pass self._format = format if hasattr(self,'formats_combo'): self.formats_combo.setCurrentIndex(self.formats_combo.findText("None" if format is None else str(format))) def save(self,ds_parent): dcwd_ds = data_store('write_channel_dialog_data',self.get_name(),ds_parent) dcwd_ds['value'] = str(self.get_value()) dcwd_ds['use_presets_write'] = str(self._use_presets_write) if len(self.get_formats()): dcwd_ds['format'] = str(self._format) if self.link_format_checkbox.checkState() == 2: dcwd_ds['link'] = 'True' dcwd_ds['increment'] = str(self.get_increment()) dcwd_ds['visible'] = str( self.isVisible() ) dcwd_ds['xpos'] = str( self.pos().x() ) dcwd_ds['ypos'] = str( self.pos().y() ) def load(self,ds_parent): dcwd_ds = ds_parent.get_child('write_channel_dialog_data') if dcwd_ds: assert dcwd_ds.get_value() == self.get_name() self.set_value( dcwd_ds['value'] ) self._use_presets_write = dcwd_ds['use_presets_write'] == 'True' if dcwd_ds['format'] : index = self.formats_combo.findText(dcwd_ds['format']) if index != -1: self.formats_combo.setCurrentIndex(index) if len(self.get_formats()): if dcwd_ds['link']: self.link_format_checkbox.setCheckState( 2 ) if dcwd_ds['increment']: self.set_increment( dcwd_ds['increment'] ) if dcwd_ds['visible'] == "True": self.show() self.move(int(dcwd_ds['xpos']),int(dcwd_ds['ypos'])) class display_item_group(QtGui.QWidget): def __init__(self,channel_group_object): QtGui.QWidget.__init__(self) self.display_items = [] self.init_interface() self.build_interface() self._continuous_read = False self._font = QtGui.QFont() #used for display items self._channel_group = channel_group_object self.populate_from_channel_group(channel_group_object) self._expecting_data = False def populate_from_channel_group(self,channel_group_object): for channel in channel_group_object: di = display_item(channel) di.setFont(self._font) self.connect(di,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.read_channel_list ) self.connect(di,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.write_channel_list ) self.add_display_item(di) self.sort() def add_display_item(self,item): self.display_items.append(item) def sort(self): self.display_items.sort(key = lambda item: item.get_name().upper() ) def inclusive_filter(self,filter_list): #filter_list is a list of category names to filter on for di in self.display_items: if di.get_category() in filter_list: di.display(True) else: di.display(False) self.build_interface() def init_interface(self): layout = QtGui.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignTop) self.gc = QtGui.QWidget() self.grid_container_container = QtGui.QWidget() container_container_layout = QtGui.QHBoxLayout() container_container_layout.setAlignment(QtCore.Qt.AlignLeft) self.grid_container_container.setLayout(container_container_layout) container_container_layout.addWidget(self.gc) layout.addWidget(self.grid_container_container) self.setLayout(layout) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect(self,SIGNAL("customContextMenuRequested(QPoint)"),self.contextMenuRequested) @pyqtSlot() def resize_main_window(self): self.build_interface() def build_interface(self): self._grid = QtGui.QGridLayout() self._grid.setAlignment(QtCore.Qt.AlignLeft) self._grid.setHorizontalSpacing(10) self._grid.setVerticalSpacing(5) self.grid_container_container.layout().removeWidget(self.gc) self.gc = QtGui.QWidget() self.grid_container_container.layout().addWidget(self.gc) self.gc.setLayout(self._grid) for di in self.display_items: di.setParent(None) y_pos = 0 x_pos = 0 column_height = 0 self.columnWidths = [] self.columnWidths.append(0) for di in self.display_items: if di.displayed(): if column_height > 0 and column_height + di.sizeHint().height() + self._grid.verticalSpacing() > self.parentWidget().size().height() - 60: #self.frameGeometry().height() #60 offset may have something to do with grid.contentsMargins() and layout placement, possibly another grid.verticalSpacing() too. Used to prevent frame from oversizing and creating vertical scroll y_pos = 0 x_pos += 1 self.columnWidths.append(0) column_height = 0 self._grid.addWidget(di, y_pos, x_pos) di.column_placement = x_pos column_height += di.sizeHint().height() + self._grid.verticalSpacing() y_pos += 1 def read_channel_list(self,channel_list): self._expecting_data = True self.emit(SIGNAL('request_read_channel_list(PyQt_PyObject)'),channel_list) def write_channel_list(self,channel_list): self.emit(SIGNAL('request_write_channel_list(PyQt_PyObject)'),channel_list) def update_column_width(self, di): if di.displayed(): if di.sizeHint().width() > self.columnWidths[di.column_placement]: self._grid.setColumnMinimumWidth(di.column_placement, di.sizeHint().width()) self.columnWidths[di.column_placement] = di.sizeHint().width() def receive_channel_data(self,data_dict): for di in self.display_items: di.update_from_dict(data_dict) self.update_column_width(di) if self._expecting_data and self._continuous_read: self._expecting_data = False self.request_read_all() def receive_passive_channel_data(self,data_dict): for di in self.display_items: di.update_from_dict(data_dict) self.update_column_width(di) def contextMenuRequested(self, point): menu = QtGui.QMenu() # write menu item action_write = QtGui.QAction("Read All",menu) self.connect(action_write,SIGNAL("triggered()"),self.request_read_all) menu.addAction(action_write) # continuous read action_continuous_read = QtGui.QAction("Continuous Read",menu,checkable=True) menu.addAction(action_continuous_read) if self._continuous_read: action_continuous_read.setChecked(True) self.connect(action_continuous_read,SIGNAL("toggled(bool)"),self.set_continuous_read) # all tab items menu tab_use_write_presets = QtGui.QAction("All Use Write Presets",menu) self.connect(tab_use_write_presets,SIGNAL('triggered()'), lambda: self.use_write_presets(True)) menu.addAction(tab_use_write_presets) tab_no_write_presets = QtGui.QAction("All No Write Presets",menu) self.connect(tab_no_write_presets,SIGNAL('triggered()'), lambda: self.use_write_presets(False)) menu.addAction(tab_no_write_presets) tab_use_read_presets = QtGui.QAction("All Use Read Presets",menu) self.connect(tab_use_read_presets,SIGNAL('triggered()'), lambda: self.use_read_presets(True)) menu.addAction(tab_use_read_presets) tab_no_read_presets = QtGui.QAction("All No Read Presets",menu) self.connect(tab_no_read_presets,SIGNAL('triggered()'), lambda: self.use_read_presets(False)) menu.addAction(tab_no_read_presets) tab_flash_on_change = QtGui.QAction("All Flash on Change",menu) self.connect(tab_flash_on_change,SIGNAL('triggered()'), lambda: self.flash_on_change(True)) menu.addAction(tab_flash_on_change) tab_no_flash_on_change = QtGui.QAction("All No Flash on Change",menu) self.connect(tab_no_flash_on_change,SIGNAL('triggered()'), lambda: self.flash_on_change(False)) menu.addAction(tab_no_flash_on_change) increase_font_size = QtGui.QAction("Increase Font Size",menu) self.connect(increase_font_size,SIGNAL('triggered()'), lambda: self.change_font_size(1)) menu.addAction(increase_font_size) decrease_font_size = QtGui.QAction("Decrease Font Size",menu) self.connect(decrease_font_size,SIGNAL('triggered()'), lambda: self.change_font_size(-1)) menu.addAction(decrease_font_size) menu.exec_(self.mapToGlobal(point)) self.connect(self,SIGNAL("change_font_size(int)"),self.change_font_size) def change_font_size(self, increment): self._font.setPointSize(max(self._font.pointSize()+increment, 1)) for di in self.display_items: di.setFont(self._font) self.build_interface() if increment != 0: print "Font changed to {}pt.".format(self._font.pointSize()) def request_read_all(self): channel_list = [] for di in self.display_items: if di.displayed(): channel_list.append(di.get_name()) self.read_channel_list(channel_list) def mouseDoubleClickEvent(self, event): self.request_read_all() def set_continuous_read(self,bool): self._continuous_read = bool if bool: self.request_read_all() def get_continuous_read(self): return self._continuous_read def wake(self): if self._continuous_read: self.request_read_all() def save(self,ds_parent): dig_ds = data_store('display_group',None,ds_parent) dig_ds['continuous'] = str(self._continuous_read) dig_ds['font_size'] = str(self._font.pointSize()) for di in self.display_items: di_ds = data_store('display_item',di.get_name(),dig_ds) di.save(di_ds) def load(self,ds_parent): dig_ds = ds_parent.get_child('display_group') self._continuous_read = dig_ds['continuous'] == 'True' if dig_ds['font_size'] is not None: self._font.setPointSize(int(dig_ds['font_size'])) self.change_font_size(increment=0) #force redraw for di_ds in dig_ds: di_name = di_ds.get_value() for di in self.display_items: if di.get_name() == di_name: di.load(di_ds) def use_write_presets(self,bool): for di in self.display_items: di.setUseWritePresets(bool) def use_read_presets(self,bool): for di in self.display_items: di.setUseReadPresets(bool) def flash_on_change(self,bool): for di in self.display_items: di.setFlash(bool) class display_category_group(QtGui.QWidget): def __init__(self,channel_group_object): QtGui.QWidget.__init__(self) self.channel_group_object = channel_group_object self._categories = self._create_category_list() self._check_boxes = {} #dictionary of qcheckboxes indexed by category name self.suppress_update = False self.init_interface() def get_categories(self): return self._categories def get_selected_categories(self): selected_categories = [] for (category_name,check_box) in self._check_boxes.items(): if check_box.checkState(): selected_categories.append(category_name) return selected_categories def _create_category_list(self): categories = [] for channel in self.channel_group_object: categories.append( self._get_category(channel) ) categories = list(set(categories)) categories.sort(key=lambda s: str(s).upper()) return categories def _get_category(self,channel): try: return channel.get_category() except AttributeError: return None def init_interface(self): layout = QtGui.QHBoxLayout() catLabelwidget = QtGui.QLabel(self) catLabelwidget.setText("<p style=font-size:16px;><b>Categories:</b></p>") layout.addWidget(catLabelwidget,0) cat_sel_group_widget = QtGui.QWidget(self) cat_layout = QtGui.QGridLayout() cat_sel_group_widget.setLayout(cat_layout) layout.addWidget(cat_sel_group_widget,0) layout.addWidget(QtGui.QWidget()) x_pos=0 y_pos=0 for category_name in self.get_categories(): new_cat_check_box = QtGui.QCheckBox(str(category_name),cat_sel_group_widget) cat_layout.addWidget(new_cat_check_box,y_pos,x_pos) self._check_boxes[category_name] = new_cat_check_box new_cat_check_box.setCheckState(QtCore.Qt.Unchecked) self.connect(new_cat_check_box, QtCore.SIGNAL("stateChanged(int)"), self._on_change) y_pos += 1 if y_pos > MAX_CAT_ROWS: x_pos += 1 y_pos = 0 self.setLayout(layout) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect(self,SIGNAL("customContextMenuRequested(QPoint)"),self.contextMenuRequested) def _on_change(self,state): if not self.suppress_update: self.emit(QtCore.SIGNAL('stateChanged()')) def save(self,ds_parent): cats_store = data_store('categories',None,ds_parent) selected_categories = self.get_selected_categories() for cat in selected_categories: #only store checked categories cat = str(cat) data_store(cat,None,cats_store) def load(self,ds_parent): cat_ds = ds_parent.get_child('categories') for cat in cat_ds: cat_name = cat.get_name() if cat_name == "None": cat_name = None if cat_name in self._check_boxes.keys(): self._check_boxes[cat_name].setCheckState(QtCore.Qt.Checked) @pyqtSlot(QtCore.QPoint) def contextMenuRequested(self, point): menu = QtGui.QMenu() # Select all menu item action_select = QtGui.QAction("Select All",menu) self.connect(action_select,SIGNAL("triggered()"), lambda: self.select_all(True)) menu.addAction(action_select) # Select all menu item action_deselect = QtGui.QAction("De-select All",menu) self.connect(action_deselect,SIGNAL("triggered()"), lambda: self.select_all(False)) menu.addAction(action_deselect) menu.exec_(self.mapToGlobal(point)) def select_all(self, select): self.suppress_update = True for cat in self._check_boxes.values(): cat.setCheckState(QtCore.Qt.Checked if select else QtCore.Qt.Unchecked) self.suppress_update = False self.emit(QtCore.SIGNAL('stateChanged()'))
[docs]class tab_view(QtGui.QWidget): '''describes the view of the registers and the category selection and interaction beteen''' def __init__(self,channel_group_object,parent): QtGui.QWidget.__init__(self, parent) self._name = "Empty" self._name_locked = False self.channel_group_object = channel_group_object self.dig = display_item_group(self.channel_group_object) self.connect(self.dig,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.read_channel_list ) self.connect(self.dig,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.write_channel_list ) self.connect(self, SIGNAL('resize_main_window()'), self.dig, SLOT('resize_main_window()')) self.connect(self,SIGNAL('channel_data_ready(PyQt_PyObject)'), self.dig.receive_channel_data ) self.connect(self,SIGNAL('passive_observer_data(PyQt_PyObject)'), self.dig.receive_passive_channel_data ) self.dcg = display_category_group(channel_group_object) self.connect(self.dcg, QtCore.SIGNAL("stateChanged()"), self._categories_changed) self.init_interface() @pyqtSlot() def resize_main_window(self): self.emit(SIGNAL('resize_main_window()')) def init_interface(self): layout = QtGui.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignLeft) scroll_area = QtGui.QScrollArea() scroll_area.setWidget(self.dig) scroll_area.setWidgetResizable(True) layout.addWidget(scroll_area,0) #add the display_item_group at the top layout.setStretch(0,80) layout.addWidget(self.dcg,0) #add the display_category_group at the bottom layout.setStretch(1,20) self.setLayout(layout) self.show() def update_from_dict(self,data_dict): self.dig.update_from_dict(data_dict) def get_name(self): return self._name def set_name(self,name,write_locked=False): if self._name_locked: if write_fixed: self._name = name self._name_locked = True self.emit(QtCore.SIGNAL('nameChanged(QWidget)'),self) else: self._name = name self.emit(QtCore.SIGNAL('nameChanged(QWidget)'),self) def _categories_changed(self): selected_categories = self.dcg.get_selected_categories() selected_categories.sort(key=lambda s: str(s).upper()) name = str(selected_categories) name = name.lstrip('[').rstrip(']').replace("'","") if not len(name): name = "Empty" self.set_name(name,write_locked=False) self.dig.inclusive_filter(selected_categories) def read_channel_list(self,channel_list): self.emit(SIGNAL('request_read_channel_list(PyQt_PyObject)'),channel_list) def write_channel_list(self,channel_list): self.emit(SIGNAL('request_write_channel_list(PyQt_PyObject)'),channel_list) def receive_channel_data(self,data_dict): self.emit(SIGNAL('channel_data_ready(PyQt_PyObject)'),data_dict) def receive_passive_channel_data(self,data_dict): self.emit(SIGNAL('passive_observer_data(PyQt_PyObject)'),data_dict) def wake(self): self.dig.wake() def save(self,ds_parent): tv = data_store('tab_view',None,ds_parent) cat_ds = data_store('cat',None,tv) self.dcg.save(cat_ds) dig_ds = data_store('dig',None,tv) self.dig.save(dig_ds) def load(self,ds_parent): tv = ds_parent.get_child('tab_view') cat_ds = tv.get_child('cat') self.dcg.load(cat_ds) dig_ds = tv.get_child('dig') self.dig.load(dig_ds) def tab_use_write_presets(self,bool): self.dig.use_write_presets(bool) def tab_use_read_presets(self,bool): self.dig.use_read_presets(bool) def tab_flash_on_change(self,bool): self.dig.flash_on_change(bool) def tab_change_font_size(self,increment): self.dig.change_font_size(increment)
[docs]class tab_group(QtGui.QTabWidget): '''a group of tab_views''' def __init__(self,channel_group): QtGui.QTabWidget.__init__(self) self.channel_group_object = channel_group self._active_tab = None self.init_interface() def init_interface(self): #add the 'add_tab' button on right btnAdd = QtGui.QToolButton() btnAdd.setMinimumWidth(3) btnAdd.setText(" + ") btnAdd.setToolTip("New Tab") btnAdd.setEnabled(True) self.setCornerWidget(btnAdd, Qt.Qt.TopRightCorner) self.setTabShape(1) self.setUsesScrollButtons(True) self.connect(btnAdd, QtCore.SIGNAL('clicked()'), self.add_tab_view) self.connect(self, QtCore.SIGNAL('tabCloseRequested(int)'), self.remove_tab) self.connect(self, QtCore.SIGNAL('currentChanged(int)'), self.active_changed) self.connect(self, QtCore.SIGNAL('currentChanged(int)'), self.resize_main_window) self.add_tab_view() @pyqtSlot() def resize_main_window(self): self.emit(SIGNAL('resize_main_window()')) def mouseDoubleClickEvent(self, event): self.add_tab_view() def add_tab_view(self): tv = tab_view(self.channel_group_object,self) self.connect_tv(tv) self.addTab(tv,'Empty') self.update_tab_rules() self.tab_view_name_change(tv) self.setCurrentIndex( self.count()-1 ) return tv def connect_tv(self,tv_widget): self.connect(tv_widget, QtCore.SIGNAL('nameChanged(QWidget)'), self.tab_view_name_change) self.connect(tv_widget,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.read_channel_list ) self.connect(tv_widget,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.write_channel_list ) self.connect(self,SIGNAL('passive_observer_data(PyQt_PyObject)'), tv_widget.receive_passive_channel_data ) def tab_view_name_change(self,tab_view_object): index = self.find_widget_in_tabs(tab_view_object) name = self.widget(index).get_name() self.setTabText(index,name) def find_widget_in_tabs(self,widget): #returns index for i in range(self.count()): if self.widget(i) == widget: return i def remove_tab(self,index): self.removeTab(index) self.update_tab_rules() def update_tab_rules(self): self.setTabsClosable(self.count() > 1) self.setMovable(self.count() > 1) def disconnect_tab(self,tab_view_widget): if tab_view_widget != None: self.disconnect(self,SIGNAL('channel_data_ready(PyQt_PyObject)'), tab_view_widget.receive_channel_data ) self.disconnect(self,SIGNAL('tab_use_write_presets(bool)'), tab_view_widget.tab_use_write_presets ) self.disconnect(self,SIGNAL('tab_use_read_presets(bool)'), tab_view_widget.tab_use_read_presets ) self.disconnect(self,SIGNAL('tab_flash_on_change(bool)'), tab_view_widget.tab_flash_on_change ) self.disconnect(self,SIGNAL('tab_change_font_size(int)'), tab_view_widget.tab_change_font_size) self.disconnect(self,SIGNAL('resize_main_window()'), tab_view_widget, SLOT('resize_main_window()')) def connect_tab(self,tab_view_widget): if tab_view_widget != None: self.connect(self,SIGNAL('channel_data_ready(PyQt_PyObject)'), tab_view_widget.receive_channel_data ) self.connect(self,SIGNAL('tab_use_write_presets(bool)'), tab_view_widget.tab_use_write_presets ) self.connect(self,SIGNAL('tab_use_read_presets(bool)'), tab_view_widget.tab_use_read_presets ) self.connect(self,SIGNAL('tab_flash_on_change(bool)'), tab_view_widget.tab_flash_on_change ) self.connect(self,SIGNAL('tab_change_font_size(int)'), tab_view_widget.tab_change_font_size) self.connect(self,SIGNAL('resize_main_window()'), tab_view_widget, SLOT('resize_main_window()')) tab_view_widget.wake() def active_changed(self,index): if self._active_tab is not None: self.disconnect_tab(self._active_tab) self._active_tab = self.widget(index) self.connect_tab(self._active_tab) def read_channel_list(self,channel_list): self.emit(SIGNAL('request_read_channel_list(PyQt_PyObject)'),channel_list) def write_channel_list(self,channel_list): self.emit(SIGNAL('request_write_channel_list(PyQt_PyObject)'),channel_list) def receive_channel_data(self,data_dict): self.emit(SIGNAL('channel_data_ready(PyQt_PyObject)'),data_dict) def receive_passive_channel_data(self,queue): #better to try and crash than test and not crash data_dict = queue.get_nowait() self.emit(SIGNAL('passive_observer_data(PyQt_PyObject)'),data_dict) def save(self,ds_parent): tg = data_store('tab_group',None,ds_parent) for i in range(self.count()): tab = self.widget(i) tab_ds = data_store('tab',None,tg) tab.save(tab_ds) def load(self,parent_ds): self.disconnect_tab(self._active_tab) self.clear() #are the tabs really gone?? for tg_ds in parent_ds: if tg_ds.get_name() == 'tab_group': for tab in tg_ds: #create a tab view from the store tv = self.add_tab_view() tv.load(tab) def tab_use_write_presets(self,bool): self.emit(SIGNAL('tab_use_write_presets(bool)'),bool) def tab_use_read_presets(self,bool): self.emit(SIGNAL('tab_use_read_presets(bool)'),bool) def tab_flash_on_change(self,bool): self.emit(SIGNAL('tab_flash_on_change(bool)'),bool) def tab_change_font_size(self,increment): self.emit(SIGNAL('tab_change_font_size(int)'),increment)
class gui_logger(QtCore.QObject): def __init__(self,channel_group): QtCore.QObject.__init__(self) self._logger = None self.connect_dialog = None self._channel_group = channel_group self.logger_view = logger_view(channel_group,None) self.logger_view.hide() self.create_connect_dialog() def display_select_channels(self): self.logger_view.show() def log(self): if self._logger: self._logger.remove_all_channels_and_sub_groups() channel_names = self.logger_view.get_selected_channel_name_list() for channel_name in channel_names: self._logger.add( self._channel_group[channel_name] ) #cannot directly use the logger here, send it to the back ground worker to actually read self.emit(SIGNAL('request_background_call(PyQt_PyObject)'),self._logger.log ) else: raise Exception('Logger is not connected') def save(self,ds_parent): lv_ds = data_store('logger_view',None,ds_parent) self.logger_view.save(lv_ds) data_store('logger_dbase',self._get_dbase_filename(),ds_parent) data_store('logger_table',self._get_table_name(),ds_parent) def load(self,ds_parent): lv_ds = ds_parent.get_child('logger_view') if lv_ds: self.logger_view.load(lv_ds) self._set_dbase_filename( str(ds_parent['logger_dbase'])) self._set_table_name( str(ds_parent['logger_table'])) def display_connect(self): self.connect_dialog.show() def _disp_save_dialog(self): fname = QtGui.QFileDialog.getSaveFileName(None, 'Connect Database', '.',"SQLite Files (*.sqlite);;All files (*.*)", options = QtGui.QFileDialog.DontConfirmOverwrite) if fname: self.filename_box.setText(fname) def create_connect_dialog(self): self.connect_dialog = QtGui.QWidget() layout = QtGui.QVBoxLayout() # database file name box dbase_file_group = QtGui.QWidget() dbase_file_group_layout = QtGui.QHBoxLayout() dbase_file_group.setLayout(dbase_file_group_layout) layout.addWidget( QtGui.QLabel("Database File:")) self.filename_box = QtGui.QLineEdit( ) self.filename_box.setFocus() dbase_file_group_layout.addWidget(self.filename_box) file_dialog_button = QtGui.QPushButton("...") self.connect(file_dialog_button,SIGNAL('clicked()'), self._disp_save_dialog ) dbase_file_group_layout.addWidget(file_dialog_button) layout.addWidget(dbase_file_group) #table name layout.addWidget( QtGui.QLabel("Table Name:")) self.tablename_box = QtGui.QLineEdit( ) layout.addWidget(self.tablename_box) # write close buttons_widget = QtGui.QWidget() buttons_layout = QtGui.QHBoxLayout() replace_button = QtGui.QPushButton("Replace Table") self.connect(replace_button,SIGNAL("clicked()"),self._replace_table) buttons_layout.addWidget( replace_button ) append_button = QtGui.QPushButton("Append Table") self.connect(append_button,SIGNAL("clicked()"),self._append_table) buttons_layout.addWidget(append_button) cancel_button = QtGui.QPushButton("Cancel") self.connect(cancel_button,SIGNAL("clicked()"), self.connect_dialog.close ) buttons_layout.addWidget(cancel_button) buttons_widget.setLayout(buttons_layout) layout.addWidget(buttons_widget) self.connect_dialog.setLayout(layout) self.connect_dialog.hide() def _get_dbase_filename(self): return str(self.filename_box.text()) def _get_table_name(self): return str(self.tablename_box.text()) def _set_dbase_filename(self,name): self.filename_box.setText(name) def _set_table_name(self,name): self.tablename_box.setText(name) def _append_table(self): self._logger = lab_core.logger(self._channel_group,database=self._get_dbase_filename()) self._logger.append_table(self._get_table_name()) self.connect_dialog.close() def _replace_table(self): self._logger = lab_core.logger(self._channel_group,database=self._get_dbase_filename()) self._logger.new_table(self._get_table_name(),replace_table=True) self.connect_dialog.close() class logger_item(QtGui.QCheckBox,channel_wrapper): def __init__(self,channel_object): QtGui.QLabel.__init__(self) channel_wrapper.__init__(self,channel_object) self._displayed = False if self.get_description(): self.setToolTip(self.get_description()) self.update_display() self.setCheckState(QtCore.Qt.Checked) def update_display(self): self.setText( self.get_name() ) def display(self,state): self._displayed = state def displayed(self): return self._displayed def selected(self): return self.checkState() == QtCore.Qt.Checked def save(self,ds_parent): li_ds = data_store('logger_item',self.get_name(),ds_parent) li_ds['selected'] = str( self.selected() ) def load(self,ds_parent): li_ds = ds_parent.get_child('logger_item') if li_ds: assert li_ds.get_value() == self.get_name() if li_ds['selected'] == 'True': self.setCheckState(QtCore.Qt.Checked) elif li_ds['selected'] == 'False': self.setCheckState(QtCore.Qt.Unchecked) class logger_item_group(QtGui.QWidget): def __init__(self,channel_group_object): QtGui.QWidget.__init__(self) self.logger_items = [] self.init_interface() self.build_interface() self._channel_group = channel_group_object self.populate_from_channel_group(channel_group_object) self._expecting_data = False def populate_from_channel_group(self,channel_group_object): for channel in channel_group_object: li = logger_item(channel) self.add_logger_item(li) self.sort() def add_logger_item(self,item): self.logger_items.append(item) def sort(self): self.logger_items.sort(key = lambda item: item.get_name().upper() ) def inclusive_filter(self,filter_list): #filter_list is a list of category names to filter on for di in self.logger_items: if di.get_category() in filter_list: di.display(True) else: di.display(False) self.build_interface() def init_interface(self): layout = QtGui.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignTop) self.grid_container = QtGui.QWidget() self.grid_container_container = QtGui.QWidget() container_container_layout = QtGui.QHBoxLayout() container_container_layout.setAlignment(QtCore.Qt.AlignLeft) self.grid_container_container.setLayout(container_container_layout) container_container_layout.addWidget(self.grid_container) #container_container_layout.addStretch(80) layout.addWidget(self.grid_container_container) #layout.addStretch(80) self.setLayout(layout) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect(self,SIGNAL("customContextMenuRequested(QPoint)"),self.contextMenuRequested) def build_interface(self): self._grid = QtGui.QGridLayout() self._grid.setHorizontalSpacing(10) self._grid.setVerticalSpacing(5) self._grid.setAlignment(QtCore.Qt.AlignLeft) self.grid_container_container.layout().removeWidget(self.grid_container) self.grid_container = QtGui.QWidget() self.grid_container_container.layout().addWidget(self.grid_container) self.grid_container.setLayout(self._grid) for di in self.logger_items: di.setParent(None) y_pos = 0 x_pos = 0 column_height = 0 self.columnWidths = [] self.columnWidths.append(0) for di in self.logger_items: if di.displayed(): if column_height > 0 and column_height + di.sizeHint().height() + self._grid.verticalSpacing() > self.parentWidget().size().height() - 60: y_pos = 0 x_pos += 1 self.columnWidths.append(0) column_height = 0 self._grid.addWidget(di, y_pos, x_pos) di.column_placement = x_pos column_height += di.sizeHint().height() + self._grid.verticalSpacing() y_pos += 1 def get_selected_channel_name_list(self): channel_name_list = [] for li in self.logger_items: if li.selected(): channel_name_list.append( li.get_name() ) return channel_name_list def save(self,ds_parent): lig_ds = data_store('logger_group',None,ds_parent) for li in self.logger_items: li_ds = data_store('logger_item_container',li.get_name(),lig_ds) li.save(li_ds) def load(self,ds_parent): lig_ds = ds_parent.get_child('logger_group') for li_ds in lig_ds: li_name = li_ds.get_value() for li in self.logger_items: if li.get_name() == li_name: li.load(li_ds) @pyqtSlot(QtCore.QPoint) def contextMenuRequested(self, point): menu = QtGui.QMenu() # Select all menu item action_select = QtGui.QAction("Select All",menu) self.connect(action_select,SIGNAL("triggered()"), lambda: self.select_all(True)) menu.addAction(action_select) # Select all menu item action_deselect = QtGui.QAction("De-select All",menu) self.connect(action_deselect,SIGNAL("triggered()"), lambda: self.select_all(False)) menu.addAction(action_deselect) menu.exec_(self.mapToGlobal(point)) def select_all(self, select): for li in self.logger_items: if li.displayed(): li.setCheckState(QtCore.Qt.Checked if select else QtCore.Qt.Unchecked)
[docs]class logger_view(QtGui.QWidget): '''describes the view of the registers and the category selection and interaction beteen''' def __init__(self,channel_group_object,parent): QtGui.QWidget.__init__(self, parent) self.channel_group_object = channel_group_object self.lig = logger_item_group(self.channel_group_object) self.dcg = display_category_group(channel_group_object) self.connect(self.dcg, QtCore.SIGNAL("stateChanged()"), self._categories_changed) self.setWindowTitle('Select Logger Channels') self.init_interface() def init_interface(self): layout = QtGui.QVBoxLayout() scroll_area = QtGui.QScrollArea() scroll_area.setWidget(self.lig) scroll_area.setWidgetResizable(True) layout.addWidget(scroll_area,0) #add the display_item_group at the top layout.setStretch(0,80) layout.addWidget(self.dcg,0) #add the display_category_group at the bottom layout.setStretch(1,20) self.setLayout(layout) self.resize(800,600) self.show() def _categories_changed(self): selected_categories = self.dcg.get_selected_categories() selected_categories.sort(key=lambda s: str(s).upper()) self.lig.inclusive_filter(selected_categories) def get_selected_channel_name_list(self): return self.lig.get_selected_channel_name_list() def resizeEvent(self, event): self.lig.build_interface() def save(self,ds_parent): tv = data_store('logger_view',None,ds_parent) cat_ds = data_store('cat',None,tv) self.dcg.save(cat_ds) lig_ds = data_store('lig',None,tv) self.lig.save(lig_ds) def load(self,ds_parent): tv = ds_parent.get_child('logger_view') cat_ds = tv.get_child('cat') self.dcg.load(cat_ds) lig_ds = tv.get_child('lig') self.lig.load(lig_ds)
class background_worker(QtCore.QThread): #background worker thread, it is the only thing that read and writes registers def __init__(self,channel_group): QtCore.QThread.__init__(self) self._channel_group = channel_group self._calls = [] self.queue = Queue.Queue(5) self.running = True def read_channel_list(self,read_list): # do not re-enqueue multiple requests to read the same channels for item in list(self.queue.queue): if item[0] == 'read' and set(item[1]) == set(read_list): return self.queue.put ( ('read',read_list) ) def write_channel_list(self,write_list): self.queue.put ( ('write',write_list) ) def dump_channel_list(self,dump_list): self.queue.put ( ('dump',dump_list) ) def background_call(self,call): self.queue.put ( ('call',call) ) def run(self): while self.running: try: #write is first so if a read follows a write the new value will be set (data_type,data) = self.queue.get() if data_type == 'write' : self._write_channel_list(data) elif data_type == 'dump': self._read_dump_list(data) elif data_type == 'read': self._read_channel_list(data) elif data_type == 'call': data() else: print 'background worker broken' except Exception as e: e = e else: e = None finally: if e: print traceback.format_exc() print e def _read_channel_list_core(self,read_list): #list is a list of channel_names to read # build a list of actual channels from names channels = [] for name in read_list: if self._channel_group[name].is_readable(): channels.append(self._channel_group[name]) output = {} try: results = self._channel_group.read_channel_list(channels) except Exception as e: print e print traceback.format_exc() # print "attempting to read individually" results = {} for channel in channels: results[channel.get_name()] = "READ_ERROR" return results def _read_channel_list(self,read_list): results = self._read_channel_list_core(read_list) self.emit(SIGNAL('channel_data_ready(PyQt_PyObject)'),results) def _read_dump_list(self, dump_list): results = self._read_channel_list_core(dump_list) self.emit(SIGNAL('dump_data_ready(PyQt_PyObject)'),results) def _write_channel_list(self,write_list): #list is a list of tuples of channel_name and value to write for (channel_name,value) in write_list: print u"Write {} to {}".format(channel_name,value) try: self._channel_group[channel_name].write_unformatted(value) except Exception as e: e = e else: e = None finally: if e: print traceback.format_exc() raise e def close(self): self.queue.put( ('call',self.stop)) while self.running: pass def stop(self): self.running = False class ltc_lab_gui_main_window(QtGui.QMainWindow): def __init__(self,channel_group): QtGui.QMainWindow.__init__(self) self._tg = tab_group(channel_group) self._channel_group = channel_group self.logger = None self._gui_logger = gui_logger(channel_group) self.connect(self,SIGNAL('tab_use_write_presets(bool)'), self._tg.tab_use_write_presets ) self.connect(self,SIGNAL('tab_user_read_presets(bool)'), self._tg.tab_use_read_presets ) self.connect(self,SIGNAL('tab_flash_on_change(bool)'), self._tg.tab_flash_on_change ) self.connect(self,SIGNAL('change_font_size(int)'), self._tg.tab_change_font_size ) self._alpha = 0 self.palette = QtGui.QPalette() self.setAutoFillBackground(True) self.setPalette(self.palette) self.highlight = QtCore.QPropertyAnimation( self,"alpha") self.highlight.setDuration(3500) #msecs self.highlight.setStartValue(255) self.highlight.setEasingCurve(QtCore.QEasingCurve.OutCirc) self.highlight.setEndValue(0) self.init_interface() self._data = {} #dictionary of most recent data for dump def init_interface(self): self.setCentralWidget(self._tg) self.menu_bar = self.create_menu_bar() self.move(300, 150) #self.setWindowTitle('GUI') #moved to set_passive_observer_mode() self.resize(800,600) self.activateWindow() self.raise_() def _get_alpha(self): return self._alpha def _set_alpha(self, value): self._alpha = value color = QtGui.QColor(255,230,230,alpha=self._alpha) self.palette.setColor(self.backgroundRole(), color) self.setPalette(self.palette) alpha = QtCore.pyqtProperty(int, _get_alpha, _set_alpha) def _queue_overflow_highlight(self): self.highlight.stop() self.highlight.start(QtCore.QPropertyAnimation.KeepWhenStopped) def showEvent(self, event): self.emit(SIGNAL('resize_main_window()')) def resizeEvent(self, event): self.emit(SIGNAL('resize_main_window()')) def mouseReleaseEvent(self, event): pass def create_menu_bar(self): menu_bar = self.menuBar() # file file_menu = QtGui.QMenu("File",menu_bar) menu_bar.addMenu(file_menu) file_open = QtGui.QAction("Open...",file_menu) #file_open.setShortcut('Ctrl+O') file_open.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_O)) file_menu.addAction(file_open) self.connect(file_open,SIGNAL('triggered()'), self._disp_open_dialog ) file_save_as = QtGui.QAction("Save As...",file_menu) #file_save_as.setShortcut('Ctrl+Shift+S') file_save_as.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.SHIFT + QtCore.Qt.Key_S)) file_menu.addAction(file_save_as) self.connect(file_save_as,SIGNAL('triggered()'), self._disp_save_dialog ) file_save_default = QtGui.QAction("Save Default",file_menu) #file_save_default.setShortcut('Ctrl+S') file_save_default.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_S)) self.connect(file_save_default,SIGNAL('triggered()'), lambda: self.save_file('default.guicfg') ) file_menu.addAction(file_save_default) file_old_dump = QtGui.QAction("Dump Cached Data...",file_menu) self.connect(file_old_dump,SIGNAL('triggered()'), self._disp_dump_old_dialog ) file_menu.addAction(file_old_dump) file_new_dump = QtGui.QAction("Dump New Data...",file_menu) self.connect(file_new_dump,SIGNAL('triggered()'), self._disp_dump_new_dialog ) file_menu.addAction(file_new_dump) self.file_passive = QtGui.QAction("Passive Observer",file_menu,checkable=True) file_menu.addAction(self.file_passive) self.connect(self.file_passive,SIGNAL("toggled(bool)"),self.set_passive_observer_mode) self.file_close = QtGui.QAction("Close",file_menu) #self.file_close.setShortcut('Ctrl+Q') self.file_close.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Q)) self.connect(self.file_close,SIGNAL('triggered()'), self.close ) file_menu.addAction(self.file_close) #tab menu tab_menu = QtGui.QMenu("Tab",menu_bar) menu_bar.addMenu(tab_menu) tab_use_write_presets = QtGui.QAction("All Use Write Presets",tab_menu) self.connect(tab_use_write_presets,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('tab_use_write_presets(bool)'),True)) tab_menu.addAction(tab_use_write_presets) tab_no_write_presets = QtGui.QAction("All No Write Presets",tab_menu) self.connect(tab_no_write_presets,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('tab_use_write_presets(bool)'),False)) tab_menu.addAction(tab_no_write_presets) tab_use_read_presets = QtGui.QAction("All Use Read Presets",tab_menu) self.connect(tab_use_read_presets,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('tab_user_read_presets(bool)'),True)) tab_menu.addAction(tab_use_read_presets) tab_no_read_presets = QtGui.QAction("All No Read Presets",tab_menu) self.connect(tab_no_read_presets,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('tab_user_read_presets(bool)'),False)) tab_menu.addAction(tab_no_read_presets) tab_flash_on_change = QtGui.QAction("All Flash on Change",tab_menu) self.connect(tab_flash_on_change,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('tab_flash_on_change(bool)'),True)) tab_menu.addAction(tab_flash_on_change) tab_no_flash_on_change = QtGui.QAction("All No Flash on Change",tab_menu) self.connect(tab_no_flash_on_change,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('tab_flash_on_change(bool)'),False)) tab_menu.addAction(tab_no_flash_on_change) increase_font_size = QtGui.QAction("Increase Font Size",tab_menu) increase_font_size.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Plus)) self.connect(increase_font_size,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('change_font_size(int)'),1)) tab_menu.addAction(increase_font_size) decrease_font_size = QtGui.QAction("Decrease Font Size",tab_menu) decrease_font_size.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Minus)) self.connect(decrease_font_size,SIGNAL('triggered()'), lambda: self.emit(SIGNAL('change_font_size(int)'),-1)) tab_menu.addAction(decrease_font_size) #logger menu logger_menu = QtGui.QMenu("Logger",menu_bar) menu_bar.addMenu(logger_menu) logger_connect = QtGui.QAction("Connect Database ...",logger_menu) self.connect(logger_connect,SIGNAL('triggered()'), self._gui_logger.display_connect) logger_menu.addAction(logger_connect) logger_select_channels = QtGui.QAction("Select Channels...",logger_menu) self.connect(logger_select_channels,SIGNAL('triggered()'), self._gui_logger.display_select_channels) logger_menu.addAction(logger_select_channels) logger_log = QtGui.QAction("Log",logger_menu) #logger_log.setShortcut('Ctrl+L') logger_log.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_L)) self.connect(logger_log,SIGNAL('triggered()'), self._gui_logger.log) logger_menu.addAction(logger_log) return menu_bar def read_channel_list(self,channel_list): self.emit(SIGNAL('request_read_channel_list(PyQt_PyObject)'),channel_list) def write_channel_list(self,channel_list): self.emit(SIGNAL('request_write_channel_list(PyQt_PyObject)'),channel_list) def receive_dump_data(self,data_dict): self._data = data_dict self.dump(self._dump_file_name) def receive_channel_data(self,data_dict): self._data.update(data_dict) self.emit(SIGNAL('channel_data_ready(PyQt_PyObject)'),data_dict) def receive_background_call_request(self,call): self.emit(SIGNAL('request_background_call(PyQt_PyObject)'),call ) def receive_passive_channel_data(self,queue): self.emit(SIGNAL('passive_observer_data(PyQt_PyObject)'),queue) def _disp_save_dialog(self): fname = QtGui.QFileDialog.getSaveFileName(self, 'Save file', '.',"Gui Configuration files (*.guicfg);;All files (*.*);;XML files (*.xml)") if fname: self.save_file(fname) def _disp_open_dialog(self): fname = QtGui.QFileDialog.getOpenFileName(self, 'Open file', '.',"Gui Configuration files (*.guicfg);;All files (*.*);;XML files (*.xml)") if fname: self.load_file(fname) def _disp_dump_old_dialog(self): fname = QtGui.QFileDialog.getSaveFileName(self, 'Save Dump file', '.',"Text Files (*.txt);;All files (*.*)") if fname: self.dump(fname) def _disp_dump_new_dialog(self): fname = QtGui.QFileDialog.getSaveFileName(self, 'Save Dump file', '.',"Text Files (*.txt);;All files (*.*)") if fname: self._dump_file_name = fname channel_name_list = [channel.get_name() for channel in self._channel_group] self.emit(SIGNAL('request_dump_channel_list(PyQt_PyObject)'),channel_name_list) def dump(self,file_name): with open(file_name,'w') as f: for name,value in sorted(self._data.items(), key=lambda item: item[0]): f.write('{}: {}\n'.format(name,value)) f.close() def load_file(self,file_name): ds = data_store() ds.load(file_name) self.load(ds) def save_file(self,file_name): ds = data_store('lab_gui') self.save(ds) ds.save(file_name) def load(self,parent_ds): for ds in parent_ds: if ds.get_name() == 'tab_group': self._tg.load(ds) if ds.get_name() == 'logger_container': self._gui_logger.load(ds) if ds.get_name() == 'main_window': ds_size = ds.get_child('size') if ds_size: self.resize( int(ds_size['x']), int(ds_size['y']) ) def show_passive_error(self,channel_list): #print 'Error: In passive mode, reading and writing not allowed' raise Exception('In passive mode, reading and writing not allowed') def save(self,parent_ds): tgc_ds = data_store('tab_group',None,parent_ds) self._tg.save(tgc_ds) lc = data_store('logger_container',None,parent_ds) self._gui_logger.save(lc) mw = data_store('main_window',None,parent_ds) size = data_store('size',None,mw) size['x'] = str(self.size().width()) size['y'] = str(self.size().height()) def set_passive_observer_mode(self, passive): if not passive: self.file_passive.setChecked(False) self.connect(self._tg,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.read_channel_list ) self.connect(self._tg,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.write_channel_list ) self.disconnect(self._tg,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.show_passive_error ) self.disconnect(self._tg,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.show_passive_error ) self.disconnect(self._gui_logger,SIGNAL('request_background_call(PyQt_PyObject)'), self.show_passive_error ) self.connect(self,SIGNAL('channel_data_ready(PyQt_PyObject)'), self._tg.receive_channel_data ) self.connect(self._gui_logger,SIGNAL('request_background_call(PyQt_PyObject)'), self.receive_background_call_request ) self.disconnect(self,SIGNAL('passive_observer_data(PyQt_PyObject)'), self._tg.receive_passive_channel_data ) self.setWindowTitle(self._channel_group.get_name()) elif passive: self.disconnect(self.file_passive,SIGNAL("toggled(bool)"),self.set_passive_observer_mode) self.file_passive.setChecked(True) self.connect(self.file_passive,SIGNAL("toggled(bool)"),self.set_passive_observer_mode) self.disconnect(self._tg,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.read_channel_list ) self.disconnect(self._tg,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.write_channel_list ) self.connect(self._tg,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.show_passive_error ) self.connect(self._tg,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.show_passive_error ) self.connect(self._gui_logger,SIGNAL('request_background_call(PyQt_PyObject)'), self.show_passive_error ) self.disconnect(self,SIGNAL('channel_data_ready(PyQt_PyObject)'), self._tg.receive_channel_data ) self.disconnect(self._gui_logger,SIGNAL('request_background_call(PyQt_PyObject)'), self.receive_background_call_request ) self.connect(self,SIGNAL('passive_observer_data(PyQt_PyObject)'), self._tg.receive_passive_channel_data ) self.setWindowTitle('{} - PASSIVE OBSERVER MODE'.format(self._channel_group.get_name())) self.connect(self,SIGNAL('resize_main_window()'), self._tg, SLOT('resize_main_window()')) def close(self): QtGui.QMainWindow.close(self) def closeEvent(self, event): self.emit(SIGNAL('close_main()')) QtGui.QMainWindow.close(self) class ltc_lab_gui_app(QtGui.QApplication): def __init__(self,channel_group,passive=False,cfg_file='default.guicfg'): QtGui.QApplication.__init__(self, sys.argv) #need to clone in case there are remote objects; they wont play nice with qt threads self._channel_group = channel_group.clone() self.main_window = ltc_lab_gui_main_window(channel_group) # icon path is relative to calling script, so we need to find an absolute path back to PyICe icon_path = os.path.join(os.path.dirname(__file__), "tssop.ico") self.main_window.setWindowIcon(QtGui.QIcon(icon_path)) #open the default configuration if it exists try: self.main_window.load_file(cfg_file) except Exception as e: pass self.main_window.cb = self.clipboard() self.worker = background_worker(channel_group) self.worker.start() self.connect(self.worker,SIGNAL('channel_data_ready(PyQt_PyObject)'), self.main_window.receive_channel_data) self.connect(self.worker,SIGNAL('dump_data_ready(PyQt_PyObject)'), self.main_window.receive_dump_data) self.connect(self,SIGNAL('queue_overflow()'), self.main_window._queue_overflow_highlight) self.connect(self.main_window,SIGNAL('request_background_call(PyQt_PyObject)'), self.worker.background_call ) self.connect(self.main_window,SIGNAL('request_read_channel_list(PyQt_PyObject)'), self.worker.read_channel_list) self.connect(self.main_window,SIGNAL('request_dump_channel_list(PyQt_PyObject)'), self.worker.dump_channel_list) self.connect(self.main_window,SIGNAL('request_write_channel_list(PyQt_PyObject)'), self.worker.write_channel_list) self.connect(self.main_window,SIGNAL('close_main()'), self.exit) if passive: self.connect(self,SIGNAL('passive_observer_data(PyQt_PyObject)'), self.main_window.receive_passive_channel_data) self.main_window.set_passive_observer_mode(True) else: self.main_window.set_passive_observer_mode(False) self.connect(self.main_window,SIGNAL('close_main()'), self.worker.close) self.passive_queue = Queue.Queue(5) self.main_window.show() def passive_data(self,data_dict): try: self.passive_queue.put_nowait(data_dict) self.emit(SIGNAL('passive_observer_data(PyQt_PyObject)'), self.passive_queue) self.q_is_full = False except Queue.Full: if not self.q_is_full: self.q_is_full = True #print "Warning: Background GUI unable to keep up with main thread's data rate @ {}".format(datetime.datetime.now()) self.emit(SIGNAL('queue_overflow()')) if __name__ == '__main__': import lab_instruments import lab_core import twi_instrument #from xml_registers.EXAMPLE import ltc3350_formats xml_file_name = "xml_registers/EXAMPLE/LTC3350.xml" master = lab_core.master("Demonstration GUI") twi_dummy = master.get_twi_dummy_interface(delay=0.0001) #twi_dummy = master.get_twi_dummy_interface(delay=0) twii = twi_instrument.twi_instrument(twi_dummy) master.add(twii) twii.populate_from_file(xml_file_name,access_list=['user']) #master['meas_cap'].set_display_format_str('04X','0x',' V') master.add_channel_delta_timer('time_d') # master.add_channel_delta_timer('freq',reciprocal=True) # master.add_channel_total_timer('time_t') # master.add_channel_counter('count_u') # master.add_channel_counter('count_d', init=100, inc=-1) timer = lab_instruments.timer() timer.add_channel_total_seconds('seconds') timer.add_channel_total_minutes('minutes') master.add(timer) #integrator = lab_instruments.integrator(5) #integrator.add_channel_integration_seconds('int_s') #int_ch = master.add_channel_dummy('int_ch') #int_ch.write(1) #integrator.register_integrand_channel(int_ch) #master.add(integrator) #differentiator = lab_instruments.differentiator() #differentiator.add_channel_differentiation_seconds('dv') #differentiator.register_derivative_channel(int_ch) #master.add(differentiator) #master.add_channel_dummy('output') #master.write('output',0) # def comp_in(master,input): # print "input {}".format(input) # if master.read('output'): # if input < 5.5: # master.write('output',0) # else: # if input > 5.9: # master.write('output',1) # master.add_channel_virtual('input',write_function=lambda input: comp_in(master,input)) # th = lab_instruments.threshold_finder(master['input'],master['output'],4,7,.001,comparator_input_channel_sense=master['input'],threshold=0.5) # th.add_channel_all('tf') # master.add(th) # th.find() master.gui() #master.background_gui() #while True: # master.read_all_channels() # time.sleep(0.001) # self._alpha = 0 # self.palette = QtGui.QPalette() # self.setAutoFillBackground(True) # self.setPalette(self.palette) # self.highlight = QtCore.QPropertyAnimation( self,"alpha") # self.highlight.setDuration(3500) #msecs # self.highlight.setStartValue(255) # self.highlight.setEasingCurve(QtCore.QEasingCurve.OutCirc) # self.highlight.setEndValue(40) # safety yellow is more visible, so can be faded further # def _get_alpha(self): # return self._alpha # def _set_alpha(self, value): # self._alpha = value # #color = QtGui.QColor(255,182,192,alpha=self._alpha) #LightPink # color = QtGui.QColor(255,162,172,alpha=self._alpha) #LT # color = QtGui.QColor(180,180,255,alpha=self._alpha) #ADI # color = QtGui.QColor(100,255,100,alpha=self._alpha) #Nuclear # color = QtGui.QColor(180,255,0,alpha=self._alpha) #Safety # self.palette.setColor(self.backgroundRole(), color) # self.setPalette(self.palette) # alpha = QtCore.pyqtProperty(int, _get_alpha, _set_alpha) # if self._data_changed and self._flash: # self.highlight.stop() # self.highlight.start(QtCore.QPropertyAnimation.KeepWhenStopped) # else: # if self.highlight.state() == QtCore.QAbstractAnimation.Stopped: # self.alpha = 0