'''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