From 11bb6bb1cfc18895d45dc2a34ac28cd03617b63b Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Mon, 18 Apr 2016 16:09:42 -0400 Subject: [PATCH] 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. --- configtool/printer.py | 233 +++++++++++++++++++++++++++++++++++ configtool/printerpanel.py | 244 ++++--------------------------------- 2 files changed, 254 insertions(+), 223 deletions(-) create mode 100644 configtool/printer.py diff --git a/configtool/printer.py b/configtool/printer.py new file mode 100644 index 0000000..5cd332e --- /dev/null +++ b/configtool/printer.py @@ -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 diff --git a/configtool/printerpanel.py b/configtool/printerpanel.py index 7eb7211..26134af 100644 --- a/configtool/printerpanel.py +++ b/configtool/printerpanel.py @@ -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