""" FieldSpecEditor.py This application will read in a field spec XML file and allow the user to edit any of the fields in it. """ import os import sys import wx import wx.grid as gridlib import dabo import dabo.common.fieldSpecParser as fieldSpecParser import dabo.dEvents as dEvents dabo.ui.loadUI("wx") class FieldSpecGrid(gridlib.Grid): """ This grid is responsible for displaying the current settings of the fieldSpecs.xml file for a table in the current app. Each row in the grid will contain the settings for a field in the table, which control how the field is displayed in the app, or whether it is displayed at all. This grid is also responsible for reversing the process when requested by its form: given the current state of its cell contents, it will generate a dictionary like the one it was created from. The form will use this generated dict to save the modified settings back into XML form. """ def __init__(self, parent, tblDict=None): gridlib.Grid.__init__(self, parent, -1) # Cell renderer and editor classes strRend = gridlib.GridCellStringRenderer strEdt = gridlib.GridCellTextEditor boolRend = gridlib.GridCellBoolRenderer boolEdt = gridlib.GridCellBoolEditor numRend = gridlib.GridCellNumberRenderer numEdt = gridlib.GridCellNumberEditor floatRend = gridlib.GridCellFloatRenderer floatEdt = gridlib.GridCellFloatEditor self.fldInfo = { "type": {"type": "string", "col": 1, "colheading": "Field Type", "rend": strRend, "edt": None}, "caption": {"type": "string", "col": 0, "colheading": "Caption", "rend": strRend, "edt": strEdt}, "searchInclude": {"type": "bool", "col": 2, "colheading": "Search Page?", "rend": boolRend, "edt": boolEdt}, "searchOrder": {"type": "int", "col": 3, "colheading": "Search Order", "rend": numRend, "edt": numEdt}, "listColWidth": {"type": "int", "col": 4, "colheading": "List Width", "rend": numRend, "edt": numEdt}, "listInclude": {"type": "bool", "col": 5, "colheading": "List Page?", "rend": boolRend, "edt": boolEdt}, "listOrder": {"type": "int", "col": 6, "colheading": "List Order", "rend": numRend, "edt": numEdt}, "editInclude": {"type": "bool", "col": 7, "colheading": "Edit Page?", "rend": boolRend, "edt": boolEdt}, "editOrder": {"type": "int", "col": 8, "colheading": "Edit Order", "rend": numRend, "edt": numEdt}, "editReadOnly": {"type": "bool", "col": 9, "colheading": "Read-Only?", "rend": boolRend, "edt": boolEdt} } if tblDict is not None: rows = len(tblDict) cols = len(tblDict[tblDict.keys()[0]]) self.CreateGrid(rows, cols) row = 0 colLabelSet = False for fld in tblDict.keys(): self.SetRowLabelValue(row, fld) fldData = tblDict[fld] for prop in fldData.keys(): col = self.fldInfo[prop]["col"] if col < 0: # Not edited continue if not colLabelSet: self.SetColLabelValue(col, self.fldInfo[prop]["colheading"]) val = fldData[prop] typ = self.fldInfo[prop]["type"] self.SetCellValue(row, col, val) if self.fldInfo[prop]["rend"] is not None: rend = self.fldInfo[prop]["rend"]() self.SetCellRenderer(row, col, rend) if self.fldInfo[prop]["edt"] is not None: edtClass = self.fldInfo[prop]["edt"] edtr = edtClass() self.SetCellEditor(row, col, edtr) row += 1 colLabelSet = True font = self.GetFont() font.SetWeight(wx.BOLD) attr = gridlib.GridCellAttr() attr.SetFont(font) attr.SetBackgroundColour(wx.LIGHT_GREY) attr.SetReadOnly(True) attr.SetAlignment(wx.RIGHT, -1) # self.SetColAttr(0, attr) # There is a bug in wxGTK for this method... self.AutoSizeColumns(True) self.AutoSizeRows(True) def getCurrentValues(self): """ Returns a dictionary containing the current state of the edited values for each field in the grid. """ ret = {} # Get a list of props and their column numbers propCols = {} props = self.fldInfo.keys() for k in props: propCols[k] = self.fldInfo[k]["col"] for row in range(self.GetNumberRows()): fldName = self.GetRowLabelValue(row) ret[fldName] = {} for prop in props: ret[fldName][prop] = self.GetCellValue(row, propCols[prop]) return ret class FieldEditorForm(dabo.ui.dForm): def __init__(self, fieldSpecFile=None): FieldEditorForm.doDefault() self.Caption = "Field Spec Editor" self.debug = True # output verbose self.specFile = fieldSpecFile # Dict of available preview forms self.previewForms = {} # See if we've checked if the user wants to save their changes self.askedToSave = False self.Width = 950 self.Height = 440 # This is the main dictionary that holds all the field spec data. # There will be an element for each table in the app. Each of those # elements will be a dictionary, with one element for each field. # Each field will have several properties that affect how it appears # (or doesn't appear) on each part of the form. self.mainDict = {} # As the user selects different tables and fields, these will be # updated to reflect the current selection. self.currTableDict = {} self.currFieldDict = {} # Read in the field spec file if self.specFile is None: # See if it was passed as a parameter try: self.specFile = sys.argv[1] except: pass if self.specFile is None: wildcard = "XML Files (*.xml)|*.xml|" \ "All files (*.*)|*.*" openDlg = wx.FileDialog(None, "Select a file...", wildcard=wildcard, defaultDir=os.getcwd(), style=wx.OPEN ) #| wx.HIDE_READONLY) res = openDlg.ShowModal() if res == wx.ID_OK: self.specFile = openDlg.GetPath() # Development shortcuts! # if os.path.exists("/home/ed/projects/demo/webtest"): # self.specFile = "/home/ed/projects/demo/webtest/fieldSpecs.xml" # else: # self.specFile = "/Volumes/ED/projects/demo/dabotest/fieldSpecs.xml" if self.specFile is None: dabo.errorLog.write("No field spec file specified; exiting.") return # Read the XML into a local dictionary self.mainDict = fieldSpecParser.importFieldSpecs(self.specFile) # Create the form controls. The design will be one page per table. # Each page will contain a list/grid with a summary for each field. # Selecting a row in the grid updates a set of editable controls so # that a user can change the settings for any field. self.instantiateControls() def initEvents(self): FieldEditorForm.doDefault() self.bindEvent(dEvents.Close, self.__onClose) def __onClose(self, evt): if not self.askedToSave: currDict = self.getCurrentValues() if currDict != self.mainDict: resp = dabo.ui.dMessageBox.areYouSure(message="Do you want to save your changes?", title = "Save Changes") if resp is None: # User pressed 'Cancel' evt.Continue = False return if resp: self.save(currDict) self.askedToSave = True def getCurrentValues(self): ret = {} for pgNum in range(self.pgf.PageCount): pg = self.pgf.GetPage(pgNum) tbl = pg.Caption grd = [k for k in pg.GetChildren() if isinstance(k, wx.grid.Grid)][0] grd.SaveEditControlValue() ret[tbl] = grd.getCurrentValues() return ret def saveTmp(self, file): holdSpecFile = self.specFile self.specFile = file self.save() self.specFile = holdSpecFile def save(self, vals=None): if vals is None: vals = self.getCurrentValues() tblTemplate = self.getTableTemplate() fldTemplate = self.getFieldTemplate() tableXML = "" for tbl in vals.keys(): tblData = vals[tbl] flds = tblData.keys() fieldXML = "" for fld in flds: fldData = tblData[fld] # Make these record zeros for False instead of the default "" sInc = fldData["searchInclude"] if not sInc: sInc = "0" eInc = fldData["editInclude"] if not eInc: eInc = "0" lInc = fldData["listInclude"] if not lInc: lInc = "0" fldVals = (fld, fldData["type"], fldData["caption"], sInc, fldData["searchOrder"], lInc, fldData["listColWidth"], fldData["listOrder"], eInc, fldData["editReadOnly"], fldData["editOrder"]) fieldXML += fldTemplate % fldVals tableXML += tblTemplate % (tbl, fieldXML) xml = self.getBaseXML() % tableXML open(self.specFile, "w").write(xml) return def instantiateControls(self): labelWidth = 150 labelAlignment = wx.ALIGN_RIGHT # Create the pageframe self.pgf = dabo.ui.dPageFrame(self) tbls = self.mainDict.keys() self.pgf.PageCount = len(tbls) pgNum = 0 # Small defaults self.minHt = 10 self.minWd = 10 for tbl in tbls: pg = self.pgf.GetPage(pgNum) pg.Caption = tbl pgNum += 1 lbl = dabo.ui.dLabel(pg) lbl.Caption = "Editing Form for table: " + tbl lbl.FontSize = 18 lbl.FontBold = True lbl.ForeColor = (0,0,128) grd = FieldSpecGrid(pg, self.mainDict[tbl]) pgsz = wx.BoxSizer(wx.VERTICAL) pgsz.Add(lbl, 0, wx.EXPAND | wx.ALIGN_CENTER) pgsz.Add( (10, 10), 0) pgsz.Add(grd, 1, wx.EXPAND) pg.SetSizer(pgsz) pgsz.Layout() btn = dabo.ui.dCommandButton(self) btn.Caption = " Preview Form " self.Bind(wx.EVT_BUTTON, self.onPreview, id=btn.GetId() ) sz = wx.BoxSizer(wx.VERTICAL) sz.Add(self.pgf, 1, wx.EXPAND) sz.Add(btn, 0, wx.ALIGN_CENTRE) self.SetSizer(sz) sz.Layout() def onPreview(self, evt): tbl = self.pgf.GetPageText(self.pgf.GetSelection()) tmpSpecFile = "./.tmpSpec.xml" self.saveTmp(tmpSpecFile) create = True if self.previewForms.has_key(tbl): if isinstance(self.previewForms[tbl], dabo.ui.dDataNavForm): create = False if create: self.previewForms[tbl] = dabo.ui.dDataNavForm(self, previewMode=True, tbl=tbl) prev = self.previewForms[tbl] prev.Caption = tbl + " - Preview" prev.setFieldSpecs(tmpSpecFile, tbl) prev.Show(True) prev.Raise() def getBaseXML(self): ret = """ %s """ return ret def getTableTemplate(self): return """ %s
""" def getFieldTemplate(self): return """ """ if __name__ == "__main__": app = dabo.dApp() app.MainFormClass = FieldEditorForm app.setup() if app.MainForm.specFile: # Only run if there is a valid spec file app.start() else: dabo.errorLog.write("No valid spec file.")