#!/bin/env python import os import wx import re import time VERSION = "0.1" try: from agw import customtreectrl as CT except ImportError: import wx.lib.agw.customtreectrl as CT from config_helptext import helpText supportedCPUs = ['ATmega168', 'ATmega328P', 'ATmega644P', 'ATmega644PA', 'ATmega1280', 'ATmega1284P', 'ATmega2560', 'AT90USB1286'] BSIZE = (90, 60) BSIZESMALL = (90, 30) reDefine = re.compile("\s*#define\s+(\S+)\s+(\S+)") reDefQS = re.compile("\s*#define\s+(\S+)\s+(\"[^\"]*\")") reDefTS = re.compile("\s*(DEFINE_TEMP_SENSOR\\([^)]*\\))") reDefHT = re.compile("\s*(DEFINE_HEATER\\([^)]*\\))") reDefBool = re.compile("\s*#define\s+(\S+)\s+") reIncArduino = re.compile("\s*#include\s+\"arduino.h\"") reSensor3 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") reSensor4 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") reHeater = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") reInteger = re.compile("^\d+$") reFloat = re.compile("^\d+(\.\d+)?$") rePin = re.compile("^[AD]IO\d+$") reAVR = re.compile("__AVR_(\w+)__") defineValueFormat = "#define %-30.30s %s\n" defineBoolFormat = "#define %s\n" defineHeaterFormat = "#define HEATER_%s HEATER_%s\n" defineULFormat = "#define %-30.30s %sUL\n" defineDCExtruderFormat = "#define %-30.30s HEATER_%s\n" class Page: def __init__(self): self.modified = False self.valid = True self.fieldValid = {} self.textControls = {} self.checkBoxes = {} self.radioButtons = {} self.choices = {} def addTextCtrl(self, name, labelWidth, ht, validator): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name) self.fieldValid[name] = True tc.Bind(wx.EVT_TEXT, validator) self.textControls[name] = tc lsz.Add(tc) if ht: hts = ht else: if name in helpText.keys(): hts = helpText[name] else: hts = "" tc.SetToolTipString(hts) return lsz def addCheckBox(self, name, ht, validator): if name in self.labels.keys(): lbl = self.labels[name] else: lbl = name cb = wx.CheckBox(self, wx.ID_ANY, lbl) cb.Bind(wx.EVT_CHECKBOX, validator) self.checkBoxes[name] = cb if ht: hts = ht else: if name in helpText.keys(): hts = helpText[name] else: hts = "" cb.SetToolTipString(hts) return cb def addRadioButton(self, name, style, ht, validator): rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style) self.Bind(wx.EVT_RADIOBUTTON, validator, rb) self.radioButtons[name] = rb if ht: hts = ht else: if name in helpText.keys(): hts = helpText[name] else: hts = "" rb.SetToolTipString(hts) return rb def addChoice(self, name, choices, selection, labelWidth, ht, validator): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) ch = wx.Choice(self, wx.ID_ANY, choices = choices, name = name) ch.Bind(wx.EVT_CHOICE, validator) ch.SetSelection(selection) lsz.Add(ch) self.choices[name] = ch if ht: hts = ht else: if name in helpText.keys(): hts = helpText[name] else: hts = "" ch.SetToolTipString(hts) return lsz def setChoice(self, name, cfgValues, choices, default): if name in cfgValues.keys(): bv = cfgValues[name] else: bv = self.defaultClock try: s = choices.index(bv) except: try: s = choices.index(default) except: s = 0 self.choices[name].SetSelection(s) def onTextCtrlInteger(self, evt): self.assertModified(True) tc = evt.GetEventObject() name = tc.GetName() w = tc.GetValue().strip() if w == "": valid = True else: m = reInteger.match(w) if m: valid = True else: valid = False self.setFieldValidity(name, valid) if valid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() evt.Skip() def onTextCtrlFloat(self, evt): self.assertModified(True) tc = evt.GetEventObject() name = tc.GetName() w = tc.GetValue().strip() if w == "": valid = True else: m = reFloat.match(w) if m: valid = True else: valid = False self.setFieldValidity(name, valid) if valid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() evt.Skip() def onTextCtrlPin(self, evt): self.assertModified(True) tc = evt.GetEventObject() self.validatePin(tc) evt.Skip() def onTextCtrl(self, evt): self.assertModified(True) evt.Skip() def onChoice(self, evt): self.assertModified(True) evt.Skip() def validatePin(self, tc): name = tc.GetName() w = tc.GetValue().strip() if w == "": valid = True else: m = reInteger.match(w) if m: valid = True else: if self.includeArduino: m = rePin.match(w) if m: valid = True else: valid = False else: valid = False self.setFieldValidity(name, valid) if valid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() def onCheckBox(self, evt): self.assertModified(True) evt.Skip() def assertModified(self, flag): if flag != self.modified: self.parent.assertModified(self.id, flag) self.modified = flag def setFieldValidity(self, name, flag): self.fieldValid[name] = flag pgValid = True for k in self.fieldValid.keys(): if not self.fieldValid[k]: pgValid = False break self.assertValid(pgValid) def assertValid(self, flag): if flag != self.valid: self.parent.assertValid(self.id, flag) self.valid = flag class MechanicalPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.id = idPg self.parent = parent self.spmKeys = ['STEPS_PER_M_X', 'STEPS_PER_M_Y', 'STEPS_PER_M_Z', 'STEPS_PER_M_E'] self.mfrKeys = ['MAXIMUM_FEEDRATE_X', 'MAXIMUM_FEEDRATE_Y', 'MAXIMUM_FEEDRATE_Z', 'MAXIMUM_FEEDRATE_E'] self.msrKeys = ['SEARCH_FEEDRATE_X', 'SEARCH_FEEDRATE_Y', 'SEARCH_FEEDRATE_Z'] self.eclKeys = ['ENDSTOP_CLEARANCE_X', 'ENDSTOP_CLEARANCE_Y', 'ENDSTOP_CLEARANCE_Z'] self.minmaxKeys = ['X_MIN', 'X_MAX', 'Y_MIN', 'Y_MAX', 'Z_MIN', 'Z_MAX'] self.labels = {'STEPS_PER_M_X': "X:", 'STEPS_PER_M_Y': "Y:", 'STEPS_PER_M_Z': "Z:", 'STEPS_PER_M_E' : "E:", 'MAXIMUM_FEEDRATE_X': "X:", 'MAXIMUM_FEEDRATE_Y': "Y:", 'MAXIMUM_FEEDRATE_Z': "Z:", 'MAXIMUM_FEEDRATE_E': "E:", 'SEARCH_FEEDRATE_X': "X:", 'SEARCH_FEEDRATE_Y': "Y:", 'SEARCH_FEEDRATE_Z': "Z:", 'ENDSTOP_CLEARANCE_X': "X:", 'ENDSTOP_CLEARANCE_Y': "Y:", 'ENDSTOP_CLEARANCE_Z': "Z:", 'X_MIN': "Min X:", 'X_MAX': "Max X:", 'Y_MIN': "Min Y:", 'Y_MAX': "Max Y:", 'Z_MIN': "Min Z:", 'Z_MAX': "Max Z:", 'E_ABSOLUTE': "Absolute E Coordinates"} labelWidth = 40; sz = wx.GridBagSizer() sz.AddSpacer((10, 10), pos = (0, 0)) b = wx.StaticBox(self, wx.ID_ANY, "Steps Per Meter") kh = 'STEPS_PER_M' ht = None if kh in helpText.keys(): ht = helpText[kh] sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k in self.spmKeys: tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 1)) b = wx.StaticBox(self, wx.ID_ANY, "Maximum Feedrate") kh = 'MAXIMUM_FEEDRATE' ht = None if kh in helpText.keys(): ht = helpText[kh] sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k in self.mfrKeys: tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 5)) b = wx.StaticBox(self, wx.ID_ANY, "Search Feedrate") kh = 'SEARCH_FEEDRATE' ht = None if kh in helpText.keys(): ht = helpText[kh] sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k in self.msrKeys: tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 7)) b = wx.StaticBox(self, wx.ID_ANY, "Endstop Clearance") kh = 'ENDSTOP_CLEARANCE' ht = None if kh in helpText.keys(): ht = helpText[kh] sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k in self.eclKeys: tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (3, 1)) b = wx.StaticBox(self, wx.ID_ANY, "Travel Limits") kh = 'MINMAX' ht = None if kh in helpText.keys(): ht = helpText[kh] sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k in self.minmaxKeys: tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (3, 3)) cb = self.addCheckBox('E_ABSOLUTE', None, self.onCheckBox) sz.Add(cb, pos = (3, 7), flag = wx.ALIGN_CENTER_VERTICAL) bsz = wx.BoxSizer(wx.VERTICAL) b = wx.Button(self, wx.ID_ANY, "Calculate\nBelt Driven", size = BSIZE) b.SetToolTipString("Open the calculator for Axes that are belt-driven") bsz.Add(b, 1, wx.ALL, 5) b = wx.Button(self, wx.ID_ANY, "Calculate\nScrew Driven", size = BSIZE) bsz.Add(b, 1, wx.ALL, 5) b.SetToolTipString("Open the calculator for Axes that are screw-driven") sz.Add(bsz, pos = (1, 3)) self.SetSizer(sz) def insertValues(self, cfgValues): self.assertValid(True) for k in self.fieldValid.keys(): self.fieldValid[k] = True for k in self.textControls.keys(): if k in cfgValues.keys(): self.textControls[k].SetValue(cfgValues[k]) else: self.textControls[k].SetValue("") for k in self.checkBoxes.keys(): if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) self.assertModified(False) def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 1. MECHANICAL/HARDWARE *\n\ * *\n\ \\***************************************************************************/\n") for k in self.spmKeys + self.mfrKeys + self.msrKeys + self.eclKeys + \ self.minmaxKeys: v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) k = 'E_ABSOLUTE' cb = self.checkBoxes[k] if cb.IsChecked(): fp.write(defineBoolFormat % k) class AccelerationPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.parent = parent self.id = idPg self.accTypeKeys = ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', 'ACCELERATION_TEMPORAL'] self.jerkKeys = ['MAX_JERK_X', 'MAX_JERK_Y', 'MAX_JERK_Z', 'MAX_JERK_E'] self.labels = {'ACCELERATION_REPRAP': "RepRap", 'ACCELERATION_RAMPING': "Ramping", 'ACCELERATION_TEMPORAL': "Temporal", 'ACCELERATION' : "Acceleration:", 'LOOKAHEAD': "Look Ahead", 'MAX_JERK_X': "X:", 'MAX_JERK_Y': "Y:", 'MAX_JERK_Z': "Z:", 'MAX_JERK_E': "E:"} sz = wx.GridBagSizer() sz.AddSpacer((20, 40), pos = (0, 0)) b = wx.StaticBox(self, wx.ID_ANY, "Acceleration Type:") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) style = wx.RB_GROUP for k in self.accTypeKeys: rb = self.addRadioButton(k, style, None, self.onAccTypeSelect) style = 0 sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) sbox.AddSpacer((5, 5)) rb = wx.RadioButton(self, wx.ID_ANY, "None", style = style) rb.SetValue(True) self.Bind(wx.EVT_RADIOBUTTON, self.onAccTypeSelect, rb) sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 1)) b = wx.StaticBox(self, wx.ID_ANY, "Ramping Parameters:") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) k = 'ACCELERATION' tc = self.addTextCtrl(k, 80, None, self.onTextCtrlInteger) self.textControls[k].Enable(False) sbox.Add(tc) sbox.AddSpacer((5, 5)) k = 'LOOKAHEAD' cb = self.addCheckBox(k, None, self.onCheckBox) self.checkBoxes[k].Enable(False) sbox.Add(cb, 1, wx.ALIGN_CENTER_HORIZONTAL) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 3)) b = wx.StaticBox(self, wx.ID_ANY, "Maximum Jerk") kh = 'MAX_JERK' ht = None if kh in helpText.keys(): ht = helpText[kh] sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k in self.jerkKeys: tc = self.addTextCtrl(k, 40, ht, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.AddSpacer((80, 20), pos = (1, 4)) sz.Add(sbox, pos = (1, 5)) self.SetSizer(sz) def onAccTypeSelect(self, evt): self.assertModified(True) rb = evt.GetEventObject() label = rb.GetLabel() if label == self.labels['ACCELERATION_RAMPING']: ena = True else: ena = False self.checkBoxes['LOOKAHEAD'].Enable(ena) self.textControls['ACCELERATION'].Enable(ena) evt.Skip() def insertValues(self, cfgValues): self.assertValid(True) for k in self.fieldValid.keys(): self.fieldValid[k] = True self.checkBoxes['LOOKAHEAD'].Enable(False) self.textControls['ACCELERATION'].Enable(False) for k in self.textControls.keys(): if k in cfgValues.keys(): self.textControls[k].SetValue(cfgValues[k]) else: self.textControls[k].SetValue("") for tag in ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', 'ACCELERATION_TEMPORAL']: if tag in cfgValues.keys() and cfgValues[tag]: self.radioButtons[tag].SetValue(True) if tag == 'ACCELERATION_RAMPING': self.checkBoxes['LOOKAHEAD'].Enable(True) self.textControls['ACCELERATION'].Enable(True) k = 'LOOKAHEAD' if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) self.assertModified(False) def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 2. ACCELERATION *\n\ * *\n\ \\***************************************************************************/\n") for tag in ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', 'ACCELERATION_TEMPORAL']: rb = self.radioButtons[tag] if rb.GetValue(): fp.write(defineBoolFormat % tag) k = 'ACCELERATION' v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) k = 'LOOKAHEAD' cb = self.checkBoxes[k] if cb.IsChecked(): fp.write(defineBoolFormat % k) for k in ['MAX_JERK_X', 'MAX_JERK_Y', 'MAX_JERK_Z', 'MAX_JERK_E']: v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) class PinoutsPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.parent = parent self.id = idPg self.pinXKeys = [('X_STEP_PIN', 0), ('X_DIR_PIN', 0), ('X_MIN_PIN', 0), ('X_MAX_PIN', 0), ('X_ENABLE_PIN', 0), ('X_INVERT_DIR', 1), ('X_INVERT_MIN', 1), ('X_INVERT_MAX', 1), ('X_INVERT_ENABLE', 1)] self.pinYKeys = [('Y_STEP_PIN', 0), ('Y_DIR_PIN', 0), ('Y_MIN_PIN', 0), ('Y_MAX_PIN', 0), ('Y_ENABLE_PIN', 0), ('Y_INVERT_DIR', 1), ('Y_INVERT_MIN', 1), ('Y_INVERT_MAX', 1), ('Y_INVERT_ENABLE', 1)] self.pinZKeys = [('Z_STEP_PIN', 0), ('Z_DIR_PIN', 0), ('Z_MIN_PIN', 0), ('Z_MAX_PIN', 0), ('Z_ENABLE_PIN', 0), ('Z_INVERT_DIR', 1), ('Z_INVERT_MIN', 1), ('Z_INVERT_MAX', 1), ('Z_INVERT_ENABLE', 1)] self.pinEKeys = [('E_STEP_PIN', 0), ('E_DIR_PIN', 0), ('E_ENABLE_PIN', 0), ('E_INVERT_DIR', 1), ('E_INVERT_ENABLE', 1)] self.pinMiscKeys = [('USE_INTERNAL_PULLUPS', 1), ('TX_ENABLE_PIN', 0), ('RX_ENABLE_PIN', 0), ('PS_ON_PIN', 0), ('PS_MOSFET_PIN', 0), ('STEPPER_ENABLE_PIN', 0), ('STEPPER_INVERT_ENABLE', 1), ('SD_CARD_DETECT', 0), ('SD_WRITE_PROTECT', 0), ('DEBUG_LED_PIN', 0)] self.labels = {'INCLUDE_ARDUINO': "Include arduino.h header file", 'USE_INTERNAL_PULLUPS': "Use Internal Pullups", 'TX_ENABLE_PIN': "Tx Enable Pin:", 'RX_ENABLE_PIN': "Rx Enable Pin:", 'X_STEP_PIN': "Step Pin:", 'X_DIR_PIN': "Direction Pin:", 'X_MIN_PIN': "Minimum Pin:", 'X_MAX_PIN': "Maximum Pin:", 'X_ENABLE_PIN': "Enable Pin:", 'X_INVERT_DIR': "Invert Direction", 'X_INVERT_MIN': "Invert Minimum", 'X_INVERT_MAX': "Invert Maximum", 'X_INVERT_ENABLE': "Invert Enable", 'Y_STEP_PIN': "Step Pin:", 'Y_DIR_PIN': "Direction Pin:", 'Y_MIN_PIN': "Minimum Pin:", 'Y_MAX_PIN': "Maximum Pin:", 'Y_ENABLE_PIN': "Enable Pin:", 'Y_INVERT_DIR': "Invert Direction", 'Y_INVERT_MIN': "Invert Minimum", 'Y_INVERT_MAX': "Invert Maximum", 'Y_INVERT_ENABLE': "Invert Enable", 'Z_STEP_PIN': "Step Pin:", 'Z_DIR_PIN': "Direction Pin:", 'Z_MIN_PIN': "Minimum Pin:", 'Z_MAX_PIN': "Maximum Pin:", 'Z_ENABLE_PIN': "Enable Pin:", 'Z_INVERT_DIR': "Invert Direction", 'Z_INVERT_MIN': "Invert Minimum", 'Z_INVERT_MAX': "Invert Maximum", 'Z_INVERT_ENABLE': "Invert Enable", 'E_STEP_PIN': "Step Pin:", 'E_DIR_PIN': "Direction Pin:", 'E_ENABLE_PIN': "Enable Pin:", 'E_INVERT_DIR': "Invert Direction", 'E_INVERT_ENABLE': "Invert Enable", 'PS_ON_PIN': "PSU On Pin:", 'PS_MOSFET_PIN': "PSU MOSFET Pin:", 'STEPPER_ENABLE_PIN': "Stepper Enable Pin:", 'STEPPER_INVERT_ENABLE': "Stepper Invert Enable", 'SD_CARD_DETECT': "SD Detect Pin:", 'SD_WRITE_PROTECT': "Write Protect Pin:", 'DEBUG_LED_PIN': "Debug LED Pin:"} labelWidth = 80 labelWidthM = 100 self.includeArduino = False sz = wx.GridBagSizer() sz.AddSpacer((10, 10), pos = (0, 0)) b = wx.StaticBox(self, wx.ID_ANY, "X Axis") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k, ctype in self.pinXKeys: if ctype == 0: tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) sbox.Add(tc) else: cb = self.addCheckBox(k, None, self.onCheckBox) sbox.Add(cb, 1, wx.LEFT, 30) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 1)) b = wx.StaticBox(self, wx.ID_ANY, "Y Axis") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k, ctype in self.pinYKeys: if ctype == 0: tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) sbox.Add(tc) else: cb = self.addCheckBox(k, None, self.onCheckBox) sbox.Add(cb, 1, wx.LEFT, 30) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 3)) b = wx.StaticBox(self, wx.ID_ANY, "Z Axis") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k, ctype in self.pinZKeys: if ctype == 0: tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) sbox.Add(tc) else: cb = self.addCheckBox(k, None, self.onCheckBox) sbox.Add(cb, 1, wx.LEFT, 30) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 5)) b = wx.StaticBox(self, wx.ID_ANY, "E Axis") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) for k, ctype in self.pinEKeys: if ctype == 0: tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) sbox.Add(tc) else: cb = self.addCheckBox(k, None, self.onCheckBox) sbox.Add(cb, 1, wx.LEFT, 30) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 7)) k = 'USE_INTERNAL_PULLUPS' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) k = 'TX_ENABLE_PIN' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (5, 1)) k = 'RX_ENABLE_PIN' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (6, 1)) k = 'PS_ON_PIN' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (3, 3)) k = 'PS_MOSFET_PIN' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (4, 3)) k = 'STEPPER_ENABLE_PIN' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (6, 3)) k = 'STEPPER_INVERT_ENABLE' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (7, 3), flag = wx.ALIGN_CENTER_HORIZONTAL) k = 'SD_CARD_DETECT' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (3, 5)) k = 'SD_WRITE_PROTECT' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (4, 5)) k = 'DEBUG_LED_PIN' tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) sz.Add(tc, pos = (3, 7)) k = 'INCLUDE_ARDUINO' cb = self.addCheckBox(k, None, self.onCheckBoxArduino) sz.Add(cb, pos = (5, 7), flag = wx.ALIGN_CENTER_HORIZONTAL) self.SetSizer(sz) def revalidatePins(self): for k in self.textControls.keys(): self.validatePin(self.textControls[k]) self.parent.revalidatePins(self.includeArduino) def onCheckBoxArduino(self, evt): self.assertModified(True) cb = evt.GetEventObject() self.includeArduino = cb.IsChecked() self.revalidatePins() evt.Skip() def setIncludeArduino(self, flag): self.includeArduino = flag def insertValues(self, cfgValues): self.assertValid(True) for k in self.fieldValid.keys(): self.fieldValid[k] = True for k in self.textControls.keys(): if k in cfgValues.keys(): self.textControls[k].SetValue(cfgValues[k]) else: self.textControls[k].SetValue("") for k in self.checkBoxes.keys(): if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) elif (k == 'INCLUDE_ARDUINO') and self.includeArduino: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) self.revalidatePins() self.assertModified(False) def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 3. PINOUTS *\n\ * *\n\ \\***************************************************************************/\n") if self.includeArduino: fp.write("\n#include \"arduino.h\"\n\n") for k, cttype in self.pinXKeys + self.pinYKeys + self.pinZKeys + \ self.pinEKeys + self.pinMiscKeys: if cttype == 1: if self.checkBoxes[k].IsChecked(): fp.write(defineBoolFormat % k) else: v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) class SensorsPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.parent = parent self.id = idPg self.includeArduino = False self.sensorTypeKeys = ['TEMP_MAX6675', 'TEMP_THERMISTOR', 'TEMP_AD595', 'TEMP_PT100', 'TEMP_INTERCOM'] self.sensorType = {'TEMP_MAX6675': 'TT_MAX6675', 'TEMP_THERMISTOR': 'TT_THERMISTOR', 'TEMP_AD595': 'TT_AD595', 'TEMP_PT100': 'TT_PT100', 'TEMP_INTERCOM': 'TT_INTERCOM'} self.labels = {'TEMP_HYSTERESIS': "Temperature hysteresis:", 'TEMP_RESIDENCY_TIME': "Temperature residency time:", 'TEMP_EWMA': "Temperature EMWA:", 'TEMP_MAX6675': "MAX6675", 'TEMP_THERMISTOR': "Thermistor", 'TEMP_AD595': 'AD595', 'TEMP_PT100': "PT100", 'TEMP_INTERCOM': "Intercom"} labelWidth = 160 sz = wx.GridBagSizer() sz.AddSpacer((30, 30), pos = (0, 0)) self.sensors = [] b = wx.StaticBox(self, wx.ID_ANY, "Sensor Types") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) ht = None if 'TEMP_TYPES' in helpText.keys(): ht = helpText['TEMP_TYPES'] for k in self.sensorTypeKeys: cb = self.addCheckBox(k, ht, self.onCheckBoxSensorType) sbox.Add(cb, 1, wx.LEFT + wx.RIGHT, 10) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (1, 1), span = (5, 1)) k = 'TEMP_HYSTERESIS' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) sz.Add(tc, pos = (1, 3)) k = 'TEMP_RESIDENCY_TIME' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) sz.Add(tc, pos = (3, 3)) k = 'TEMP_EWMA' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlEWMA) sz.Add(tc, pos = (5, 3)) self.lb = SensorList(self) sz.Add(self.lb, pos = (7, 1), span = (1, 3)) bsz = wx.BoxSizer(wx.VERTICAL) self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL) self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd) self.bAdd.Enable(False) if 'ADDSENSOR' in helpText.keys(): self.bAdd.SetToolTipString(helpText['ADDSENSOR']) bsz.Add(self.bAdd) bsz.AddSpacer((10, 10)) self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL) self.bDelete.Enable(False) self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete) bsz.Add(self.bDelete) if 'DELSENSOR' in helpText.keys(): self.bDelete.SetToolTipString(helpText['DELSENSOR']) sz.Add(bsz, pos = (7, 4)) self.SetSizer(sz) def onCheckBoxSensorType(self, evt): self.assertModified(True) ct = 0 for tt in self.sensorTypeKeys: if self.checkBoxes[tt].IsChecked(): ct += 1 if ct == 0: self.bAdd.Enable(False) else: self.bAdd.Enable(True) evt.Skip() def onTextCtrlEWMA(self, evt): self.assertModified(True) tc = evt.GetEventObject() name = tc.GetName() w = tc.GetValue().strip() if w == "": valid = True else: m = reFloat.match(w) if m: v = float(w) if v < 0.1 or v > 1.0: valid = False else: valid = True else: valid = False self.setFieldValidity(name, valid) if valid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() evt.Skip() def revalidatePins(self, flag): self.includeArduino = flag tableValid = True for i in range(len(self.sensors)): pn = self.sensors[i][2] if pn == "": valid = False else: m = reInteger.match(pn) if m: valid = True else: if self.includeArduino: m = rePin.match(pn) if m: valid = True else: valid = False else: valid = False self.lb.setItemValidity(i, valid) if not valid: tableValid = False self.setFieldValidity('SENSORS', tableValid) def setItemSelected(self, n): self.selection = n if n is None: self.bDelete.Enable(False) else: self.bDelete.Enable(True) def doAdd(self, evt): sl = [] for tt in self.sensorTypeKeys: if self.checkBoxes[tt].IsChecked(): sl.append(self.sensorType[tt]) nm = [] for s in self.sensors: nm.append(s[0]) dlg = AddSensorDlg(self, nm, sl, self.includeArduino) rc = dlg.ShowModal() if rc == wx.ID_OK: tt = dlg.getValues() dlg.Destroy() if rc != wx.ID_OK: return self.sensors.append(tt) self.lb.updateList(self.sensors) self.limitSensorTypeControls() def doDelete(self, evt): if self.selection is None: return self.assertModified(True) del self.sensors[self.selection] self.lb.updateList(self.sensors) self.limitSensorTypeControls() def limitSensorTypeControls(self): using = {} for s in self.sensors: using[s[1]] = True for k in self.sensorTypeKeys: self.checkBoxes[k].Enable(True) for tt in using.keys(): if not tt.startswith("TT_"): continue k = "TEMP_" + tt[3:] if k in self.sensorTypeKeys: self.checkBoxes[k].Enable(False) def insertValues(self, cfgValues): for k in self.textControls.keys(): if k in cfgValues.keys(): self.textControls[k].SetValue(cfgValues[k]) else: self.textControls[k].SetValue("") for k in self.checkBoxes.keys(): if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) ct = 0 for k in self.sensorTypeKeys: if self.checkBoxes[k].IsChecked(): ct += 1 self.bAdd.Enable(ct != 0) self.assertModified(False) def setSensors(self, sensors): self.sensors = sensors self.lb.updateList(self.sensors) self.revalidatePins(self.includeArduino) self.limitSensorTypeControls() def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 4. TEMPERATURE SENSORS *\n\ * *\n\ \\***************************************************************************/\n") for k in self.checkBoxes.keys(): if self.checkBoxes[k].IsChecked(): fp.write(defineBoolFormat % k) for k in self.textControls.keys(): v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) for s in self.sensors: sstr = ",".join(s) fp.write("DEFINE_TEMP_SENSOR(" + sstr + ")\n") class SensorList(wx.ListCtrl): def __init__(self, parent): self.parent = parent self.currentItem = None wx.ListCtrl.__init__(self, parent, wx.ID_ANY, size = (495 + 4, 100), style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES) self.valid = [] self.InsertColumn(0, "Name") self.InsertColumn(1, "Sensor Type") self.InsertColumn(2, "Pin") self.InsertColumn(3, "Additional") self.SetColumnWidth(0, 55) self.SetColumnWidth(1, 105) self.SetColumnWidth(2, 55) self.SetColumnWidth(3, 280) self.SetItemCount(0) self.attr2 = wx.ListItemAttr() self.attr2.SetBackgroundColour("light blue") self.attr3 = wx.ListItemAttr() self.attr3.SetBackgroundColour("pink") self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) def updateList(self, sensorList): self.sensorList = sensorList self.valid = [True] * len(sensorList) self.currentItem = None self.parent.setItemSelected(None) i = self.GetFirstSelected() while i != -1: self.Select(i, False) i = self.GetFirstSelected() self.SetItemCount(len(sensorList)) def setItemValidity(self, i, flag = False): if i < 0 or i >= len(self.sensorList): return self.valid[i] = flag self.Refresh() def OnItemSelected(self, event): self.currentItem = event.m_itemIndex self.parent.setItemSelected(self.currentItem) def OnItemDeselected(self, event): self.currentItem = None self.parent.setItemSelected(None) def getColumnText(self, index, col): item = self.GetItem(index, col) return item.GetText() def OnGetItemText(self, item, col): if item < 0 or item >= len(self.sensorList): return "Error - no sensors" s = self.sensorList[item] if col == 0: return s[0] elif col == 1: return s[1] elif col == 2: return s[2] elif len(s) == 3: return "" else: return s[3] def OnGetItemAttr(self, item): if not self.valid[item]: return self.attr3 if item % 2 == 1: return self.attr2 else: return None class AddSensorDlg(wx.Dialog): def __init__(self, parent, names, sl, arduinoFlag): wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add Temperature Sensor", size = (400, 204)) self.Bind(wx.EVT_CLOSE, self.onCancel) self.names = names self.arduinoFlag = arduinoFlag self.nameValid = False self.pinValid = False sz = wx.BoxSizer(wx.VERTICAL) csz = wx.GridBagSizer() csz.AddSpacer((10, 10), pos = (0, 0)) b = wx.StaticBox(self, wx.ID_ANY, "Sensor Type:") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) style = wx.RB_GROUP self.rbs = {} for k in sl: rb = wx.RadioButton(self, wx.ID_ANY, k, style = style) self.rbs[k] = rb self.Bind(wx.EVT_RADIOBUTTON, self.onSensorType, rb) if k in helpText.keys(): hts = helpText[k] else: hts = "" rb.SetToolTipString(hts) style = 0 sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) sbox.AddSpacer((5, 5)) csz.Add(sbox, pos = (1, 3)) vsz = wx.BoxSizer(wx.VERTICAL) vsz.AddSpacer((10, 10)) lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, "Sensor Name:", size = (80, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) self.tcName = wx.TextCtrl(self, wx.ID_ANY, "") self.tcName.SetBackgroundColour("pink") self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry) lsz.Add(self.tcName) self.tcName.SetToolTipString("Enter a unique name for this sensor") vsz.Add(lsz) lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) self.tcPin = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT) self.tcPin.Bind(wx.EVT_TEXT, self.onPinEntry) self.tcPin.SetBackgroundColour("pink") lsz.Add(self.tcPin) self.tcPin.SetToolTipString("Enter a pin number/name for this sensor") vsz.Add(lsz) lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, "Additional:", size = (80, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) self.tcAddtl = wx.TextCtrl(self, wx.ID_ANY, "") self.tcAddtl.Bind(wx.EVT_TEXT, self.onAddtlEntry) self.selectSensorType(sl[0]) lsz.Add(self.tcAddtl) self.tcAddtl.SetToolTipString("Enter additional information required by " "the sensor type") vsz.Add(lsz) csz.Add(vsz, pos = (1, 1)) csz.AddSpacer((10, 10), pos = (1, 4)) sz.Add(csz) sz.AddSpacer((30, 30)) bsz = wx.BoxSizer(wx.HORIZONTAL) self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL) self.bSave.Bind(wx.EVT_BUTTON, self.onSave) bsz.Add(self.bSave) self.bSave.Enable(False) bsz.AddSpacer(30, 100) self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL) self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel) bsz.Add(self.bCancel) sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) self.SetSizer(sz) def onNameEntry(self, evt): tc = evt.GetEventObject() w = tc.GetValue().strip() if w == "": self.nameValid = False else: if w in self.names: self.nameValid = False else: self.nameValid = True if self.nameValid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() self.checkDlgValidity() evt.Skip() def onPinEntry(self, evt): tc = evt.GetEventObject() self.validatePin(tc) self.checkDlgValidity() evt.Skip() def validatePin(self, tc): w = tc.GetValue().strip() if w == "": self.pinValid = False else: m = reInteger.match(w) if m: self.pinValid = True else: if self.arduinoFlag: m = rePin.match(w) if m: self.pinValid = True else: self.pinValid = False else: self.pinValid = False if self.pinValid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() def checkDlgValidity(self): if self.nameValid and self.pinValid: self.bSave.Enable(True) else: self.bSave.Enable(False) def onAddtlEntry(self, evt): evt.Skip() def selectSensorType(self, lbl): if lbl == 'TT_THERMISTOR': self.tcAddtl.Enable(True); else: self.tcAddtl.Enable(False); def onSensorType(self, evt): rb = evt.GetEventObject() label = rb.GetLabel() self.selectSensorType(label) evt.Skip() def getValues(self): nm = self.tcName.GetValue() pin = self.tcPin.GetValue() addtl = self.tcAddtl.GetValue() for k in self.rbs: if self.rbs[k].GetValue(): stype = k break if stype is None: stype = "??" if stype in ['TT_THERMISTOR']: return (nm, stype, pin, addtl) else: return (nm, stype, pin) def onSave(self, evt): self.EndModal(wx.ID_OK) def onCancel(self, evt): self.EndModal(wx.ID_CANCEL) class HeatersPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.parent = parent self.id = idPg self.includeArduino = False self.labels = {'HEATER_SANITY_CHECK': "Heater Sanity Check"} sz = wx.GridBagSizer() sz.AddSpacer((30, 30), pos = (0, 0)) self.heaters = [] k = 'HEATER_SANITY_CHECK' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) self.lb = HeaterList(self) sz.Add(self.lb, pos = (1, 1), span = (1, 3)) bsz = wx.BoxSizer(wx.VERTICAL) self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL) self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd) if 'ADDHEATER' in helpText.keys(): self.bAdd.SetToolTipString(helpText['ADDHEATER']) bsz.Add(self.bAdd) bsz.AddSpacer((10, 10)) self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL) self.bDelete.Enable(False) self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete) bsz.Add(self.bDelete) if 'DELHEATER' in helpText.keys(): self.bDelete.SetToolTipString(helpText['DELHEATER']) sz.Add(bsz, pos = (1, 4)) self.SetSizer(sz) def revalidatePins(self, flag): self.includeArduino = flag tableValid = True for i in range(len(self.heaters)): pn = self.heaters[i][1] if pn == "": valid = False else: m = reInteger.match(pn) if m: valid = True else: if self.includeArduino: m = rePin.match(pn) if m: valid = True else: valid = False else: valid = False self.lb.setItemValidity(i, valid) if not valid: tableValid = False self.setFieldValidity('HEATERS', tableValid) def setItemSelected(self, n): self.selection = n if n is None: self.bDelete.Enable(False) else: self.bDelete.Enable(True) def doAdd(self, evt): nm = [] for s in self.heaters: nm.append(s[0]) dlg = AddHeaterDlg(self, nm, self.includeArduino) rc = dlg.ShowModal() if rc == wx.ID_OK: ht = dlg.getValues() dlg.Destroy() if rc != wx.ID_OK: return self.heaters.append(ht) self.lb.updateList(self.heaters) self.revalidatePins(self.includeArduino) self.parent.setHeaters(self.heaters) def doDelete(self, evt): if self.selection is None: return self.assertModified(True) del self.heaters[self.selection] self.lb.updateList(self.heaters) self.revalidatePins(self.includeArduino) self.parent.setHeaters(self.heaters) def insertValues(self, cfgValues): for k in self.checkBoxes.keys(): if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) self.assertModified(False) def setHeaters(self, heaters): self.heaters = heaters self.lb.updateList(self.heaters) self.revalidatePins(self.includeArduino) def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 5. HEATERS *\n\ * *\n\ \\***************************************************************************/\n") for k in self.checkBoxes.keys(): if self.checkBoxes[k].IsChecked(): fp.write(defineBoolFormat % k) names = [] for s in self.heaters: names.append(s[0]) sstr = ",".join(s) fp.write("DEFINE_HEATER(" + sstr + ")\n") for n in names: if n != n.upper(): fp.write(defineHeaterFormat % (n.upper(), n)) class HeaterList(wx.ListCtrl): def __init__(self, parent): self.parent = parent self.currentItem = None wx.ListCtrl.__init__(self, parent, wx.ID_ANY, size = (165 + 4, 100), style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES) self.valid = [] self.InsertColumn(0, "Name") self.InsertColumn(1, "Pin") self.InsertColumn(2, "PWM") self.SetColumnWidth(0, 55) self.SetColumnWidth(1, 55) self.SetColumnWidth(2, 55) self.SetItemCount(0) self.attr2 = wx.ListItemAttr() self.attr2.SetBackgroundColour("light blue") self.attr3 = wx.ListItemAttr() self.attr3.SetBackgroundColour("pink") self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) def updateList(self, heaterList): self.heaterList = heaterList self.valid = [True] * len(heaterList) self.currentItem = None self.parent.setItemSelected(None) i = self.GetFirstSelected() while i != -1: self.Select(i, False) i = self.GetFirstSelected() self.SetItemCount(len(heaterList)) def setItemValidity(self, i, flag = False): if i < 0 or i >= len(self.heaterList): return self.valid[i] = flag self.Refresh() def OnItemSelected(self, event): self.currentItem = event.m_itemIndex self.parent.setItemSelected(self.currentItem) def OnItemDeselected(self, event): self.currentItem = None self.parent.setItemSelected(None) def getColumnText(self, index, col): item = self.GetItem(index, col) return item.GetText() def OnGetItemText(self, item, col): if item < 0 or item >= len(self.heaterList): return "Error - no heaters" s = self.heaterList[item] if col == 0: return s[0] elif col == 1: return s[1] elif col == 2: if s[2] == "1": return "True" else: return "False" def OnGetItemAttr(self, item): if not self.valid[item]: return self.attr3 if item % 2 == 1: return self.attr2 else: return None class AddHeaterDlg(wx.Dialog): def __init__(self, parent, names, arduinoFlag): wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add Heater", size = (400, 204)) self.Bind(wx.EVT_CLOSE, self.onCancel) self.names = names self.arduinoFlag = arduinoFlag self.nameValid = False self.pinValid = False sz = wx.BoxSizer(wx.VERTICAL) gsz = wx.GridBagSizer() gsz.AddSpacer((20, 20), pos = (0, 0)) lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, "Heater Name:", size = (80, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) self.tcName = wx.TextCtrl(self, wx.ID_ANY, "") self.tcName.SetBackgroundColour("pink") self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry) lsz.Add(self.tcName) self.tcName.SetToolTipString("Enter a unique name for this heater") gsz.Add(lsz, pos = (1, 1)) lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1), style = wx.ALIGN_RIGHT) lsz.Add(st) self.tcPin = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT) self.tcPin.Bind(wx.EVT_TEXT, self.onPinEntry) self.tcPin.SetBackgroundColour("pink") lsz.Add(self.tcPin) self.tcPin.SetToolTipString("Enter a pin number/name for this heater") gsz.Add(lsz, pos = (3, 1)) self.cbPwm = wx.CheckBox(self, wx.ID_ANY, "PWM") self.cbPwm.SetToolTipString("Help for PWM") gsz.AddSpacer((50, 15), pos = (1, 2)) gsz.Add(self.cbPwm, pos = (1, 3)) gsz.AddSpacer((20, 20), pos = (4, 4)) sz.Add(gsz) sz.AddSpacer((30, 30)) bsz = wx.BoxSizer(wx.HORIZONTAL) self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL) self.bSave.Bind(wx.EVT_BUTTON, self.onSave) bsz.Add(self.bSave) self.bSave.Enable(False) bsz.AddSpacer(30, 100) self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL) self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel) bsz.Add(self.bCancel) sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) self.SetSizer(sz) def onNameEntry(self, evt): tc = evt.GetEventObject() w = tc.GetValue().strip() if w == "": self.nameValid = False else: if w in self.names: self.nameValid = False else: self.nameValid = True if self.nameValid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() self.checkDlgValidity() evt.Skip() def onPinEntry(self, evt): tc = evt.GetEventObject() self.validatePin(tc) self.checkDlgValidity() evt.Skip() def validatePin(self, tc): w = tc.GetValue().strip() if w == "": self.pinValid = False else: m = reInteger.match(w) if m: self.pinValid = True else: if self.arduinoFlag: m = rePin.match(w) if m: self.pinValid = True else: self.pinValid = False else: self.pinValid = False if self.pinValid: tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) else: tc.SetBackgroundColour("pink") tc.Refresh() def checkDlgValidity(self): if self.nameValid and self.pinValid: self.bSave.Enable(True) else: self.bSave.Enable(False) def getValues(self): nm = self.tcName.GetValue() pin = self.tcPin.GetValue() if self.cbPwm.IsChecked(): pwm = "1" else: pwm = "0" return (nm, pin, pwm) def onSave(self, evt): self.EndModal(wx.ID_OK) def onCancel(self, evt): self.EndModal(wx.ID_CANCEL) class CommunicationsPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.parent = parent self.id = idPg self.defaultBaud = '115200' self.bauds = ['19200', '38400', '57600', self.defaultBaud] self.labels = {'BAUD': "Baud Rate:", 'USB_SERIAL': "USB Serial", 'XONXOFF': "Use XON/XOFF Flow Control"} sz = wx.GridBagSizer() sz.AddSpacer((20, 40), pos = (0, 0)) ch = self.addChoice('BAUD', self.bauds, self.bauds.index(self.defaultBaud), 60, None, self.onChoice) sz.Add(ch, pos = (1, 1)) k = 'USB_SERIAL' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (3, 1)) k = 'XONXOFF' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (5, 1)) self.SetSizer(sz) def insertValues(self, cfgValues): self.assertValid(True) for k in self.fieldValid.keys(): self.fieldValid[k] = True for k in self.checkBoxes.keys(): if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) self.setChoice('BAUD', cfgValues, self.bauds, self.defaultBaud) self.assertModified(False) def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 6. COMMUNICATION *\n\ * *\n\ \\***************************************************************************/\n") k = 'BAUD' v = self.choices[k].GetSelection() if v != "": fp.write(defineValueFormat % (k, self.bauds[v])) for k in self.checkBoxes.keys(): cb = self.checkBoxes[k] if cb.IsChecked(): fp.write(defineBoolFormat % k) class MiscellaneousPage(wx.Panel, Page): def __init__(self, parent, nb, idPg): wx.Panel.__init__(self, nb, wx.ID_ANY) Page.__init__(self) self.parent = parent self.id = idPg self.labels = {'MOTHERBOARD': "Motherboard", 'F_CPU': "CPU Clock Rate:", 'EECONFIG': "Enable EEPROM Storage", 'DEBUG': "Turn on debugging", 'BANG_BANG': "Enable", 'BANG_BANG_ON': "On PWM level:", 'BANG_BANG_OFF': "Off PWM level:", 'MOVEBUFFER_SIZE': "Move buffer size:", 'DC_EXTRUDER': "Heater:", 'DC_EXTRUDER_PWM': "PWM:", 'USE_WATCHDOG': "Use the watchdog timer", 'REFERENCE': "Analog Reference:", 'STEP_INTERRUPT_INTERRUPTIBLE': "STEP Interrupt", 'TH_COUNT': "Temperature History size:", 'FAST_PWM': "Fast PWM", 'ENDSTOP_STEPS': "Endstop steps:", 'CANNED_CYCLE': "Canned Cycle:"} self.prefixKeys = ['MOTHERBOARD', 'F_CPU'] self.defaultClock = '16000000' self.clocks = ['8000000', self.defaultClock, '20000000'] self.heaterNameNone = "" self.heaterNames = [self.heaterNameNone] self.processors = [] self.defaultRef = 'REFERENCE_AVCC' self.references = [self.defaultRef, 'REFERENCE_AREF', 'REFERENCE_1V1', 'REFERENCE_2V56'] sz = wx.GridBagSizer() sz.AddSpacer((20, 40), pos = (0, 0)) sz.AddSpacer((40, 40), pos = (0, 2)) sz.AddSpacer((40, 40), pos = (0, 4)) sz.AddSpacer((20, 30), pos = (1, 0)) sz.AddSpacer((20, 30), pos = (2, 0)) sz.AddSpacer((20, 30), pos = (3, 0)) sz.AddSpacer((20, 30), pos = (4, 0)) sz.AddSpacer((20, 30), pos = (5, 0)) sz.AddSpacer((20, 30), pos = (6, 0)) labelWidth = 140 k = 'MOTHERBOARD' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (1, 1)) k = 'EECONFIG' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (2, 1)) k = 'DEBUG' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (3, 1)) k = 'USE_WATCHDOG' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (4, 1)) k = 'STEP_INTERRUPT_INTERRUPTIBLE' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (5, 1)) k = 'FAST_PWM' cb = self.addCheckBox(k, None, self.onCheckBox) sz.Add(cb, pos = (6, 1)) b = wx.StaticBox(self, wx.ID_ANY, "BANG BANG Bed Control") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) k = 'BANG_BANG' cb = self.addCheckBox(k, None, self.onCheckBox) sbox.Add(cb, 1, wx.LEFT, 60) sbox.AddSpacer((5, 20)) k = 'BANG_BANG_ON' tc = self.addTextCtrl(k, 80, None, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) k = 'BANG_BANG_OFF' tc = self.addTextCtrl(k, 80, None, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (7, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) k = 'F_CPU' ch = self.addChoice(k, self.clocks, self.clocks.index(self.defaultClock), labelWidth, None, self.onChoice) sz.Add(ch, pos = (1, 3)) k = 'REFERENCE' ch = self.addChoice(k, self.references, self.references.index(self.defaultRef), labelWidth, None, self.onChoice) sz.Add(ch, pos = (2, 3)) b = wx.StaticBox(self, wx.ID_ANY, "DC Motor Extruder") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((5, 5)) k = 'DC_EXTRUDER' ch = self.addChoice(k, self.heaterNames, 0, 60, None, self.onChoice) sbox.Add(ch) sbox.AddSpacer((5, 5)) k = 'DC_EXTRUDER_PWM' tc = self.addTextCtrl(k, 60, None, self.onTextCtrlInteger) sbox.Add(tc) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (7, 3), flag = wx.ALIGN_CENTER_HORIZONTAL) k = 'MOVEBUFFER_SIZE' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) sz.Add(tc, pos = (1, 5)) k = 'TH_COUNT' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) sz.Add(tc, pos = (2, 5)) k = 'ENDSTOP_STEPS' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) sz.Add(tc, pos = (3, 5)) k = 'CANNED_CYCLE' tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrl) sz.Add(tc, pos = (4, 5)) b = wx.StaticBox(self, wx.ID_ANY, "Processor Type(s):") sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox.AddSpacer((125, 5)) ht = "Choose the processor(s) this configuration will work with" for k in supportedCPUs: cb = self.addCheckBox(k, ht, self.onCheckBox) sbox.Add(cb) sbox.AddSpacer((5, 5)) sbox.AddSpacer((5, 5)) sz.Add(sbox, pos = (6, 5), span = (3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) self.SetSizer(sz) def setHeaters(self, hlist, cfgValues = None): k = 'DC_EXTRUDER' if cfgValues and k in cfgValues.keys(): currentChoice = cfgValues[k][len("HEATER_"):] else: v = self.choices[k].GetSelection() currentChoice = self.heaterNames[v] h = [s[0] for s in hlist] self.heaterNames = [self.heaterNameNone] + h self.choices[k].Clear() for h in self.heaterNames: self.choices[k].Append(h) try: v = self.heaterNames.index(currentChoice) except: v = 0 self.choices[k].SetSelection(v) def setProcessors(self, plist): self.processors = plist for p in supportedCPUs: if p in self.processors: self.checkBoxes[p].SetValue(True) else: self.checkBoxes[p].SetValue(False) def insertValues(self, cfgValues): self.assertValid(True) for k in self.fieldValid.keys(): self.fieldValid[k] = True for k in self.checkBoxes.keys(): if k in cfgValues.keys() and cfgValues[k]: self.checkBoxes[k].SetValue(True) else: self.checkBoxes[k].SetValue(False) for k in self.textControls.keys(): if k in cfgValues.keys(): self.textControls[k].SetValue(cfgValues[k]) else: self.textControls[k].SetValue("") self.setChoice('F_CPU', cfgValues, self.clocks, self.defaultClock) self.setChoice('REFERENCE', cfgValues, self.references, self.defaultRef) self.assertModified(False) def savePrefixValues(self, fp): if len(self.processors) != 0: nest = 0 for i in self.processors: fp.write("%s#ifndef __AVR_%s__\n" % (" " * nest, i)) nest += 1 fp.write("%s#error incorrect cpu type in Makefile!\n" % (" " * nest)) for i in self.processors: nest -= 1 fp.write("%s#endif\n" % (" " * nest)) fp.write("\n\n") for k in self.checkBoxes.keys(): if k not in self.prefixKeys: continue cb = self.checkBoxes[k] if cb.IsChecked(): fp.write(defineBoolFormat % k) for k in self.textControls.keys(): if k not in self.prefixKeys: continue v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) k = 'F_CPU' v = self.choices[k].GetSelection() fp.write(defineULFormat % (k, self.clocks[v])) def saveValues(self, fp): self.assertModified(False) fp.write( "\n/***************************************************************************\\\n\ * *\n\ * 7. MISCELLANEOUS *\n\ * *\n\ \\***************************************************************************/\n") for k in self.checkBoxes.keys(): if k in self.prefixKeys: continue cb = self.checkBoxes[k] if cb.IsChecked(): fp.write(defineBoolFormat % k) for k in self.textControls.keys(): if k in self.prefixKeys: continue v = self.textControls[k].GetValue() if v != "": fp.write(defineValueFormat % (k, v)) k = 'REFERENCE' v = self.choices[k].GetSelection() fp.write(defineValueFormat % (k, self.references[v])) k = 'DC_EXTRUDER' v = self.choices[k].GetSelection() if self.heaterNames[v] != self.heaterNameNone: fp.write(defineDCExtruderFormat % (k, self.heaterNames[v])) class MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Firmware Configurator", size = (980, 450)) self.Bind(wx.EVT_CLOSE, self.onClose) self.cfgValues = {} self.heaters = [] self.dir = "." panel = wx.Panel(self, -1) sz = wx.BoxSizer(wx.HORIZONTAL) bsz = wx.BoxSizer(wx.VERTICAL) self.bLoadConfig = wx.Button(panel, wx.ID_ANY, "Load\nConfig", size = BSIZE) panel.Bind(wx.EVT_BUTTON, self.onLoadConfig, self.bLoadConfig) bsz.Add(self.bLoadConfig) self.bLoadConfig.SetToolTipString("Choose a configuration file and load its contents") bsz.AddSpacer((5, 5)) self.bSaveConfig = wx.Button(panel, wx.ID_ANY, "Save\nConfig", size = BSIZE) panel.Bind(wx.EVT_BUTTON, self.onSaveConfig, self.bSaveConfig) bsz.Add(self.bSaveConfig) self.bSaveConfig.SetToolTipString("Save the current configuration into a C header file") self.nb = wx.Notebook(panel, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = MechanicalPage(self, self.nb, len(self.pages)) text = "Mechanical" self.nb.AddPage(self.pgMech, text) self.pages.append(self.pgMech) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgAcc = AccelerationPage(self, self.nb, len(self.pages)) text = "Acceleration" self.nb.AddPage(self.pgAcc, text) self.pages.append(self.pgAcc) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgPins = PinoutsPage(self, self.nb, len(self.pages)) text = "Pinouts" self.nb.AddPage(self.pgPins, text) self.pages.append(self.pgPins) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgSensors = SensorsPage(self, self.nb, len(self.pages)) text = "Temperature Sensors" self.nb.AddPage(self.pgSensors, text) self.pages.append(self.pgSensors) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgHeaters = HeatersPage(self, self.nb, len(self.pages)) text = "Heaters" self.nb.AddPage(self.pgHeaters, text) self.pages.append(self.pgHeaters) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages)) text = "Communications" self.nb.AddPage(self.pgCommunications, text) self.pages.append(self.pgCommunications) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages)) text = "Miscellaneous" self.nb.AddPage(self.pgMiscellaneous, text) self.pages.append(self.pgMiscellaneous) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) sz.Add(bsz, 0, wx.ALL, 5) panel.SetSizer(sz) def buildHeaterPage(self, nb): pg = wx.Panel(nb, wx.ID_ANY) return pg def buildCommPage(self, nb): pg = wx.Panel(nb, wx.ID_ANY) return pg def buildMiscPage(self, nb): pg = wx.Panel(nb, wx.ID_ANY) return pg def assertModified(self, pg, flag = True): self.pageModified[pg] = flag self.modifyTab(pg) def assertValid(self, pg, flag = True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.bSaveConfig.Enable(False) else: self.bSaveConfig.Enable(True) def modifyTab(self, pg): if self.pageModified[pg] and not self.pageValid[pg]: pfx = "?* " elif self.pageModified[pg]: pfx = "* " elif not self.pageValid[pg]: pfx = "? " else: pfx = "" self.nb.SetPageText(pg, pfx + self.titles[pg]) def revalidatePins(self, flag): self.pgSensors.revalidatePins(flag) self.pgHeaters.revalidatePins(flag) def setHeaters(self, hlist): self.pgMiscellaneous.setHeaters(hlist) def onClose(self, evt): if not self.confirmLoseChanges("Exit"): return self.Destroy() def confirmLoseChanges(self, msg): if True not in self.pageModified: return True dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\nThere are changes that will be lost.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return False return True def onLoadConfig(self, evt): if not self.confirmLoseChanges("Load a new Configuration"): return wildcard = "C Header files (*.h)|*.h" dlg = wx.FileDialog(self, message = "Choose a Config file", defaultDir = self.dir, defaultFile = "", wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR) path = None if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() dlg.Destroy() if path is None: return self.dir = os.path.dirname(path) self.loadConfigFile(path) for pg in self.pages: pg.insertValues(self.cfgValues) self.pgSensors.setSensors(self.sensors) self.pgHeaters.setHeaters(self.heaters) self.pgMiscellaneous.setHeaters(self.heaters, self.cfgValues) self.pgMiscellaneous.setProcessors(self.processors) def loadConfigFile(self, fn): try: lst = list(open(fn)) except: return False self.pgPins.setIncludeArduino(False) self.sensors = [] self.heaters = [] self.processors = [] self.cfgValues = {} self.cfgValues['E_ABSOLUTE'] = False prevLines = "" for ln in lst: if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if ln.lstrip().startswith("//"): continue if ln.lstrip().startswith("#if"): m = re.findall(reAVR, ln) inv = [] for p in m: if p in supportedCPUs: self.processors.append(p) else: inv.append(p) if len(inv) > 0: if len(inv) == 1: a = " is" b = "it" else: a = "s are" b = "them" dlg = wx.MessageDialog(self, "The following processor type %s not supported:\n %s\n\ Please add %s to \"supportedCPUs\"" % (a, ", ".join(inv), b), "Unsupported Processor Type(s)", wx.OK + wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() continue if ln.lstrip().startswith("#define"): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: self.cfgValues[t[0]] = t[1] continue m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2: self.cfgValues[t[0]] = t[1] continue m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1: self.cfgValues[t[0]] = True else: m = reDefTS.search(ln) if m: t = m.groups() if len(t) == 1: s = self.parseSensor(t[0]) if s: self.sensors.append(s) continue m = reDefHT.search(ln) if m: t = m.groups() if len(t) == 1: s = self.parseHeater(t[0]) if s: self.heaters.append(s) continue m = reIncArduino.search(ln) if m: self.pgPins.setIncludeArduino(True) return True def parseSensor(self, s): m = reSensor4.search(s) if m: t = m.groups() if len(t) == 4: return t m = reSensor3.search(s) if m: t = m.groups() if len(t) == 3: return t return None def parseHeater(self, s): m = reHeater.search(s) if m: t = m.groups() if len(t) == 3: return t return None def onSaveConfig(self, evt): wildcard = "C Header files (*.h)|*.h" dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir, defaultFile = "", wildcard = wildcard, style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) val = dlg.ShowModal() if val != wx.ID_OK: dlg.Destroy() return path = dlg.GetPath() dlg.Destroy() ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" fp = file(path, 'w') timestamp = time.strftime("%Y-%b-%d %H:%M:%S", time.localtime()) fp.write("// Generated by Teacup Configurator version %s - %s\n\n" % (VERSION, timestamp)) self.pgMiscellaneous.savePrefixValues(fp) for pg in self.pages: pg.saveValues(fp) fp.close() dlg = wx.MessageDialog(self, "File \"%s\" successfully written" % path, "Save Successful", wx.OK + wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() if __name__ == '__main__': app = wx.PySimpleApp() frame = MyFrame() frame.Show(True) app.MainLoop()