def __init__(self, font): self.modified = False self.valid = True self.fieldValid = {} self.textControls = {} self.checkBoxes = {} self.radioButtons = {} self.radioButtonBoxes = {} self.choices = {} self.deco = Decoration() self.font = font self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Configtool", size=(880, 550)) self.Bind(wx.EVT_CLOSE, self.onClose) self.Bind(wx.EVT_SIZE, self.onResize) self.deco = Decoration() panel = wx.Panel(self, -1) panel.SetBackgroundColour(self.deco.getBackgroundColour()) panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.settings = Settings(self, cmd_folder) self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) self.settings.folder = cmd_folder self.heaters = [] self.savePrtEna = False self.saveBrdEna = False self.protPrtFile = False self.protBrdFile = False sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(panel, wx.ID_ANY, size=(880, 550), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.printerFileName = None self.printerTabDecor = "" self.printerBaseText = "Printer" self.pgPrinter = PrinterPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgPrinter, self.printerBaseText) self.boardFileName = None self.boardTabDecor = "" self.boardBaseText = "Board" self.pgBoard = BoardPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgBoard, self.boardBaseText) panel.Fit() self.panel = panel sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.makeMenu()
def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.configFile = None self.protFileLoaded = False self.settings = settings self.cfgValues = {} self.heaters = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = MechanicalPage(self, self.nb, len(self.pages), self.settings.font) 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), self.settings.font) 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.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages), self.settings.font) 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) self.SetSizer(sz) self.Fit()
def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.settings = settings self.protFileLoaded = False self.deco = Decoration() self.configFile = None self.cfgValues = {} self.heaters = [] self.sensors = [] self.candHeatPins = [] self.candThermPins = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = self.registerPage(CpuPage, "CPU") self.pgPins = self.registerPage(PinoutsPage, "Pinouts") self.pgDisplay = self.registerPage(DisplayPage, "Display") self.pgHeaters = self.registerPage(HeatersPage, "Heaters") self.pgSensors = self.registerPage(SensorsPage, "Temperature Sensors", heatersPage=self.pgHeaters) self.pgCommunications = self.registerPage(CommunicationsPage, "Communications") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit()
def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.configFile = None self.protFileLoaded = False self.settings = settings self.cfgValues = {} self.heaters = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = self.registerPage(MechanicalPage, "Mechanical") self.pgAcc = self.registerPage(AccelerationPage, "Acceleration") self.pgMiscellaneous = self.registerPage(MiscellaneousPage, "Miscellaneous") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit()
def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Configtool", size = (880, 550)) self.Bind(wx.EVT_CLOSE, self.onClose) self.Bind(wx.EVT_SIZE, self.onResize) self.deco = Decoration() panel = wx.Panel(self, -1) panel.SetBackgroundColour(self.deco.getBackgroundColour()) panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.settings = Settings(self, cmd_folder) self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) self.settings.folder = cmd_folder self.heaters = [] self.savePrtEna = False self.saveBrdEna = False self.protPrtFile = False self.protBrdFile = False sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(panel, wx.ID_ANY, size = (880, 550), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.printerFileName = None self.printerTabDecor = "" self.printerBaseText = "Printer" self.pgPrinter = PrinterPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgPrinter, self.printerBaseText) self.boardFileName = None self.boardTabDecor = "" self.boardBaseText = "Board" self.pgBoard = BoardPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgBoard, self.boardBaseText) panel.Fit() self.panel = panel sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.makeMenu()
def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.settings = settings self.protFileLoaded = False self.deco = Decoration() self.configFile = None self.cfgValues = {} self.heaters = [] self.sensors = [] self.candHeatPins = [] self.candThermPins = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = self.registerPage(CpuPage, "CPU") self.pgPins = self.registerPage(PinoutsPage, "Pinouts") self.pgDisplay = self.registerPage(DisplayPage, "Display") self.pgHeaters = self.registerPage(HeatersPage, "Heaters") self.pgSensors = self.registerPage(SensorsPage, "Temperature Sensors", heatersPage = self.pgHeaters) self.pgCommunications = self.registerPage(CommunicationsPage, "Communications") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit()
def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.protFileLoaded = False self.settings = settings self.printer = Printer(self.settings) self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = self.registerPage(MechanicalPage, "Mechanical") self.pgAcc = self.registerPage(AccelerationPage, "Acceleration") self.pgMiscellaneous = self.registerPage(MiscellaneousPage, "Miscellaneous") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit()
class BoardPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.settings = settings self.protFileLoaded = False self.deco = Decoration() self.configFile = None self.cfgValues = {} self.heaters = [] self.sensors = [] self.candHeatPins = [] self.candThermPins = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = CpuPage(self, self.nb, len(self.pages), self.settings.font) text = "CPU" self.nb.AddPage(self.pgCpu, text) self.pages.append(self.pgCpu) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgPins = PinoutsPage(self, self.nb, len(self.pages), self.settings.font) 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.pgHeaters = HeatersPage(self, self.nb, len(self.pages), self.settings.font) 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.pgSensors = SensorsPage(self, self.nb, len(self.pages), self.pgHeaters, self.settings.font) 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.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages), self.settings.font) 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) sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def getCPUInfo(self): vF_CPU = None if 'F_CPU' in self.cfgValues.keys(): vF_CPU = self.cfgValues['F_CPU'][0] vCPU = None if 'CPU' in self.cfgValues.keys(): vCPU = self.cfgValues['CPU'][0] return vF_CPU, vCPU def assertModified(self, pg, flag = True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def getFileName(self): return self.configFile def assertValid(self, pg, flag = True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSaveBoard(False, False) else: self.parent.enableSaveBoard(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setBoardTabDecor(pfx) def setHeaters(self, ht): self.parent.setHeaters(ht) 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 + "?\n" "There are changes to your board " "configuration 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 board configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Board configuration (board.*.h)|*.h" else: wildcard = "Board configuration (board.*.h)|board.*.h" dlg = wx.FileDialog(self, message = "Choose a board 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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): cfgFn = os.path.join(self.cfgDir, "board.generic.h") try: self.cfgBuffer = list(open(cfgFn)) except: return False, cfgFn try: self.userBuffer = list(open(fn)) except: return False, fn self.configFile = fn self.processors = [] self.sensors = [] self.heaters = [] self.candHeatPins = [] self.candThermPins = [] self.candProcessors = [] self.candClocks = [] self.tempTables = {} gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.cfgNames = [] self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace("\n\n ", "\n\n ") helpTextString = helpTextString.replace("\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseCandidateValues(ln): continue if self.parseDefineName(ln): continue # Ignore candidates in the metadata file. self.candHeatPins = [] self.candThermPins = [] self.candProcessors = [] self.candClocks = [] self.tempTables = {} gatheringHelpText = False prevLines = "" for ln in self.userBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False continue if reHelpTextStart.match(ln): gatheringHelpText = True continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseCandidateValues(ln): continue elif self.parseDefineValue(ln): continue 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 for k in range(len(self.sensors)): tn = self.sensors[k][0].upper() if tn in self.tempTables.keys(): self.sensors[k][3] = self.tempTables[tn] else: self.sensors[k][3] = None if os.path.basename(fn) in protectedFiles: self.parent.enableSaveBoard(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSaveBoard(True, True) self.parent.setBoardTabFile(os.path.basename(fn)) self.pgHeaters.setCandidatePins(self.candHeatPins) self.pgSensors.setCandidatePins(self.candThermPins) self.pgCpu.setCandidateProcessors(self.candProcessors) self.pgCpu.setCandidateClocks(self.candClocks) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) self.pgSensors.setSensors(self.sensors) self.pgHeaters.setHeaters(self.heaters) return True, None def parseDefineName(self, ln): m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1: self.cfgNames.append(t[0]) return True return False def parseDefineValue(self, ln): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt[0], True return True elif len(tt) > 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt, True return True m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2 and (t[0] in self.cfgNames): if reDefineBL.search(ln): self.cfgValues[t[0]] = t[1], True else: self.cfgValues[t[0]] = t[1], False return True m = reDefBoolBL.search(ln) if m: t = m.groups() if len(t) == 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = True return True return False def parseCandidateValues(self, ln): m = reCandThermPins.match(ln) if m: t = m.groups() if len(t) == 1: self.candThermPins.append(t[0]) return True m = reCandHeatPins.match(ln) if m: t = m.groups() if len(t) == 1: self.candHeatPins.append(t[0]) return True m = reCandProcessors.match(ln) if m: t = m.groups() if len(t) == 1: self.candProcessors.append(t[0]) return True m = reCandCPUClocks.match(ln) if m: t = m.groups() if len(t) == 1: self.candClocks.append(t[0]) return True m = reDefTT.match(ln) if m: t = m.groups() if len(t) == 2: s = self.parseTempTable(t[1]) if s: self.tempTables[t[0]] = s return True return False def parseSensor(self, s): m = reSensor.search(s) if m: t = m.groups() if len(t) == 4: return list(t) return None def parseHeater(self, s): m = reHeater.search(s) if m: t = m.groups() if len(t) == 3: return list(t) return None def parseTempTable(self, s): m = reTempTable4.search(s) if m: t = m.groups() if len(t) == 4: return list(t) m = reTempTable7.search(s) if m: t = m.groups() if len(t) == 7: return list(t) return None def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Board configuration (board.*.h)|board.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setBoardTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSaveBoard(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog(self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("board."): dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" "File name must begin with \"board.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] skipToSensorEnd = False skipToHeaterEnd = False tempTables = {} candThermPinsWritten = False candHeatPinsWritten = False candProcessorsWritten = False candCPUClocksWritten = False for ln in self.cfgBuffer: m = reStartSensors.match(ln) if m: fp.write(ln) fp.write("// name type pin " "additional\n"); ttString = "\n" ttString += "// Beta algorithm r0 beta r2 vadc\n" ttString += "// Steinhart-Hart rp t0 r0 t1 " ttString += "r1 t2 r2\n" for s in self.sensors: sstr = "%-10s%-15s%-7s" % ((s[0] + ","), (s[1] + ","), (s[2] + ",")) if s[3] is None: sstr += "0" else: sstr += "THERMISTOR_%s" % s[0].upper() tt = s[3] if len(tt) == 4: ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-6s%s)\n" % \ (s[0].upper(), (tt[0] + ","), (tt[1] + ","), (tt[2] + ","), tt[3]) else: ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-8s%-6s%-8s%-6s%s)\n" % \ (s[0].upper(), (tt[0] + ","), (tt[1] + ","), (tt[2] + ","), (tt[3] + ","), (tt[4] + ","), (tt[5] + ","), tt[6]) fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr) fp.write(ttString) skipToSensorEnd = True continue if skipToSensorEnd: m = reEndSensors.match(ln) if m: fp.write(ln) skipToSensorEnd = False continue m = reStartHeaters.match(ln) if m: fp.write(ln) fp.write("// name port pwm\n") for s in self.heaters: sstr = "%-10s%-7s%s" % ((s[0] + ","), (s[1] + ","), s[2]) fp.write("DEFINE_HEATER(%s)\n" % sstr) fp.write("\n") for s in self.heaters: fp.write(defineHeaterFormat % (s[0].upper(), s[0])) skipToHeaterEnd = True continue if skipToHeaterEnd: m = reEndHeaters.match(ln) if m: fp.write(ln) skipToHeaterEnd = False continue if reCandThermPins.match(ln): if not candThermPinsWritten: for pin in self.candThermPins: fp.write("//#define TEMP_SENSOR_PIN " + pin + "\n") candThermPinsWritten = True continue if reCandHeatPins.match(ln): if not candHeatPinsWritten: for pin in self.candHeatPins: fp.write("//#define HEATER_PIN " + pin + "\n") candHeatPinsWritten = True continue if reCandProcessors.match(ln): if not candProcessorsWritten: for pin in self.candProcessors: fp.write("//#define CPU_TYPE " + pin + "\n") candProcessorsWritten = True continue if reCandCPUClocks.match(ln): if not candCPUClocksWritten: for pin in self.candClocks: fp.write("//#define F_CPU_OPT " + pin + "\n") candCPUClocksWritten = True continue m = reDefine.match(ln) if m: t = m.groups() if len(t) == 2 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v[1] == False: fp.write("//") fp.write(defineValueFormat % (t[0], v[0])) else: if t[0] == 'TX_ENABLE_PIN' or t[0] == 'RX_ENABLE_PIN': # Known to be absent in the GUI, probably won't be added anytime soon. fp.write(ln) else: print "Value key " + t[0] + " not found in GUI." continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v == "" or v == False: fp.write("//") fp.write(defineBoolFormat % t[0]) else: if t[0] == 'MOTHERBOARD': # Known to be absent in the GUI, also won't be added anytime soon. fp.write(ln) else: print "Boolean key " + t[0] + " not found in GUI." continue fp.write(ln) fp.close() return self.generateTempTables() def generateTempTables(self): if not generateTempTables(self.sensors, self.settings): dlg = wx.MessageDialog(self, "Error writing to file thermistortable.h.", "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False return True
def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.settings = settings self.protFileLoaded = False self.deco = Decoration() self.configFile = None self.cfgValues = {} self.heaters = [] self.sensors = [] self.candHeatPins = [] self.candThermPins = [] self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = CpuPage(self, self.nb, len(self.pages), self.settings.font) text = "CPU" self.nb.AddPage(self.pgCpu, text) self.pages.append(self.pgCpu) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgPins = PinoutsPage(self, self.nb, len(self.pages), self.settings.font) 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.pgHeaters = HeatersPage(self, self.nb, len(self.pages), self.settings.font) 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.pgSensors = SensorsPage(self, self.nb, len(self.pages), self.pgHeaters, self.settings.font) 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.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages), self.settings.font) 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) sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit()
class ConfigFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Configtool", size=(880, 550)) self.Bind(wx.EVT_CLOSE, self.onClose) self.Bind(wx.EVT_SIZE, self.onResize) self.deco = Decoration() panel = wx.Panel(self, -1) panel.SetBackgroundColour(self.deco.getBackgroundColour()) panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.settings = Settings(self, cmd_folder) self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) self.settings.folder = cmd_folder self.heaters = [] self.savePrtEna = False self.saveBrdEna = False self.protPrtFile = False self.protBrdFile = False sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(panel, wx.ID_ANY, size=(880, 550), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.printerFileName = None self.printerTabDecor = "" self.printerBaseText = "Printer" self.pgPrinter = PrinterPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgPrinter, self.printerBaseText) self.boardFileName = None self.boardTabDecor = "" self.boardBaseText = "Board" self.pgBoard = BoardPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgBoard, self.boardBaseText) panel.Fit() self.panel = panel sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.makeMenu() def onClose(self, evt): if not self.pgPrinter.confirmLoseChanges("exit"): return if not self.pgBoard.confirmLoseChanges("exit"): return self.Destroy() def onResize(self, evt): self.panel.SetSize(self.GetClientSize()) self.Refresh() evt.Skip() def setPrinterTabFile(self, fn): self.printerFileName = fn self.updatePrinterTab() def setPrinterTabDecor(self, prefix): self.printerTabDecor = prefix self.updatePrinterTab() def updatePrinterTab(self): txt = self.printerTabDecor + self.printerBaseText if self.printerFileName: txt += " <%s>" % self.printerFileName self.nb.SetPageText(0, txt) def setBoardTabFile(self, fn): self.boardFileName = fn self.updateBoardTab() def setBoardTabDecor(self, prefix): self.boardTabDecor = prefix self.updateBoardTab() def updateBoardTab(self): txt = self.boardTabDecor + self.boardBaseText if self.boardFileName: txt += " <%s>" % self.boardFileName self.nb.SetPageText(1, txt) def setHeaters(self, ht): self.heaters = ht self.pgPrinter.setHeaters(ht) def makeMenu(self): file_menu = wx.Menu() file_menu.Append( ID_LOAD_CONFIG, "Load config.h", "Load config.h and its named printer and board files.") self.Bind(wx.EVT_MENU, self.onLoadConfig, id=ID_LOAD_CONFIG) file_menu.Enable(ID_LOAD_CONFIG, False) file_menu.Append(ID_SAVE_CONFIG, "Save config.h", "Save config.h file.") self.Bind(wx.EVT_MENU, self.onSaveConfig, id=ID_SAVE_CONFIG) file_menu.Enable(ID_SAVE_CONFIG, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_PRINTER, "Load printer", "Load a printer configuration file.") self.Bind(wx.EVT_MENU, self.pgPrinter.onLoadConfig, id=ID_LOAD_PRINTER) file_menu.Append(ID_SAVE_PRINTER, "Save printer", "Save printer configuration.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfig, id=ID_SAVE_PRINTER) file_menu.Enable(ID_SAVE_PRINTER, False) file_menu.Append(ID_SAVE_PRINTER_AS, "Save printer as...", "Save printer configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfigAs, id=ID_SAVE_PRINTER_AS) file_menu.Enable(ID_SAVE_PRINTER_AS, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_BOARD, "Load board", "Load a board configuration file.") self.Bind(wx.EVT_MENU, self.pgBoard.onLoadConfig, id=ID_LOAD_BOARD) file_menu.Append(ID_SAVE_BOARD, "Save board", "Save board configuration.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfig, id=ID_SAVE_BOARD) file_menu.Enable(ID_SAVE_BOARD, False) file_menu.Append(ID_SAVE_BOARD_AS, "Save board as...", "Save board configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfigAs, id=ID_SAVE_BOARD_AS) file_menu.Enable(ID_SAVE_BOARD_AS, False) file_menu.AppendSeparator() file_menu.Append(wx.ID_EXIT, "E&xit", "Exit the application.") self.Bind(wx.EVT_MENU, self.onClose, id=wx.ID_EXIT) self.fileMenu = file_menu menu_bar = wx.MenuBar() menu_bar.Append(file_menu, "&File") edit_menu = wx.Menu() edit_menu.Append(ID_SETTINGS, "Settings", "Change settings.") self.Bind(wx.EVT_MENU, self.onEditSettings, id=ID_SETTINGS) self.editMenu = edit_menu menu_bar.Append(edit_menu, "&Edit") build_menu = wx.Menu() build_menu.Append(ID_BUILD, "Build", "Build the executable.") self.Bind(wx.EVT_MENU, self.onBuild, id=ID_BUILD) build_menu.Append(ID_UPLOAD, "Upload", "Upload the executable.") self.Bind(wx.EVT_MENU, self.onUpload, id=ID_UPLOAD) self.buildMenu = build_menu menu_bar.Append(build_menu, "&Build") help_menu = wx.Menu() help_menu.Append(ID_HELP, "Help", "Find help.") self.Bind(wx.EVT_MENU, self.onHelp, id=ID_HELP) help_menu.Append(ID_REPORT, "Report problem", "Report a problem to Teacup maintainers.") self.Bind(wx.EVT_MENU, self.onReportProblem, id=ID_REPORT) help_menu.AppendSeparator() help_menu.Append(ID_ABOUT, "About Teacup") self.Bind(wx.EVT_MENU, self.onAbout, id=ID_ABOUT) self.helpMenu = help_menu menu_bar.Append(help_menu, "&Help") self.SetMenuBar(menu_bar) loadFlag = self.checkEnableLoadConfig() self.checkEnableUpload() if loadFlag: self.loadConfigFile("config.h") def onSaveBoardConfig(self, evt): rc = self.pgBoard.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSaveBoardConfigAs(self, evt): rc = self.pgBoard.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfig(self, evt): rc = self.pgPrinter.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfigAs(self, evt): rc = self.pgPrinter.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def checkEnableLoadConfig(self): fn = os.path.join(cmd_folder, "config.h") if os.path.isfile(fn): self.fileMenu.Enable(ID_LOAD_CONFIG, True) self.buildMenu.Enable(ID_BUILD, True) return True else: self.fileMenu.Enable(ID_LOAD_CONFIG, False) self.buildMenu.Enable(ID_BUILD, False) return False def checkEnableUpload(self): fn = os.path.join(cmd_folder, "teacup.hex") if os.path.isfile(fn): self.buildMenu.Enable(ID_UPLOAD, True) else: self.buildMenu.Enable(ID_UPLOAD, False) def enableSavePrinter(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_PRINTER, saveFlag) self.fileMenu.Enable(ID_SAVE_PRINTER_AS, saveAsFlag) self.savePrtEna = saveAsFlag self.protPrtFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveBoard(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_BOARD, saveFlag) self.fileMenu.Enable(ID_SAVE_BOARD_AS, saveAsFlag) self.saveBrdEna = saveAsFlag self.protBrdFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveConfig(self, flag): self.fileMenu.Enable(ID_SAVE_CONFIG, flag) def onLoadConfig(self, evt): self.loadConfigFile("config.h") def loadConfigFile(self, fn): if not self.pgPrinter.confirmLoseChanges("load config"): return False if not self.pgBoard.confirmLoseChanges("load config"): return False pfile, bfile = self.getConfigFileNames(fn) if not pfile: self.message( "Config file did not contain a printer file " "include statement.", "Config error") return False else: if not self.pgPrinter.loadConfigFile(pfile): self.message( "There was a problem loading the printer config file:\n%s" % pfile, "Config error") return False if not bfile: self.message( "Config file did not contain a board file " "include statement.", "Config error") return False else: if not self.pgBoard.loadConfigFile(bfile): self.message( "There was a problem loading the board config file:\n%s" % bfile, "Config error") return False return True def getConfigFileNames(self, fn): pfile = None bfile = None path = os.path.join(cmd_folder, fn) try: cfgBuffer = list(open(path)) except: self.message("Unable to process config file %s." % fn, "File error") return None, None for ln in cfgBuffer: if not ln.lstrip().startswith("#include"): continue m = reInclude.search(ln) if m: t = m.groups() if len(t) == 1: if "printer." in t[0]: if pfile: self.message( "Multiple printer file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: pfile = os.path.join(cmd_folder, t[0]) elif "board." in t[0]: if bfile: self.message( "Multiple board file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: bfile = os.path.join(cmd_folder, t[0]) else: self.message( "Unable to parse include statement:\n%s" % ln, "Config error") return pfile, bfile def onSaveConfig(self, evt): fn = os.path.join(cmd_folder, "config.h") try: fp = open(fn, 'w') except: self.message("Unable to open config.h for output.", "File error") return False bfn = self.pgBoard.getFileName() if self.pgBoard.isModified() and self.pgBoard.isValid(): if not self.pgBoard.saveConfigFile(bfn): return False else: self.pgBoard.generateTempTables() pfn = self.pgPrinter.getFileName() if self.pgPrinter.isModified() and self.pgPrinter.isValid(): if not self.pgPrinter.saveConfigFile(pfn): return False prefix = cmd_folder + os.path.sep lpfx = len(prefix) if bfn.startswith(prefix): rbfn = bfn[lpfx:] else: rbfn = bfn if pfn.startswith(prefix): rpfn = pfn[lpfx:] else: rpfn = pfn fp.write("\n") fp.write("// Configuration for controller board.\n") fp.write("#include \"%s\"\n" % rbfn) fp.write("\n") fp.write("// Configuration for printer board.\n") fp.write("#include \"%s\"\n" % rpfn) fp.close() self.checkEnableLoadConfig() return True def onBuild(self, evt): self.onBuildorUpload(True) def onUpload(self, evt): self.onBuildorUpload(False) def onBuildorUpload(self, buildFlag): if not (self.pgPrinter.hasData() or self.pgBoard.hasData()): dlg = wx.MessageDialog( self, "Data needs to be loaded. " "Click Yes to load config.h.", "Data missing", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return self.loadConfigFile("config.h") else: if self.pgPrinter.isModified(): dlg = wx.MessageDialog( self, "Printer data needs to be saved. Click " "Yes to save printer configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protPrtFile: rc = self.onSavePrinterConfigAs(None) else: rc = self.onSavePrinterConfig(None) if not rc: return if self.pgBoard.isModified(): dlg = wx.MessageDialog( self, "Board data needs to be saved. Click " "Yes to save board configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protBrdFile: rc = self.onSaveBoardConfigAs(None) else: rc = self.onSaveBoardConfig(None) if not rc: return if not self.verifyConfigLoaded(): dlg = wx.MessageDialog( self, "Loaded configuration does not match the " "config.h file. Click Yes to save config.h.", "Configuration changed", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if not self.onSaveConfig(None): return f_cpu, cpu = self.pgBoard.getCPUInfo() if not cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU type.", "CPU type error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if not f_cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU clock rate.", "CPU clock rate error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if buildFlag: dlg = Build(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() self.checkEnableUpload() else: dlg = Upload(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() def verifyConfigLoaded(self): pfile, bfile = self.getConfigFileNames("config.h") lpfile = self.pgPrinter.getFileName() lbfile = self.pgBoard.getFileName() return ((pfile == lpfile) and (bfile == lbfile)) def onEditSettings(self, evt): dlg = SettingsDlg(self, self.settings) rc = dlg.ShowModal() dlg.Destroy() def onHelp(self, evt): self.message( "Find help by hovering slowly over the buttons and text " "fields. Tooltip should appear, explaining things.", "Find help", style=wx.OK) def onReportProblem(self, evt): import urllib import webbrowser import subprocess from sys import platform # Testing allowed URLs up to 32 kB in size. Longer URLs are simply chopped. mailRecipients ="reply+0004dc756da9f0641af0a3834c580ad5be469f4f6b" \ "5d4cfc92cf00000001118c958a92a169ce051faa8c@" \ "reply.github.com,[email protected]" mailSubject = "Teacup problem report" mailBody = "Please answer these questions before hitting \"send\":\n\n" \ "What did you try to do?\n\n\n" \ "What did you expect to happen?\n\n\n" \ "What happened instead?\n\n\n\n" \ "To allow developers to help, configuration files are " \ "attached, with help comments stripped:\n" for f in self.pgBoard.getFileName(), self.pgPrinter.getFileName(): if not f: mailBody += "\n(no file loaded)\n" continue mailBody += "\n" + os.path.basename(f) + ":\n" mailBody += "----------------------------------------------\n" try: fc = open(f).read() fc = reHelpText.sub("", fc) mailBody += fc except: mailBody += "(could not read this file)\n" mailBody += "----------------------------------------------\n" url = "mailto:" + urllib.quote(mailRecipients) + \ "?subject=" + urllib.quote(mailSubject) + \ "&body=" + urllib.quote(mailBody) # This is a work around a bug in gvfs-open coming with (at least) Ubuntu # 15.04. gvfs-open would open mailto:///[email protected] instead of # the requested mailto:[email protected]. if platform.startswith("linux"): try: subprocess.check_output(["gvfs-open", "--help"]) # Broken gvfs-open exists, so it might be used. # Try to open the URL directly. for urlOpener in "thunderbird", "evolution", "firefox", "mozilla", \ "epiphany", "konqueror", "chromium-browser", \ "google-chrome": try: subprocess.check_output([urlOpener, url], stderr=subprocess.STDOUT) return except: pass except: pass webbrowser.open_new(url) def onAbout(self, evt): # Get the contributors' top 10 with something like this: # export B=experimental # git log $B | grep "Author:" | sort | uniq | while \ # read A; do N=$(git log $B | grep "$A" | wc -l); echo "$N $A"; done | \ # sort -rn self.message( "Teacup Firmware is a 3D Printer and CNC machine controlling " "firmware with emphasis on performance, efficiency and " "outstanding quality. What Teacup does, shall it do very well." "\n\n\n" "Lots of people hard at work! Top 10 contributors:\n\n" " Markus Hitter (542 commits)\n" " Michael Moon (322 commits)\n" " Phil Hord (55 commits)\n" " Jeff Bernardis (51 commits)\n" " Markus Amsler (47 commits)\n" " David Forrest (27 commits)\n" " Jim McGee (15 commits)\n" " Ben Jackson (12 commits)\n" " Bas Laarhoven (10 commits)\n" " Stephan Walter (9 commits)\n" " Roland Brochard (3 commits)\n" " Jens Ch. Restemeier (3 commits)\n", "About Teacup", style=wx.OK) def message(self, text, title, style=wx.OK + wx.ICON_ERROR): dlg = wx.MessageDialog(self, text, title, style) dlg.ShowModal() dlg.Destroy()
class PrinterPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.protFileLoaded = False self.settings = settings self.printer = Printer(self.settings) self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = self.registerPage(MechanicalPage, "Mechanical") self.pgAcc = self.registerPage(AccelerationPage, "Acceleration") self.pgMiscellaneous = self.registerPage(MiscellaneousPage, "Miscellaneous") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def registerPage(self, klass, label, *args, **kwargs): page = klass(self, self.nb, len(self.pages), *args, font=self.settings.font, **kwargs) self.nb.AddPage(page, label) self.pages.append(page) self.titles.append(label) self.pageModified.append(False) self.pageValid.append(True) return page def assertModified(self, pg, flag=True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return self.printer.hasData() def getFileName(self): return self.printer.configFile def assertValid(self, pg, flag=True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSavePrinter(False, False) else: self.parent.enableSavePrinter(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setPrinterTabDecor(pfx) def setHeaters(self, ht): return self.pgMiscellaneous.setHeaters(ht) 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 + "?\n" "There are changes to your printer " "configuration 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 printer configuration"): return if platform.startswith("darwin"): # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.h" dlg = wx.FileDialog(self, message="Choose a printer 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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): ok, file = self.printer.loadConfigFile(fn) if not ok: return ok, file if os.path.basename(fn) in protectedFiles: self.parent.enableSavePrinter(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSavePrinter(True, True) self.parent.setPrinterTabFile(os.path.basename(fn)) for pg in self.pages: pg.insertValues(self.printer.cfgValues) pg.setHelpText(self.printer.helpText) k = 'DC_EXTRUDER' if k in self.printer.cfgValues.keys( ) and self.printer.cfgValues[k][1] == True: self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0]) else: self.pgMiscellaneous.setOriginalHeater(None) return True, None def onSaveConfig(self, evt): path = self.getFileName() return self.saveConfigFile(path) def onSaveConfigAs(self, evt): if platform.startswith("darwin"): # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setPrinterTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSavePrinter(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog( self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("printer."): dlg = wx.MessageDialog( self, "Illegal file name: %s.\n" "File name must begin with \"printer.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: self.printer.saveConfigFile(path, values) except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False return True
class Page: def __init__(self, font): self.modified = False self.valid = True self.fieldValid = {} self.textControls = {} self.textControlsOriginal = {} self.checkBoxes = {} self.radioButtons = {} self.radioButtonBoxes = {} self.choices = {} self.choicesOriginal = {} self.boolChoices = {} self.deco = Decoration() self.font = font self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) def enableAll(self, flag=True): for c in self.textControls.keys(): self.textControls[c].Enable(flag) for c in self.checkBoxes.keys(): self.checkBoxes[c].Enable(flag) for c in self.radioButtons.keys(): self.radioButtons[c].Enable(flag) for c in self.choices.keys(): self.choices[c].Enable(flag) def addTextCtrl(self, name, labelWidth, validator): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText( self, wx.ID_ANY, self.labels[name] + " ", size=(labelWidth, -1), style=wx.ALIGN_RIGHT, ) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetTcLabel) tc = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_RIGHT, name=name) tc.SetFont(self.font) self.fieldValid[name] = True tc.Bind(wx.EVT_TEXT, validator) self.textControls[name] = tc lsz.Add(tc) return lsz def addCheckBox(self, name, validator): if name in self.labels.keys(): lbl = self.labels[name] else: lbl = name cb = wx.CheckBox(self, wx.ID_ANY, lbl) cb.SetFont(self.font) cb.Bind(wx.EVT_CHECKBOX, validator) self.checkBoxes[name] = cb return cb def addRadioButton(self, name, style, validator, sbox=None): rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style=style) rb.SetFont(self.font) self.Bind(wx.EVT_RADIOBUTTON, validator, rb) self.radioButtons[name] = rb if sbox is not None: self.radioButtonBoxes[name] = sbox return rb def addChoice(self, name, choices, selection, labelWidth, validator, size=(-1, -1)): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText( self, wx.ID_ANY, self.labels[name], size=(labelWidth, -1), style=wx.ALIGN_RIGHT, ) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, choices=choices, size=size, name=name) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, validator) ch.SetSelection(selection) lsz.Add(ch) self.choices[name] = ch return lsz def addPinChoice(self, name, labelWidth): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText( self, wx.ID_ANY, self.labels[name], size=(labelWidth, -1), style=wx.ALIGN_RIGHT, ) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, name=name, style=wx.CB_SORT) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.AppendItems(["-"] + pinNames) ch.Bind(wx.EVT_CHOICE, self.onChoice) self.choices[name] = ch lsz.Add(ch) return lsz # addChoice handles #defines with value, this handles choices for # sets of boolean #defines. def addBoolChoice(self, name, allowBlank, labelWidth, validator, size=(-1, -1)): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText( self, wx.ID_ANY, self.labels[name], size=(labelWidth, -1), style=wx.ALIGN_RIGHT, ) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, size=size, name=name) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, validator) if allowBlank: ch.Append("(none)") ch.SetSelection(0) lsz.Add(ch) self.boolChoices[name] = ch return lsz def setChoice(self, name, cfgValues, default): if name in cfgValues.keys() and cfgValues[name][1] == True: bv = cfgValues[name][0] else: bv = default s = self.choices[name].FindString(bv) if s < 0: s = self.choices[name].FindString(default) if s < 0: 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 onCheckBox(self, evt): self.assertModified(True) evt.Skip() def setHelpText(self, ht): for k in self.textControls.keys(): if k in ht.keys(): self.textControls[k].SetToolTip(ht[k]) for k in self.checkBoxes.keys(): if k in ht.keys(): self.checkBoxes[k].SetToolTip(ht[k]) for k in self.radioButtons.keys(): if k in ht.keys(): self.radioButtons[k].SetToolTip(ht[k]) if k in self.radioButtonBoxes.keys(): self.radioButtonBoxes[k].SetToolTip(ht[k]) for k in self.choices.keys(): if k in ht.keys(): self.choices[k].SetToolTip(ht[k]) for k in self.boolChoices.keys(): for candidate in ht.keys(): if candidate.startswith(k): self.boolChoices[k].SetToolTip(ht[candidate]) break def insertValues(self, cfgValues): self.assertValid(True) self.enableAll(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.textControlsOriginal[k] = cfgValues[k] if cfgValues[k][1] == True: self.textControls[k].SetValue(str(cfgValues[k][0])) else: self.textControls[k].SetValue("") else: print("Key " + k + " not found in config data.") for k in self.choices.keys(): if k in cfgValues.keys(): self.choicesOriginal[k] = cfgValues[k] self.setChoice(k, cfgValues, "-") else: print("Key " + k + " not found in config data.") for k in self.boolChoices.keys(): choice = self.boolChoices[k] # Remove items left behind from the previous configuration. while choice.GetCount( ) and not choice.GetString(choice.GetCount() - 1).startswith("("): choice.Delete(choice.GetCount() - 1) # Add items found in this configuration. for cfg in cfgValues.keys(): if cfg.startswith(k): if cfg in self.labels.keys(): choice.Append(self.labels[cfg]) else: choice.Append(cfg) # As we want to write the configuration name later, not the user # friendly string, we store the configuration name as client data. n = choice.GetCount() - 1 choice.SetClientData(n, cfg) if cfgValues[cfg]: choice.SetSelection(n) self.assertModified(False) def getValues(self): self.assertModified(False) result = {} for k in self.checkBoxes.keys(): cb = self.checkBoxes[k] result[k] = cb.IsChecked() for k in self.textControls.keys(): v = self.textControls[k].GetValue() if v == "": if k in self.textControlsOriginal.keys(): result[k] = self.textControlsOriginal[k][0], False else: result[k] = "", False else: result[k] = v, True for k in self.radioButtons.keys(): result[k] = self.radioButtons[k].GetValue(), True for k in self.choices.keys(): v = self.choices[k].GetSelection() s = self.choices[k].GetString(v) if s == "-": if k in self.choicesOriginal.keys(): result[k] = self.choicesOriginal[k][0], False else: result[k] = "", False else: result[k] = s, True for k in self.boolChoices.keys(): choice = self.boolChoices[k] for i in range(choice.GetCount()): s = choice.GetClientData(i) if s: result[s] = i == choice.GetSelection() return result 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 ConfigFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Configtool", size = (880, 550)) self.Bind(wx.EVT_CLOSE, self.onClose) self.Bind(wx.EVT_SIZE, self.onResize) self.deco = Decoration() panel = wx.Panel(self, -1) panel.SetBackgroundColour(self.deco.getBackgroundColour()) panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.settings = Settings(self, cmd_folder) self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) self.settings.folder = cmd_folder self.heaters = [] self.savePrtEna = False self.saveBrdEna = False self.protPrtFile = False self.protBrdFile = False sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(panel, wx.ID_ANY, size = (880, 550), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.printerFileName = None self.printerTabDecor = "" self.printerBaseText = "Printer" self.pgPrinter = PrinterPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgPrinter, self.printerBaseText) self.boardFileName = None self.boardTabDecor = "" self.boardBaseText = "Board" self.pgBoard = BoardPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgBoard, self.boardBaseText) panel.Fit() self.panel = panel sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.makeMenu() def onClose(self, evt): if not self.pgPrinter.confirmLoseChanges("exit"): return if not self.pgBoard.confirmLoseChanges("exit"): return self.Destroy() def onResize(self, evt): self.panel.SetSize(self.GetClientSize()) self.Refresh() evt.Skip(); def setPrinterTabFile(self, fn): self.printerFileName = fn self.updatePrinterTab() def setPrinterTabDecor(self, prefix): self.printerTabDecor = prefix self.updatePrinterTab() def updatePrinterTab(self): txt = self.printerTabDecor + self.printerBaseText if self.printerFileName: txt += " <%s>" % self.printerFileName self.nb.SetPageText(0, txt) def setBoardTabFile(self, fn): self.boardFileName = fn self.updateBoardTab() def setBoardTabDecor(self, prefix): self.boardTabDecor = prefix self.updateBoardTab() def updateBoardTab(self): txt = self.boardTabDecor + self.boardBaseText if self.boardFileName: txt += " <%s>" % self.boardFileName self.nb.SetPageText(1, txt) def setHeaters(self, ht): self.heaters = ht self.pgPrinter.setHeaters(ht) def makeMenu(self): file_menu = wx.Menu() file_menu.Append(ID_LOAD_CONFIG, "Load config.h", "Load config.h and its named printer and board files.") self.Bind(wx.EVT_MENU, self.onLoadConfig, id = ID_LOAD_CONFIG) file_menu.Enable(ID_LOAD_CONFIG, False) file_menu.Append(ID_SAVE_CONFIG, "Save config.h", "Save config.h file.") self.Bind(wx.EVT_MENU, self.onSaveConfig, id = ID_SAVE_CONFIG) file_menu.Enable(ID_SAVE_CONFIG, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_PRINTER, "Load printer", "Load a printer configuration file.") self.Bind(wx.EVT_MENU, self.pgPrinter.onLoadConfig, id = ID_LOAD_PRINTER) file_menu.Append(ID_SAVE_PRINTER, "Save printer", "Save printer configuration.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfig, id = ID_SAVE_PRINTER) file_menu.Enable(ID_SAVE_PRINTER, False) file_menu.Append(ID_SAVE_PRINTER_AS, "Save printer as...", "Save printer configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfigAs, id = ID_SAVE_PRINTER_AS) file_menu.Enable(ID_SAVE_PRINTER_AS, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_BOARD, "Load board", "Load a board configuration file.") self.Bind(wx.EVT_MENU, self.pgBoard.onLoadConfig, id = ID_LOAD_BOARD) file_menu.Append(ID_SAVE_BOARD, "Save board", "Save board configuration.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfig, id = ID_SAVE_BOARD) file_menu.Enable(ID_SAVE_BOARD, False) file_menu.Append(ID_SAVE_BOARD_AS, "Save board as...", "Save board configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfigAs, id = ID_SAVE_BOARD_AS) file_menu.Enable(ID_SAVE_BOARD_AS, False) file_menu.AppendSeparator() file_menu.Append(wx.ID_EXIT, "E&xit", "Exit the application.") self.Bind(wx.EVT_MENU, self.onClose, id = wx.ID_EXIT) self.fileMenu = file_menu menu_bar = wx.MenuBar() menu_bar.Append(file_menu, "&File") edit_menu = wx.Menu() edit_menu.Append(ID_SETTINGS, "Settings", "Change settings.") self.Bind(wx.EVT_MENU, self.onEditSettings, id = ID_SETTINGS) self.editMenu = edit_menu menu_bar.Append(edit_menu, "&Edit") build_menu = wx.Menu() build_menu.Append(ID_BUILD, "Build", "Build the executable.") self.Bind(wx.EVT_MENU, self.onBuild, id = ID_BUILD) build_menu.Append(ID_UPLOAD, "Upload", "Upload the executable.") self.Bind(wx.EVT_MENU, self.onUpload, id = ID_UPLOAD) self.buildMenu = build_menu menu_bar.Append(build_menu, "&Build") help_menu = wx.Menu() help_menu.Append(ID_HELP, "Help", "Find help.") self.Bind(wx.EVT_MENU, self.onHelp, id = ID_HELP) help_menu.Append(ID_REPORT, "Report problem", "Report a problem to Teacup maintainers.") self.Bind(wx.EVT_MENU, self.onReportProblem, id = ID_REPORT) help_menu.AppendSeparator() help_menu.Append(ID_ABOUT, "About Teacup") self.Bind(wx.EVT_MENU, self.onAbout, id = ID_ABOUT) self.helpMenu = help_menu menu_bar.Append(help_menu, "&Help") self.SetMenuBar(menu_bar) loadFlag = self.checkEnableLoadConfig() self.checkEnableUpload() if loadFlag: self.loadConfigFile("config.h") def onSaveBoardConfig(self, evt): rc = self.pgBoard.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSaveBoardConfigAs(self, evt): rc = self.pgBoard.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfig(self, evt): rc = self.pgPrinter.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfigAs(self, evt): rc = self.pgPrinter.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def checkEnableLoadConfig(self): fn = os.path.join(cmd_folder, "config.h") if os.path.isfile(fn): self.fileMenu.Enable(ID_LOAD_CONFIG, True) self.buildMenu.Enable(ID_BUILD, True) return True else: self.fileMenu.Enable(ID_LOAD_CONFIG, False) self.buildMenu.Enable(ID_BUILD, False) return False def checkEnableUpload(self): fn = os.path.join(cmd_folder, "teacup.hex") if os.path.isfile(fn): self.buildMenu.Enable(ID_UPLOAD, True) else: self.buildMenu.Enable(ID_UPLOAD, False) def enableSavePrinter(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_PRINTER, saveFlag) self.fileMenu.Enable(ID_SAVE_PRINTER_AS, saveAsFlag) self.savePrtEna = saveAsFlag self.protPrtFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveBoard(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_BOARD, saveFlag) self.fileMenu.Enable(ID_SAVE_BOARD_AS, saveAsFlag) self.saveBrdEna = saveAsFlag self.protBrdFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveConfig(self, flag): self.fileMenu.Enable(ID_SAVE_CONFIG, flag) def onLoadConfig(self, evt): self.loadConfigFile("config.h") def loadConfigFile(self, fn): if not self.pgPrinter.confirmLoseChanges("load config"): return False if not self.pgBoard.confirmLoseChanges("load config"): return False pfile, bfile = self.getConfigFileNames(fn) if not pfile: self.message("Config file did not contain a printer file " "include statement.", "Config error") return False else: if not self.pgPrinter.loadConfigFile(pfile): self.message("There was a problem loading the printer config file:\n%s" % pfile, "Config error") return False if not bfile: self.message("Config file did not contain a board file " "include statement.", "Config error") return False else: if not self.pgBoard.loadConfigFile(bfile): self.message("There was a problem loading the board config file:\n%s" % bfile, "Config error") return False return True def getConfigFileNames(self, fn): pfile = None bfile = None path = os.path.join(cmd_folder, fn) try: cfgBuffer = list(open(path)) except: self.message("Unable to process config file %s." % fn, "File error") return None, None for ln in cfgBuffer: if not ln.lstrip().startswith("#include"): continue m = reInclude.search(ln) if m: t = m.groups() if len(t) == 1: if "printer." in t[0]: if pfile: self.message("Multiple printer file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: pfile = os.path.join(cmd_folder, t[0]) elif "board." in t[0]: if bfile: self.message("Multiple board file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: bfile = os.path.join(cmd_folder, t[0]) else: self.message("Unable to parse include statement:\n%s" % ln, "Config error") return pfile, bfile def onSaveConfig(self, evt): fn = os.path.join(cmd_folder, "config.h") try: fp = open(fn, 'w') except: self.message("Unable to open config.h for output.", "File error") return False bfn = self.pgBoard.getFileName() if self.pgBoard.isModified() and self.pgBoard.isValid(): if not self.pgBoard.saveConfigFile(bfn): return False else: self.pgBoard.generateTempTables() pfn = self.pgPrinter.getFileName() if self.pgPrinter.isModified() and self.pgPrinter.isValid(): if not self.pgPrinter.saveConfigFile(pfn): return False prefix = cmd_folder + os.path.sep lpfx = len(prefix) if bfn.startswith(prefix): rbfn = bfn[lpfx:] else: rbfn = bfn if pfn.startswith(prefix): rpfn = pfn[lpfx:] else: rpfn = pfn fp.write("\n") fp.write("// Configuration for controller board.\n") fp.write("#include \"%s\"\n" % rbfn) fp.write("\n") fp.write("// Configuration for printer board.\n") fp.write("#include \"%s\"\n" % rpfn) fp.close() self.checkEnableLoadConfig() return True def onBuild(self, evt): self.onBuildorUpload(True) def onUpload(self, evt): self.onBuildorUpload(False) def onBuildorUpload(self, buildFlag): if not (self.pgPrinter.hasData() or self.pgBoard.hasData()): dlg = wx.MessageDialog(self, "Data needs to be loaded. " "Click Yes to load config.h.", "Data missing", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return self.loadConfigFile("config.h") else: if self.pgPrinter.isModified(): dlg = wx.MessageDialog(self, "Printer data needs to be saved. Click " "Yes to save printer configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protPrtFile: rc = self.onSavePrinterConfigAs(None) else: rc = self.onSavePrinterConfig(None) if not rc: return if self.pgBoard.isModified(): dlg = wx.MessageDialog(self, "Board data needs to be saved. Click " "Yes to save board configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protBrdFile: rc = self.onSaveBoardConfigAs(None) else: rc = self.onSaveBoardConfig(None) if not rc: return if not self.verifyConfigLoaded(): dlg = wx.MessageDialog(self, "Loaded configuration does not match the " "config.h file. Click Yes to save config.h.", "Configuration changed", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if not self.onSaveConfig(None): return f_cpu, cpu = self.pgBoard.getCPUInfo() if not cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU type.", "CPU type error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if not f_cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU clock rate.", "CPU clock rate error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if buildFlag: dlg = Build(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() self.checkEnableUpload() else: dlg = Upload(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() def verifyConfigLoaded(self): pfile, bfile = self.getConfigFileNames("config.h") lpfile = self.pgPrinter.getFileName() lbfile = self.pgBoard.getFileName() return ((pfile == lpfile) and (bfile == lbfile)) def onEditSettings(self, evt): dlg = SettingsDlg(self, self.settings) rc = dlg.ShowModal() dlg.Destroy() def onHelp(self, evt): self.message("Find help by hovering slowly over the buttons and text " "fields. Tooltip should appear, explaining things.", "Find help", style = wx.OK) def onReportProblem(self, evt): import urllib import webbrowser import subprocess from sys import platform # Testing allowed URLs up to 32 kB in size. Longer URLs are simply chopped. mailRecipients ="reply+0004dc756da9f0641af0a3834c580ad5be469f4f6b" \ "5d4cfc92cf00000001118c958a92a169ce051faa8c@" \ "reply.github.com,[email protected]" mailSubject = "Teacup problem report" mailBody = "Please answer these questions before hitting \"send\":\n\n" \ "What did you try to do?\n\n\n" \ "What did you expect to happen?\n\n\n" \ "What happened instead?\n\n\n\n" \ "To allow developers to help, configuration files are " \ "attached, with help comments stripped:\n" for f in self.pgBoard.getFileName(), self.pgPrinter.getFileName(): if not f: mailBody += "\n(no file loaded)\n" continue mailBody += "\n" + os.path.basename(f) + ":\n" mailBody += "----------------------------------------------\n" try: fc = open(f).read() fc = reHelpText.sub("", fc) mailBody += fc except: mailBody += "(could not read this file)\n" mailBody += "----------------------------------------------\n" url = "mailto:" + urllib.quote(mailRecipients) + \ "?subject=" + urllib.quote(mailSubject) + \ "&body=" + urllib.quote(mailBody) # This is a work around a bug in gvfs-open coming with (at least) Ubuntu # 15.04. gvfs-open would open mailto:///[email protected] instead of # the requested mailto:[email protected]. if platform.startswith("linux"): try: subprocess.check_output(["gvfs-open", "--help"]) # Broken gvfs-open exists, so it might be used. # Try to open the URL directly. for urlOpener in "thunderbird", "evolution", "firefox", "mozilla", \ "epiphany", "konqueror", "chromium-browser", \ "google-chrome": try: subprocess.check_output([urlOpener, url], stderr=subprocess.STDOUT) return except: pass except: pass webbrowser.open_new(url) def onAbout(self, evt): # Get the contributors' top 10 with something like this: # export B=experimental # git log $B | grep "Author:" | sort | uniq | while \ # read A; do N=$(git log $B | grep "$A" | wc -l); echo "$N $A"; done | \ # sort -rn self.message("Teacup Firmware is a 3D Printer and CNC machine controlling " "firmware with emphasis on performance, efficiency and " "outstanding quality. What Teacup does, shall it do very well." "\n\n\n" "Lots of people hard at work! Top 10 contributors:\n\n" " Markus Hitter (542 commits)\n" " Michael Moon (322 commits)\n" " Phil Hord (55 commits)\n" " Jeff Bernardis (51 commits)\n" " Markus Amsler (47 commits)\n" " David Forrest (27 commits)\n" " Jim McGee (15 commits)\n" " Ben Jackson (12 commits)\n" " Bas Laarhoven (10 commits)\n" " Stephan Walter (9 commits)\n" " Roland Brochard (3 commits)\n" " Jens Ch. Restemeier (3 commits)\n", "About Teacup", style = wx.OK) def message(self, text, title, style = wx.OK + wx.ICON_ERROR): dlg = wx.MessageDialog(self, text, title, style) dlg.ShowModal() dlg.Destroy()
class ConfigFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Configtool", size = (880, 550)) self.Bind(wx.EVT_CLOSE, self.onClose) self.Bind(wx.EVT_SIZE, self.onResize) self.deco = Decoration() panel = wx.Panel(self, -1) panel.SetBackgroundColour(self.deco.getBackgroundColour()) panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.settings = Settings(self, cmd_folder) self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) self.settings.folder = cmd_folder self.heaters = [] self.savePrtEna = False self.saveBrdEna = False self.protPrtFile = False self.protBrdFile = False sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(panel, wx.ID_ANY, size = (880, 550), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.printerFileName = None self.printerTabDecor = "" self.printerBaseText = "Printer" self.pgPrinter = PrinterPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgPrinter, self.printerBaseText) self.boardFileName = None self.boardTabDecor = "" self.boardBaseText = "Board" self.pgBoard = BoardPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgBoard, self.boardBaseText) panel.Fit() self.panel = panel sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.makeMenu() def onClose(self, evt): if not self.pgPrinter.confirmLoseChanges("exit"): return if not self.pgBoard.confirmLoseChanges("exit"): return self.Destroy() def onResize(self, evt): self.panel.SetSize(self.GetClientSize()) self.Refresh() evt.Skip(); def setPrinterTabFile(self, fn): self.printerFileName = fn self.updatePrinterTab() def setPrinterTabDecor(self, prefix): self.printerTabDecor = prefix self.updatePrinterTab() def updatePrinterTab(self): txt = self.printerTabDecor + self.printerBaseText if self.printerFileName: txt += " <%s>" % self.printerFileName self.nb.SetPageText(0, txt) def setBoardTabFile(self, fn): self.boardFileName = fn self.updateBoardTab() def setBoardTabDecor(self, prefix): self.boardTabDecor = prefix self.updateBoardTab() def updateBoardTab(self): txt = self.boardTabDecor + self.boardBaseText if self.boardFileName: txt += " <%s>" % self.boardFileName self.nb.SetPageText(1, txt) def setHeaters(self, ht): self.heaters = ht self.pgPrinter.setHeaters(ht) def makeMenu(self): file_menu = wx.Menu() file_menu.Append(ID_LOAD_CONFIG, "Load config.h", "Load config.h and its named printer and board files.") self.Bind(wx.EVT_MENU, self.onLoadConfig, id = ID_LOAD_CONFIG) file_menu.Enable(ID_LOAD_CONFIG, False) file_menu.Append(ID_SAVE_CONFIG, "Save config.h", "Save config.h file.") self.Bind(wx.EVT_MENU, self.onSaveConfig, id = ID_SAVE_CONFIG) file_menu.Enable(ID_SAVE_CONFIG, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_PRINTER, "Load printer", "Load a printer configuration file.") self.Bind(wx.EVT_MENU, self.pgPrinter.onLoadConfig, id = ID_LOAD_PRINTER) file_menu.Append(ID_SAVE_PRINTER, "Save printer", "Save printer configuration.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfig, id = ID_SAVE_PRINTER) file_menu.Enable(ID_SAVE_PRINTER, False) file_menu.Append(ID_SAVE_PRINTER_AS, "Save printer as...", "Save printer configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfigAs, id = ID_SAVE_PRINTER_AS) file_menu.Enable(ID_SAVE_PRINTER_AS, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_BOARD, "Load board", "Load a board configuration file.") self.Bind(wx.EVT_MENU, self.pgBoard.onLoadConfig, id = ID_LOAD_BOARD) file_menu.Append(ID_SAVE_BOARD, "Save board", "Save board configuration.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfig, id = ID_SAVE_BOARD) file_menu.Enable(ID_SAVE_BOARD, False) file_menu.Append(ID_SAVE_BOARD_AS, "Save board as...", "Save board configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfigAs, id = ID_SAVE_BOARD_AS) file_menu.Enable(ID_SAVE_BOARD_AS, False) file_menu.AppendSeparator() file_menu.Append(wx.ID_EXIT, "E&xit", "Exit the application.") self.Bind(wx.EVT_MENU, self.onClose, id = wx.ID_EXIT) self.fileMenu = file_menu menu_bar = wx.MenuBar() menu_bar.Append(file_menu, "&File") edit_menu = wx.Menu() edit_menu.Append(ID_SETTINGS, "Settings", "Change settings.") self.Bind(wx.EVT_MENU, self.onEditSettings, id = ID_SETTINGS) self.editMenu = edit_menu menu_bar.Append(edit_menu, "&Edit") build_menu = wx.Menu() build_menu.Append(ID_BUILD, "Build", "Build the executable.") self.Bind(wx.EVT_MENU, self.onBuild, id = ID_BUILD) build_menu.Append(ID_UPLOAD, "Upload", "Upload the executable.") self.Bind(wx.EVT_MENU, self.onUpload, id = ID_UPLOAD) self.buildMenu = build_menu menu_bar.Append(build_menu, "&Build") self.SetMenuBar(menu_bar) loadFlag = self.checkEnableLoadConfig() self.checkEnableUpload() if loadFlag: self.loadConfigFile("config.h") def onSaveBoardConfig(self, evt): rc = self.pgBoard.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSaveBoardConfigAs(self, evt): rc = self.pgBoard.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfig(self, evt): rc = self.pgPrinter.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfigAs(self, evt): rc = self.pgPrinter.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def checkEnableLoadConfig(self): fn = os.path.join(cmd_folder, "config.h") if os.path.isfile(fn): self.fileMenu.Enable(ID_LOAD_CONFIG, True) self.buildMenu.Enable(ID_BUILD, True) return True else: self.fileMenu.Enable(ID_LOAD_CONFIG, False) self.buildMenu.Enable(ID_BUILD, False) return False def checkEnableUpload(self): fn = os.path.join(cmd_folder, "teacup.hex") if os.path.isfile(fn): self.buildMenu.Enable(ID_UPLOAD, True) else: self.buildMenu.Enable(ID_UPLOAD, False) def enableSavePrinter(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_PRINTER, saveFlag) self.fileMenu.Enable(ID_SAVE_PRINTER_AS, saveAsFlag) self.savePrtEna = saveAsFlag self.protPrtFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveBoard(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_BOARD, saveFlag) self.fileMenu.Enable(ID_SAVE_BOARD_AS, saveAsFlag) self.saveBrdEna = saveAsFlag self.protBrdFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveConfig(self, flag): self.fileMenu.Enable(ID_SAVE_CONFIG, flag) def onLoadConfig(self, evt): self.loadConfigFile("config.h") def loadConfigFile(self, fn): if not self.pgPrinter.confirmLoseChanges("load config"): return False if not self.pgBoard.confirmLoseChanges("load config"): return False pfile, bfile = self.getConfigFileNames(fn) if not pfile: self.message("Config file did not contain a printer file " "include statement.", "Config error") return False else: if not self.pgPrinter.loadConfigFile(pfile): self.message("There was a problem loading the printer config file:\n%s" % pfile, "Config error") return False if not bfile: self.message("Config file did not contain a board file " "include statement.", "Config error") return False else: if not self.pgBoard.loadConfigFile(bfile): self.message("There was a problem loading the board config file:\n%s" % bfile, "Config error") return False return True def getConfigFileNames(self, fn): pfile = None bfile = None path = os.path.join(cmd_folder, fn) try: cfgBuffer = list(open(path)) except: self.message("Unable to process config file %s." % fn, "File error") return None, None for ln in cfgBuffer: if not ln.lstrip().startswith("#include"): continue m = reInclude.search(ln) if m: t = m.groups() if len(t) == 1: if "printer." in t[0]: if pfile: self.message("Multiple printer file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: pfile = os.path.join(cmd_folder, t[0]) elif "board." in t[0]: if bfile: self.message("Multiple board file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: bfile = os.path.join(cmd_folder, t[0]) else: self.message("Unable to parse include statement:\n%s" % ln, "Config error") return pfile, bfile def onSaveConfig(self, evt): fn = os.path.join(cmd_folder, "config.h") try: fp = open(fn, 'w') except: self.message("Unable to open config.h for output.", "File error") return False bfn = self.pgBoard.getFileName() if self.pgBoard.isModified() and self.pgBoard.isValid(): if not self.pgBoard.saveConfigFile(bfn): return False else: self.pgBoard.generateTempTables() pfn = self.pgPrinter.getFileName() if self.pgPrinter.isModified() and self.pgPrinter.isValid(): if not self.pgPrinter.saveConfigFile(pfn): return False prefix = cmd_folder + os.path.sep lpfx = len(prefix) if bfn.startswith(prefix): rbfn = bfn[lpfx:] else: rbfn = bfn if pfn.startswith(prefix): rpfn = pfn[lpfx:] else: rpfn = pfn fp.write("\n") fp.write("// Configuration for controller board.\n") fp.write("#include \"%s\"\n" % rbfn) fp.write("\n") fp.write("// Configuration for printer board.\n") fp.write("#include \"%s\"\n" % rpfn) fp.close() self.checkEnableLoadConfig() return True def onBuild(self, evt): self.onBuildorUpload(True) def onUpload(self, evt): self.onBuildorUpload(False) def onBuildorUpload(self, buildFlag): if not (self.pgPrinter.hasData() or self.pgBoard.hasData()): dlg = wx.MessageDialog(self, "Data needs to be loaded. " "Click Yes to load config.h.", "Data missing", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return self.loadConfigFile("config.h") else: if self.pgPrinter.isModified(): dlg = wx.MessageDialog(self, "Printer data needs to be saved. Click " "Yes to save printer configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protPrtFile: rc = self.onSavePrinterConfigAs(None) else: rc = self.onSavePrinterConfig(None) if not rc: return if self.pgBoard.isModified(): dlg = wx.MessageDialog(self, "Board data needs to be saved. Click " "Yes to save board configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protBrdFile: rc = self.onSaveBoardConfigAs(None) else: rc = self.onSaveBoardConfig(None) if not rc: return if not self.verifyConfigLoaded(): dlg = wx.MessageDialog(self, "Loaded configuration does not match the " "config.h file. Click Yes to save config.h.", "Configuration changed", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if not self.onSaveConfig(None): return f_cpu, cpu = self.pgBoard.getCPUInfo() if not cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU type.", "CPU type error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if not f_cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU clock rate.", "CPU clock rate error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if buildFlag: dlg = Build(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() self.checkEnableUpload() else: dlg = Upload(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() def verifyConfigLoaded(self): pfile, bfile = self.getConfigFileNames("config.h") lpfile = self.pgPrinter.getFileName() lbfile = self.pgBoard.getFileName() return ((pfile == lpfile) and (bfile == lbfile)) def onEditSettings(self, evt): dlg = SettingsDlg(self, self.settings) rc = dlg.ShowModal() dlg.Destroy() def message(self, text, title, style = wx.OK + wx.ICON_ERROR): dlg = wx.MessageDialog(self, text, title, style) dlg.ShowModal() dlg.Destroy()
class BoardPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.protFileLoaded = False self.settings = settings self.board = Board(self.settings) self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = self.registerPage(CpuPage, "CPU") self.pgPins = self.registerPage(PinoutsPage, "Pinouts") self.pgDisplay = self.registerPage(DisplayPage, "Display") self.pgHeaters = self.registerPage(HeatersPage, "Heaters") self.pgSensors = self.registerPage( SensorsPage, "Temperature Sensors", heatersPage=self.pgHeaters ) self.pgCommunications = self.registerPage(CommunicationsPage, "Communications") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def registerPage(self, klass, label, *args, **kwargs): page = klass( self, self.nb, len(self.pages), *args, font=self.settings.font, **kwargs ) self.nb.AddPage(page, label) self.pages.append(page) self.titles.append(label) self.pageModified.append(False) self.pageValid.append(True) return page def getCPUInfo(self): return self.board.getCPUInfo() def assertModified(self, pg, flag=True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return True in self.pageModified def isValid(self): return not (False in self.pageValid) def hasData(self): return self.board.hasData() def getFileName(self): return self.board.getFileName() def assertValid(self, pg, flag=True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSaveBoard(False, False) else: self.parent.enableSaveBoard(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setBoardTabDecor(pfx) def setHeaters(self, ht): self.parent.setHeaters(ht) 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 + "?\n" "There are changes to your board " "configuration 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 board configuration"): return if platform.startswith("darwin"): # Mac OS X appears to be a bit limited on wildcards. wildcard = "Board configuration (board.*.h)|*.h" else: wildcard = "Board configuration (board.*.h)|board.*.h" dlg = wx.FileDialog( self, message="Choose a board config file", defaultDir=self.dir, defaultFile="", wildcard=wildcard, style=wx.FD_OPEN | wx.FD_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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog( self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR, ) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): ok, file = self.board.loadConfigFile(fn) if not ok: return ok, file if os.path.basename(fn) in protectedFiles: self.parent.enableSaveBoard(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSaveBoard(True, True) self.parent.setBoardTabFile(os.path.basename(fn)) self.pgHeaters.setCandidatePins(self.board.candHeatPins) self.pgSensors.setCandidatePins(self.board.candThermPins) self.pgCpu.setCandidateProcessors(self.board.candProcessors) self.pgCpu.setCandidateClocks(self.board.candClocks) for pg in self.pages: pg.insertValues(self.board.cfgValues) pg.setHelpText(self.board.helpText) self.pgSensors.setSensors(self.board.sensors) self.pgHeaters.setHeaters(self.board.heaters) return True, None def onSaveConfig(self, evt): path = self.getFileName() return self.saveConfigFile(path) def onSaveConfigAs(self, evt): if platform.startswith("darwin"): # Mac OS X appears to be a bit limited on wildcards. wildcard = "Board configuration (board.*.h)|*.h" else: wildcard = "Board configuration (board.*.h)|board.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setBoardTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSaveBoard(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog( self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR, ) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("board."): dlg = wx.MessageDialog( self, "Illegal file name: %s.\n" 'File name must begin with "board."' % path, "Illegal file name", wx.OK + wx.ICON_ERROR, ) dlg.ShowModal() dlg.Destroy() return False values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: self.board.saveConfigFile(path, values) except: dlg = wx.MessageDialog( self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR, ) dlg.ShowModal() dlg.Destroy() return False return self.generateTempTables() def generateTempTables(self): if not generateTempTables(self.board.sensors, self.settings): dlg = wx.MessageDialog( self, "Error writing to file thermistortable.h.", "File error", wx.OK + wx.ICON_ERROR, ) dlg.ShowModal() dlg.Destroy() return False return True
class PrinterPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.configFile = None self.protFileLoaded = False self.settings = settings self.cfgValues = {} self.heaters = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = MechanicalPage(self, self.nb, len(self.pages), self.settings.font) 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), self.settings.font) 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.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages), self.settings.font) 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) self.SetSizer(sz) self.Fit() def getFileName(self): return self.configFile def assertModified(self, pg, flag=True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def assertValid(self, pg, flag=True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSavePrinter(False, False) else: self.parent.enableSavePrinter(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setPrinterTabDecor(pfx) def setHeaters(self, ht): return self.pgMiscellaneous.setHeaters(ht) 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 + "?\n" "There are changes to your printer " "configuration 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 printer configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.h" dlg = wx.FileDialog(self, message="Choose a printer 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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): cfgFn = os.path.join(self.cfgDir, "printer.generic.h") try: self.cfgBuffer = list(open(cfgFn)) except: return False, cfgFn try: self.userBuffer = list(open(fn)) except: return False, fn self.configFile = fn self.processors = [] gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.cfgNames = [] self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace( "\n\n ", "\n\n ") helpTextString = helpTextString.replace( "\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseDefineName(ln): continue gatheringHelpText = False prevLines = "" for ln in self.userBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False continue if reHelpTextStart.match(ln): gatheringHelpText = True continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseDefineValue(ln): continue if os.path.basename(fn) in protectedFiles: self.parent.enableSavePrinter(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSavePrinter(True, True) self.parent.setPrinterTabFile(os.path.basename(fn)) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) k = 'DC_EXTRUDER' if k in self.cfgValues.keys() and self.cfgValues[k][1] == True: self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0]) else: self.pgMiscellaneous.setOriginalHeater(None) return True, None def parseDefineName(self, ln): m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1: self.cfgNames.append(t[0]) return True return False def parseDefineValue(self, ln): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt[0], True return True elif len(tt) > 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt, True return True m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2 and (t[0] in self.cfgNames): if reDefineBL.search(ln): self.cfgValues[t[0]] = t[1], True else: self.cfgValues[t[0]] = t[1], False return True m = reDefBoolBL.search(ln) if m: t = m.groups() if len(t) == 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = True return True return False def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Printer configuration (printer.*.h)|printer.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setPrinterTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSavePrinter(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog( self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("printer."): dlg = wx.MessageDialog( self, "Illegal file name: %s.\n" "File name must begin with \"printer.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] for ln in self.cfgBuffer: m = reDefine.match(ln) if m: t = m.groups() if len(t) == 2 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v[1] == False: fp.write("//") fp.write(defineValueFormat % (t[0], v[0])) else: if t[0] == 'CANNED_CYCLE': # Known to be absent in the GUI. Worse, this value is replaced # by the one in the metadata file. # # TODO: make value reading above recognize wether this value is # commented out or not. Reading the value its self works # already. Hint: it's the rule using reDefQS, reDefQSm, etc. # # TODO: add a multiline text field in the GUI to deal with this. # # TODO: write this value out properly. In /* comments */, if # disabled. # # TODO: currently, the lines beyond the ones with the #define are # treated like arbitrary comments. Having the former TODOs # done, this will lead to duplicates. fp.write(ln) else: print "Value key " + t[0] + " not found in GUI." continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v == "" or v == False: fp.write("//") fp.write(defineBoolFormat % t[0]) else: print "Boolean key " + t[0] + " not found in GUI." continue fp.write(ln) fp.close() return True
class Page: def __init__(self, font): self.modified = False self.valid = True self.fieldValid = {} self.textControls = {} self.checkBoxes = {} self.radioButtons = {} self.radioButtonBoxes = {} self.choices = {} self.deco = Decoration() self.font = font self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) def enableAll(self, flag = True): for c in self.textControls.keys(): self.textControls[c].Enable(flag) for c in self.checkBoxes.keys(): self.checkBoxes[c].Enable(flag) for c in self.radioButtons.keys(): self.radioButtons[c].Enable(flag) for c in self.choices.keys(): self.choices[c].Enable(flag) def addTextCtrl(self, name, labelWidth, validator): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name] + " ", size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetTcLabel) tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name) tc.SetFont(self.font) self.fieldValid[name] = True tc.Bind(wx.EVT_TEXT, validator) self.textControls[name] = tc lsz.Add(tc) return lsz def addCheckBox(self, name, validator): if name in self.labels.keys(): lbl = self.labels[name] else: lbl = name cb = wx.CheckBox(self, wx.ID_ANY, lbl) cb.SetFont(self.font) cb.Bind(wx.EVT_CHECKBOX, validator) self.checkBoxes[name] = cb return cb def addRadioButton(self, name, style, validator, sbox = None): rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style) rb.SetFont(self.font) self.Bind(wx.EVT_RADIOBUTTON, validator, rb) self.radioButtons[name] = rb if sbox is not None: self.radioButtonBoxes[name] = sbox return rb def addChoice(self, name, choices, selection, labelWidth, validator, size = (-1, -1)): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, choices = choices, size = size, name = name) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, validator) ch.SetSelection(selection) lsz.Add(ch) self.choices[name] = ch return lsz def addPinChoice(self, name, choiceVal, pins, allowBlank , labelWidth): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) if allowBlank: opts = ["-"] + pins else: opts = pins ch = wx.Choice(self, wx.ID_ANY, choices = opts, name = name, style = wx.CB_SORT) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, self.onChoice) self.choices[name] = ch try: sv = self.pinNames.index(choiceVal) except: sv = 0 ch.SetSelection(sv) lsz.Add(ch) return lsz def setChoice(self, name, cfgValues, default): if name in cfgValues.keys(): bv = cfgValues[name] else: bv = default s = self.choices[name].FindString(bv) if s < 0: s = self.choices[name].FindString(default) if s < 0: 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 onCheckBox(self, evt): self.assertModified(True) evt.Skip() def setHelpText(self, ht): for k in self.textControls.keys(): if k in ht.keys(): self.textControls[k].SetToolTipString(ht[k]) for k in self.checkBoxes.keys(): if k in ht.keys(): self.checkBoxes[k].SetToolTipString(ht[k]) for k in self.radioButtons.keys(): if k in ht.keys(): self.radioButtons[k].SetToolTipString(ht[k]) if k in self.radioButtonBoxes.keys(): self.radioButtonBoxes[k].SetToolTipString(ht[k]) for k in self.choices.keys(): if k in ht.keys(): self.choices[k].SetToolTipString(ht[k]) def getValues(self): self.assertModified(False) result = {} for k in self.checkBoxes.keys(): cb = self.checkBoxes[k] result[k] = cb.IsChecked() for k in self.textControls.keys(): v = self.textControls[k].GetValue() result[k] = v for k in self.radioButtons.keys(): result[k] = self.radioButtons[k].GetValue() for k in self.choices.keys(): v = self.choices[k].GetSelection() result[k] = self.choices[k].GetString(v) return result 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 PrinterPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.configFile = None self.protFileLoaded = False self.settings = settings self.cfgValues = {} self.heaters = [] self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = MechanicalPage(self, self.nb, len(self.pages), self.settings.font) 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), self.settings.font) 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.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages), self.settings.font) 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) self.SetSizer(sz) self.Fit() def getFileName(self): return self.configFile def assertModified(self, pg, flag = True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def assertValid(self, pg, flag = True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSavePrinter(False, False) else: self.parent.enableSavePrinter(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setPrinterTabDecor(pfx) def setHeaters(self, ht): return self.pgMiscellaneous.setHeaters(ht) 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 + "?\n" "There are changes to your printer " "configuration 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 printer configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.h" dlg = wx.FileDialog(self, message = "Choose a printer 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) rc = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): try: self.cfgBuffer = list(open(fn)) except: return False self.configFile = fn self.processors = [] gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace("\n\n ", "\n\n ") helpTextString = helpTextString.replace("\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if ln.lstrip().startswith("//"): continue if ln.lstrip().startswith("#define"): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1: self.cfgValues[t[0]] = tt[0] continue elif len(tt) > 1: self.cfgValues[t[0]] = tt 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 if os.path.basename(fn) in protectedFiles: self.parent.enableSavePrinter(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSavePrinter(True, True) self.parent.setPrinterTabFile(os.path.basename(fn)) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) k = 'DC_EXTRUDER' if k in self.cfgValues.keys(): self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k]) else: self.pgMiscellaneous.setOriginalHeater(None) return True def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Printer configuration (printer.*.h)|printer.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setPrinterTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSavePrinter(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog(self, "Unable to overwrite %s." % path, "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("printer."): dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" "File name must begin with \"printer.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} labelFound = [] for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] for ln in self.cfgBuffer: m = reDefineBL.match(ln) if m: t = m.groups() if len(t) == 2: if t[0] in values.keys() and values[t[0]] != "": fp.write(defineValueFormat % (t[0], values[t[0]])) self.cfgValues[t[0]] = values[t[0]] labelFound.append(t[0]) elif t[0] in values.keys(): fp.write("//" + ln) if t[0] in self.cfgValues.keys(): del self.cfgValues[t[0]] labelFound.append(t[0]) else: fp.write(ln) continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1: if t[0] in values.keys() and values[t[0]]: fp.write(defineBoolFormat % t[0]) self.cfgValues[t[0]] = True labelFound.append(t[0]) elif t[0] in values.keys(): fp.write("//" + ln) if t[0] in self.cfgValues.keys(): del self.cfgValues[t[0]] labelFound.append(t[0]) else: fp.write(ln) continue m = reCommDefBL.match(ln) if m: t = m.groups() if len(t) == 2: if t[0] in values.keys() and values[t[0]] != "": fp.write(defineValueFormat % (t[0], values[t[0]])) self.cfgValues[t[0]] = values[t[0]] labelFound.append(t[0]) elif t[0] in values.keys(): fp.write(ln) labelFound.append(t[0]) else: fp.write(ln) continue m = reCommDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1: if t[0] in values.keys() and values[t[0]]: fp.write(defineBoolFormat % t[0]) self.cfgValues[t[0]] = True labelFound.append(t[0]) elif t[0] in values.keys(): fp.write(ln) labelFound.append(t[0]) else: fp.write(ln) continue fp.write(ln) for k in labelFound: del values[k] newLabels = "" for k in values.keys(): if newLabels == "": newLabels = k else: newLabels += ", " + k self.addNewDefine(fp, k, values[k]) if newLabels != "": dlg = wx.MessageDialog(self, "New defines added to printer config:\n" + newLabels, "New defines", wx.OK + wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() fp.close() return True def addNewDefine(self, fp, key, val): fp.write("\n") fp.write("/** \\def %s\n" % key) fp.write(" Add help text here.\n") fp.write("*/\n") if val == True: fp.write(defineBoolFormat % key) elif val == False: fp.write("//#define %s\n" % key) elif val == "": fp.write("//#define %s\n" % key) else: fp.write(defineValueFormat % (key, val))
class Page: def __init__(self, font): self.modified = False self.valid = True self.fieldValid = {} self.textControls = {} self.textControlsOriginal = {} self.checkBoxes = {} self.radioButtons = {} self.radioButtonBoxes = {} self.choices = {} self.choicesOriginal = {} self.boolChoices = {} self.deco = Decoration() self.font = font self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) def enableAll(self, flag = True): for c in self.textControls.keys(): self.textControls[c].Enable(flag) for c in self.checkBoxes.keys(): self.checkBoxes[c].Enable(flag) for c in self.radioButtons.keys(): self.radioButtons[c].Enable(flag) for c in self.choices.keys(): self.choices[c].Enable(flag) def addTextCtrl(self, name, labelWidth, validator): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name] + " ", size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetTcLabel) tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name) tc.SetFont(self.font) self.fieldValid[name] = True tc.Bind(wx.EVT_TEXT, validator) self.textControls[name] = tc lsz.Add(tc) return lsz def addCheckBox(self, name, validator): if name in self.labels.keys(): lbl = self.labels[name] else: lbl = name cb = wx.CheckBox(self, wx.ID_ANY, lbl) cb.SetFont(self.font) cb.Bind(wx.EVT_CHECKBOX, validator) self.checkBoxes[name] = cb return cb def addRadioButton(self, name, style, validator, sbox = None): rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style) rb.SetFont(self.font) self.Bind(wx.EVT_RADIOBUTTON, validator, rb) self.radioButtons[name] = rb if sbox is not None: self.radioButtonBoxes[name] = sbox return rb def addChoice(self, name, choices, selection, labelWidth, validator, size = (-1, -1)): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, choices = choices, size = size, name = name) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, validator) ch.SetSelection(selection) lsz.Add(ch) self.choices[name] = ch return lsz def addPinChoice(self, name, choiceVal, pins, allowBlank , labelWidth): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) if allowBlank: opts = ["-"] + pins else: opts = pins ch = wx.Choice(self, wx.ID_ANY, choices = opts, name = name, style = wx.CB_SORT) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, self.onChoice) self.choices[name] = ch try: sv = self.pinNames.index(choiceVal) except: sv = 0 ch.SetSelection(sv) lsz.Add(ch) return lsz # addChoice handles #defines with value, this handles choices for # sets of boolean #defines. def addBoolChoice(self, name, allowBlank, labelWidth, validator, size = (-1, -1)): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size = (labelWidth, -1), style = wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, size = size, name = name) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, validator) if allowBlank: ch.Append("(none)") ch.SetSelection(0) lsz.Add(ch) self.boolChoices[name] = ch return lsz def setChoice(self, name, cfgValues, default): if name in cfgValues.keys() and cfgValues[name][1] == True: bv = cfgValues[name][0] else: bv = default s = self.choices[name].FindString(bv) if s < 0: s = self.choices[name].FindString(default) if s < 0: 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 onCheckBox(self, evt): self.assertModified(True) evt.Skip() def setHelpText(self, ht): for k in self.textControls.keys(): if k in ht.keys(): self.textControls[k].SetToolTipString(ht[k]) for k in self.checkBoxes.keys(): if k in ht.keys(): self.checkBoxes[k].SetToolTipString(ht[k]) for k in self.radioButtons.keys(): if k in ht.keys(): self.radioButtons[k].SetToolTipString(ht[k]) if k in self.radioButtonBoxes.keys(): self.radioButtonBoxes[k].SetToolTipString(ht[k]) for k in self.choices.keys(): if k in ht.keys(): self.choices[k].SetToolTipString(ht[k]) for k in self.boolChoices.keys(): for candidate in ht.keys(): if candidate.startswith(k): self.boolChoices[k].SetToolTipString(ht[candidate]) break def insertValues(self, cfgValues): self.assertValid(True) self.enableAll(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.textControlsOriginal[k] = cfgValues[k] if cfgValues[k][1] == True: self.textControls[k].SetValue(str(cfgValues[k][0])) else: self.textControls[k].SetValue("") else: print "Key " + k + " not found in config data." for k in self.choices.keys(): if k in cfgValues.keys(): self.choicesOriginal[k] = cfgValues[k] self.setChoice(k, cfgValues, "-") else: print "Key " + k + " not found in config data." for k in self.boolChoices.keys(): choice = self.boolChoices[k] # Remove items left behind from the previous configuration. while (choice.GetCount() and not choice.GetString(choice.GetCount() - 1).startswith('(')): choice.Delete(choice.GetCount() - 1) # Add items found in this configuration. for cfg in cfgValues.keys(): if cfg.startswith(k): if cfg in self.labels.keys(): choice.Append(self.labels[cfg]) else: choice.Append(cfg) # As we want to write the configuration name later, not the user # friendly string, we store the configuration name as client data. n = choice.GetCount() - 1 choice.SetClientData(n, cfg) if cfgValues[cfg]: choice.SetSelection(n) self.assertModified(False) def getValues(self): self.assertModified(False) result = {} for k in self.checkBoxes.keys(): cb = self.checkBoxes[k] result[k] = cb.IsChecked() for k in self.textControls.keys(): v = self.textControls[k].GetValue() if v == "": if k in self.textControlsOriginal.keys(): result[k] = self.textControlsOriginal[k][0], False else: result[k] = "", False else: result[k] = v, True for k in self.radioButtons.keys(): result[k] = self.radioButtons[k].GetValue(), True for k in self.choices.keys(): v = self.choices[k].GetSelection() s = self.choices[k].GetString(v) if s == "-": if k in self.choicesOriginal.keys(): result[k] = self.choicesOriginal[k][0], False else: result[k] = "", False else: result[k] = s, True for k in self.boolChoices.keys(): choice = self.boolChoices[k] for i in range(choice.GetCount()): s = choice.GetClientData(i) if s: result[s] = (i == choice.GetSelection()) return result 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 BoardPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.settings = settings self.protFileLoaded = False self.deco = Decoration() self.configFile = None self.cfgValues = {} self.heaters = [] self.sensors = [] self.candHeatPins = [] self.candThermPins = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = self.registerPage(CpuPage, "CPU") self.pgPins = self.registerPage(PinoutsPage, "Pinouts") self.pgDisplay = self.registerPage(DisplayPage, "Display") self.pgHeaters = self.registerPage(HeatersPage, "Heaters") self.pgSensors = self.registerPage(SensorsPage, "Temperature Sensors", heatersPage=self.pgHeaters) self.pgCommunications = self.registerPage(CommunicationsPage, "Communications") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def registerPage(self, klass, label, *args, **kwargs): page = klass(self, self.nb, len(self.pages), *args, font=self.settings.font, **kwargs) self.nb.AddPage(page, label) self.pages.append(page) self.titles.append(label) self.pageModified.append(False) self.pageValid.append(True) return page def getCPUInfo(self): vF_CPU = None if 'F_CPU' in self.cfgValues.keys(): vF_CPU = self.cfgValues['F_CPU'][0] vCPU = None if 'CPU' in self.cfgValues.keys(): vCPU = self.cfgValues['CPU'][0] return vF_CPU, vCPU def assertModified(self, pg, flag=True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def getFileName(self): return self.configFile def assertValid(self, pg, flag=True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSaveBoard(False, False) else: self.parent.enableSaveBoard(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setBoardTabDecor(pfx) def setHeaters(self, ht): self.parent.setHeaters(ht) 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 + "?\n" "There are changes to your board " "configuration 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 board configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Board configuration (board.*.h)|*.h" else: wildcard = "Board configuration (board.*.h)|board.*.h" dlg = wx.FileDialog(self, message="Choose a board 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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): cfgFn = os.path.join(self.cfgDir, "board.generic.h") try: self.cfgBuffer = list(open(cfgFn)) except: return False, cfgFn try: self.userBuffer = list(open(fn)) except: return False, fn self.configFile = fn self.processors = [] self.sensors = [] self.heaters = [] self.candHeatPins = [] self.candThermPins = [] self.candProcessors = [] self.candClocks = [] self.tempTables = {} gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.cfgNames = [] self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace( "\n\n ", "\n\n ") helpTextString = helpTextString.replace( "\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseCandidateValues(ln): continue if self.parseDefineName(ln): continue # Ignore candidates in the metadata file. self.candHeatPins = [] self.candThermPins = [] self.candProcessors = [] self.candClocks = [] self.tempTables = {} gatheringHelpText = False prevLines = "" for ln in self.userBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False continue if reHelpTextStart.match(ln): gatheringHelpText = True continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseCandidateValues(ln): continue elif self.parseDefineValue(ln): continue 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 # Parsing done. All parsed stuff is now in these arrays and dicts. # Uncomment for debugging. #print self.processors #print self.sensors #print self.heaters #print self.candHeatPins #print self.candThermPins #print self.candProcessors #print self.candClocks #print self.tempTables #print self.cfgValues # #defines with a value and booleans. #print self.cfgNames # Names found in the generic file. #print self.helpText for k in range(len(self.sensors)): tn = self.sensors[k][0].upper() if tn in self.tempTables.keys(): self.sensors[k][3] = self.tempTables[tn] else: self.sensors[k][3] = None if os.path.basename(fn) in protectedFiles: self.parent.enableSaveBoard(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSaveBoard(True, True) self.parent.setBoardTabFile(os.path.basename(fn)) self.pgHeaters.setCandidatePins(self.candHeatPins) self.pgSensors.setCandidatePins(self.candThermPins) self.pgCpu.setCandidateProcessors(self.candProcessors) self.pgCpu.setCandidateClocks(self.candClocks) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) self.pgSensors.setSensors(self.sensors) self.pgHeaters.setHeaters(self.heaters) return True, None def parseDefineName(self, ln): m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1: self.cfgNames.append(t[0]) return True return False def parseDefineValue(self, ln): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt[0], True return True elif len(tt) > 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt, True return True m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2 and (t[0] in self.cfgNames): if reDefineBL.search(ln): self.cfgValues[t[0]] = t[1], True else: self.cfgValues[t[0]] = t[1], False return True m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1 and (t[0] in self.cfgNames): if reDefBoolBL.search(ln): self.cfgValues[t[0]] = True else: self.cfgValues[t[0]] = False return True return False def parseCandidateValues(self, ln): m = reCandThermPins.match(ln) if m: t = m.groups() if len(t) == 1: self.candThermPins.append(t[0]) return True m = reCandHeatPins.match(ln) if m: t = m.groups() if len(t) == 1: self.candHeatPins.append(t[0]) return True m = reCandProcessors.match(ln) if m: t = m.groups() if len(t) == 1: self.candProcessors.append(t[0]) return True m = reCandCPUClocks.match(ln) if m: t = m.groups() if len(t) == 1: self.candClocks.append(t[0]) return True m = reDefTT.match(ln) if m: t = m.groups() if len(t) == 2: s = self.parseTempTable(t[1]) if s: self.tempTables[t[0]] = s return True return False def parseSensor(self, s): m = reSensor.search(s) if m: t = m.groups() if len(t) == 4: return list(t) return None def parseHeater(self, s): m = reHeater4.search(s) if m: t = m.groups() if len(t) == 4: return list(t) # reHeater3 deprecated, for compatibility with old config files only. m = reHeater3.search(s) if m: t = m.groups() if len(t) == 3: t = list(t) t.insert(2, '0') return t # End of deprecated part. return None def parseTempTable(self, s): m = reTempTable4.search(s) if m: t = m.groups() if len(t) == 4: return list(t) m = reTempTable7.search(s) if m: t = m.groups() if len(t) == 7: return list(t) return None def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Board configuration (board.*.h)|board.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setBoardTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSaveBoard(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog( self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("board."): dlg = wx.MessageDialog( self, "Illegal file name: %s.\n" "File name must begin with \"board.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] skipToSensorEnd = False skipToHeaterEnd = False candThermPinsWritten = False candHeatPinsWritten = False candProcessorsWritten = False candCPUClocksWritten = False for ln in self.cfgBuffer: m = reStartSensors.match(ln) if m: fp.write(ln) fp.write("// name type pin " "additional\n") ttString = "\n" ttString += "// Beta algorithm r0 beta r2 vadc\n" ttString += "// Steinhart-Hart rp t0 r0 t1 " ttString += "r1 t2 r2\n" for s in self.sensors: sstr = "%-10s%-15s%-7s" % ((s[0] + ","), (s[1] + ","), (s[2] + ",")) if s[3] is None: sstr += "0" else: sstr += "THERMISTOR_%s" % s[0].upper() tt = s[3] if len(tt) == 4: ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-6s%s)\n" % \ (s[0].upper(), (tt[0] + ","), (tt[1] + ","), (tt[2] + ","), tt[3]) else: ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-8s%-6s%-8s%-6s%s)\n" % \ (s[0].upper(), (tt[0] + ","), (tt[1] + ","), (tt[2] + ","), (tt[3] + ","), (tt[4] + ","), (tt[5] + ","), tt[6]) fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr) fp.write(ttString) skipToSensorEnd = True continue if skipToSensorEnd: m = reEndSensors.match(ln) if m: fp.write(ln) skipToSensorEnd = False continue m = reStartHeaters.match(ln) if m: fp.write(ln) fp.write("// name pin invert pwm\n") for s in self.heaters: sstr = "%-10s%-9s%-8s%s" % ((s[0] + ","), (s[1] + ","), (s[2] + ","), s[3]) fp.write("DEFINE_HEATER(%s)\n" % sstr) fp.write("\n") for s in self.heaters: fp.write(defineHeaterFormat % (s[0].upper(), s[0])) skipToHeaterEnd = True continue if skipToHeaterEnd: m = reEndHeaters.match(ln) if m: fp.write(ln) skipToHeaterEnd = False continue if reCandThermPins.match(ln): if not candThermPinsWritten: for pin in self.candThermPins: fp.write("//#define TEMP_SENSOR_PIN " + pin + "\n") candThermPinsWritten = True continue if reCandHeatPins.match(ln): if not candHeatPinsWritten: for pin in self.candHeatPins: fp.write("//#define HEATER_PIN " + pin + "\n") candHeatPinsWritten = True continue if reCandProcessors.match(ln): if not candProcessorsWritten: for pin in self.candProcessors: fp.write("//#define CPU_TYPE " + pin + "\n") candProcessorsWritten = True continue if reCandCPUClocks.match(ln): if not candCPUClocksWritten: for pin in self.candClocks: fp.write("//#define F_CPU_OPT " + pin + "\n") candCPUClocksWritten = True continue m = reDefine.match(ln) if m: t = m.groups() if len(t) == 2 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v[1] == False: fp.write("//") fp.write(defineValueFormat % (t[0], v[0])) else: print "Value key " + t[0] + " not found in GUI." continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v == "" or v == False: fp.write("//") fp.write(defineBoolFormat % t[0]) else: if t[0] == 'MOTHERBOARD': # Known to be absent in the GUI, also won't be added anytime soon. fp.write(ln) else: print "Boolean key " + t[0] + " not found in GUI." continue fp.write(ln) fp.close() return self.generateTempTables() def generateTempTables(self): if not generateTempTables(self.sensors, self.settings): dlg = wx.MessageDialog(self, "Error writing to file thermistortable.h.", "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False return True
class BoardPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.settings = settings self.protFileLoaded = False self.deco = Decoration() self.configFile = None self.cfgValues = {} self.heaters = [] self.sensors = [] self.candHeatPins = [] self.candThermPins = [] self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgCpu = CpuPage(self, self.nb, len(self.pages), self.settings.font) text = "CPU" self.nb.AddPage(self.pgCpu, text) self.pages.append(self.pgCpu) self.titles.append(text) self.pageModified.append(False) self.pageValid.append(True) self.pgPins = PinoutsPage(self, self.nb, len(self.pages), self.settings.font) 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.pgHeaters = HeatersPage(self, self.nb, len(self.pages), self.settings.font) 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.pgSensors = SensorsPage(self, self.nb, len(self.pages), self.pgHeaters, self.settings.font) 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.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages), self.settings.font) 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) sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def getCPUInfo(self): vF_CPU = None if 'F_CPU' in self.cfgValues.keys(): vF_CPU = self.cfgValues['F_CPU'] vCPU = None if 'CPU' in self.cfgValues.keys(): vCPU = self.cfgValues['CPU'] return vF_CPU, vCPU def assertModified(self, pg, flag = True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def getFileName(self): return self.configFile def assertValid(self, pg, flag = True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSaveBoard(False, False) else: self.parent.enableSaveBoard(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setBoardTabDecor(pfx) def setHeaters(self, ht): self.parent.setHeaters(ht) 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 + "?\n" "There are changes to your board " "configuration 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 board configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Board configuration (board.*.h)|*.h" else: wildcard = "Board configuration (board.*.h)|board.*.h" dlg = wx.FileDialog(self, message = "Choose a board 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) rc = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): try: self.cfgBuffer = list(open(fn)) except: return False self.configFile = fn self.processors = [] self.sensors = [] self.heaters = [] self.candHeatPins = [] self.candThermPins = [] self.candProcessors = [] self.candClocks = [] tempTables = {} gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace("\n\n ", "\n\n ") helpTextString = helpTextString.replace("\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if ln.lstrip().startswith("//"): m = reCandThermPins.match(ln) if m: t = m.groups() if len(t) == 1: self.candThermPins.append(t[0]) continue m = reCandHeatPins.match(ln) if m: t = m.groups() if len(t) == 1: self.candHeatPins.append(t[0]) continue m = reCandProcessors.match(ln) if m: t = m.groups() if len(t) == 1: self.candProcessors.append(t[0]) continue m = reCandCPUClocks.match(ln) if m: t = m.groups() if len(t) == 1: self.candClocks.append(t[0]) continue m = reDefTT.match(ln) if m: t = m.groups() if len(t) == 2: s = self.parseTempTable(t[1]) if s: tempTables[t[0]] = s continue continue if ln.lstrip().startswith("#define"): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1: self.cfgValues[t[0]] = tt[0] continue elif len(tt) > 1: self.cfgValues[t[0]] = tt continue m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2: self.cfgValues[t[0]] = t[1] if reFloatAttr.search(ln): pass 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 for k in range(len(self.sensors)): tn = self.sensors[k][0].upper() if tn in tempTables.keys(): self.sensors[k][3] = tempTables[tn] else: self.sensors[k][3] = None if os.path.basename(fn) in protectedFiles: self.parent.enableSaveBoard(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSaveBoard(True, True) self.parent.setBoardTabFile(os.path.basename(fn)) self.pgHeaters.setCandidatePins(self.candHeatPins) self.pgSensors.setCandidatePins(self.candThermPins) self.pgCpu.setCandidateProcessors(self.candProcessors) self.pgCpu.setCandidateClocks(self.candClocks) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) self.pgSensors.setSensors(self.sensors) self.pgHeaters.setHeaters(self.heaters) return True def parseSensor(self, s): m = reSensor.search(s) if m: t = m.groups() if len(t) == 4: return list(t) return None def parseHeater(self, s): m = reHeater.search(s) if m: t = m.groups() if len(t) == 3: return list(t) return None def parseTempTable(self, s): m = reTempTable4.search(s) if m: t = m.groups() if len(t) == 4: return list(t) m = reTempTable7.search(s) if m: t = m.groups() if len(t) == 7: return list(t) return None def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Board configuration (board.*.h)|board.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setBoardTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSaveBoard(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog(self, "Unable to overwrite %s." % path, "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("board."): dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" "File name must begin with \"board.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} labelFound = [] for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] skipToSensorEnd = False skipToHeaterEnd = False tempTables = {} for ln in self.cfgBuffer: m = reStartSensors.match(ln) if m: fp.write(ln) fp.write("// name type pin " "additional\n"); ttString = "\n" ttString += "// Beta algorithm r0 beta r2 vadc\n" ttString += "// Steinhart-Hart rp t0 r0 t1 " ttString += "r1 t2 r2\n" for s in self.sensors: sstr = "%-10s%-15s%-7s" % ((s[0] + ","), (s[1] + ","), (s[2] + ",")) if s[3] is None: sstr += "0" else: sstr += "THERMISTOR_%s" % s[0].upper() tt = s[3] if len(tt) == 4: ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-6s%s)\n" % \ (s[0].upper(), (tt[0] + ","), (tt[1] + ","), (tt[2] + ","), tt[3]) else: ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-8s%-6s%-8s%-6s%s)\n" % \ (s[0].upper(), (tt[0] + ","), (tt[1] + ","), (tt[2] + ","), (tt[3] + ","), (tt[4] + ","), (tt[5] + ","), tt[6]) fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr) fp.write(ttString) skipToSensorEnd = True continue if skipToSensorEnd: m = reEndSensors.match(ln) if m: fp.write(ln) skipToSensorEnd = False continue m = reStartHeaters.match(ln) if m: fp.write(ln) fp.write("// name port pwm\n") for s in self.heaters: sstr = "%-10s%-7s%s" % ((s[0] + ","), (s[1] + ","), s[2]) fp.write("DEFINE_HEATER(%s)\n" % sstr) fp.write("\n") for s in self.heaters: fp.write(defineHeaterFormat % (s[0].upper(), s[0])) skipToHeaterEnd = True continue if skipToHeaterEnd: m = reEndHeaters.match(ln) if m: fp.write(ln) skipToHeaterEnd = False continue m = reDefineBL.match(ln) if m: t = m.groups() if len(t) == 2: if t[0] in values.keys() and values[t[0]] != "": fp.write(defineValueFormat % (t[0], values[t[0]])) self.cfgValues[t[0]] = values[t[0]] labelFound.append(t[0]) elif t[0] in values.keys(): fp.write("//" + ln) if t[0] in self.cfgValues.keys(): del self.cfgValues[t[0]] labelFound.append(t[0]) else: fp.write(ln) continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1: if t[0] in values.keys() and values[t[0]]: fp.write(defineBoolFormat % t[0]) self.cfgValues[t[0]] = True labelFound.append(t[0]) elif t[0] in values.keys(): fp.write("//" + ln) if t[0] in self.cfgValues.keys(): del self.cfgValues[t[0]] labelFound.append(t[0]) else: fp.write(ln) continue m = reCommDefBL.match(ln) if m: t = m.groups() if len(t) == 2: if t[0] in values.keys() and values[t[0]] != "": fp.write(defineValueFormat % (t[0], values[t[0]])) self.cfgValues[t[0]] = values[t[0]] labelFound.append(t[0]) elif t[0] in values.keys(): fp.write(ln) labelFound.append(t[0]) else: fp.write(ln) continue m = reCommDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1: if t[0] in values.keys() and values[t[0]]: fp.write(defineBoolFormat % t[0]) self.cfgValues[t[0]] = True labelFound.append(t[0]) elif t[0] in values.keys(): fp.write(ln) labelFound.append(t[0]) else: fp.write(ln) continue fp.write(ln) for k in labelFound: del values[k] newLabels = "" for k in values.keys(): if newLabels == "": newLabels = k else: newLabels += ", " + k self.addNewDefine(fp, k, values[k]) if newLabels != "": dlg = wx.MessageDialog(self, "New defines added to board config:\n" + newLabels, "New defines", wx.OK + wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() fp.close() return self.generateTempTables() def generateTempTables(self): if not generateTempTables(self.sensors, self.settings): dlg = wx.MessageDialog(self, "Error writing to file thermistortable.h.", "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False return True def addNewDefine(self, fp, key, val): fp.write("\n") fp.write("/** \\def %s\n" % key) fp.write(" Add help text here.\n") fp.write("*/\n") if val == True: fp.write(defineBoolFormat % key) elif val == False: fp.write("//#define %s\n" % key) elif val == "": fp.write("//#define %s\n" % key) else: fp.write(defineValueFormat % (key, val))
class Page: def __init__(self, font): self.modified = False self.valid = True self.fieldValid = {} self.textControls = {} self.textControlsOriginal = {} self.checkBoxes = {} self.radioButtons = {} self.radioButtonBoxes = {} self.choices = {} self.choicesOriginal = {} self.deco = Decoration() self.font = font self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) def enableAll(self, flag=True): for c in self.textControls.keys(): self.textControls[c].Enable(flag) for c in self.checkBoxes.keys(): self.checkBoxes[c].Enable(flag) for c in self.radioButtons.keys(): self.radioButtons[c].Enable(flag) for c in self.choices.keys(): self.choices[c].Enable(flag) def addTextCtrl(self, name, labelWidth, validator): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name] + " ", size=(labelWidth, -1), style=wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetTcLabel) tc = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_RIGHT, name=name) tc.SetFont(self.font) self.fieldValid[name] = True tc.Bind(wx.EVT_TEXT, validator) self.textControls[name] = tc lsz.Add(tc) return lsz def addCheckBox(self, name, validator): if name in self.labels.keys(): lbl = self.labels[name] else: lbl = name cb = wx.CheckBox(self, wx.ID_ANY, lbl) cb.SetFont(self.font) cb.Bind(wx.EVT_CHECKBOX, validator) self.checkBoxes[name] = cb return cb def addRadioButton(self, name, style, validator, sbox=None): rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style=style) rb.SetFont(self.font) self.Bind(wx.EVT_RADIOBUTTON, validator, rb) self.radioButtons[name] = rb if sbox is not None: self.radioButtonBoxes[name] = sbox return rb def addChoice(self, name, choices, selection, labelWidth, validator, size=(-1, -1)): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size=(labelWidth, -1), style=wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) ch = wx.Choice(self, wx.ID_ANY, choices=choices, size=size, name=name) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, validator) ch.SetSelection(selection) lsz.Add(ch) self.choices[name] = ch return lsz def addPinChoice(self, name, choiceVal, pins, allowBlank, labelWidth): lsz = wx.BoxSizer(wx.HORIZONTAL) st = wx.StaticText(self, wx.ID_ANY, self.labels[name], size=(labelWidth, -1), style=wx.ALIGN_RIGHT) st.SetFont(self.font) lsz.Add(st, 1, wx.TOP, offsetChLabel) if allowBlank: opts = ["-"] + pins else: opts = pins ch = wx.Choice(self, wx.ID_ANY, choices=opts, name=name, style=wx.CB_SORT) ch.SetBackgroundColour(self.deco.getBackgroundColour()) ch.SetFont(self.font) ch.Bind(wx.EVT_CHOICE, self.onChoice) self.choices[name] = ch try: sv = self.pinNames.index(choiceVal) except: sv = 0 ch.SetSelection(sv) lsz.Add(ch) return lsz def setChoice(self, name, cfgValues, default): if name in cfgValues.keys() and cfgValues[name][1] == True: bv = cfgValues[name][0] else: bv = default s = self.choices[name].FindString(bv) if s < 0: s = self.choices[name].FindString(default) if s < 0: 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 onCheckBox(self, evt): self.assertModified(True) evt.Skip() def setHelpText(self, ht): for k in self.textControls.keys(): if k in ht.keys(): self.textControls[k].SetToolTipString(ht[k]) for k in self.checkBoxes.keys(): if k in ht.keys(): self.checkBoxes[k].SetToolTipString(ht[k]) for k in self.radioButtons.keys(): if k in ht.keys(): self.radioButtons[k].SetToolTipString(ht[k]) if k in self.radioButtonBoxes.keys(): self.radioButtonBoxes[k].SetToolTipString(ht[k]) for k in self.choices.keys(): if k in ht.keys(): self.choices[k].SetToolTipString(ht[k]) def insertValues(self, cfgValues): self.assertValid(True) self.enableAll(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.textControlsOriginal[k] = cfgValues[k] if cfgValues[k][1] == True: self.textControls[k].SetValue(str(cfgValues[k][0])) else: self.textControls[k].SetValue("") else: print "Key " + k + " not found in config data." self.assertModified(False) def getValues(self): self.assertModified(False) result = {} for k in self.checkBoxes.keys(): cb = self.checkBoxes[k] result[k] = cb.IsChecked() for k in self.textControls.keys(): v = self.textControls[k].GetValue() if v == "": if k in self.textControlsOriginal.keys(): result[k] = self.textControlsOriginal[k][0], False else: result[k] = "", False else: result[k] = v, True for k in self.radioButtons.keys(): result[k] = self.radioButtons[k].GetValue(), True for k in self.choices.keys(): v = self.choices[k].GetSelection() result[k] = self.choices[k].GetString(v), True return result 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 PrinterPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.configFile = None self.protFileLoaded = False self.settings = settings self.cfgValues = {} self.heaters = [] self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = MechanicalPage(self, self.nb, len(self.pages), self.settings.font) 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), self.settings.font) 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.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages), self.settings.font) 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) self.SetSizer(sz) self.Fit() def getFileName(self): return self.configFile def assertModified(self, pg, flag=True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def assertValid(self, pg, flag=True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSavePrinter(False, False) else: self.parent.enableSavePrinter(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setPrinterTabDecor(pfx) def setHeaters(self, ht): return self.pgMiscellaneous.setHeaters(ht) 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 + "?\n" "There are changes to your printer " "configuration 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 printer configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.h" dlg = wx.FileDialog(self, message="Choose a printer 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) rc = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): try: self.cfgBuffer = list(open(fn)) except: return False self.configFile = fn self.processors = [] gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace( "\n\n ", "\n\n ") helpTextString = helpTextString.replace( "\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if ln.lstrip().startswith("//"): continue if ln.lstrip().startswith("#define"): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1: self.cfgValues[t[0]] = tt[0] continue elif len(tt) > 1: self.cfgValues[t[0]] = tt 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 if os.path.basename(fn) in protectedFiles: self.parent.enableSavePrinter(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSavePrinter(True, True) self.parent.setPrinterTabFile(os.path.basename(fn)) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) k = 'DC_EXTRUDER' if k in self.cfgValues.keys(): self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k]) else: self.pgMiscellaneous.setOriginalHeater(None) return True def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Printer configuration (printer.*.h)|printer.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setPrinterTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSavePrinter(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog(self, "Unable to overwrite %s." % path, "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("printer."): dlg = wx.MessageDialog( self, "Illegal file name: %s.\n" "File name must begin with \"printer.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} labelFound = [] for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] for ln in self.cfgBuffer: m = reDefineBL.match(ln) if m: t = m.groups() if len(t) == 2: if t[0] in values.keys() and values[t[0]] != "": fp.write(defineValueFormat % (t[0], values[t[0]])) self.cfgValues[t[0]] = values[t[0]] labelFound.append(t[0]) elif t[0] in values.keys(): fp.write("//" + ln) if t[0] in self.cfgValues.keys(): del self.cfgValues[t[0]] labelFound.append(t[0]) else: fp.write(ln) continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1: if t[0] in values.keys() and values[t[0]]: fp.write(defineBoolFormat % t[0]) self.cfgValues[t[0]] = True labelFound.append(t[0]) elif t[0] in values.keys(): fp.write("//" + ln) if t[0] in self.cfgValues.keys(): del self.cfgValues[t[0]] labelFound.append(t[0]) else: fp.write(ln) continue m = reCommDefBL.match(ln) if m: t = m.groups() if len(t) == 2: if t[0] in values.keys() and values[t[0]] != "": fp.write(defineValueFormat % (t[0], values[t[0]])) self.cfgValues[t[0]] = values[t[0]] labelFound.append(t[0]) elif t[0] in values.keys(): fp.write(ln) labelFound.append(t[0]) else: fp.write(ln) continue m = reCommDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1: if t[0] in values.keys() and values[t[0]]: fp.write(defineBoolFormat % t[0]) self.cfgValues[t[0]] = True labelFound.append(t[0]) elif t[0] in values.keys(): fp.write(ln) labelFound.append(t[0]) else: fp.write(ln) continue fp.write(ln) for k in labelFound: del values[k] newLabels = "" for k in values.keys(): if newLabels == "": newLabels = k else: newLabels += ", " + k self.addNewDefine(fp, k, values[k]) if newLabels != "": dlg = wx.MessageDialog( self, "New defines added to printer config:\n" + newLabels, "New defines", wx.OK + wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() fp.close() return True def addNewDefine(self, fp, key, val): fp.write("\n") fp.write("/** \\def %s\n" % key) fp.write(" Add help text here.\n") fp.write("*/\n") if val == True: fp.write(defineBoolFormat % key) elif val == False: fp.write("//#define %s\n" % key) elif val == "": fp.write("//#define %s\n" % key) else: fp.write(defineValueFormat % (key, val))
class ConfigFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Teacup Configtool", size=(880, 550)) self.Bind(wx.EVT_CLOSE, self.onClose) self.Bind(wx.EVT_SIZE, self.onResize) self.deco = Decoration() panel = wx.Panel(self, -1) panel.SetBackgroundColour(self.deco.getBackgroundColour()) panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.settings = Settings(self, cmd_folder) self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) self.settings.folder = cmd_folder self.heaters = [] self.savePrtEna = False self.saveBrdEna = False self.protPrtFile = False self.protBrdFile = False sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(panel, wx.ID_ANY, size=(880, 550), style=wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.printerFileName = None self.printerTabDecor = "" self.printerBaseText = "Printer" self.pgPrinter = PrinterPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgPrinter, self.printerBaseText) self.boardFileName = None self.boardTabDecor = "" self.boardBaseText = "Board" self.pgBoard = BoardPanel(self, self.nb, self.settings) self.nb.AddPage(self.pgBoard, self.boardBaseText) panel.Fit() self.panel = panel sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.makeMenu() def onClose(self, evt): if not self.pgPrinter.confirmLoseChanges("exit"): return if not self.pgBoard.confirmLoseChanges("exit"): return self.Destroy() def onResize(self, evt): self.panel.SetSize(self.GetClientSize()) self.Refresh() evt.Skip() def setPrinterTabFile(self, fn): self.printerFileName = fn self.updatePrinterTab() def setPrinterTabDecor(self, prefix): self.printerTabDecor = prefix self.updatePrinterTab() def updatePrinterTab(self): txt = self.printerTabDecor + self.printerBaseText if self.printerFileName: txt += " <%s>" % self.printerFileName self.nb.SetPageText(0, txt) def setBoardTabFile(self, fn): self.boardFileName = fn self.updateBoardTab() def setBoardTabDecor(self, prefix): self.boardTabDecor = prefix self.updateBoardTab() def updateBoardTab(self): txt = self.boardTabDecor + self.boardBaseText if self.boardFileName: txt += " <%s>" % self.boardFileName self.nb.SetPageText(1, txt) def setHeaters(self, ht): self.heaters = ht self.pgPrinter.setHeaters(ht) def makeMenu(self): file_menu = wx.Menu() file_menu.Append( ID_LOAD_CONFIG, "Load config.h", "Load config.h and its named printer and board files.") self.Bind(wx.EVT_MENU, self.onLoadConfig, id=ID_LOAD_CONFIG) file_menu.Enable(ID_LOAD_CONFIG, False) file_menu.Append(ID_SAVE_CONFIG, "Save config.h", "Save config.h file.") self.Bind(wx.EVT_MENU, self.onSaveConfig, id=ID_SAVE_CONFIG) file_menu.Enable(ID_SAVE_CONFIG, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_PRINTER, "Load printer", "Load a printer configuration file.") self.Bind(wx.EVT_MENU, self.pgPrinter.onLoadConfig, id=ID_LOAD_PRINTER) file_menu.Append(ID_SAVE_PRINTER, "Save printer", "Save printer configuration.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfig, id=ID_SAVE_PRINTER) file_menu.Enable(ID_SAVE_PRINTER, False) file_menu.Append(ID_SAVE_PRINTER_AS, "Save printer as...", "Save printer configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSavePrinterConfigAs, id=ID_SAVE_PRINTER_AS) file_menu.Enable(ID_SAVE_PRINTER_AS, False) file_menu.AppendSeparator() file_menu.Append(ID_LOAD_BOARD, "Load board", "Load a board configuration file.") self.Bind(wx.EVT_MENU, self.pgBoard.onLoadConfig, id=ID_LOAD_BOARD) file_menu.Append(ID_SAVE_BOARD, "Save board", "Save board configuration.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfig, id=ID_SAVE_BOARD) file_menu.Enable(ID_SAVE_BOARD, False) file_menu.Append(ID_SAVE_BOARD_AS, "Save board as...", "Save board configuration to a new file.") self.Bind(wx.EVT_MENU, self.onSaveBoardConfigAs, id=ID_SAVE_BOARD_AS) file_menu.Enable(ID_SAVE_BOARD_AS, False) file_menu.AppendSeparator() file_menu.Append(wx.ID_EXIT, "E&xit", "Exit the application.") self.Bind(wx.EVT_MENU, self.onClose, id=wx.ID_EXIT) self.fileMenu = file_menu menu_bar = wx.MenuBar() menu_bar.Append(file_menu, "&File") edit_menu = wx.Menu() edit_menu.Append(ID_SETTINGS, "Settings", "Change settings.") self.Bind(wx.EVT_MENU, self.onEditSettings, id=ID_SETTINGS) self.editMenu = edit_menu menu_bar.Append(edit_menu, "&Edit") build_menu = wx.Menu() build_menu.Append(ID_BUILD, "Build", "Build the executable.") self.Bind(wx.EVT_MENU, self.onBuild, id=ID_BUILD) build_menu.Append(ID_UPLOAD, "Upload", "Upload the executable.") self.Bind(wx.EVT_MENU, self.onUpload, id=ID_UPLOAD) self.buildMenu = build_menu menu_bar.Append(build_menu, "&Build") self.SetMenuBar(menu_bar) loadFlag = self.checkEnableLoadConfig() self.checkEnableUpload() if loadFlag: self.loadConfigFile("config.h") def onSaveBoardConfig(self, evt): rc = self.pgBoard.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSaveBoardConfigAs(self, evt): rc = self.pgBoard.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfig(self, evt): rc = self.pgPrinter.onSaveConfig(evt) if rc: self.checkEnableLoadConfig() return rc def onSavePrinterConfigAs(self, evt): rc = self.pgPrinter.onSaveConfigAs(evt) if rc: self.checkEnableLoadConfig() return rc def checkEnableLoadConfig(self): fn = os.path.join(cmd_folder, "config.h") if os.path.isfile(fn): self.fileMenu.Enable(ID_LOAD_CONFIG, True) self.buildMenu.Enable(ID_BUILD, True) return True else: self.fileMenu.Enable(ID_LOAD_CONFIG, False) self.buildMenu.Enable(ID_BUILD, False) return False def checkEnableUpload(self): fn = os.path.join(cmd_folder, "teacup.hex") if os.path.isfile(fn): self.buildMenu.Enable(ID_UPLOAD, True) else: self.buildMenu.Enable(ID_UPLOAD, False) def enableSavePrinter(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_PRINTER, saveFlag) self.fileMenu.Enable(ID_SAVE_PRINTER_AS, saveAsFlag) self.savePrtEna = saveAsFlag self.protPrtFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveBoard(self, saveFlag, saveAsFlag): self.fileMenu.Enable(ID_SAVE_BOARD, saveFlag) self.fileMenu.Enable(ID_SAVE_BOARD_AS, saveAsFlag) self.saveBrdEna = saveAsFlag self.protBrdFile = not saveFlag if self.savePrtEna and self.saveBrdEna: self.enableSaveConfig(True) else: self.enableSaveConfig(False) def enableSaveConfig(self, flag): self.fileMenu.Enable(ID_SAVE_CONFIG, flag) def onLoadConfig(self, evt): self.loadConfigFile("config.h") def loadConfigFile(self, fn): if not self.pgPrinter.confirmLoseChanges("load config"): return False if not self.pgBoard.confirmLoseChanges("load config"): return False pfile, bfile = self.getConfigFileNames(fn) if not pfile: self.message( "Config file did not contain a printer file " "include statement.", "Config error") return False else: if not self.pgPrinter.loadConfigFile(pfile): self.message( "There was a problem loading the printer config file:\n%s" % pfile, "Config error") return False if not bfile: self.message( "Config file did not contain a board file " "include statement.", "Config error") return False else: if not self.pgBoard.loadConfigFile(bfile): self.message( "There was a problem loading the board config file:\n%s" % bfile, "Config error") return False return True def getConfigFileNames(self, fn): pfile = None bfile = None path = os.path.join(cmd_folder, fn) try: cfgBuffer = list(open(path)) except: self.message("Unable to process config file %s." % fn, "File error") return None, None for ln in cfgBuffer: if not ln.lstrip().startswith("#include"): continue m = reInclude.search(ln) if m: t = m.groups() if len(t) == 1: if "printer." in t[0]: if pfile: self.message( "Multiple printer file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: pfile = os.path.join(cmd_folder, t[0]) elif "board." in t[0]: if bfile: self.message( "Multiple board file include statements.\n" "Ignoring %s." % ln, "Config error", wx.OK + wx.ICON_WARNING) else: bfile = os.path.join(cmd_folder, t[0]) else: self.message( "Unable to parse include statement:\n%s" % ln, "Config error") return pfile, bfile def onSaveConfig(self, evt): fn = os.path.join(cmd_folder, "config.h") try: fp = open(fn, 'w') except: self.message("Unable to open config.h for output.", "File error") return False bfn = self.pgBoard.getFileName() if self.pgBoard.isModified() and self.pgBoard.isValid(): if not self.pgBoard.saveConfigFile(bfn): return False else: self.pgBoard.generateTempTables() pfn = self.pgPrinter.getFileName() if self.pgPrinter.isModified() and self.pgPrinter.isValid(): if not self.pgPrinter.saveConfigFile(pfn): return False prefix = cmd_folder + os.path.sep lpfx = len(prefix) if bfn.startswith(prefix): rbfn = bfn[lpfx:] else: rbfn = bfn if pfn.startswith(prefix): rpfn = pfn[lpfx:] else: rpfn = pfn fp.write("\n") fp.write("// Configuration for controller board.\n") fp.write("#include \"%s\"\n" % rbfn) fp.write("\n") fp.write("// Configuration for printer board.\n") fp.write("#include \"%s\"\n" % rpfn) fp.close() self.checkEnableLoadConfig() return True def onBuild(self, evt): self.onBuildorUpload(True) def onUpload(self, evt): self.onBuildorUpload(False) def onBuildorUpload(self, buildFlag): if not (self.pgPrinter.hasData() or self.pgBoard.hasData()): dlg = wx.MessageDialog( self, "Data needs to be loaded. " "Click Yes to load config.h.", "Data missing", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return self.loadConfigFile("config.h") else: if self.pgPrinter.isModified(): dlg = wx.MessageDialog( self, "Printer data needs to be saved. Click " "Yes to save printer configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protPrtFile: rc = self.onSavePrinterConfigAs(None) else: rc = self.onSavePrinterConfig(None) if not rc: return if self.pgBoard.isModified(): dlg = wx.MessageDialog( self, "Board data needs to be saved. Click " "Yes to save board configuration.", "Changes pending", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if self.protBrdFile: rc = self.onSaveBoardConfigAs(None) else: rc = self.onSaveBoardConfig(None) if not rc: return if not self.verifyConfigLoaded(): dlg = wx.MessageDialog( self, "Loaded configuration does not match the " "config.h file. Click Yes to save config.h.", "Configuration changed", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return if not self.onSaveConfig(None): return f_cpu, cpu = self.pgBoard.getCPUInfo() if not cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU type.", "CPU type error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if not f_cpu: dlg = wx.MessageDialog(self, "Unable to determine CPU clock rate.", "CPU clock rate error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return if buildFlag: dlg = Build(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() self.checkEnableUpload() else: dlg = Upload(self, self.settings, f_cpu, cpu) dlg.ShowModal() dlg.Destroy() def verifyConfigLoaded(self): pfile, bfile = self.getConfigFileNames("config.h") lpfile = self.pgPrinter.getFileName() lbfile = self.pgBoard.getFileName() return ((pfile == lpfile) and (bfile == lbfile)) def onEditSettings(self, evt): dlg = SettingsDlg(self, self.settings) rc = dlg.ShowModal() dlg.Destroy() def message(self, text, title, style=wx.OK + wx.ICON_ERROR): dlg = wx.MessageDialog(self, text, title, style) dlg.ShowModal() dlg.Destroy()
class PrinterPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.protFileLoaded = False self.settings = settings self.printer = Printer(self.settings) self.dir = os.path.join(self.settings.folder, "config") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = self.registerPage(MechanicalPage, "Mechanical") self.pgAcc = self.registerPage(AccelerationPage, "Acceleration") self.pgMiscellaneous = self.registerPage(MiscellaneousPage, "Miscellaneous") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def registerPage(self, klass, label, *args, **kwargs): page = klass(self, self.nb, len(self.pages), *args, font = self.settings.font, **kwargs) self.nb.AddPage(page, label) self.pages.append(page) self.titles.append(label) self.pageModified.append(False) self.pageValid.append(True) return page def assertModified(self, pg, flag = True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return self.printer.hasData() def getFileName(self): return self.printer.configFile def assertValid(self, pg, flag = True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSavePrinter(False, False) else: self.parent.enableSavePrinter(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setPrinterTabDecor(pfx) def setHeaters(self, ht): return self.pgMiscellaneous.setHeaters(ht) 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 + "?\n" "There are changes to your printer " "configuration 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 printer configuration"): return if platform.startswith("darwin"): # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.h" dlg = wx.FileDialog(self, message = "Choose a printer 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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): ok, file = self.printer.loadConfigFile(fn) if not ok: return ok, file if os.path.basename(fn) in protectedFiles: self.parent.enableSavePrinter(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSavePrinter(True, True) self.parent.setPrinterTabFile(os.path.basename(fn)) for pg in self.pages: pg.insertValues(self.printer.cfgValues) pg.setHelpText(self.printer.helpText) k = 'DC_EXTRUDER' if k in self.printer.cfgValues.keys() and self.printer.cfgValues[k][1] == True: self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0]) else: self.pgMiscellaneous.setOriginalHeater(None) return True, None def onSaveConfig(self, evt): path = self.getFileName() return self.saveConfigFile(path) def onSaveConfigAs(self, evt): if platform.startswith("darwin"): # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setPrinterTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSavePrinter(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog(self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("printer."): dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" "File name must begin with \"printer.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: self.printer.saveConfigFile(path, values) except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False return True
class PrinterPanel(wx.Panel): def __init__(self, parent, nb, settings): wx.Panel.__init__(self, nb, wx.ID_ANY) self.parent = parent self.deco = Decoration() self.configFile = None self.protFileLoaded = False self.settings = settings self.cfgValues = {} self.heaters = [] self.dir = os.path.join(self.settings.folder, "config") self.cfgDir = os.path.join(self.settings.folder, "configtool") self.SetBackgroundColour(self.deco.getBackgroundColour()) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) sz = wx.BoxSizer(wx.HORIZONTAL) self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font) self.pages = [] self.titles = [] self.pageModified = [] self.pageValid = [] self.pgMech = self.registerPage(MechanicalPage, "Mechanical") self.pgAcc = self.registerPage(AccelerationPage, "Acceleration") self.pgMiscellaneous = self.registerPage(MiscellaneousPage, "Miscellaneous") sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) self.SetSizer(sz) self.Fit() def registerPage(self, klass, label, *args, **kwargs): page = klass(self, self.nb, len(self.pages), *args, font = self.settings.font, **kwargs) self.nb.AddPage(page, label) self.pages.append(page) self.titles.append(label) self.pageModified.append(False) self.pageValid.append(True) return page def getFileName(self): return self.configFile def assertModified(self, pg, flag = True): self.pageModified[pg] = flag self.modifyTab(pg) def isModified(self): return (True in self.pageModified) def isValid(self): return not (False in self.pageValid) def hasData(self): return (self.configFile != None) def assertValid(self, pg, flag = True): self.pageValid[pg] = flag self.modifyTab(pg) if False in self.pageValid: self.parent.enableSavePrinter(False, False) else: self.parent.enableSavePrinter(not self.protFileLoaded, 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]) if True in self.pageModified and False in self.pageValid: pfx = "?* " elif True in self.pageModified: pfx = "* " elif False in self.pageValid: pfx = "? " else: pfx = "" self.parent.setPrinterTabDecor(pfx) def setHeaters(self, ht): return self.pgMiscellaneous.setHeaters(ht) 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 + "?\n" "There are changes to your printer " "configuration 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 printer configuration"): return if platform == "darwin": # Mac OS X appears to be a bit limited on wildcards. wildcard = "Printer configuration (printer.*.h)|*.h" else: wildcard = "Printer configuration (printer.*.h)|printer.*.h" dlg = wx.FileDialog(self, message = "Choose a printer 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) rc, efn = self.loadConfigFile(path) if not rc: dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def loadConfigFile(self, fn): cfgFn = os.path.join(self.cfgDir, "printer.generic.h") try: self.cfgBuffer = list(open(cfgFn)) except: return False, cfgFn try: self.userBuffer = list(open(fn)) except: return False, fn self.configFile = fn gatheringHelpText = False helpTextString = "" helpKey = None self.cfgValues = {} self.cfgNames = [] self.helpText = {} prevLines = "" for ln in self.cfgBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False helpTextString = helpTextString.strip() # Keep paragraphs with double-newline. helpTextString = helpTextString.replace("\n\n ", "\n\n") # Keep indented lines, typically a list. helpTextString = helpTextString.replace("\n\n ", "\n\n ") helpTextString = helpTextString.replace("\n ", "\n\n ") # Remove all other newlines and indents. helpTextString = helpTextString.replace("\n ", " ") hk = helpKey.split() for k in hk: self.helpText[k] = helpTextString helpTextString = "" helpKey = None continue else: helpTextString += ln continue m = reHelpTextStart.match(ln) if m: t = m.groups() gatheringHelpText = True helpKey = t[0] continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseDefineName(ln): continue gatheringHelpText = False prevLines = "" for ln in self.userBuffer: if gatheringHelpText: if reHelpTextEnd.match(ln): gatheringHelpText = False continue if reHelpTextStart.match(ln): gatheringHelpText = True continue if ln.rstrip().endswith("\\"): prevLines += ln.rstrip()[:-1] continue if prevLines != "": ln = prevLines + ln prevLines = "" if self.parseDefineValue(ln): continue # Parsing done. All parsed stuff is now in these arrays and dicts. # Uncomment for debugging. #print self.cfgValues # #defines with a value and booleans. #print self.cfgNames # Names found in the generic file. #print self.helpText if os.path.basename(fn) in protectedFiles: self.parent.enableSavePrinter(False, True) self.protFileLoaded = True else: self.protFileLoaded = False self.parent.enableSavePrinter(True, True) self.parent.setPrinterTabFile(os.path.basename(fn)) for pg in self.pages: pg.insertValues(self.cfgValues) pg.setHelpText(self.helpText) k = 'DC_EXTRUDER' if k in self.cfgValues.keys() and self.cfgValues[k][1] == True: self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0]) else: self.pgMiscellaneous.setOriginalHeater(None) return True, None def parseDefineName(self, ln): m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1: self.cfgNames.append(t[0]) return True return False def parseDefineValue(self, ln): m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: m = reDefQSm.search(ln) if m: t = m.groups() tt = re.findall(reDefQSm2, t[1]) if len(tt) == 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt[0], True return True elif len(tt) > 1 and (t[0] in self.cfgNames): self.cfgValues[t[0]] = tt, True return True m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2 and (t[0] in self.cfgNames): if reDefineBL.search(ln): self.cfgValues[t[0]] = t[1], True else: self.cfgValues[t[0]] = t[1], False return True m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1 and (t[0] in self.cfgNames): if reDefBoolBL.search(ln): self.cfgValues[t[0]] = True else: self.cfgValues[t[0]] = False return True return False def onSaveConfig(self, evt): path = self.configFile return self.saveConfigFile(path) def onSaveConfigAs(self, evt): wildcard = "Printer configuration (printer.*.h)|printer.*.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() rc = self.saveConfigFile(path) if rc: self.parent.setPrinterTabFile(os.path.basename(path)) self.protFileLoaded = False self.parent.enableSavePrinter(True, True) return rc def saveConfigFile(self, path): if os.path.basename(path) in protectedFiles: dlg = wx.MessageDialog(self, "It's not allowed to overwrite files " "distributed by Teacup. Choose another name.", "Protected file error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False if not os.path.basename(path).startswith("printer."): dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" "File name must begin with \"printer.\"" % path, "Illegal file name", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False ext = os.path.splitext(os.path.basename(path))[1] self.dir = os.path.dirname(path) if ext == "": path += ".h" try: fp = file(path, 'w') except: dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, "File error", wx.OK + wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return False self.configFile = path values = {} for pg in self.pages: v1 = pg.getValues() for k in v1.keys(): values[k] = v1[k] for ln in self.cfgBuffer: m = reDefine.match(ln) if m: t = m.groups() if len(t) == 2 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v[1] == False: fp.write("//") fp.write(defineValueFormat % (t[0], v[0])) else: if t[0] == 'CANNED_CYCLE': # Known to be absent in the GUI. Worse, this value is replaced # by the one in the metadata file. # # TODO: make value reading above recognize wether this value is # commented out or not. Reading the value its self works # already. Hint: it's the rule using reDefQS, reDefQSm, etc. # # TODO: add a multiline text field in the GUI to deal with this. # # TODO: write this value out properly. In /* comments */, if # disabled. # # TODO: currently, the lines beyond the ones with the #define are # treated like arbitrary comments. Having the former TODOs # done, this will lead to duplicates. fp.write(ln) else: print "Value key " + t[0] + " not found in GUI." continue m = reDefBoolBL.match(ln) if m: t = m.groups() if len(t) == 1 and t[0] in values.keys(): v = values[t[0]] self.cfgValues[t[0]] = v if v == "" or v == False: fp.write("//") fp.write(defineBoolFormat % t[0]) else: print "Boolean key " + t[0] + " not found in GUI." continue fp.write(ln) fp.close() return True