class DataCapturePanel(wx.Panel): """ A panel to start the data capture process, optionally exporting the results to a file. """ def __init__(self, parent, global_store, *args, **kwargs): wx.Panel.__init__(self, parent, *args, **kwargs) self.global_store = global_store self.capture_dialogs = 0 # Panel. panel_box = wx.BoxSizer(wx.HORIZONTAL) ## Capture. capture_static_box = wx.StaticBox(self, label='Capture') capture_box = wx.StaticBoxSizer(capture_static_box, wx.VERTICAL) panel_box.Add(capture_box, flag=wx.CENTER|wx.ALL, border=5) ### Start. self.start_button = wx.Button(self, label='Start') self.Bind(wx.EVT_BUTTON, self.OnBeginCapture, self.start_button) capture_box.Add(self.start_button, flag=wx.CENTER) ### Continuous. self.continuous_checkbox = wx.CheckBox(self, label='Continuous') capture_box.Add(self.continuous_checkbox, flag=wx.CENTER) ## Export. export_static_box = wx.StaticBox(self, label='Export') export_box = wx.StaticBoxSizer(export_static_box, wx.HORIZONTAL) panel_box.Add(export_box, proportion=1, flag=wx.CENTER|wx.ALL, border=5) ### Enabled. self.export_enabled = wx.CheckBox(self, label='') self.export_enabled.Value = True export_box.Add(self.export_enabled, flag=wx.CENTER) ### Export path. export_path_box = wx.BoxSizer(wx.VERTICAL) export_box.Add(export_path_box, proportion=1, flag=wx.CENTER) #### Directory. self.directory_browse_button = DirBrowseButton(self, labelText='Directory:') export_path_box.Add(self.directory_browse_button, flag=wx.EXPAND) #### Last file. last_file_box = wx.BoxSizer(wx.HORIZONTAL) export_path_box.Add(last_file_box, flag=wx.EXPAND) last_file_box.Add(wx.StaticText(self, label='Last output: '),flag=wx.ALIGN_CENTER_VERTICAL)#|) wx.ALIGN_RIGHT) self.last_file_name = wx.TextCtrl(self, style=wx.TE_READONLY) self.last_file_name.BackgroundColour = wx.LIGHT_GREY last_file_box.Add(self.last_file_name, proportion=1) self.SetSizer(panel_box) def OnBeginCapture(self, evt=None): # Prevent accidental double-clicking. self.start_button.Disable() def enable_button(): sleep(1) wx.CallAfter(self.start_button.Enable) thr = Thread(target=enable_button) thr.daemon = True thr.start() all_variables = [var for var in list(self.global_store.variables.values()) if var.enabled] output_variables = sift(all_variables, OutputVariable) input_variables = [var for var in sift(all_variables, InputVariable) if var.resource_name != ''] condition_variables = sift(all_variables, ConditionVariable) if not output_variables: output_variables.append(OutputVariable(order=0, name='<Dummy>', enabled=True)) output_variables, num_items = sort_output_variables(output_variables) condition_variables = sort_condition_variables(condition_variables) resource_names = [tuple(var.resource_name for var in group) for group in output_variables] measurement_resource_names = [var.resource_name for var in input_variables] condition_resource_names = [tuple(set(flatten([var.resource_names for var in group]))) for group in condition_variables] continuous = self.continuous_checkbox.Value missing_resources = set() unreadable_resources = set() unwritable_resources = set() missing_devices = set() pulse_program = self.global_store.pulse_program if pulse_program is not None: pulse_program = pulse_program.with_resources try: pulse_program.generate_waveforms(dry_run=True) except PulseError as e: MessageDialog(self, '\n'.join(e[0]), 'Pulse program error', monospace=True).Show() return except Exception as e: MessageDialog(self, str(e), 'Pulse program error').Show() return pulse_awg, pulse_oscilloscope = None, None pulse_channels = {} try: pulse_awg = self.global_store.devices[pulse_program.awg].device if pulse_awg is None: raise KeyError except KeyError: missing_devices.add(pulse_program.awg) else: # Gather used channel numbers. pulse_channels = dict((k, v) for k, v in list(pulse_program.output_channels.items()) if v is not None) actual_channels = list(range(1, len(pulse_awg.channels))) invalid_channels = [k for k, v in list(pulse_channels.items()) if v not in actual_channels] if invalid_channels: MessageDialog(self, 'Invalid channels for: {0}'.format(', '.join(invalid_channels)), 'Invalid channels').Show() return try: pulse_oscilloscope = self.global_store.devices[pulse_program.oscilloscope].device if pulse_oscilloscope is None: raise KeyError except KeyError: missing_devices.add(pulse_program.oscilloscope) try: pulse_config = PulseConfiguration(pulse_program, pulse_channels, pulse_awg, pulse_oscilloscope) except TypeError as e: MessageDialog(self, str(e), 'Device configuration error').Show() return else: pulse_config = None resources = [] for group in resource_names: group_resources = [] for name in group: if name == '': group_resources.append((str(len(resources)), None)) elif name not in self.global_store.resources: missing_resources.add(name) else: resource = self.global_store.resources[name] if resource.writable: group_resources.append((name, resource)) else: unwritable_resources.add(name) resources.append(tuple(group_resources)) measurement_resources = [] measurement_units = [] for name in measurement_resource_names: if name not in self.global_store.resources: missing_resources.add(name) else: resource = self.global_store.resources[name] if resource.readable: measurement_resources.append((name, resource)) measurement_units.append(resource.display_units) else: unreadable_resources.add(name) condition_resources = [] for group in condition_resource_names: group_resources = [] for name in group: if name not in self.global_store.resources: missing_resources.add(name) else: resource = self.global_store.resources[name] if resource.readable: group_resources.append((name, resource)) else: #the name may already have been put here by the loop assigning #to measurement_resources if name not in unreadable_resources: unreadable_resources.add(name) condition_resources.append(tuple(group_resources)) mismatched_resources = [] for (res_name, resource), var in zip(flatten(resources), flatten(output_variables)): if resource is None: continue if resource.units is not None: if not (var.type == 'quantity' and resource.verify_dimensions(var.units, exception=False, from_string=True)): mismatched_resources.append((res_name, var.name)) else: if var.type not in ['float', 'integer']: mismatched_resources.append((res_name, var.name)) for items, msg in [ (missing_resources, 'Missing resources'), (unreadable_resources, 'Unreadable resources'), (unwritable_resources, 'Unwritable resources'), (missing_devices, 'Missing devices')]: if items: MessageDialog(self, ', '.join('"{0}"'.format(x) for x in sorted(items)), msg).Show() if mismatched_resources: MessageDialog(self, ', '.join('Mismatched resource type for resource name {0} with variable name {1}'.format(x[0], x[1]) for x in mismatched_resources), 'Mismatched resources').Show() if (missing_resources or unreadable_resources or unwritable_resources or missing_devices or mismatched_resources): return # Check that all the condition arguments are compatible with one another. for cvar in flatten(condition_variables): # Get a condition. for cond in cvar.conditions: value1 = cond.arg1 value2 = cond.arg2 resource1 = None resource2 = None # If working with resources, use their values as the values, and make the resource available if cond.type1 == 'resource name': resource1 = [resource for (name, resource) in flatten(condition_resources) if name == cond.arg1][0] value1 = resource1.value if cond.type2 == 'resource name': resource2 = [resource for (name, resource) in flatten(condition_resources) if name == cond.arg2][0] value2 = resource2.value # Check if the other argument is in the allowed values if hasattr(resource1,'allowed_values') and resource1.allowed_values is not None: if value2 not in resource1.allowed_values: MessageDialog(self, 'In the condition {0}, {1} is not in allowed_values of {2}.'.format(cond, value2, cond.arg1),'Condition error').Show() return if hasattr(resource2,'allowed_values') and resource2.allowed_values is not None: if value1 not in resource2.allowed_values: MessageDialog(self, 'In the condition {0}, {1} is not in allowed_values of {2}.'.format(cond, value1, cond.arg2),'Condition error').Show() return # Check if units agree. if resource1 is not None and resource1.units is not None: try: value1.assert_dimensions(value2) except ValueError: MessageDialog(self, 'In the condition {0}, {1} does not have a dimension.'.format(cond, value2),'Condition error').Show() return except IncompatibleDimensions: MessageDialog(self, 'In the condition {0}, {1} and {2} do not have matching dimensions.'.format(cond, value1, value2),'Condition error').Show() return if resource2 is not None and resource2.units is not None: try: value2.assert_dimensions(value1) except ValueError: MessageDialog(self, 'In the condition {0}, {1} does not have a dimension.'.format(cond, value1),'Condition error').Show() return except IncompatibleDimensions: MessageDialog(self, 'In the condition {0}, {1} and {2} do not have matching dimensions.'.format(cond, value1, value2),'Condition error').Show() return exporting = False if self.export_enabled.Value: dir = self.directory_browse_button.GetValue() # YYYY-MM-DD_HH-MM-SS.csv name = '{0:04}-{1:02}-{2:02}_{3:02}-{4:02}-{5:02}.csv'.format(*localtime()) if not dir: MessageDialog(self, 'No directory selected.', 'Export path').Show() return if not os.path.isdir(dir): MessageDialog(self, 'Invalid directory selected', 'Export path').Show() return file_path = os.path.join(dir, name) if os.path.exists(file_path): MessageDialog(self, file_path, 'File exists').Show() return # Everything looks alright, so open the file. export_file = open(file_path, 'w') export_csv = csv.writer(export_file) exporting = True # Show the path in the GUI. self.last_file_name.Value = file_path # Write the header. export_csv.writerow(['Time (s)'] + ['{0.name} ({0.units})'.format(var) if var.units is not None else var.name for var in flatten(output_variables)] + ['{0.name} ({1})'.format(var, units) if units is not None else var.name for var, units in zip(input_variables, measurement_units)]) self.capture_dialogs += 1 dlg = DataCaptureDialog(self, resources, output_variables, num_items, measurement_resources, input_variables, condition_resources, condition_variables, pulse_config, continuous=continuous) dlg.SetMinSize((500, -1)) for name in measurement_resource_names: wx.CallAfter(pub.sendMessage, 'data_capture.start', name=name) # Export buffer. max_buf_size = 10 buf = [] buf_lock = Lock() def flush(): export_csv.writerows(buf) export_file.flush() while buf: buf.pop() def data_callback(cur_time, values, measurement_values): for name, value in zip(measurement_resource_names, measurement_values): wx.CallAfter(pub.sendMessage, 'data_capture.data', name=name, value=value) # Extract values out of quantities, since the units have already been taken care of in the header. values = [x.original_value if hasattr(x, 'original_value') else x for x in values] measurement_values = [x.original_value if hasattr(x, 'original_value') else x for x in measurement_values] if exporting: with buf_lock: buf.append([cur_time] + values + measurement_values) if len(buf) >= max_buf_size: flush() def close_callback(): self.capture_dialogs -= 1 if exporting: with buf_lock: flush() export_file.close() for name in measurement_resource_names: wx.CallAfter(pub.sendMessage, 'data_capture.stop', name=name) dlg.data_callback = data_callback dlg.close_callback = close_callback dlg.Show() dlg.start()
class PreferencesDialog(wx.Dialog): __doc__ = globals()['__doc__'] def __init__(self,parent,prefs=None,tablist=None): if not prefs: prefs = neveredit.util.Preferences.getPreferences() self.preferences = prefs if not tablist: tablist = ["GeneralPanel","ScriptEditorPanel","TextPanel", "UserControlsPanel"] # tablist list all tabs that will be activated, the other ones do # not show self.tablist = tablist resourceText = PreferencesDialog_xrc.data # resource = wx.xrc.EmptyXmlResource() resource = wx.xrc.XmlResource() # resource.LoadFromString(resourceText) resource.LoadFromBuffer(resourceText) dialog = resource.LoadDialog(parent,"PrefDialog") notebook = wx.xrc.XRCCTRL(dialog,"PrefNotebook") generalPanel = wx.xrc.XRCCTRL(dialog,"GeneralPanel") if "GeneralPanel" in self.tablist: self.appDirButton = DirBrowseButton(generalPanel,-1,(500,30), labelText=_('NWN Directory'), buttonText=_('Select...'), startDirectory= prefs['NWNAppDir']) self.appDirButton.SetValue(prefs['NWNAppDir']) resource.AttachUnknownControl('AppDir', self.appDirButton, generalPanel) else: index = self.__getPanelIndex(notebook,generalPanel) notebook.DeletePage(index) if "ScriptEditorPanel" in self.tablist: self.scriptAntiAlias = wx.xrc.XRCCTRL(dialog,"ScriptAntiAlias") self.scriptAntiAlias.SetValue(prefs['ScriptAntiAlias']) self.scriptAutoCompile = wx.xrc.XRCCTRL(dialog,"ScriptAutoCompile") self.scriptAutoCompile.SetValue(prefs['ScriptAutoCompile']) else: index = self.__getPanelIndex(notebook, wx.xrc.XRCCTRL(dialog, "ScriptEditorPanel")) notebook.DeletePage(index) if "TextPanel" in self.tablist : self.DefaultLocStringLang = wx.xrc.XRCCTRL( dialog,"DefaultLocStringLang") self.DefaultLocStringLang.SetSelection(neveredit.file.Language.\ convertFromBIOCode(prefs["DefaultLocStringLang"])) else: index = self.__getPanelIndex(notebook, wx.xrc.XRCCTRL(dialog, "TextPanel")) notebook.DeletePage(index) # Set up the User Controls Panel # Create Controls # Fix length # Set value if "UserControlsPanel" in self.tablist : self.mwUpKey = wx.xrc.XRCCTRL(dialog,"mwUpKey") self.mwUpKey.SetMaxLength(1) self.mwUpKey.SetValue(prefs['GLW_UP']) self.mwDownKey = wx.xrc.XRCCTRL(dialog,"mwDownKey") self.mwDownKey.SetMaxLength(1) self.mwDownKey.SetValue(prefs['GLW_DOWN']) self.mwLeftKey = wx.xrc.XRCCTRL(dialog,"mwLeftKey") self.mwLeftKey.SetMaxLength(1) self.mwLeftKey.SetValue(prefs['GLW_LEFT']) self.mwRightKey = wx.xrc.XRCCTRL(dialog,"mwRightKey") self.mwRightKey.SetMaxLength(1) self.mwRightKey.SetValue(prefs['GLW_RIGHT']) else: index = self.__getPanelIndex(notebook, wx.xrc.XRCCTRL(dialog, "UserControlsPanel")) notebook.DeletePage(index) dialog.Bind(wx.EVT_BUTTON, self.OnOk, id=wx.xrc.XRCID("ID_OK")) dialog.Bind(wx.EVT_BUTTON, self.OnCancel, id=wx.xrc.XRCID("ID_CANCEL")) # self.PostCreate(dialog) def __getPanelIndex(self,notebook,panel): index = [notebook.GetPage(i).GetId() for i in range(notebook.GetPageCount())]\ .index(panel.GetId()) return index def getValues(self): values = {} if "GeneralPanel" in self.tablist: values.update({'NWNAppDir':self.appDirButton.GetValue()}) if "ScriptEditorPanel" in self.tablist: values.update({'ScriptAntiAlias': self.scriptAntiAlias.GetValue(), 'ScriptAutoCompile': self.scriptAutoCompile.GetValue()}) if "TextPanel" in self.tablist: values.update({"DefaultLocStringLang":neveredit.file.Language.\ convertToBIOCode(self.DefaultLocStringLang.GetSelection())}) # Update value of direction keys for Model/GLWindow if "UserControlsPanel" in self.tablist: values.update({'GLW_UP': self.mwUpKey.GetValue(), 'GLW_DOWN': self.mwDownKey.GetValue(), 'GLW_LEFT': self.mwLeftKey.GetValue(), 'GLW_RIGHT': self.mwRightKey.GetValue()}) return values def ShowAndInterpret(self): self.CentreOnParent() if self.ShowModal() == wx.ID_OK: result = self.getValues() self.preferences.values.update(result) self.preferences.save() return True else: return False def OnCancel(self, event): self.EndModal(wx.ID_CANCEL) def OnOk(self, event): self.EndModal(wx.ID_OK)