Created
November 21, 2016 18:49
-
-
Save blakejohnson/b60fea03e6db96d359d39e1757e6c67b to your computer and use it in GitHub Desktop.
atom/enaml exception issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from atom.api import (Atom, List, ContainerList, Dict, observe, Callable, Typed, Unicode) | |
import enaml | |
class DictManager(Atom): | |
""" | |
Control - Presenter for a dictionary of items. | |
i.e. give the ability to add/delete rename items | |
""" | |
itemDict = Typed(dict) | |
displayFilter = Callable() # filter which items to display later | |
possibleItems = List() # a list of classes that can possibly be added to this list | |
displayList = ContainerList() | |
onChangeDelegate = Callable() | |
def __init__(self, itemDict={}, displayFilter=lambda x: True, **kwargs): | |
self.displayFilter = displayFilter | |
super(DictManager, self).__init__(itemDict=itemDict, displayFilter=displayFilter, **kwargs) | |
def add_item(self, parent): | |
""" | |
Create a new item dialog window and handle the result | |
""" | |
pass | |
# with enaml.imports(): | |
# from widgets.dialogs import AddItemDialog | |
# dialogBox = AddItemDialog(parent, modelNames=[i.__name__ for i in self.possibleItems], objText='') | |
# dialogBox.exec_() | |
# if dialogBox.result: | |
# if dialogBox.newLabel not in self.itemDict.keys(): | |
# self.itemDict[dialogBox.newLabel] = self.possibleItems[dialogBox.newModelNum](label=dialogBox.newLabel) | |
# self.displayList.append(dialogBox.newLabel) | |
# else: | |
# print("WARNING: Can't use duplicate label %s"%dialogBox.newLabel) | |
def remove_item(self, itemLabel): | |
#check that the item exists before removing from the list | |
if itemLabel in self.itemDict.keys(): | |
self.itemDict.pop(itemLabel) | |
# TODO: once ContainerDicts land see if we still need this | |
self.displayList.remove(itemLabel) | |
def name_changed(self, oldLabel, newLabel): | |
# Add copy of changing item | |
self.itemDict[newLabel] = self.itemDict[oldLabel] | |
# update display list | |
idx = self.displayList.index(oldLabel) | |
self.displayList[idx] = newLabel | |
# remove old label from itemDict list | |
if oldLabel in self.itemDict.keys(): | |
self.itemDict.pop(oldLabel) | |
else: | |
print("WARNING: %s is not in the list"%oldLabel) | |
# update label to new label list | |
self.itemDict[newLabel].label = newLabel | |
if self.onChangeDelegate: | |
self.onChangeDelegate(oldLabel, newLabel) | |
def update_enable(self, itemLabel, checkState): | |
self.itemDict[itemLabel].enabled = checkState | |
@observe('itemDict') | |
def update_display_list(self, change): | |
""" | |
Eventualy itemDict will be a ContainerDict and this will fire on all events. | |
Will have to be more careful about whether it is a "create" event or "update" | |
""" | |
self.displayList = sorted([v.label for v in self.itemDict.values() if self.displayFilter(v)]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Reusable widgets for handling lists and dictionaries | |
""" | |
from qt_list_str_widget import QtListStrWidget | |
from enaml.widgets.api import Window, Container, PushButton, Form, Label, Field, ComboBox | |
from enaml.stdlib.mapped_view import MappedView | |
from enaml.core.api import Looper | |
from enaml.layout.api import hbox, vbox, spacer | |
enamldef DictManagerView(Container): dictView: | |
""" | |
Display a list of items on the left; a view of the selected item on the right and some add/delete buttons. | |
""" | |
attr dictManager | |
attr viewMap # dictionary mapping classes to views | |
attr viewkwargs # extra parameters to pass to the model view | |
attr modelName # attr name for the model in the view | |
attr labelValidator # callable validator for QtListStrWidget labels | |
constraints = [ | |
hbox( | |
vbox( | |
itemList, | |
hbox(addButton, removeButton, spacer), | |
spacer | |
), | |
selectedItemView, | |
spacer | |
) | |
] | |
QtListStrWidget: itemList: | |
items << [(item, dictManager.itemDict[item].enabled) for item in dictManager.displayList] | |
validator = labelValidator | |
initialized :: | |
itemList.item_changed.connect(dictManager.name_changed) | |
itemList.enable_changed.connect(dictManager.update_enable) | |
PushButton: addButton: | |
text = "Add" | |
clicked :: dictManager.add_item(self) | |
PushButton: removeButton: | |
text = "Remove" | |
clicked :: dictManager.remove_item(itemList.selected_item) | |
Container: selectedItemView: | |
MappedView: | |
model << dictManager.itemDict[itemList.selected_item] if itemList.selected_item else None | |
typemap = dictView.viewMap | |
modelkey = dictView.modelName | |
kwargs = dictView.viewkwargs if dictView.viewkwargs else {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# from atom.api import (Bool, List, Dict, observe, set_default, Unicode, Enum, Int, Callable, Typed) | |
from enaml.widgets.api import ComboBox | |
#A combo box connected to a Enum trait | |
enamldef EnumComboBox(ComboBox): | |
attr obj | |
attr enumName | |
attr itemList = list(obj.get_member(enumName).items) | |
items = itemList | |
index << itemList.index(getattr(obj, enumName)) | |
index :: setattr(obj, enumName, itemList[index]) | |
tool_tip = obj.get_member(enumName).metadata["desc"] | |
enamldef EnumFloatComboBox(ComboBox): | |
attr obj | |
attr floatName | |
attr itemList = [str(item) for item in list(obj.get_member(floatName).items)] | |
items = itemList | |
index << itemList.index(str(getattr(obj, floatName))) | |
index :: setattr(obj, floatName, float(itemList[index])) | |
tool_tip = obj.get_member(floatName).metadata["desc"] | |
enamldef EnumIntComboBox(ComboBox): | |
attr obj | |
attr intName | |
attr itemList = [str(item) for item in list(obj.get_member(intName).items)] | |
items = itemList | |
index << itemList.index(str(getattr(obj, intName))) | |
index :: setattr(obj, intName, int(itemList[index])) | |
tool_tip = obj.get_member(intName).metadata["desc"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Measurement filters | |
""" | |
from atom.api import Atom, Int, Float, Str, Bool, Enum | |
class MeasFilter(Atom): | |
label = Str() | |
enabled = Bool(True) | |
data_source = Str().tag(desc="Where the measurement data is pushed from.") | |
class Plotter(MeasFilter): | |
plot_mode = Enum('amp/phase', 'real/imag', 'quad').tag(desc='Filtered data scope mode.') | |
class WriteToHDF5(MeasFilter): | |
filename = Str('').tag(desc='Path to file where records will be saved.') | |
compression = Bool(True) | |
class Averager(MeasFilter): | |
axis = Str('').tag(desc='Name of the axis to average along.') | |
class Channelizer(MeasFilter): | |
if_freq = Float(10e6).tag(desc='The I.F. frequency for digital demodulation.') | |
bandwidth = Float(5e6).tag(desc='Low-pass filter bandwidth') | |
sampling_rate = Float(250e6).tag(desc='The sampling rate of the digitizer.') | |
phase = Float(0.0).tag(desc='Phase rotation to apply in rad.') | |
decim_factor_1 = Int(1).tag(desc="First stage polyphase decimation (before multiplication with reference).") | |
decim_factor_2 = Int(1).tag(desc="Second stage polyphase decimation (before IIR filter).") | |
decim_factor_3 = Int(1).tag(desc="Third stage polyphase decimation (after IIR filter).") | |
class KernelIntegrator(MeasFilter): | |
kernel = Str('').tag(desc="Integration kernel vector.") | |
bias = Float(0.0).tag(desc="Bias after integration.") | |
simple_kernel = Bool(True) | |
box_car_start = Int(1) | |
box_car_stop = Int(1) | |
if_freq = Float(10e6) | |
sampling_rate = Float(250e6) | |
measFilterList = [Averager, | |
Channelizer, | |
KernelIntegrator, | |
Plotter, | |
WriteToHDF5] | |
if __name__ == "__main__": | |
import enaml | |
from enaml.qt.qt_application import QtApplication | |
from DictManager import DictManager | |
# Work around annoying problem with multiple class definitions | |
from MeasFilters import Channelizer, Averager, KernelIntegrator, Plotter, WriteToHDF5 | |
M1 = Channelizer(label='M1') | |
M2 = Averager(label='M2') | |
M3 = KernelIntegrator(label='M3') | |
M4 = Plotter(label='M4') | |
M5 = WriteToHDF5(label='M5') | |
filters = {'M1': M1, | |
'M2': M2, | |
'M3': M3, | |
'M4': M4, | |
'M5': M5} | |
manager = DictManager( | |
itemDict=filters, | |
possibleItems=measFilterList) | |
with enaml.imports(): | |
from MeasFiltersViews import MeasFilterManagerWindow | |
app = QtApplication() | |
view = MeasFilterManagerWindow(filterLib=manager) | |
view.show() | |
app.start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from enaml.widgets.api import Window, Label, Field, Form, Container, GroupBox, ComboBox, \ | |
CheckBox, PushButton, SpinBox, RadioButton | |
from enaml.stdlib.fields import FloatField, IntField | |
from enaml.core.api import Conditional, Looper | |
from enaml.layout.api import hbox, vbox, spacer | |
from enum_combos import EnumComboBox | |
from DictManagerView import DictManagerView | |
import MeasFilters | |
from numpy import pi | |
enamldef ChannelizerForm(GroupBox): | |
attr myFilter | |
title := '{} ({})'.format(myFilter.label, myFilter.__class__.__name__) | |
hug_width = 'medium' | |
Form: | |
Label: | |
text = 'I.F. Freq (MHz)' | |
FloatField: | |
value << myFilter.if_freq/1e6 | |
value :: myFilter.if_freq = value*1e6 | |
tool_tip = myFilter.get_member('if_freq').metadata['desc'] | |
Label: | |
text = 'Bandwidth (MHz)' | |
FloatField: | |
value << myFilter.bandwidth/1e6 | |
value :: myFilter.bandwidth = value*1e6 | |
tool_tip = myFilter.get_member('bandwidth').metadata['desc'] | |
Label: | |
text = 'Sampling Rate (MS/s)' | |
FloatField: | |
value << myFilter.sampling_rate/1e6 | |
value :: myFilter.sampling_rate = value*1e6 | |
tool_tip = myFilter.get_member('sampling_rate').metadata['desc'] | |
Label: | |
text = "First Stage Decimation" | |
IntField: | |
value := myFilter.decim_factor_1 | |
tool_tip = myFilter.get_member('decim_factor_1').metadata['desc'] | |
Label: | |
text = "Second Stage Decimation" | |
IntField: | |
value := myFilter.decim_factor_2 | |
tool_tip = myFilter.get_member('decim_factor_2').metadata['desc'] | |
Label: | |
text = "Third Stage Decimation" | |
IntField: | |
value := myFilter.decim_factor_3 | |
tool_tip = myFilter.get_member('decim_factor_3').metadata['desc'] | |
Label: | |
text = "Phase (deg.)" | |
FloatField: | |
value << (180/pi)*myFilter.phase | |
value :: myFilter.phase = (pi/180)*value | |
Label: | |
text = "Data source" | |
Field: | |
text := myFilter.data_source | |
enamldef KernelIntegratorForm(GroupBox): | |
attr myFilter | |
title := '{} ({})'.format(myFilter.label, myFilter.__class__.__name__) | |
Form: | |
Label: | |
text = "Data source" | |
Field: | |
text := myFilter.data_source | |
Label: | |
text = 'Arbitrary kernel' | |
Field: | |
text := myFilter.kernel | |
tool_tip = myFilter.get_member('kernel').metadata["desc"] | |
Label: | |
text = 'Simple kernel' | |
GroupBox: | |
Form: | |
Label: | |
text = 'Box car start' | |
IntField: | |
value := myFilter.box_car_start | |
Label: | |
text = 'Box car stop' | |
IntField: | |
value := myFilter.box_car_stop | |
Label: | |
text = 'IF frequency (MHz)' | |
FloatField: | |
value << myFilter.if_freq / 1e6 | |
value :: myFilter.if_freq = value * 1e6 | |
Label: | |
text = 'Sampling rate (MS/s)' | |
FloatField: | |
value << myFilter.sampling_rate / 1e6 | |
value :: myFilter.sampling_rate = value * 1e6 | |
Label: | |
text = '' | |
Container: | |
constraints = [hbox(rb1, rb2, spacer)] | |
RadioButton: rb1: | |
text = 'arbitrary' | |
checked << not myFilter.simple_kernel | |
RadioButton: rb2: | |
text = 'simple' | |
checked := myFilter.simple_kernel | |
Label: | |
text = 'Bias' | |
FloatField: | |
value := myFilter.bias | |
tool_tip = myFilter.get_member('bias').metadata["desc"] | |
enamldef PlotterView(GroupBox): | |
attr myFilter | |
title := '{} ({})'.format(myFilter.label, myFilter.__class__.__name__) | |
Form: | |
Label: | |
text = "Plot mode" | |
EnumComboBox: | |
obj := myFilter | |
enumName = 'plot_mode' | |
Label: | |
text = "Data source" | |
Field: | |
text := myFilter.data_source | |
enamldef WriteToHDF5View(GroupBox): | |
attr myFilter | |
title := '{} ({})'.format(myFilter.label, myFilter.__class__.__name__) | |
Form: | |
Label: | |
text = "Filename" | |
Field: | |
text := myFilter.filename | |
Label: | |
text = "Compression" | |
CheckBox: | |
checked := myFilter.compression | |
Label: | |
text = "Data source" | |
Field: | |
text := myFilter.data_source | |
enamldef AveragerView(GroupBox): | |
attr myFilter | |
title := '{} ({})'.format(myFilter.label, myFilter.__class__.__name__) | |
Form: | |
Label: | |
text = "Averaging Axis" | |
Field: | |
text := myFilter.axis | |
Label: | |
text = "Data source" | |
Field: | |
text := myFilter.data_source | |
#Dummy empty view | |
enamldef EmptyMeasFilterForm(Container): | |
attr myFilter | |
#Map filters to views | |
filterViewMap = {type(None): EmptyMeasFilterForm, | |
MeasFilters.Channelizer: ChannelizerForm, | |
MeasFilters.KernelIntegrator: KernelIntegratorForm, | |
MeasFilters.WriteToHDF5: WriteToHDF5View, | |
MeasFilters.Averager: AveragerView, | |
MeasFilters.Plotter: PlotterView} | |
enamldef MeasFilterManager(Container): measFilterManager: | |
attr filterLib | |
DictManagerView: | |
dictManager = filterLib | |
modelName = 'myFilter' | |
viewMap = filterViewMap | |
enamldef MeasFilterManagerWindow(Window): measFilterManagerTest: | |
attr filterLib | |
title = 'Filter Manager' | |
MeasFilterManager: | |
filterLib := measFilterManagerTest.filterLib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" Enaml widget for editing a list of string | |
""" | |
#------------------------------------------------------------------------------- | |
# Imports: | |
#------------------------------------------------------------------------------- | |
from atom.api import (Bool, List, Tuple, ContainerList, observe, set_default, Unicode, Enum, Int, Signal, Callable) | |
from enaml.widgets.api import RawWidget | |
from enaml.core.declarative import d_ | |
from enaml.qt.QtGui import QListWidget, QListWidgetItem, QAbstractItemView, QColor | |
from enaml.qt.QtCore import Qt | |
class QtListStrWidget(RawWidget): | |
""" A Qt4 implementation of an Enaml ProxyListStrView. | |
""" | |
__slots__ = '__weakref__' | |
#: The list of (str, enabled) tuples being viewed | |
items = d_(List(Tuple() ) ) | |
#: The index of the currently selected str | |
selected_index = d_(Int(-1)) | |
#: The currently selected str | |
selected_item = d_(Unicode()) | |
#: Whether or not the items should be checkable | |
checkable = d_(Bool(True)) | |
#: Whether or not the items should be editable | |
editable = d_(Bool(True)) | |
validator = d_(Callable()) | |
hug_width = set_default('weak') | |
item_changed = Signal() | |
enable_changed = Signal() | |
#-------------------------------------------------------------------------- | |
# Initialization API | |
#-------------------------------------------------------------------------- | |
def create_widget(self, parent): | |
""" | |
Create the QListWidget widget. | |
""" | |
# Create the list model and accompanying controls: | |
widget = QListWidget(parent) | |
for item, checked in self.items: | |
self.add_item(widget, item, checked) | |
# set selected_item here so that first change fires an 'update' rather than 'create' event | |
self.selected_item = u'' | |
if self.items: | |
self.selected_index = 0 | |
self.selected_item = self.items[0][0] | |
widget.setCurrentRow(0) | |
widget.itemSelectionChanged.connect(self.on_selection) | |
widget.itemChanged.connect(self.on_edit) | |
return widget | |
def add_item(self, widget, item, checked=True): | |
itemWidget = QListWidgetItem(item) | |
if self.checkable: | |
itemWidget.setCheckState(Qt.Checked if checked else Qt.Unchecked) | |
if self.editable: | |
_set_item_flag(itemWidget, Qt.ItemIsEditable, True) | |
widget.addItem(itemWidget) | |
self.apply_validator(itemWidget, itemWidget.text()) | |
#-------------------------------------------------------------------------- | |
# Signal Handlers | |
#-------------------------------------------------------------------------- | |
def on_selection(self): | |
""" | |
The signal handler for the index changed signal. | |
""" | |
widget = self.get_widget() | |
self.selected_index = widget.currentRow() | |
self.selected_item = self.items[widget.currentRow()][0] if self.selected_index >= 0 else u'' | |
def on_edit(self, item): | |
""" | |
The signal handler for the item changed signal. | |
""" | |
widget = self.get_widget() | |
itemRow = widget.indexFromItem(item).row() | |
oldLabel = self.items[itemRow][0] | |
newLabel = item.text() | |
# only signal the enable change when the labels are the same and is in | |
# the item list, also only signal a name change when the labels are not | |
# the same and the newlabel is not in the item list | |
item_labels = [_[0] for _ in self.items] | |
if newLabel == oldLabel and newLabel in item_labels: | |
self.items[itemRow] = (newLabel, item.checkState() == Qt.Checked) | |
self.enable_changed(item.text(), self.items[itemRow][1]) | |
elif oldLabel != newLabel and newLabel not in item_labels: | |
self.item_changed(oldLabel, newLabel) | |
self.selected_item = newLabel | |
self.items[itemRow] = (newLabel, item.checkState() == Qt.Checked) | |
self.apply_validator(item, newLabel) | |
#-------------------------------------------------------------------------- | |
# ProxyListStrView API | |
#-------------------------------------------------------------------------- | |
def set_items(self, items, widget=None): | |
widget = self.get_widget() | |
count = widget.count() | |
nitems = len(items) | |
for idx, item in enumerate(items[:count]): | |
itemWidget = widget.item(idx) | |
# Update checked state before the text so that we can distinguish a checked state change from a label change | |
itemWidget.setCheckState(Qt.Checked if self.items[idx][1] else Qt.Unchecked) | |
itemWidget.setText(item[0]) | |
self.apply_validator(itemWidget, item[0]) | |
if nitems > count: | |
for item in items[count:]: | |
self.add_item(widget, item[0]) | |
elif nitems < count: | |
for idx in reversed(xrange(nitems, count)): | |
widget.takeItem(idx) | |
#-------------------------------------------------------------------------- | |
# Utility methods | |
#-------------------------------------------------------------------------- | |
def apply_validator(self, item, label): | |
if self.validator and not self.validator(label): | |
item.setTextColor(QColor(255,0,0)) | |
else: | |
item.setTextColor(QColor(0,0,0)) | |
#-------------------------------------------------------------------------- | |
# Observers | |
#-------------------------------------------------------------------------- | |
@observe('items') | |
def _update_items(self, change): | |
""" An observer which sends state change to the proxy. """ | |
#this callback may be called before the widget is initialized | |
widget = self.get_widget() | |
if widget == None: | |
return | |
self.set_items(self.items) | |
# update the selected item because the current row has changed | |
self.selected_item = self.items[widget.currentRow()][0] if self.selected_index >= 0 else u'' | |
# Helper methods | |
def _set_item_flag(item, flag, enabled): | |
""" | |
Set or unset the given item flag for the item. | |
""" | |
flags = item.flags() | |
if enabled: | |
flags |= flag | |
else: | |
flags &= ~flag | |
item.setFlags(flags) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment