Configtool: refactor printerpanel for MVC.

Move model functionality out of printerpanel.py into a new class,
Printer, so we can more easily add commandline driven tests in
the future and to help identify code reuse opportunities.
This commit is contained in:
Phil Hord 2016-04-18 16:09:42 -04:00 committed by Markus Hitter
parent d3062ca1dd
commit 11bb6bb1cf
2 changed files with 254 additions and 223 deletions

233
configtool/printer.py Normal file
View File

@ -0,0 +1,233 @@
import os
import re
from sys import platform
from configtool.data import (defineValueFormat, defineBoolFormat,
reHelpTextStart, reHelpTextEnd,
reDefine, reDefineBL, reDefQS, reDefQSm,
reDefQSm2, reDefBool, reDefBoolBL)
class Printer:
def __init__(self):
self.configFile = None
self.cfgValues = {}
def hasData(self):
return (self.configFile != None)
def getFileName(self):
return self.configFile
def loadConfigFile(self, cfgDir, fn):
cfgFn = os.path.join(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)
# Set all boolean generic configuration items to False, so items not yet
# existing in the user configuration default to disabled.
for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
# 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 array 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
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()
# Accept booleans, but not those for which a value exists already.
# Booleans already existing as values are most likely misconfigured
# manual edits (or result of a bug).
if len(t) == 1 and t[0] in self.cfgNames \
and not (t[0] in self.cfgValues \
and isinstance(self.cfgValues[t[0]], tuple)):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def saveConfigFile(self, path, values):
fp = file(path, 'w')
self.configFile = path
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

View File

@ -13,6 +13,7 @@ from configtool.mechanicalpage import MechanicalPage
from configtool.accelerationpage import AccelerationPage from configtool.accelerationpage import AccelerationPage
from configtool.miscellaneouspage import MiscellaneousPage from configtool.miscellaneouspage import MiscellaneousPage
from configtool.protectedfiles import protectedFiles from configtool.protectedfiles import protectedFiles
from configtool.printer import Printer
class PrinterPanel(wx.Panel): class PrinterPanel(wx.Panel):
@ -21,13 +22,12 @@ class PrinterPanel(wx.Panel):
self.parent = parent self.parent = parent
self.deco = Decoration() self.deco = Decoration()
self.configFile = None
self.protFileLoaded = False self.protFileLoaded = False
self.settings = settings self.settings = settings
self.cfgValues = {} self.printer = Printer()
self.heaters = []
self.dir = os.path.join(self.settings.folder, "config") self.dir = os.path.join(self.settings.folder, "config")
self.cfgDir = os.path.join(self.settings.folder, "configtool") self.cfgDir = os.path.join(self.settings.folder, "configtool")
@ -65,9 +65,6 @@ class PrinterPanel(wx.Panel):
self.pageValid.append(True) self.pageValid.append(True)
return page return page
def getFileName(self):
return self.configFile
def assertModified(self, pg, flag = True): def assertModified(self, pg, flag = True):
self.pageModified[pg] = flag self.pageModified[pg] = flag
self.modifyTab(pg) self.modifyTab(pg)
@ -79,7 +76,10 @@ class PrinterPanel(wx.Panel):
return not (False in self.pageValid) return not (False in self.pageValid)
def hasData(self): def hasData(self):
return (self.configFile != None) return self.printer.hasData()
def getFileName(self):
return self.printer.configFile
def assertValid(self, pg, flag = True): def assertValid(self, pg, flag = True):
self.pageValid[pg] = flag self.pageValid[pg] = flag
@ -170,104 +170,9 @@ class PrinterPanel(wx.Panel):
return return
def loadConfigFile(self, fn): def loadConfigFile(self, fn):
cfgFn = os.path.join(self.cfgDir, "printer.generic.h") ok, file = self.printer.loadConfigFile(self.cfgDir, fn)
try: if not ok:
self.cfgBuffer = list(open(cfgFn)) return ok, file
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)
# Set all boolean generic configuration items to False, so items not yet
# existing in the user configuration default to disabled.
for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
# 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: if os.path.basename(fn) in protectedFiles:
self.parent.enableSavePrinter(False, True) self.parent.enableSavePrinter(False, True)
@ -278,72 +183,19 @@ class PrinterPanel(wx.Panel):
self.parent.setPrinterTabFile(os.path.basename(fn)) self.parent.setPrinterTabFile(os.path.basename(fn))
for pg in self.pages: for pg in self.pages:
pg.insertValues(self.cfgValues) pg.insertValues(self.printer.cfgValues)
pg.setHelpText(self.helpText) pg.setHelpText(self.printer.helpText)
k = 'DC_EXTRUDER' k = 'DC_EXTRUDER'
if k in self.cfgValues.keys() and self.cfgValues[k][1] == True: if k in self.printer.cfgValues.keys() and self.printer.cfgValues[k][1] == True:
self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0]) self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k][0])
else: else:
self.pgMiscellaneous.setOriginalHeater(None) self.pgMiscellaneous.setOriginalHeater(None)
return True, 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()
# Accept booleans, but not those for which a value exists already.
# Booleans already existing as values are most likely misconfigured
# manual edits (or result of a bug).
if len(t) == 1 and t[0] in self.cfgNames \
and not (t[0] in self.cfgValues \
and isinstance(self.cfgValues[t[0]], tuple)):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def onSaveConfig(self, evt): def onSaveConfig(self, evt):
path = self.configFile path = self.getFileName()
return self.saveConfigFile(path) return self.saveConfigFile(path)
def onSaveConfigAs(self, evt): def onSaveConfigAs(self, evt):
@ -386,6 +238,12 @@ class PrinterPanel(wx.Panel):
dlg.Destroy() dlg.Destroy()
return False return False
values = {}
for pg in self.pages:
v1 = pg.getValues()
for k in v1.keys():
values[k] = v1[k]
ext = os.path.splitext(os.path.basename(path))[1] ext = os.path.splitext(os.path.basename(path))[1]
self.dir = os.path.dirname(path) self.dir = os.path.dirname(path)
@ -393,7 +251,7 @@ class PrinterPanel(wx.Panel):
path += ".h" path += ".h"
try: try:
fp = file(path, 'w') self.printer.saveConfigFile(path, values)
except: except:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR) "File error", wx.OK + wx.ICON_ERROR)
@ -401,64 +259,4 @@ class PrinterPanel(wx.Panel):
dlg.Destroy() dlg.Destroy()
return False 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 return True