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") 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 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()