Configtool: begin command line automation.
Add a command line handler mode to Configtool to permit automation for scripts. Traumflug's review note: restored Python 3 checks.
This commit is contained in:
parent
11bb6bb1cf
commit
eafb8e0bfb
653
configtool.py
653
configtool.py
|
|
@ -1,5 +1,16 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Note: syntax in this file has to be kept compatible with Pyton 3, else
|
||||||
|
# Python errors in the compilation stage already, without showing
|
||||||
|
# user help about the incompatibility. To test, simply run
|
||||||
|
#
|
||||||
|
# python3 ./configtool.py
|
||||||
|
#
|
||||||
|
# This should show the explaining text rather than a SyntaxError.
|
||||||
|
#
|
||||||
|
# If you feel like porting Configtool to Python 3 compatibility altogether:
|
||||||
|
# patches are welcome! See https://docs.python.org/3/howto/pyporting.html
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
if sys.version_info.major >= 3:
|
if sys.version_info.major >= 3:
|
||||||
|
|
@ -9,621 +20,55 @@ if sys.version_info.major >= 3:
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
try:
|
import getopt
|
||||||
import wx
|
|
||||||
except:
|
|
||||||
print("ImportError: No module named wx\n\n"
|
|
||||||
"wxPython is not installed. This program requires wxPython to run.\n"
|
|
||||||
"See your package manager and/or http://wxpython.org/download.php.")
|
|
||||||
time.sleep(10)
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import inspect
|
import inspect
|
||||||
from configtool.data import reHelpText
|
|
||||||
|
|
||||||
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(
|
from configtool.gui import StartGui
|
||||||
inspect.currentframe()))[0]))
|
|
||||||
|
|
||||||
from configtool.decoration import Decoration
|
|
||||||
from configtool.settings import Settings, SettingsDlg
|
|
||||||
from configtool.printerpanel import PrinterPanel
|
|
||||||
from configtool.boardpanel import BoardPanel
|
|
||||||
from configtool.build import Build, Upload
|
|
||||||
from configtool.data import reInclude
|
|
||||||
|
|
||||||
ID_LOAD_PRINTER = 1000
|
def cmdLoad(arg):
|
||||||
ID_SAVE_PRINTER = 1001
|
print("Want to load %s, but don't know how.\n" % arg)
|
||||||
ID_SAVE_PRINTER_AS = 1002
|
|
||||||
ID_LOAD_BOARD = 1010
|
|
||||||
ID_SAVE_BOARD = 1011
|
|
||||||
ID_SAVE_BOARD_AS = 1012
|
|
||||||
ID_LOAD_CONFIG = 1020
|
|
||||||
ID_SAVE_CONFIG = 1021
|
|
||||||
ID_BUILD = 1030
|
|
||||||
ID_UPLOAD = 1031
|
|
||||||
ID_SETTINGS = 1040
|
|
||||||
ID_HELP = 1050
|
|
||||||
ID_REPORT = 1051
|
|
||||||
ID_ABOUT = 1052
|
|
||||||
|
|
||||||
|
def cmdHelp():
|
||||||
|
print("""Usage: %s [options]
|
||||||
|
|
||||||
class ConfigFrame(wx.Frame):
|
Running without any options starts the gui (normal operation).
|
||||||
def __init__(self):
|
Following options are available for command line automation:
|
||||||
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()
|
-h, --help Show this help text.
|
||||||
|
|
||||||
panel = wx.Panel(self, -1)
|
-l <file>, --load=<file> Load a specific printer config, board config
|
||||||
panel.SetBackgroundColour(self.deco.getBackgroundColour())
|
or .ini file.
|
||||||
panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
|
""" % sys.argv[0])
|
||||||
|
|
||||||
self.settings = Settings(self, cmd_folder)
|
def CommandLine(argv):
|
||||||
self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
|
""" Parse and act on command line arguments. All script automation commands
|
||||||
wx.FONTWEIGHT_BOLD)
|
result in sys.exit() (i.e. they do not return from this function). Other
|
||||||
self.settings.folder = cmd_folder
|
options like --debug will return to allow the gui to launch.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(argv, "hl:", ["help", "load="])
|
||||||
|
except getopt.GetoptError as err:
|
||||||
|
print(err)
|
||||||
|
print("Use '%s --help' to get help with command line options." %
|
||||||
|
sys.argv[0])
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
self.heaters = []
|
# Check for HELP first.
|
||||||
self.savePrtEna = False
|
for opt, arg in opts:
|
||||||
self.saveBrdEna = False
|
if opt in ("-h", "--help"):
|
||||||
self.protPrtFile = False
|
cmdHelp()
|
||||||
self.protBrdFile = False
|
sys.exit()
|
||||||
|
|
||||||
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,mah@jump-ing.de"
|
|
||||||
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:///user@example.com instead of
|
|
||||||
# the requested mailto:user@example.com.
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
# Now parse other options.
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ("-l", "--load"):
|
||||||
|
cmdLoad(arg)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = wx.App(False)
|
CommandLine(sys.argv[1:])
|
||||||
frame = ConfigFrame()
|
|
||||||
frame.Show(True)
|
cmdFolder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(
|
||||||
app.MainLoop()
|
inspect.currentframe()))[0]))
|
||||||
|
StartGui(cmdFolder)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,622 @@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
try:
|
||||||
|
import wx
|
||||||
|
except:
|
||||||
|
print("ImportError: No module named wx\n\n"
|
||||||
|
"wxPython is not installed. This program requires wxPython to run.\n"
|
||||||
|
"See your package manager and/or http://wxpython.org/download.php.")
|
||||||
|
time.sleep(10)
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from configtool.data import reHelpText
|
||||||
|
from configtool.decoration import Decoration
|
||||||
|
from configtool.settings import Settings, SettingsDlg
|
||||||
|
from configtool.printerpanel import PrinterPanel
|
||||||
|
from configtool.boardpanel import BoardPanel
|
||||||
|
from configtool.build import Build, Upload
|
||||||
|
from configtool.data import reInclude
|
||||||
|
|
||||||
|
ID_LOAD_PRINTER = 1000
|
||||||
|
ID_SAVE_PRINTER = 1001
|
||||||
|
ID_SAVE_PRINTER_AS = 1002
|
||||||
|
ID_LOAD_BOARD = 1010
|
||||||
|
ID_SAVE_BOARD = 1011
|
||||||
|
ID_SAVE_BOARD_AS = 1012
|
||||||
|
ID_LOAD_CONFIG = 1020
|
||||||
|
ID_SAVE_CONFIG = 1021
|
||||||
|
ID_BUILD = 1030
|
||||||
|
ID_UPLOAD = 1031
|
||||||
|
ID_SETTINGS = 1040
|
||||||
|
ID_HELP = 1050
|
||||||
|
ID_REPORT = 1051
|
||||||
|
ID_ABOUT = 1052
|
||||||
|
|
||||||
|
|
||||||
|
cmdFolder = "placeholder"
|
||||||
|
|
||||||
|
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, cmdFolder)
|
||||||
|
self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
|
||||||
|
wx.FONTWEIGHT_BOLD)
|
||||||
|
self.settings.folder = cmdFolder
|
||||||
|
|
||||||
|
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(cmdFolder, "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(cmdFolder, "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(cmdFolder, 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(cmdFolder, 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(cmdFolder, 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(cmdFolder, "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 = cmdFolder + 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,mah@jump-ing.de"
|
||||||
|
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:///user@example.com instead of
|
||||||
|
# the requested mailto:user@example.com.
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
def StartGui(teacupFolder):
|
||||||
|
global cmdFolder
|
||||||
|
|
||||||
|
cmdFolder = teacupFolder
|
||||||
|
app = wx.App(False)
|
||||||
|
frame = ConfigFrame()
|
||||||
|
frame.Show(True)
|
||||||
|
app.MainLoop()
|
||||||
Loading…
Reference in New Issue