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.miscellaneouspage import MiscellaneousPage
from configtool.protectedfiles import protectedFiles
from configtool.printer import Printer
class PrinterPanel(wx.Panel):
@ -21,13 +22,12 @@ class PrinterPanel(wx.Panel):
self.parent = parent
self.deco = Decoration()
self.configFile = None
self.protFileLoaded = False
self.settings = settings
self.cfgValues = {}
self.heaters = []
self.printer = Printer()
self.dir = os.path.join(self.settings.folder, "config")
self.cfgDir = os.path.join(self.settings.folder, "configtool")
@ -65,9 +65,6 @@ class PrinterPanel(wx.Panel):
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)
@ -79,7 +76,10 @@ class PrinterPanel(wx.Panel):
return not (False in self.pageValid)
def hasData(self):
return (self.configFile != None)
return self.printer.hasData()
def getFileName(self):
return self.printer.configFile
def assertValid(self, pg, flag = True):
self.pageValid[pg] = flag
@ -170,104 +170,9 @@ class PrinterPanel(wx.Panel):
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)
# 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
ok, file = self.printer.loadConfigFile(self.cfgDir, fn)
if not ok:
return ok, file
if os.path.basename(fn) in protectedFiles:
self.parent.enableSavePrinter(False, True)
@ -278,72 +183,19 @@ class PrinterPanel(wx.Panel):
self.parent.setPrinterTabFile(os.path.basename(fn))
for pg in self.pages:
pg.insertValues(self.cfgValues)
pg.setHelpText(self.helpText)
pg.insertValues(self.printer.cfgValues)
pg.setHelpText(self.printer.helpText)
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])
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()
# 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):
path = self.configFile
path = self.getFileName()
return self.saveConfigFile(path)
def onSaveConfigAs(self, evt):
@ -386,6 +238,12 @@ class PrinterPanel(wx.Panel):
dlg.Destroy()
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]
self.dir = os.path.dirname(path)
@ -393,7 +251,7 @@ class PrinterPanel(wx.Panel):
path += ".h"
try:
fp = file(path, 'w')
self.printer.saveConfigFile(path, values)
except:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
@ -401,64 +259,4 @@ class PrinterPanel(wx.Panel):
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