Related
I am trying to filter an objectlistview based on a text value in a certain column of my data. I can't figure out how to construct the filter properly. I have been refering to:
http://objectlistview.sourceforge.net/python/features.html
http://objectlistview.sourceforge.net/python/majorClasses.html
I am able to get Filter.Head(n) to work sucessfully but not ableto get Filter.TextSearch(objectListView, columns=(), text="") to work because I don't understand what the parameter types are suppost to be.
I have written the below code:
def OnFilterMeter(self,event):
list = self.exception_panel.col_list ## The lenght of my column list
col = list[len(list)-1] ## I want to search the last column
meter_flt = Filter.TextSearch(self.exception_panel,columns = (col), text = "10") ## Create filter to find string 10 in last column
self.exception_panel.SetFilter(meter_flt)
self.exception_panel.populate()
I can't understand why this doesn't work. The program doesn't filter the list when it tries to populate the list view again. At the very least it should show all blanks because it cannot find any items that are equal to "10". Please help. Thank you
Below is my code :
class Display_Manager(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, -1, title)
self.exception_panel = Exception_Display(self,0)
# self.exception_panel2 = Exception_Display(self,1)
## Setup FileMenu Bar Setup
filemenu= wx.Menu() ## Create Menu Bar
filemenu.Append(ID_LOG,"Login","Login as user with password.") ## Clicking shoudl load a single dailyi report which user selects
filemenu.Append(ID_LOAD,"Load Report","Load Daliy Exception Report") ## Clicking shoudl load a single dailyi report which user selects
filemenu.Append(ID_LOADS,"Load All","Load All Daliy Exception Reports") ## Clicking should load all daily reports in a directory which user selects
filemenu.Append(ID_REFRESH,"Refresh DB","Refresh Database. Overwrites any un saved data.") ## Clicking should refreseh the database and overwrite any unsaved changes
filemenu.Append(ID_SAVE,"Save DB","Saves and commits any changes to database.") ## Clicking will save and commit any changes or new data to the database
filemenu.Append(ID_EXIT,"E&xit"," Terminate the program") ## exit the program
## Setup Edit Menu Bar
editmenu = wx.Menu()
editmenu.Append(ID_ADD,"Add","Add Exception")
editmenu.Append(ID_DELETE,"Delete","Delete Exception")
editmenu.Append(ID_UNDO,"Stepback DB","Go back to a previous version of the database")
editmenu.Append(ID_REDO,"Stepfoward DB","Go foward to a previous version of the database")
## Setup Report Menu
reportmenu = wx.Menu()
reportmenu.Append(ID_REPORT,"Exception Report","Generate an Excel Report of selected exceptions.")
reportmenu.Append(ID_REPORT2,"Meter Report","Generate an Excel Report of selected exceptions.")
reportmenu.Append(ID_REPORT3,"Transformer Report","Generate an Excel Report of selected exceptions.")
## Setup Bucket Menu
bucketmenu = wx.Menu()
bucketmenu.Append(ID_METERPROB,"Meter Problem","Place the meter in the meter problem bucket.")
bucketmenu.Append(ID_LOWVOLT,"Investigate Low Voltage","Place the meter in the investigate bucket.")
bucketmenu.Append(ID_TURNS,"Turns Ratio","Place the meter in the turns ratio bucket.")
## Setup Configure Menu Menu
configmenu = wx.Menu()
configmenu.Append(ID_FILTER,"Set Filters","Filter by district and company.")
configmenu.Append(ID_SAVEFIL,"Save Filters","Save Filters for Later use.")
configmenu.Append(ID_LOADFIL,"Load Filters","Load Filters for Later use.")
## Add file menu bar
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File")
menuBar.Append(editmenu, "&Edit")
menuBar.Append(reportmenu, "&Reports")
menuBar.Append(configmenu, "&Config")
menuBar.Append(bucketmenu, "&Bucket")
self.SetMenuBar(menuBar)
## Create Toolbar
tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER |
wx.TB_FLAT | wx.TB_TEXT)
tb.AddSimpleTool(10, wx.Bitmap('images/database.png'), 'Save Database')
tb.AddSimpleTool(20, wx.Bitmap('images/excel.png'), 'Get Quick Excel Report')
tb.AddSimpleTool(70, wx.Bitmap('images/analyze.png'), 'Analyze Selected Exceptions')
tb.AddSimpleTool(71, wx.Bitmap('images/refresh.png'), 'Refresh Selected Relationships')
tb.AddSeparator()
tb.AddSimpleTool(30, wx.Bitmap('images/today.png'), 'Filter only the latest data.')
tb.AddSimpleTool(40, wx.Bitmap('images/all_time.png'), 'Filter All Data from all time')
tb.AddSimpleTool(41, wx.Bitmap('images/date_range.png'), 'Select a date range to view data over')
tb.AddSeparator()
tb.AddSimpleTool(50, wx.Bitmap('images/AMI_Meter.png'), 'Bucket as meter problem')
tb.AddSimpleTool(60, wx.Bitmap('images/violation.png'), 'Bucket to District')
tb.AddSimpleTool(61, wx.Bitmap('images/turns.png'), 'Bucket Turns Ratio')
tb.AddSimpleTool(62, wx.Bitmap('images/plan.png'), 'Bucket for further review for planning engineer')
tb.AddSeparator()
tb.AddSimpleTool(63, wx.Bitmap('images/cyme.png'), 'Load CYME model for specific meter')
tb.AddSimpleTool(64, wx.Bitmap('images/relate_dot.png'), 'Cluster & Relate Meter Exceptions')
tb.AddSeparator()
tb.AddSimpleTool(65, wx.Bitmap('images/bucket_m.png'), 'Filter Meter Bucket')
tb.AddSimpleTool(66, wx.Bitmap('images/bucket_v.png'), 'Filter Violation Bucket')
tb.AddSimpleTool(67, wx.Bitmap('images/bucket_t.png'), 'Filter Turns Ratio Bucket')
tb.AddSimpleTool(68, wx.Bitmap('images/bucket_p.png'), 'Filter Planning Bucket')
tb.SetToolBitmapSize((84,84))
tb.Realize()
self.Bind(wx.EVT_TOOL,self.OnRefresh,id =71)
self.Bind(wx.EVT_TOOL,self.OnAnalyze,id =70)
self.Bind(wx.EVT_TOOL,self.OnSave,id =10)
self.Bind(wx.EVT_TOOL,self.OnBucketMeter,id =50)
self.Bind(wx.EVT_TOOL,self.OnVioMeter,id =60)
self.Bind(wx.EVT_TOOL,self.OnTurnsMeter,id =61)
self.Bind(wx.EVT_TOOL,self.OnPlanMeter,id =62)
self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =65)
# self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =66)
# self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =67)
# self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =68)
## Create Sizers
# self.main_sizer = wx.BoxSizer(wx.VERTICAL)
# self.top_sizer = wx.BoxSizer(wx.HORIZONTAL)
# self.bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)
# self.main_sizer.Add(self.top_sizer,0,wx.EXPAND)
# self.main_sizer.Add(self.bottom_sizer,0,wx.EXPAND)
## Show the frame
# self.SetSizer(self.main_sizer)
self.Center()
self.Show(True)
def OnSave(self,event):
session.commit()
print "ON Save"
def OnRefresh(self,event):
self.exception_panel.populate()
self.ColorBuckets()
print "OnRelate"
def OnAnalyze(self,event):
objectList = self.exception_panel.GetSelectedObjects() ## Get list of selected objects to relate in the database
for object in objectList:
print object
object.calculate()
# print self.GetValueAt(self.GetObjectAt(event.rowIndex),event.subItemIndex)
# self.temp_value = event.editor.GetValue()
self.exception_panel.populate()
print "OnAnalze"
def OnFilterDate1(self,event):
print "on Filter date"
def OnFilterDate2(self,event):
print "on Filter date"
def OnFilterDate3(self,event):
print "on Filter date"
def OnFilterMeter(self,event):
list = self.exception_panel.col_list
col = list[len(list)-1]
# meter_flt = Filter.Head(10)
meter_flt = Filter.TextSearch(self.exception_panel,columns = (col), text = "10")
self.exception_panel.SetFilter(meter_flt)
self.exception_panel.populate()
# self.exception_panel.Refresh()
# self.exception_panel.populate()
# self.exception_panel.SetObjects(qrty_meters_excpt)
# self.exception_panel.populate()
print "On Filter Meter"
def OnBucketMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
print object
object.known_flags = 20 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def OnVioMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
object.known_flags = 10 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def OnTurnsMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
object.known_flags = 30 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def OnPlanMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
object.known_flags = 40 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def ColorBuckets(self): ## Color All Buckets according to knowflags
## Query the session for only items that have know_flags greate than 0
qrty_color_buckets = session.query(Exception).filter(Exception.known_flags != "").all()
print qrty_color_buckets
for exception in qrty_color_buckets:
print exception.known_flags == 10
flag = int(exception.known_flags) ## alias the flag
if flag == 20: ## Meter Mismatch
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#ffa500') ## Oranage
elif flag == 10: ## Violation
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#ff0000') ## Red
elif flag == 30: ## Turns Ratio
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#0066CC') ## Blue
elif flag == 40: ## Plan referal
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#FFFF00') ## Yellow
Please see the function OnFilterMeter.
The following works for me, it does a search on the second column and it shows just one matching entry.
# -*- coding: utf-8 -*-
import datetime
import wx
import wx.lib.sized_controls as SC
import ObjectListView as OLV
class MyData:
def __init__(self, year, month, day, level, sets, reps):
self.date = datetime.date(year, month, day)
self.level = level
self.sets = sets
self.reps = reps
def GetDate(self):
return self.date
def GetLevel(self):
return self.level
def GetSets(self):
return self.sets
def GetRepetitions(self):
return self.reps
class MyListCtrl(OLV.GroupListView):
def __init__(self, parent):
super(MyListCtrl, self).__init__(parent, wx.ID_ANY, style=wx.LC_REPORT)
self.SetColumns(self._ColumnDefinitions())
meter_flt = OLV.Filter.TextSearch(self, columns=self.columns[2:3],
text="7")
self.SetFilter(meter_flt)
self.SetObjects(self._DataObjects())
def _DataObjects(self):
return [MyData(2010,10,8, 1, 2, 3),
MyData(2005,10,10, 7, 2, 3),
MyData(2010,10,3, 2, 2, 3),
MyData(2012,10,10, 1, 2, 3),
MyData(2014,10,10, 1, 2, 3)
]
def _ColumnDefinitions(self):
return [OLV.ColumnDefn('Date', valueGetter='GetDate', groupKeyGetter='GetDate'),
OLV.ColumnDefn('Level', valueGetter='GetLevel', width=150),
OLV.ColumnDefn('Sets', valueGetter='GetSets', width=100, groupKeyGetter='GetSets'),
OLV.ColumnDefn('Reps', valueGetter='GetRepetitions', width=200)]
class MyFrame(SC.SizedFrame):
def __init__(self):
super(MyFrame, self).__init__(None)
pane = self.GetContentsPane()
olv = MyListCtrl(pane)
olv.SetSizerProps(expand=True, proportion=1)
if __name__ == '__main__':
import wx.lib.mixins.inspection as WIT
app = WIT.InspectableApp()
win = MyFrame()
win.Show()
app.MainLoop()
I have a problem when I do click on the listbox, In my program, I did click on the listbox will appear a video and explain about the video. I made a sample in the database are "name_link" which will appear in the listbox. expl1, expl2, expl3. Of each name_link have different information. However, that happens every time I click on one of these name_link, video that appears only Video3 and system never show the explain about video. When I click name_link video1 video2 emerging or always Video3. I am so stuck in this section.
this section during click :
tb4 = wx.StaticText(self, -1, label='explain', pos=(20, 145))
wx.TextCtrl(self, -1, pos=(80, 145), size=(220, 120))
self.opt = wx.ListBox(pan1, -1, pos=(10, 210), size=(480, 250), style= wx.TE_MULTILINE | wx.BORDER_SUNKEN)
def playFile(self, event):
self.player.Play()
def OnEnter(self, event):
self.opt.SetLabel(self.PatMatch())
def PatMatch(self):
con = sqlite3.connect('test.db')
with con:
cur = con.cursor()
for row in cur.execute("Select * From Video"):
klmt = self.inpt.GetValue()
if row[1] in klmt.lower():
self.opt.Append(row[2])
self.player.Load(row[4])
return self.Bind(wx.EVT_LISTBOX_DCLICK, self.playFile, self.op)
The data base like this :
id word name_link explain link
--- ------ ----------- --------- --------
1 python Video1 test C:\Users\Ihsan\Downloads\Video1.MP4
2 python Video2 test1 C:\Users\Ihsan\Downloads\Video2.MP4
3 python Video3 test2 C:\Users\Ihsan\Downloads\Video3.MP4
There are several issues:
You want bind wx.EVT_LISTBOX_DCLICK only once. When the event is fired, you want to read out which item (GetSelection) was selected when double-clicking. Use GetSelections for multiple selections.
Bad indentation: In the inner loop (self.player…) and with the return self.Bind… which should be in the innermost loop. As it is written now it will bind once to the last element. As written in point 1 this is not the way how it is done anyhow.
In Bind, self.op should be self.opt
See the wxPython demo on the download page to see how to use ListBox sensibly.
EDIT: Code sample added
import wx
import sqlite3
class play_model(object):
def __init__(self):
# all the sqlite init stuff goes here, it is mocked for the sake of the example
self.conn = sqlite3.connect(':memory:')
c = self.conn.cursor()
c.execute('CREATE TABLE video (word text, name_link text, explain text, link text)')
newrecs = (('python', 'Video1', 'test1', r'C:\video1.MP4'),
('python', 'Video2', 'test2', r'C:\video2.MP4'),
('notpython', 'Video3', 'test3', r'C:\video3.MP4'),
('python', 'Video4', 'test4', r'C:\video4.MP4'),
('python', 'Video5', 'test5', r'C:\video5.MP4'),
)
for tup in newrecs:
c.execute('INSERT INTO video VALUES (?, ?, ?, ?)', tup)
self.map_explain = {}
def get_videos(self, klmt):
# you want to get videos matching a parameter?
sqlstr = 'SELECT * FROM video WHERE video.word = (?)'
c = self.conn.cursor()
c.execute(sqlstr, (klmt.lower(),))
res = c.fetchall()
return res
class playframe(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
pnl = wx.Panel(self, -1)
self.explain = wx.StaticText(pnl, -1, 'Click in list to show explanation')
self.klmt = wx.TextCtrl(pnl, -1, '')
self.srch = wx.Button(pnl, -1, u'Search…')
self.vids = wx.ListBox(pnl, -1, style=wx.LB_MULTIPLE)
szmain = wx.BoxSizer(wx.VERTICAL)
szmain.Add(wx.StaticText(pnl, -1, 'Search for video category:'), 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.klmt, 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.srch, 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.vids, 1, wx.EXPAND|wx.ALL, 4)
szmain.Add(wx.StaticText(pnl, -1, 'Explanation for video'), 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.explain, 0, wx.EXPAND|wx.ALL, 4)
pnl.SetSizer(szmain)
szmain.Fit(self)
class controller(object):
def __init__(self, app):
self.model = play_model()
self.search_results = []
# print self.model.get_videos('python')
self.frm = playframe(None, -1, 'test_playframe')
self.frm.Show()
self.frm.srch.Bind(wx.EVT_BUTTON, self.on_srch)
self.frm.vids.Bind(wx.EVT_LISTBOX, self.onvid_dblclick)
def onvid_dblclick(self, evt):
sels = evt.GetEventObject().GetSelections()
for idx in sels:
self.frm.explain.SetLabel(self.search_results[idx][2])
print 'play video:', idx, self.search_results[idx]
def on_srch(self, evt):
klmt = self.frm.klmt.GetValue()
# print klmt
res = self.model.get_videos(klmt)
if res:
self.search_results = res
self.frm.vids.Clear()
self.frm.vids.AppendItems([row[1] for row in self.search_results])
else:
parent = self.frm
wx.MessageDialog(parent,
'Not found in word category: {0}'.format(klmt),
'Category not found').ShowModal()
if __name__ == '__main__':
app = wx.App(redirect=False)
controller(app)
app.MainLoop()
I was try create some code like this.
def playVideo(self, evt, temp):
self.player.Load(evt.GetClientObject())
self.Play(True)
self.pjs.Clear()
self.pjs.AppendText(evt.GetClientObject())
Its work for the video, the video can play. but for the information from column database explain, its not show. i want the information from column explain print in tb4. #nepix32
So, I have two pieces of code. The first is the GUI class:
'''
Created on Mar 6, 2013
#author: Zach
'''
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version Sep 8 2010)
## http://www.wxformbuilder.org/
##
## PLEASE DO "NOT" EDIT THIS FILE!
###########################################################################
import wx
import wx.grid
from Books import *
###########################################################################
## Class MyFrame1
###########################################################################
class MyFrame1 ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 734,344 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
bSizer1 = wx.BoxSizer( wx.VERTICAL )
bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
self.patrons_table = wx.grid.Grid( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
# Grid
self.patrons_table.CreateGrid( 0, 7 )
self.patrons_table.EnableEditing( True )
self.patrons_table.EnableGridLines( True )
self.patrons_table.SetGridLineColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_APPWORKSPACE ) )
self.patrons_table.EnableDragGridSize( True )
self.patrons_table.SetMargins( 0, 0 )
# Columns
self.patrons_table.SetColSize( 0, 100 )
self.patrons_table.SetColSize( 1, 100 )
self.patrons_table.SetColSize( 2, 100 )
self.patrons_table.SetColSize( 3, 100 )
self.patrons_table.SetColSize( 4, 100 )
self.patrons_table.SetColSize( 5, 100 )
self.patrons_table.SetColSize( 6, 100 )
self.patrons_table.EnableDragColMove( True )
self.patrons_table.EnableDragColSize( True )
self.patrons_table.SetColLabelSize( 40 )
self.patrons_table.SetColLabelValue( 0, "ID" )
self.patrons_table.SetColLabelValue( 1, "Name" )
self.patrons_table.SetColLabelValue( 2, "Address" )
self.patrons_table.SetColLabelValue( 3, "Phone" )
self.patrons_table.SetColLabelValue( 4, "Email" )
self.patrons_table.SetColLabelValue( 5, "Fees/Day")
self.patrons_table.SetColLabelValue( 6, "Fees Owed" )
self.patrons_table.SetColLabelAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
# Rows
self.patrons_table.AutoSizeRows()
self.patrons_table.EnableDragRowSize( True )
self.patrons_table.SetRowLabelSize( 80 )
self.patrons_table.SetRowLabelAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
# Label Appearance
# Cell Defaults
self.patrons_table.SetDefaultCellAlignment( wx.ALIGN_LEFT, wx.ALIGN_TOP )
self.patrons_table.SetToolTipString( u"Table of patrons in the library" )
bSizer2.Add( self.patrons_table, 7, wx.EXPAND|wx.ALL|wx.ALIGN_BOTTOM, 5 )
bSizer6 = wx.BoxSizer( wx.VERTICAL )
self.m_panel1 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer6.Add( self.m_panel1, 1, wx.EXPAND |wx.ALL, 5 )
self.m_button1 = wx.Button( self, wx.ID_ANY, u"Add", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_button1.SetToolTipString( u"Adds a patron" )
bSizer6.Add( self.m_button1, 0, wx.ALL, 5 )
self.m_button2 = wx.Button( self, wx.ID_ANY, u"Remove", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_button2.SetToolTipString( u"Removes the selected patron" )
bSizer6.Add( self.m_button2, 0, wx.ALL, 5 )
self.m_button3 = wx.Button( self, wx.ID_ANY, u"Update", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_button3.SetToolTipString( u"Updates the database" )
bSizer6.Add( self.m_button3, 0, wx.ALL, 5 )
bSizer2.Add( bSizer6, 1, wx.EXPAND, 5 )
bSizer1.Add( bSizer2, 1, wx.EXPAND, 5 )
bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
self.m_searchCtrl2 = wx.SearchCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 715,-1 ), 0 )
self.m_searchCtrl2.ShowSearchButton( True )
self.m_searchCtrl2.ShowCancelButton( False )
bSizer5.Add( self.m_searchCtrl2, 0, wx.ALL|wx.EXPAND, 5 )
bSizer1.Add( bSizer5, 0, wx.EXPAND, 5 )
self.SetSizer( bSizer1 )
self.Layout()
self.Centre( wx.BOTH )
# Connect Events
self.patrons_table.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onGridChange )
self.patrons_table.Bind( wx.grid.EVT_GRID_SELECT_CELL, self.onLeftClick )
self.m_button1.Bind( wx.EVT_BUTTON, self.addpatron )
self.m_button2.Bind( wx.EVT_BUTTON, self.removepatron )
self.m_button3.Bind( wx.EVT_BUTTON, self.updatepatronsDatabase )
self.m_searchCtrl2.Bind( wx.EVT_SEARCHCTRL_SEARCH_BTN, self.searchpatrons )
self.m_searchCtrl2.Bind( wx.EVT_TEXT_ENTER, self.searchpatrons )
#Import the Database into the table
self.lib = Library()
self.db = Database()
for i in self.db.getPatrons():
print "hello"
self.lib.addPatron(Patron(i[0], i[1], i[2], i[3], i[4]))
self.lib.resetHistory()
for i in self.lib.patrons:
self.patrons_table.InsertRows()
self.patrons_table.SetCellValue(0,0,str(i["title"]))
self.patrons_table.SetCellValue(0,1,str(i["author"]))
self.patrons_table.SetCellValue(0,2,str(i["patron_id"]))
self.patrons_table.SetCellValue(0,3,str(i["location"]))
self.patrons_table.SetCellValue(0,4,str(i["publisher"]))
self.patrons_table.SetCellValue(0,5,str(i["genre"]))
self.patrons_table.SetCellValue(0,6,str(i["copy_right"]))
self.patrons_table.SetCellValue(0,7,str(i["subject"]))
self.patrons_table.SetCellValue(0,8,str(i["is_checked_out"]))
def __del__( self ):
pass
# Virtual event handlers, overide them in your derived class
#EVENTS
def onGridChange( self, event ):# Called if one of the cell values was changed
updated_patron = Patron(self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),0),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),1),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),2),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),3),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),4),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),5),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),6),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),7),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),8),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),9),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),10))
self.lib.editPatron(self.selected_patron, updated_patron)
def onLeftClick( self, event ): #Turns the selected row into a patron
self.selected_patron = Patron(self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),0),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),1),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),2),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),3),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),4),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),5),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),6),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),7),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),8),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),9),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),10))
self.row = self.patrons_table.GetGridCursorRow()
self.column = self.patrons_table.GetGridCursorCol()
event.Skip()
def addpatron( self, event ):# adds a patron to the library and the table
self.patrons_table.InsertRows()
self.lib.addpatron(Patron())
event.Skip()
def removepatron( self, event ): #removes a patron from the table and the library
self.lib.removepatron(self.selected_patron)
self.patrons_table.DeleteRows()
print self.lib.patrons
event.Skip()
def updatepatronsDatabase( self, event ): #syncs the database with the library
self.db.mergeWithLibrary(self.lib)
def searchpatrons( self, event ):
value = self.m_searchCtrl2.GetValue()
i=0
while self.patrons_table.GetNumberRows()-1 >i:
if self.patrons_table.GetCellValue(i,2) == value:
self.patrons_table.SetGridCursor(i,2)
self.patrons_table.SelectRow(i)
self.patrons_table.Scroll(i,i)
break
i = i+1
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = MyFrame1(None)
frame.Show()
frame.Maximize()
app.MainLoop()
And the second part of the code is the engine behind the GUI class:
'''
Created on Mar 2, 2013
#author: Braden
This is a test file to try to make the GUI easier to program
It worked!
'''
import sqlite3 as sq
class Book(object):
r"""
This class defines what a book is. All books in the library are book objects
"""
def __init__(self, title = "n", author = "n", book_id = "n", location = "n", publisher = "n", genre = "n", copy_right = "n", subject = "n", is_checked_out = "n", price = "n", fee_increment = "n"):
r"""
This function sets the basic attributes of each book
"""
self.attributes = {"title":title,
"author":author,
"book_id":book_id,
"location":location,
"publisher":publisher,
"genre":genre,
"copy_right":copy_right,
"subject": subject,
"is_checked_out":is_checked_out,
"price": price,
"fee_increment":fee_increment
}
def __str__(self):
r"""
This returns a string representation of the book. This is what makes " print book" possible.
"""
return str(self.attributes)
def __getitem__(self,key):
r"""
This returns an attribute in the book. It makes "book['title']" work.
"""
return self.attributes[key]
def __setitem__(self,key,value):
r"""
This sets an attribute to a different value. It makes "book['title'] = 'My New Title' " Possilble ...I think.
"""
self.attributes[key] = value
def __cmp__(self,book):
if self.attributes == book.attributes:
return True
else:
return False
class Patron(object):
r"""
This is the Patron class. It is almost identical to the Book class just with different attributes.
"""
def __init__(self, patron_id, name, address, phone, email):
self.attributes = { "patron_id" : patron_id,
"name" : name,
"address" : address,
"phone" : phone,
"email" : email,
"fee_balance" : 0.00,
"fees_per_day" : 0.00,
"books_checked_out" : "",
"books_overdue" : ""}
def __str__(self):
return str(self.attributes)
def __getitem__(self,key):
return self.attributes[key]
def __setitem__(self,key,value):
self.attributes[key] = value
class Library(object):
r"""
This is the Library Class. It manipulates a database that has been loaded into the RAM.
"""
def __init__(self):
r"""
All this does is set the default values for the two Loaded Databases and the history.
"""
self.books = []
self.patrons= []
self.history = []
def addBook(self,book):
r"""
This appends a book object to the self.books list. Then it appends the action to the self.history list.
"""
self.books.append(book)
self.history.append(("book","add",book))
def removeBook(self, Book):
r"""
This removes a book object from the self.books list. Then it appends the action to the self.history list
"""
self.books.remove(Book)
self.history.append(("book","remove",Book))
def editBook(self, old_book, new_book):
original_id = old_book["book_id"]
r"""
This edits a book object in the self.books list. Then it appends the action to the self.history list.
"""
old_book["title"] = new_book["title"]
old_book["author"] = new_book["author"]
old_book["book_id"] = new_book["book_id"]
old_book["location"] = new_book["location"]
old_book["publisher"] = new_book["publisher"]
old_book["genre"] = new_book["genre"]
old_book["copy_right"] = new_book["copy_right"]
old_book["subject"] = new_book["subject"]
old_book["is_checked_out"] = new_book["is_checked_out"]
old_book["price"] = new_book["price"]
old_book["fee_increment"] = new_book["fee_increment"]
self.history.append(("book","edit",old_book,original_id))
def addPatron(self, patron):
r"""
This appends a new Patron object to the self.patrons list. It then appends the action to the self.history list.
"""
self.patrons.append(patron)
self.history.append(("patron","add",patron))
print "bonjour"
print self.patrons[0]
def removePatron(self,patron):
r"""
This Removes a patron object from the self.patrons list. It then appends the actino to the self.history list.
"""
self.patrons.remove(patron)
self.history.append(("patron","remove",patron))
def editPatron(self,patron,attribute,value):
r"""
This edits a patron object in the self.patrons list. It then appends the action to the self.history list.
"""
patron[attribute] = value
self.history.append(("patron","edit",patron))
def resetHistory(self):
r"""
This resets the self.history list
"""
self.history=[]
def getBook(self,book_id):
r"""
This finds a book based on its id
"""
for i in self.books:
if i["book_id"] == str(book_id):
return i
def __str__(self):
for i in self.books:
print i
for i in self.patrons:
print i
return "done"
class Database():
r"""
This is the Database Class. It modifies the library.db file.
"""
def __init__ (self):
r"""
This presets the self.database variable and the self.cursor variable
"""
self.database = sq.connect('library.db')
self.cursor = self.database.cursor()
def mergeWithLibrary(self,library):
r"""
This is my personal favorite. It updates the library.db file based off of the changes the Library object.
"""
for i in library.history:
print i
if i[0] == "book":
if i[1] == "add":
self.addBook(i[2])
elif i[1] == "remove":
self.deleteBook(i[2])
elif i[1] == "edit":
self.editBook(i[2],i[3])
elif i[0] == "patron":
if i[1] == "add":
self.addPatron(i[2])
elif i[1] == "remove":
self.deletePatron(i[2])
elif i[2] == "edit":
self.editPatron(i[2])
library.resetHistory()
def getBooks(self):
r"""
This returns all of the books in the library.db database
"""
self.cursor.execute("SELECT * FROM books")
return self.cursor.fetchall()
def getPatrons(self):
r"""
This returns all of the Patrons in the library.db database
"""
self.cursor.execute("SELECT * FROM patrons")
print self.cursor.fetchall()
print "called"
return [(0,0,0,0,0,0,0,0,0,0,0,0)]
def editBook(self,Book,id):
r"""
This updates a Book
"""
self.cursor.execute("UPDATE books SET title = '"+Book["title"]+"', author = '"+Book["author"]+"', book_id= '"+Book["book_id"]+"', location = '"+Book["location"]+"', publisher = '"+Book["publisher"]+"', genre = '"+Book["genre"]+"', copy_right = '"+Book["copy_right"]+"', subject = '"+Book["subject"]+"', is_checked_out = '"+Book["is_checked_out"]+"', price = '"+Book["price"]+"', fee_increment = '"+Book["fee_increment"]+"' WHERE book_id = '"+id+"'")
self.database.commit()
print Book["fee_increment"]
def editPatron(self,Patron):
r"""
This edits a Patron
"""
self.cursor.execute("UPDATE patrons SET name= '"+Patron["name"]+"', address = '"+Patron["address"]+"', phone = '"+str(Patron["phone"])+"', email = '"+Patron["email"]+"', fee_balance = '"+str(Patron["fee_balance"])+"', fees_per_day = '"+str(Patron["fees_per_day"])+"', books_checked_out = '"+Patron["books_checked_out"]+"', books_overdue = '" +Patron["books_overdue"]+"' WHERE id ="+str(Patron["patron_id"]))
self.database.commit()
def deleteBook(self,Book):
r"""
This deletes a book
"""
self.cursor.execute("DELETE FROM books WHERE book_id = "+"'"+Book["book_id"]+"'")
self.database.commit()
def deletePatron(self,Patron):
r"""
"""
self.cursor.execute("DELETE FROM patrons WHERE id = "+"'"+str(Patron["patron_id"])+"'")
self.database.commit()
def addBook(self,Book):
r"""
This adds a book
"""
data = [
Book["title"],
Book["author"],
Book["book_id"],
Book["location"],
Book["publisher"],
Book["genre"],
Book["copy_right"],
Book["subject"],
Book["is_checked_out"],
Book["price"],
Book["fee_increment"]
]
self.cursor.execute("INSERT INTO books VALUES(?,?,?,?,?,?,?,?,?,?,?)", data)
self.database.commit()
def addPatron(self,Patron):
r"""
This adds a Patron
"""
data = [Patron["patron_id"],
Patron["name"],
Patron["address"],
Patron["phone"],
Patron["email"],
Patron["fee_balance"],
Patron["fees_per_day"],
Patron["books_checked_out"],
Patron["books_overdue"]]
self.cursor.execute("INSERT INTO patrons VALUES(?,?,?,?,?,?,?,?,?)",data)
self.database.commit()
Every time I run my code, all my little print statement flags pop up in the console, but the window doesn't open and then python crashes. Commenting out the for statement where I import my database into the table lets the program run, but any calls to self.lib.addPatron will crash the program again. I see no problems with that function, am I just stupid?
It would have helped if you'd given us the traceback you were getting. When I run this code, I get the following:
sqlite3.OperationalError: no such table: patrons
File "c:\Users\mdriscoll\Desktop\brk.py", line 191, in <module>
frame = MyFrame1(None)
File "c:\Users\mdriscoll\Desktop\brk.py", line 115, in __init__
for i in self.db.getPatrons():
File "c:\Users\mdriscoll\Desktop\Books.py", line 184, in getPatrons
self.cursor.execute("SELECT * FROM patrons")
Since you state that you too are getting an issue on "self.lib.addPatron", then that means that this is likely the same issue. As the traceback indicates, the table does not exist. You need to create the table (even if they're empty) BEFORE you can add anything to do them. This article might help you get started: http://www.blog.pythonlibrary.org/2012/07/18/python-a-simple-step-by-step-sqlite-tutorial/
When debugging WXpython programs that crash before giving you any useful output, you can save the errors to a logfile by specifying a redirect when you start the app. So changing the line that starts the app to:
app = wx.pySimpleApp(redirect=True, filename="logfile.txt")
will save your errors to a logfile in the same folder as your script, and then you will have a better idea of what's going on.
Ok, let's get straight to the point. I got 2 questions
1. Why self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet) not working?
2. Why the custom lexer is not applied?
This is the source code
import wx, re, keyword
from wx import stc
class BaseLexer(object):
"""Defines simple interface for custom lexer objects"""
def __init__(self):
super(BaseLexer, self).__init__()
def StyleText(self, event):
raise NotImplementedError
class STC_LEX_VOTONUSA(BaseLexer):
# Define some style IDs
STC_VOTONUSA_DEFAULT = wx.NewId()
STC_VOTONUSA_VALUE = wx.NewId()
STC_VOTONUSA_OBJECT = wx.NewId()
STC_VOTONUSA_TYPE = wx.NewId()
STC_VOTONUSA_OPERATION = wx.NewId()
STC_VOTONUSA_COMMENT =wx.NewId()
STC_VOTONUSA_QUALIFIER = wx.NewId()
def __init__(self):
super(STC_LEX_VOTONUSA, self).__init__()
# Attributes
self.Comment = re.compile("""
//
""", re.X)
self.Types = re.compile("""
\\b
(void|integer|shortint|(short)?word|float|boolean|char|record|program|module)
\\b
""", re.I|re.X)
self.Operation = re.compile("""
(read|write)(line)?|if|main|case|while|use|return|exit|in_case|repeat_until
(\+|-|\*|/|%|\*\*|:|!|<|>)?=?|
(\{|\}|\(|\)|\[|\])
""", re.I|re.X)
self.Value = re.compile("""
[(`\d*)+\'\w*\']*|\'.*\'|[\+-]*\d*\.?\d*
""", re.I|re.X)
self.Qualifier = re.compile("""
interface|implementation
""", re.I|re.X)
self.Object = re.compile("""
s
""", re.I|re.X)
def GetLastWord(self, Line, CaretPos):
"""
Get the last word from a line
"""
LastWord = re.search(
"""
\s*
(
".*"| # String data type
\w*| # Any letter/number
(\+|-|\*|/|%|\*\*|:|!|<|>)?=?| # Assignment, Comparison & Mathematical
(\{|\}|\(|\)|\[|\]) # Brackets
)
\s*\Z
""",
Line[:CaretPos], re.VERBOSE)
return LastWord
def StyleText(self, event):
"""Handle the EVT_STC_STYLENEEDED event"""
Obj = event.GetEventObject()
# Get Last Correctly Styled
LastStyledPos = Obj.GetEndStyled()
# Get Styling Range
Line = Obj.LineFromPosition(LastStyledPos)
StartPos = Obj.PositionFromLine(Line)
EndPos = event.GetPosition()
# Walk the Range and Style Them
while StartPos < EndPos:
Obj.StartStyling(StartPos, 0x1f)
LastWord = self.GetLastWord(Line, CaretPos)
if self.Comment.search(LastWord):
# Set Comment Keyword style
Style = self.STC_VOTONUSA_COMMENT
elif self.Type.search(LastWord):
# Set Type Keyword style
Style = self.STC_VOTONUSA_TYPE
elif self.Operation.search(LastWord):
# Set Operation Keyword style
Style = self.STC_VOTONUSA_OPERATION
elif self.Value.search(LastWord):
# Set Value Keyword style
Style = self.STC_VOTONUSA_VALUE
elif self.Qualifier.search(LastWord):
# Set Qualifier Keyqord style
Style = self.STC_VOTONUSA_QUALIFIER
elif self.Object.search(LastWord):
# Set Object Keyword style
Style = self.STC_VOTONUSA_OBJECT
# Set the styling byte information for length of LastWord from
# current styling position (StartPos) with the given style.
Obj.SetStyling(len(LastWord), Style)
StartPos += len(LastWord)
class CustomSTC(stc.StyledTextCtrl):
def __init__(self, parent):
super(CustomSTC, self).__init__(parent)
# Attributes
self.custlex = None
Font = wx.Font(12, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
Face = Font.GetFaceName()
Size = Font.GetPointSize()
# Setup
kwlist = u" ".join(keyword.kwlist)
self.SetKeyWords(0, kwlist)
self.StyleClearAll()
self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
self.EnableLineNumbers()
FontSet = "face:%s, size:%d" % (Face, Size)
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, FontSet)
# Set Default to Black
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet)
# Set Comment to Pink
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
# Set Value to Green
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_VALUE, "fore:#00ff00" + FontSet)
# Set Object to Brown
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OBJECT, "fore:#a52a2a" + FontSet)
# Set Type to Red
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_TYPE, "fore:#ff0000" + FontSet)
# Set Operation to Blue
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OPERATION, "fore:#0000ff" + FontSet)
# Set Qualifier to Orange
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_QUALIFIER, "fore:#cc3232" + FontSet)
# Event Handlers
self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
def EnableLineNumbers(self, enable=True):
"""Enable/Disable line number margin"""
if enable:
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginMask(1, 0)
self.SetMarginWidth(1, 25)
else:
self.SetMarginWidth(1, 0)
def OnStyle(self, event):
# Delegate to custom lexer object if one exists
if self.custlex:
self.custlex.StyleText(event)
else:
event.Skip()
def SetLexer(self, lexerid, lexer=None):
"""
Overrides StyledTextCtrl.SetLexer
Adds optional param to pass in custom container
lexer object.
"""
self.custlex = lexer
super(CustomSTC, self).SetLexer(lexerid)
class NUSAIPT(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, None, wx.ID_ANY, 'VOTO NUSA IPT')
self.TextArea = CustomSTC(self)
self.Show()
app = wx.App()
frame = NUSAIPT()
app.MainLoop()
If I change
self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
into
self.SetLexer(stc.STC_LEX_CPP)
and
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
into
self.StyleSetSpec(stc.STC_C_COMMENT, "fore:#ff007f" + FontSet)
the comment highlighting worked. so the mistake should be in self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA) or STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT
Thanks in advance. Hope to see some answer soon.
Just stuck with same problem yesterday, here are my results. Hope it's not too late :)
First, i set lexer to this one, and internal lexer as you do:
self.SetLexer(stc.STC_LEX_CONTAINER)
self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
Bind to event and OnStyle method are same.
And also i changed id of styles from wx.newId() to just numbers starting from 1.
Without it i didn't see any styling at all. Also stc.STC_STYLE_DEFAULT started to work too.
Full listing:
class SCT_LEX_ERLANG_IDNOISE(BaseLexer):
STC_ERLANG_IDNOISE_DEFAULT = 1
STC_ERLANG_IDNOISE_VARIABLE = 2
STC_ERLANG_IDNOISE_ATOM = 3
STC_ERLANG_IDNOISE_MODULE = 4
STC_ERLANG_IDNOISE_KEYWORD = 5
STC_ERLANG_IDNOISE_COMMENT = 6
STC_ERLANG_IDNOISE_MACROS = 7
STC_ERLANG_IDNOISE_NUMBER = 8
def __init__(self, control):
super(SCT_LEX_ERLANG_IDNOISE, self).__init__(control)
self.typeFormatDict = {}
self.typeFormatDict["other"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
self.typeFormatDict["variable"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE
self.typeFormatDict["atom"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM
self.typeFormatDict["module"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE
self.typeFormatDict["keyword"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD
self.typeFormatDict["comment"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT
self.typeFormatDict["macros"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS
self.typeFormatDict["number"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER
def StyleText(self, event):
start = self.control.GetEndStyled()
end = event.GetPosition()
line = self.control.LineFromPosition(start)
start = self.control.PositionFromLine(line)
text = self.control.GetTextRange(start, end)
self.control.StartStyling(start, 0x1f)
lastEnd = 0
for type, start, end, value in getHighlightRules(text):
style = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
if start > lastEnd:
self.control.SetStyling(start - lastEnd, style)
if type in self.typeFormatDict:
style = self.typeFormatDict[type]
self.control.SetStyling(len(value), style)
lastEnd = end
class CustomSTC(stc.StyledTextCtrl):
def __init__(self, parent):
super(CustomSTC, self).__init__(parent)
self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
#self.SetKeyWords(0, kwlist)
self.SetLexer(stc.STC_LEX_CONTAINER)
self.EnableLineNumbers()
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, ColorSchema.formats["other"])
self.StyleClearAll()
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, ColorSchema.lineFont)
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT, ColorSchema.formats["other"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE, ColorSchema.formats["variable"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM, ColorSchema.formats["atom"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE, ColorSchema.formats["module"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD, ColorSchema.formats["keyword"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT, ColorSchema.formats["comment"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS, ColorSchema.formats["macros"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER, ColorSchema.formats["number"])
# Event Handlers
self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
def EnableLineNumbers(self, enable=True):
"""Enable/Disable line number margin"""
if enable:
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginMask(1, 0)
self.SetMarginWidth(1, 35)
else:
self.SetMarginWidth(1, 0)
def OnStyle(self, event):
if self.custlex:
self.custlex.StyleText(event)
else:
event.Skip()
I'd actually like to start by admitting that I'm terrified to ask this question. That said, I have the following combination of classes:
A Dialog Class:
class formDialog(wx.Dialog):
def __init__(self, parent, id = -1, panel = None, title = _("Unnamed Dialog"),
modal = False, sizes = (400, -1)):
wx.Dialog.__init__(self, parent, id, _(title),
style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
if panel is not None:
self._panel = panel(self)
self._panel.SetSizeHints(*sizes)
ds = wx.GridBagSizer(self._panel._gap, self._panel._gap)
ds.Add(self._panel, (0, 0), (1, 1), wx.EXPAND | wx.ALL, self._panel._gap)
ds.Add(wx.StaticLine(self), (1, 0), (1, 1), wx.EXPAND | wx.RIGHT | wx.LEFT, self._panel._gap)
self.bs = self.CreateButtonSizer(self._panel._form['Buttons'])
ds.Add(self.bs, (2, 0), (1, 1), wx.ALIGN_RIGHT | wx.ALL, self._panel._gap)
ds.AddGrowableCol(0)
ds.AddGrowableRow(0)
self.SetSizerAndFit(ds)
self.Center()
self.Bind(wx.EVT_BUTTON, self._panel.onOk, id = wx.ID_OK)
self.Bind(wx.EVT_BUTTON, self._panel.onClose, id = wx.ID_CANCEL)
self.Bind(wx.EVT_CLOSE, self._panel.onClose)
if modal:
self.ShowModal()
else:
self.Show()
A Form Class:
class Form(wx.Panel):
reqFields = [
('Defaults', {}),
('Disabled', [])
]
def __init__(self, parent = None, id = -1, gap = 2, sizes = (-1, -1)):
wx.Panel.__init__(self, parent, id)
self.SetSizeHints(*sizes)
self._gap = gap
self.itemMap = {}
if hasattr(self, '_form'):
# There are a number of fields which need to exist in the form
# dictionary. Set them to defaults if they don't exist already.
for k, d in self.reqFields:
if not self._form.has_key(k):
self._form[k] = d
self._build()
def _build(self):
"""
The Build Method automates sizer creation and element placement by parsing
a properly constructed object.
"""
# The Main Sizer for the Panel.
panelSizer = wx.GridBagSizer(self._gap, self._gap)
# Parts is an Ordered Dictionary of regions for the form.
for group, (key, data) in enumerate(self._form['Parts'].iteritems()):
flags, sep, display = key.rpartition('-') ##UnusedVariable
# HR signifies a Horizontal Rule for spacing / layout. No Data Field.
if display == 'HR':
element = wx.StaticLine(self)
style = wx.EXPAND
# Any other value contains elements that need to be placed.
else:
element = wx.Panel(self, -1)
# The Row Sizer
rowSizer = wx.GridBagSizer(self._gap, self._gap)
for row, field in enumerate(data):
for col, item in enumerate(field):
style = wx.EXPAND | wx.ALL
pieces = item.split('-')
# b for Buttons
if pieces[0] == 'b':
control = wx._controls.Button(element, -1, pieces[1])
# custom items - Retrieve from the _form object
if pieces[0] == 'custom':
control = self._form[pieces[1]](element)
# The row in the Grid needs to resize for Lists.
panelSizer.AddGrowableRow(group)
# Now the Row has to grow with the List as well.
rowSizer.AddGrowableRow(row)
# custom2 - Same as custom, but does not expand
if pieces[0] == 'custom2':
control = self._form[pieces[1]](element)
style = wx.ALL
# c for CheckBox
if pieces[0] == 'c':
control = wx.CheckBox(element, label = _(pieces[2]), name = pieces[1])
control.SetValue(int(self._form['Defaults'].get(pieces[1], 0)))
# d for Directory Picker
if pieces[0] == 'd':
control = wx.DirPickerCtrl(element, name = pieces[1])
control.GetTextCtrl().SetEditable(False)
control.GetTextCtrl().SetName(pieces[1])
control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))
# f for File Browser
if pieces[0] == 'f':
control = wx.FilePickerCtrl(element, name = pieces[1], wildcard = pieces[2])
control.GetTextCtrl().SetEditable(False)
control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))
# f2 for Save File
if pieces[0] == 'f2':
control = wx.FilePickerCtrl(element, name = pieces[1],
style = wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT | wx.FLP_USE_TEXTCTRL,
wildcard = pieces[2])
control.GetTextCtrl().SetEditable(False)
# h for Horizontal Rule - layout helper.
if pieces[0] == 'h':
control = wx.StaticLine(element)
style = wx.EXPAND
# l for Label (StaticText)
if pieces[0] == 'l':
control = wx.StaticText(element, label = _(pieces[1]))
# Labels do not expand - override default style.
style = wx.ALL | wx.ALIGN_CENTER_VERTICAL
# p for Password (TextCtrl with Style)
if pieces[0] == 'p':
control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_PASSWORD)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# s for ComboBox (Select)
if pieces[0] == 's':
control = wx.ComboBox(element, name = pieces[1],
choices = self._form['Options'].get(pieces[1], []),
style = wx.CB_READONLY)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# s2 for Spin Control
if pieces[0] == 's2':
control = wx.SpinCtrl(element, name = pieces[1], size = (55, -1),
min = int(pieces[2]), max = int(pieces[3]))
control.SetValue(int(self._form['Defaults'].get(pieces[1], 1)))
# Spin Ctrl's do not expand.
style = wx.ALL
# t for TextCtrl
if pieces[0] == 't':
control = wx.TextCtrl(element, name = pieces[1])
try:
control.SetValidator(self._form['Validators'][pieces[1]])
except KeyError: pass # No Validator Specified.
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# tr for Readonly TextCtrl
if pieces[0] == 'tr':
control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_READONLY)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# Check for elements disabled by default. Store reference to
# Element in itemMap for reference by other objects later.
if len(pieces) > 1:
if pieces[1] in self._form['Disabled']:
control.Enable(False)
self.itemMap[pieces[1]] = control
# Place the control in the row.
rowSizer.Add(control, (row, col), (1, 1), style, self._gap)
if style == wx.EXPAND | wx.ALL:
rowSizer.AddGrowableCol(col)
if 'NC' not in flags:
sb = wx.StaticBox(element, -1, _(display))
sz = wx.StaticBoxSizer(sb, wx.VERTICAL)
sz.Add(rowSizer, 1, flag = wx.EXPAND)
element.SetSizerAndFit(sz)
else:
element.SetSizerAndFit(rowSizer)
panelSizer.Add(element, (group, 0), (1, 1), wx.EXPAND | wx.ALL, self._gap)
panelSizer.AddGrowableCol(0)
self.SetSizerAndFit(panelSizer)
def getDescendants(self, elem, list):
children = elem.GetChildren()
list.extend(children)
for child in children:
self.getDescendants(child, list)
def getFields(self):
fields = []
self.getDescendants(self, fields)
# This removes children we can't retrieve values from. This should result
# in a list that only contains form fields, removing all container elements.
fields = filter(lambda x: hasattr(x, 'GetValue'), fields)
return fields
def onOk(self, evt):
self.onClose(evt)
def onClose(self, evt):
self.GetParent().Destroy()
The Form is meant to be used by subclassing like so:
class createQueue(Form):
def __init__(self, parent):
self._form = {
'Parts' : OrderedDict([
('Queue Name', [
('t-Queue Name',)
])
]),
'Buttons' : wx.OK | wx.CANCEL
}
Form.__init__(self, parent)
class generalSettings(Form):
def __init__(self, parent):
self._form = {
'Parts': OrderedDict([
('Log Settings', [
('l-Remove log messages older than: ', 's2-interval-1-10', 's-unit')
]),
('Folder Settings', [
('l-Spool Folder Location:', 'd-dir'),
('l-Temp Folder Location:', 'd-temp')
]),
('Email Notifications', [
('l-Alert Email To:', 't-alert_to'),
('l-Alert Email From:', 't-alert_from'),
('l-Status Email From:', 't-status_from'),
('l-Alert Email Server:', 't-alert_host'),
('l-Login:', 't-alert_login'),
('l-Password:', 'p-alert_password')
]),
('Admin User', [
('c-req_admin-Require Admin Rights to make changes.',)
]),
('Miscellaneous', [
('l-Print Worker Tasks:', 's2-printtasks-1-256', 'l-Job Drag Options:', 's-jobdrop')
])
]),
'Options': {
'unit': ['Hours', 'Days', 'Months'],
'jobdrop': ['Move Job to Queue', 'Copy Job to Queue']
},
'Buttons': wx.OK | wx.CANCEL
}
Form.__init__(self, parent)
These might be used like so:
formDialog(parent, panel = createQueue, title = 'Create a Queue', sizes = (200, -1))
formDialog(parent, panel = generalSettings, title = "General Settings")
Whew, that's a ton, and thanks to anyone who makes it down this far. The idea is that I want something that takes care of the monotonous parts of layout in wxPython. I am designing a user interface that will need to create 100's of different Dialogs and Forms. I wanted something that would allow me to generate the forms dynamically from a structured object.
I'd like to hear other developers' thoughts on this kind of approach. The closest I've seen to something similar is Drupal's Form API. I feel like it is viable for these reasons:
Easily rearrange fields.
No need to create / manage Sizers manually.
Compound / complex forms can be created easily.
Display helper elements (StaticBoxSizers, Static Lines) are easily added.
I am concerned that it is an undesirable approach for these reasons:
Long _build() function body in the Form Class.
May not be clear to other developers at first glance.
Uses Structured Strings to define fields.
There may be a better way.
Any thoughts, constructive, destructive, or otherwise will all be appreciated. Thanks!
You should also try wxFormDesigner or XRCed.
Since you're using wx, you should study wxglade. It's a graphical GUI builder which you use to build your GUI and it generates a .wxg file with the layout, and you can load that into your script.
The file is actually just xml, so you can programatically generate it and dynamically load different GUIs from it. Maybe that helps.