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()
Example #2
0
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()
Example #3
0
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()
Example #4
0
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()