import dabo.db, dabo.ui, dabo.biz import wx, os, time import wx.wizard as wiz def makePageTitle(wizPg, title): sizer = wx.BoxSizer(wx.VERTICAL) wizPg.SetSizer(sizer) title = wx.StaticText(wizPg, -1, title) title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)) sizer.AddWindow(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5) sizer.AddWindow(wx.StaticLine(wizPg, -1), 0, wx.EXPAND|wx.ALL, 5) return sizer class TitledPage(wiz.WizardPageSimple): def __init__(self, parent, title): wiz.WizardPageSimple.__init__(self, parent) self.sizer = makePageTitle(self, title) def onLeavePage(self, direction): return True def onEnterPage(self, direction): pass class PageIntro(TitledPage): def __init__(self, parent, title): super(PageIntro, self).__init__(parent, title) self.sizer.Add(wx.StaticText(self, -1, """ Use this wizard to quickly create a maintenance application for your database. Point the wizard at a database and specify a target directory: you'll get a full-fledged Dabo application with screens for each table in the database. You can then hand-edit the resulting application to fit your exact requirements. Currently, the database must be MySQL. Press 'Next' to enter database parameters. """)) class PageDatabase(TitledPage): def __init__(self, parent, title): super(PageDatabase, self).__init__(parent, title) self.sizer.Add(wx.StaticText(self, -1, """ Enter the parameters here, and then click 'Next'. """)) self.dbPanel = dabo.ui.dPanel(self) self.dbPanel.SetSizer(wx.BoxSizer(wx.VERTICAL)) self.sizer.Add(self.dbPanel, 1, wx.EXPAND, 0) lbl = dabo.ui.dLabel(self.dbPanel, style=wx.ALIGN_RIGHT|wx.ST_NO_AUTORESIZE) lbl.Name = "lblChoice" lbl.Width = 75 lbl.Caption = "DB Type:" dbChoice = wx.Choice(self.dbPanel, -1, choices=['MySQL', 'FireBird']) dbChoice.SetName('dbChoice') hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(lbl) hs.Add(dbChoice) self.dbPanel.GetSizer().Add(hs, 0, wx.EXPAND) for item in (('Host', 'melder.paulmcnett.com'), ('Database', 'dabotest'), ('User', 'dabo'), ('Password', 'dabo'), ('Port', '3306')): lbl = dabo.ui.dLabel(self.dbPanel, style=wx.ALIGN_RIGHT|wx.ST_NO_AUTORESIZE) lbl.Name = "lbl%s" % item[0] lbl.Width = 75 lbl.Caption = '%s:' % item[0] obj = dabo.ui.dTextBox(self.dbPanel) obj.Name = 'txt%s' % item[0] obj.Value = item[1] hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(lbl) hs.Add(obj, 1, wx.ALL, 0) self.dbPanel.GetSizer().Add(hs, 0, wx.EXPAND) def onLeavePage(self, direction): if direction == "forward": if len(self.GetParent().tableDict) > 0: if not dabo.ui.dMessageBox.areYouSure("Overwrite the current table information?"): return True # Set the wizard's connect info based on the user input: ci = self.GetParent().connectInfo ci.BackendName = self.dbPanel.dbChoice.GetStringSelection() ci.Host = self.dbPanel.txtHost.Value ci.DbName = self.dbPanel.txtDatabase.Value ci.User = self.dbPanel.txtUser.Value ci.Password = self.dbPanel.txtPassword.Value try: ci.Port = int(self.dbPanel.txtPort.Value) except ValueError: ci.Port = None # Try to get a connection:: try: connection = ci.getConnection() except: dabo.ui.dMessageBox.stop("Could not connect to the database server. " "Please check your parameters and try again.") return False # Define a Dabo cursor to interact with: dictCursorClass = ci.BackendObject.getDictCursorClass() class dCursor(dabo.db.dCursorMixin, dictCursorClass): def __init__(self): dabo.db.dCursorMixin.__init__(self) dictCursorClass.__init__(self, connection) self.superCursor = dictCursorClass self.BackendObject = ci.BackendObject cursor = dCursor() tables = cursor.getTables() self.GetParent().tableDict = {} tableOrder = 0 for table in tables: tableDict = {} count = cursor.getTableRecordCount(table) tableDict['name'] = table tableDict['recordCount'] = count tableDict['order'] = tableOrder fields = cursor.getFields(table) tableDict['fields'] = {} fieldOrder = 0 for field in fields: fieldName = field[0] tableDict['fields'][fieldName] = {} tableDict['fields'][fieldName]['name'] = fieldName tableDict['fields'][fieldName]['type'] = field[1] tableDict['fields'][fieldName]['order'] = fieldOrder tableDict['fields'][fieldName]['pk'] = field[2] fieldOrder += 1 self.GetParent().tableDict[table] = tableDict tableOrder += 1 connection.close() return True class PageTableInfo(TitledPage): def __init__(self, parent, title): super(PageTableInfo, self).__init__(parent, title) self.sizer.Add(wx.StaticText(self, -1, """ The connection to the database was successful. Here is the table information that will be used for building your maintenance application: """)) self.eb = dabo.ui.dEditBox(self) self.eb.ReadOnly = True self.eb.Value = " < No table information yet > " self.sizer.Add(self.eb, 1, wx.EXPAND) def onEnterPage(self, direction): if direction == 'forward': self.eb.Value = self.GetParent().getTableInfo() class PageTargetDirectory(TitledPage): def __init__(self, parent, title): super(PageTargetDirectory, self).__init__(parent, title) self.sizer.Add(wx.StaticText(self, -1, """ Enter the directory where you wish to place your new application. You can always move the directory later. """)) hs = wx.BoxSizer(wx.HORIZONTAL) self.txtDir = dabo.ui.dTextBox(self) self.txtDir.FontSize=8 self.txtDir.Value = '' hs.Add(self.txtDir, 1) self.cmdPick = dabo.ui.dCommandButton(self) self.cmdPick.Caption = '...' self.cmdPick.Width = 30 self.cmdPick.Height = self.txtDir.Height hs.Add(self.cmdPick, 0) self.sizer.Add(hs, 1, wx.EXPAND) self.cmdPick.Bind(wx.EVT_BUTTON, self.onPick) def onPick(self, evt): dlg = wx.DirDialog(self, "Choose a directory:", defaultPath = self.txtDir.Value, style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) if dlg.ShowModal() == wx.ID_OK: self.txtDir.Value = dlg.GetPath() dlg.Destroy() def onEnterPage(self, direction): if direction == 'forward': if self.txtDir.Value == '': self.txtDir.Value = '%s/%s' % (os.getcwd(), self.GetParent().connectInfo.DbName) def onLeavePage(self, direction): if direction == 'forward': directory = self.txtDir.Value if not os.path.exists(directory): if dabo.ui.dMessageBox.areYouSure( "The target directory %s does not exist. Do you want to create it now?" % ( directory,)): os.mkdir(directory) else: return False self.GetParent().outputDirectory = directory return True class PageGo(TitledPage): def __init__(self, parent, title): super(PageGo, self).__init__(parent, title) self.sizer.Add(wx.StaticText(self, -1, """ Press 'Finish' to create your application, or 'Back' to edit any information.""")) def onLeavePage(self, direction): if direction == "forward" and not self.GetParent().createApp(): return False else: dabo.ui.dMessageBox.info(""" Your application has been created. Please remember that the intent of this is to give you, the database developer, the ability to browse and edit all records in all tables of your database. As it stands now, there are no limits to what you can do with the data: add records, delete records, modify records. In other words, the application you just created IS DANGEROUS. Treat it as a demo, and if you do use it, use it with care and consider adding business rules and organizing your screens into master/childview parent/child relationships. I take no responsibility if your database gets hopelessly mangled, but at the same time I hope you find this wizard useful. To see your app in action, navigate to the target directory and type 'python main.py' at the commandline. pkm """) return True class WizMaintenanceApp(wiz.Wizard): def __init__(self, parent=None): wiz.Wizard.__init__(self, parent, -1, "Maintenance Application Wizard", dabo.ui.dIcons.getIconBitmap('daboIcon128')) self.tableDict = {} self.connectInfo = dabo.db.dConnectInfo() self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnWizPageChanged) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnWizPageChanging) self.Bind(wiz.EVT_WIZARD_CANCEL, self.OnWizCancel) self.Bind(wiz.EVT_WIZARD_FINISHED, self.OnWizFinished) page1 = PageIntro(self, "Introduction") page2 = PageDatabase(self, "Database Parameters") page3 = PageTableInfo(self, "Table Information") page4 = PageTargetDirectory(self, "Output Directory") page5 = PageGo(self, "Create Application") self.page1 = page1 self.FitToPage(page1) # Use the convenience Chain function to connect the pages wiz.WizardPageSimple_Chain(page1, page2) wiz.WizardPageSimple_Chain(page2, page3) wiz.WizardPageSimple_Chain(page3, page4) wiz.WizardPageSimple_Chain(page4, page5) if self.RunWizard(page1): print "Wizard completed." else: print "Wizard canceled." self.Destroy() def OnWizPageChanged(self, evt): if evt.GetDirection(): dir = "forward" else: dir = "backward" page = evt.GetPage() page.onEnterPage(dir) def OnWizPageChanging(self, evt): if evt.GetDirection(): dir = "forward" else: dir = "backward" page = evt.GetPage() if not page.onLeavePage(dir): evt.Veto() def OnWizCancel(self, evt): page = evt.GetPage() def OnWizFinished(self, evt): print "finished" def getTableInfo(self): ti = '' td = self.tableDict tables = td.keys() tables.sort() for table in tables: count = td[table]['recordCount'] ti = ''.join((ti, "\n%s - %s record%s.\n" % (table, count, count==1 and '' or 's'))) fields = td[table]['fields'].keys() fields.sort() for field in fields: ti = ''.join((ti, " %s (%s) %s\n" % (td[table]['fields'][field]['name'], td[table]['fields'][field]['type'], td[table]['fields'][field]['pk'] == True and 'PK' or ''))) return ti.strip() def createApp(self): directory = self.outputDirectory if os.path.exists(directory): td = self.tableDict ci = self.connectInfo oldDir = os.getcwd() os.chdir(directory) file = open('./main.py', 'w') file.write(getMain()) file.close() file = open('./dbConnectionDefs.py', 'w') file.write(getDbConnectionDefs(ci)) file.close() file = open('./myBizobjs.py', 'w') file.write(getMyBizobjs(td)) file.close() file = open('./myFileOpenMenu.py', 'w') file.write(getMyFileOpenMenu(td)) file.close() file = open('./myForms.py', 'w') file.write(getMyForms(td, ci)) file.close() return True else: dabo.ui.dMessageBox.stop("The target directory does not exist. Cannot continue.") return False def getMain(): return """import myFileOpenMenu if __name__ == "__main__": import dabo app = dabo.dApp() app.setup() myFileOpenMenu.fill(app.mainFrame) app.start() """ def getDbConnectionDefs(ci): code = """# dbConnectionDefs.py # Created with the Dabo Wizard 'wizMaintenanceApp' on %s. # Treat this as boilerplate code, and edit as needed. # You can define any number of DB Connection definitions here. # They will all be set up in dabo's app object for global availability. # Set up the empty dictionary dbConnectionDefs = {} # For each Connection Definition you want, repeat the following # block with different keys and values: dbConnectionDefs["%s@%s"] = {"dbType": "%s", "host": "%s", "user": "%s", "password": "%s", "dbName": "%s", "port": %s} """ % (time.ctime(), ci.User, ci.Host, ci.BackendName, ci.Host, ci.User, ci.Password, ci.DbName, ci.Port) return code def getMyBizobjs(tableDict): code = """# myBizobjs.py # Created with the Dabo Wizard 'wizMaintenanceApp' on %s. # Treat this as boilerplate code, and edit as needed. import dabo.biz as biz import dabo.dException as dException from dabo.dLocalize import _ # Each table has been assigned a bizobj, but it is up to # you to provide any needed business rules. By default, # every change to the data will be allowed. """ % (time.ctime(),) tables = tableDict.keys() tables.sort() for table in tables: # find the pk field, if any: pkField = 'No PK field found: you MUST set this manually' for field in tableDict[table]['fields'].keys(): if tableDict[table]['fields'][field]['pk'] == True: pkField = field break code = ''.join((code, """class Biz%s(biz.dBizobj): def beforeInit(self): self.Caption = "%s" self.DataSource = "%s" self.KeyField = "%s" self.RequeryOnLoad = False def afterInit(self): # Set the default values for new records added: self.defaultValues = {} def validateRecord(self): # Initially set the error message to an empty string. If this method # makes it all the way through with no biz rule violations, the string will # still be empty and the bizobj will let the commit happen. errorText = "" # Biz rules follow. These can be as complex as necessary, the key # thing is to remember to add to the error message so the user knows # why a failure happened,. # (your biz rules here) # If we return an empty string, there were no validation problems. return errorText """ % (table.title(), table.title(), table, pkField))) return code def getMyFileOpenMenu(tableDict): code = """# myFileOpenMenu.py # Created with the Dabo Wizard 'wizMaintenanceApp' on %s. import dabo.ui as ui import myForms, wx def fill(frame): menuBar = frame.GetMenuBar() actionList = frame.Application.actionList """ % time.ctime() tables = tableDict.keys() tables.sort() for table in tables: code = ''.join((code, '\tactionList.setAction("FileOpen%s", open%s)\n' % ( table.title(), table.title()))) code = ''.join((code, """ fileMenu = menuBar.GetMenu(0) # The File/Open menu should be inserted before the File/Quit, but it doesn't # work on Mac ('Open' appears as a menu item, not a submenu). if wx.Platform == '__WXMAC__': submenu = fileMenu.AppendMenu(-1, "&Open\tCtrl+O", FileOpenMenu(frame)) else: # On linux, it still just gets appended, but at least it becomes a submenu. submenu = fileMenu.PrependMenu(-1, "&Open\tCtrl+O", FileOpenMenu(frame)) class FileOpenMenu(ui.dMenu): def __init__(self, frame): FileOpenMenu.doDefault(frame) """)) for table in tables: code = ''.join((code, """ menuId = wx.NewId() self.Append(menuId, "&%s") frame.Bind(wx.EVT_MENU, self.actionList.getAction("FileOpen%s")["func"], id=menuId) """ % (table.title(), table.title()))) code = ''.join((code, '\n')) for table in tables: code = ''.join((code, """ def open%s(event): openForm(myForms.frm%s) event.Skip() """ % (table.title(), table.title()))) code = ''.join((code, """ def openForm(classRef): mainFrame = wx.GetApp().GetTopWindow() dApp = mainFrame.Application dForm = classRef(mainFrame) dForm.Show() """ )) return code def getMyForms(tableDict, ci): code = """# myForms.py # Created with the Dabo Wizard 'wizMaintenanceApp' on %s. import dabo import myBizobjs, myFileOpenMenu ### Define a base form class for the application: class MyFormBase(dabo.ui.dFormDataNav): def afterInit(self): # The connect info was already loaded by dApp, but a connection instance is # needed to pass to the bizobj's constructor. self.connection = dabo.db.dConnection(self.Application.dbConnectionDefs['%s@%s']) MyFormBase.doDefault() def afterSetMenuBar(self): myFileOpenMenu.fill(self) ### Each table gets its own form derived from MyFormBase:""" % ( time.ctime(), ci.User, ci.Host) tables = tableDict.keys() tables.sort() for table in tables: code = ''.join((code, """ class frm%s(MyFormBase): def afterInit(self): frm%s.doDefault() self.Name = "frm%s" self.Caption = "%s" # Instantiate the bizobj defined in myBizobjs.py: primaryBizobj = myBizobjs.Biz%s(self.connection) # Register it with dForm: self.addBizobj(primaryBizobj) # Tell the dFormDataNav some things about the columns in the result set, # so that it can do some automatic setup chores, such as filling in the # browse grid, edit page, and select page. If you want to control what # fields appear on the select/browse/edit page, these are the values # you want to change: self.setColumnDefs(self.getBizobj().DataSource, [ """ % (table.title(), table.title(), table.title(), table.title(), table.title()))) # Fields will sort in the order they were originally gathered from the # database. unSortedFields = tableDict[table]['fields'].keys() sortedFields = [] for field in unSortedFields: sortedFields.append([tableDict[table]['fields'][field]['order'], field]) sortedFields.sort() fields = [] for l in sortedFields: fields.append(l[1]) for field in fields: code = ''.join((code, """ {'tableName': '%s', 'fieldName': '%s', 'caption': '%s', 'type': '%s', 'showGrid': True, 'showEdit': True, 'editEdit': True},\n""" % ( table, field, field, tableDict[table]['fields'][field]['type']))) code = ''.join((code, """\n\t\t]) # Set up the SQL Builder in the bizobj: self.getBizobj().setFieldClause("") for column in self.getColumnDefs(self.getBizobj().DataSource): table, field = column['tableName'], column['fieldName'] self.getBizobj().addField(table+"."+field+" as "+field) self.getBizobj().setFromClause("%s") """ % table)) return code if __name__ == "__main__": app = wx.PySimpleApp() wiz = WizMaintenanceApp() wiz.Show(True) app.MainLoop()