Teacup_Firmware/configtool/printerpanel.py

454 lines
13 KiB
Python

import os
import wx
import re
from sys import platform
from configtool.decoration import Decoration
from configtool.data import (defineValueFormat, defineBoolFormat,
reHelpTextStart, reHelpTextEnd,
reDefine, reDefineBL, reDefQS, reDefQSm,
reDefQSm2, reDefBool, reDefBoolBL)
from configtool.mechanicalpage import MechanicalPage
from configtool.accelerationpage import AccelerationPage
from configtool.miscellaneouspage import MiscellaneousPage
from configtool.protectedfiles import protectedFiles
class PrinterPanel(wx.Panel):
def __init__(self, parent, nb, settings):
wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent
self.deco = Decoration()
self.configFile = None
self.protFileLoaded = False
self.settings = settings
self.cfgValues = {}
self.heaters = []
self.dir = os.path.join(self.settings.folder, "config")
self.cfgDir = os.path.join(self.settings.folder, "configtool")
self.SetBackgroundColour(self.deco.getBackgroundColour())
self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21),
style = wx.BK_DEFAULT)
self.nb.SetBackgroundColour(self.deco.getBackgroundColour())
self.nb.SetFont(self.settings.font)
self.pages = []
self.titles = []
self.pageModified = []
self.pageValid = []
self.pgMech = self.registerPage(MechanicalPage, "Mechanical")
self.pgAcc = self.registerPage(AccelerationPage, "Acceleration")
self.pgMiscellaneous = self.registerPage(MiscellaneousPage,
"Miscellaneous")
sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5)
self.SetSizer(sz)
self.Fit()
def registerPage(self, klass, label, *args, **kwargs):
page = klass(self, self.nb, len(self.pages), *args,
font = self.settings.font, **kwargs)
self.nb.AddPage(page, label)
self.pages.append(page)
self.titles.append(label)
self.pageModified.append(False)
self.pageValid.append(True)
return page
def getFileName(self):
return self.configFile
def assertModified(self, pg, flag = True):
self.pageModified[pg] = flag
self.modifyTab(pg)
def isModified(self):
return (True in self.pageModified)
def isValid(self):
return not (False in self.pageValid)
def hasData(self):
return (self.configFile != None)
def assertValid(self, pg, flag = True):
self.pageValid[pg] = flag
self.modifyTab(pg)
if False in self.pageValid:
self.parent.enableSavePrinter(False, False)
else:
self.parent.enableSavePrinter(not self.protFileLoaded, True)
def modifyTab(self, pg):
if self.pageModified[pg] and not self.pageValid[pg]:
pfx = "?* "
elif self.pageModified[pg]:
pfx = "* "
elif not self.pageValid[pg]:
pfx = "? "
else:
pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg])
if True in self.pageModified and False in self.pageValid:
pfx = "?* "
elif True in self.pageModified:
pfx = "* "
elif False in self.pageValid:
pfx = "? "
else:
pfx = ""
self.parent.setPrinterTabDecor(pfx)
def setHeaters(self, ht):
return self.pgMiscellaneous.setHeaters(ht)
def onClose(self, evt):
if not self.confirmLoseChanges("exit"):
return
self.Destroy()
def confirmLoseChanges(self, msg):
if True not in self.pageModified:
return True
dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n"
"There are changes to your printer "
"configuration that will be lost.",
"Changes pending",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return False
return True
def onLoadConfig(self, evt):
if not self.confirmLoseChanges("load a new printer configuration"):
return
if platform == "darwin":
# Mac OS X appears to be a bit limited on wildcards.
wildcard = "Printer configuration (printer.*.h)|*.h"
else:
wildcard = "Printer configuration (printer.*.h)|printer.*.h"
dlg = wx.FileDialog(self, message = "Choose a printer config file",
defaultDir = self.dir, defaultFile = "",
wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
if path is None:
return
self.dir = os.path.dirname(path)
rc, efn = self.loadConfigFile(path)
if not rc:
dlg = wx.MessageDialog(self, "Unable to process file %s." % efn,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return
def loadConfigFile(self, fn):
cfgFn = os.path.join(self.cfgDir, "printer.generic.h")
try:
self.cfgBuffer = list(open(cfgFn))
except:
return False, cfgFn
try:
self.userBuffer = list(open(fn))
except:
return False, fn
self.configFile = fn
gatheringHelpText = False
helpTextString = ""
helpKey = None
self.cfgValues = {}
self.cfgNames = []
self.helpText = {}
prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
helpTextString = helpTextString.strip()
# Keep paragraphs with double-newline.
helpTextString = helpTextString.replace("\n\n ", "\n\n")
# Keep indented lines, typically a list.
helpTextString = helpTextString.replace("\n\n ", "\n\n ")
helpTextString = helpTextString.replace("\n ", "\n\n ")
# Remove all other newlines and indents.
helpTextString = helpTextString.replace("\n ", " ")
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
m = reHelpTextStart.match(ln)
if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = ""
self.parseDefineName(ln)
self.parseDefineValue(ln)
# Read the user configuration. This usually overwrites all of the items
# read above, but not those missing in the user configuration, e.g.
# when reading an older config.
gatheringHelpText = False
prevLines = ""
for ln in self.userBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
continue
if reHelpTextStart.match(ln):
gatheringHelpText = True
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = ""
self.parseDefineValue(ln)
# Parsing done. All parsed stuff is now in these arrays and dicts.
# Uncomment for debugging.
#print self.cfgValues # #defines with a value and booleans.
#print self.cfgNames # Names found in the generic file.
#print self.helpText
if os.path.basename(fn) in protectedFiles:
self.parent.enableSavePrinter(False, True)
self.protFileLoaded = True
else:
self.protFileLoaded = False
self.parent.enableSavePrinter(True, True)
self.parent.setPrinterTabFile(os.path.basename(fn))
for pg in self.pages:
pg.insertValues(self.cfgValues)
pg.setHelpText(self.helpText)
k = 'DC_EXTRUDER'
if k in self.cfgValues.keys() and self.cfgValues[k][1] == True:
self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0])
else:
self.pgMiscellaneous.setOriginalHeater(None)
return True, None
def parseDefineName(self, ln):
m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1:
self.cfgNames.append(t[0])
return True
return False
def parseDefineValue(self, ln):
m = reDefQS.search(ln)
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt[0], True
return True
elif len(tt) > 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt, True
return True
m = reDefine.search(ln)
if m:
t = m.groups()
if len(t) == 2 and (t[0] in self.cfgNames):
if reDefineBL.search(ln):
self.cfgValues[t[0]] = t[1], True
else:
self.cfgValues[t[0]] = t[1], False
return True
m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1 and (t[0] in self.cfgNames):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def onSaveConfig(self, evt):
path = self.configFile
return self.saveConfigFile(path)
def onSaveConfigAs(self, evt):
wildcard = "Printer configuration (printer.*.h)|printer.*.h"
dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir,
defaultFile = "", wildcard = wildcard,
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
val = dlg.ShowModal()
if val != wx.ID_OK:
dlg.Destroy()
return
path = dlg.GetPath()
dlg.Destroy()
rc = self.saveConfigFile(path)
if rc:
self.parent.setPrinterTabFile(os.path.basename(path))
self.protFileLoaded = False
self.parent.enableSavePrinter(True, True)
return rc
def saveConfigFile(self, path):
if os.path.basename(path) in protectedFiles:
dlg = wx.MessageDialog(self, "It's not allowed to overwrite files "
"distributed by Teacup. Choose another name.",
"Protected file error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return False
if not os.path.basename(path).startswith("printer."):
dlg = wx.MessageDialog(self, "Illegal file name: %s.\n"
"File name must begin with \"printer.\"" % path,
"Illegal file name", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return False
ext = os.path.splitext(os.path.basename(path))[1]
self.dir = os.path.dirname(path)
if ext == "":
path += ".h"
try:
fp = file(path, 'w')
except:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return False
self.configFile = path
values = {}
for pg in self.pages:
v1 = pg.getValues()
for k in v1.keys():
values[k] = v1[k]
for ln in self.cfgBuffer:
m = reDefine.match(ln)
if m:
t = m.groups()
if len(t) == 2 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v[1] == False:
fp.write("//")
fp.write(defineValueFormat % (t[0], v[0]))
else:
if t[0] == 'CANNED_CYCLE':
# Known to be absent in the GUI. Worse, this value is replaced
# by the one in the metadata file.
#
# TODO: make value reading above recognize wether this value is
# commented out or not. Reading the value its self works
# already. Hint: it's the rule using reDefQS, reDefQSm, etc.
#
# TODO: add a multiline text field in the GUI to deal with this.
#
# TODO: write this value out properly. In /* comments */, if
# disabled.
#
# TODO: currently, the lines beyond the ones with the #define are
# treated like arbitrary comments. Having the former TODOs
# done, this will lead to duplicates.
fp.write(ln)
else:
print "Value key " + t[0] + " not found in GUI."
continue
m = reDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v == "" or v == False:
fp.write("//")
fp.write(defineBoolFormat % t[0])
else:
print "Boolean key " + t[0] + " not found in GUI."
continue
fp.write(ln)
fp.close()
return True