wxpython - Creating Unique GUI Elements With a Loop - python

I am attempting to create a GUI with a large amount of items where there are several sets of the same thing (six labels and six radioboxes).
What I want to do (to save space and for a learning experience) is to create some sort of loop to place these elements on the panel I am using.
Actually placing these should be easy, but the kicker is, I need them to all be unique in some way so I can individually change each label or get each value of each radiobox individually.
Below is the code I have right now, where all the elements are individually created and placed.
sizerMain = wx.BoxSizer()
## For the main control area
panelControl = wx.Panel(self,1,style = wx.MAXIMIZE)
sizerControl = wx.GridBagSizer(hgap = 4,vgap = 4)
# Add widgets
## Main content area
lblTitle = wx.StaticText(panelControl,label = "Pick Scores")
sizerControl.Add(lblTitle,pos = (0,0),
flag = wx.ALIGN_CENTER|wx.TOP|wx.LEFT|wx.BOTTOM,
border = 5)
self.btnRoll = wx.Button(panelControl,label = "Roll!")
sizerControl.Add(self.btnRoll,pos = (0,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 5)
### Radio boxes
#### Radio button tuple
rboxPick = ["Default","Strength","Dexterity","Constitution",
"Intelligence","Wisdom","Charisma"]
self.lblRoll1 = wx.StaticText(panelControl)
sizerControl.Add(self.lblRoll1,pos = (1,0),flag = wx.ALIGN_CENTER)
self.rboxRoll1 = wx.RadioBox(panelControl,label = "Roll One",choices = rboxPick)
sizerControl.Add(self.rboxRoll1,pos = (1,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll2 = wx.StaticText(panelControl)
sizerControl.Add(self.lblRoll2,pos = (2,0),flag = wx.ALIGN_CENTER)
self.rboxRoll2 = wx.RadioBox(panelControl,label = "Roll Two",choices = rboxPick)
sizerControl.Add(self.rboxRoll2,pos = (2,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll3 = wx.StaticText(panelControl)
sizerControl.Add(self.lblRoll3,pos = (3,0),flag = wx.ALIGN_CENTER)
self.rboxRoll3 = wx.RadioBox(panelControl,label = "Roll Three",choices = rboxPick)
sizerControl.Add(self.rboxRoll3,pos = (3,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll4 = wx.StaticText(panelControl)
sizerControl.Add(self.lblRoll4,pos = (4,0),flag = wx.ALIGN_CENTER)
self.rboxRoll4 = wx.RadioBox(panelControl,label = "Roll Four",choices = rboxPick)
sizerControl.Add(self.rboxRoll4,pos = (4,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll5 = wx.StaticText(panelControl)
sizerControl.Add(self.lblRoll5,pos = (5,0),flag = wx.ALIGN_CENTER)
self.rboxRoll5 = wx.RadioBox(panelControl,label = "Roll Five",choices = rboxPick)
sizerControl.Add(self.rboxRoll5,pos = (5,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll6 = wx.StaticText(panelControl)
sizerControl.Add(self.lblRoll6,pos = (6,0),flag = wx.ALIGN_CENTER)
self.rboxRoll6 = wx.RadioBox(panelControl,label = "Roll Six",choices = rboxPick)
sizerControl.Add(self.rboxRoll6,pos = (6,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
Also, it is late.. So if I am not making sense please let me know and I will be happy to re-explain..

Its been a while since I've done any wxPython coding so I'm a little rusty, but off the top of my head I can think of two ways of doing this other than neurino's solution, although there are other variations.
Method 1
Keep references to each widget in a dictionary, using the widgets label as the key, eg
rboxPick = ["Default", "Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"]
labels = ["One", "Two", "Three", "Four"]
self.rollRbs = dict()
#create the radioBoxes..
for row, label in enumerate(labels):
lbl = wx.StaticText(panelControl)
rbox = wx.RadioBox(panelControl, label="Roll %s"%(label),
choices=rboxPick)
sizerControl.Add(rbox ,pos = (row, 1),span=(1,5),
flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.rollRbs[rbox.GetLabel()] = rbox
#changing the label...
self.rollRbs["Roll One"].SetLabel("blah")
Method 2
Personally I prefer a more event driven approach. Simply bind each RadioBoxes event to the same handler. Then in the handler you can differentiate between the RadioBoxes using their label attributes.
Working example:
import wx
class GUI(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(700, 400))
panelControl = wx.Panel(self, 1, style=wx.MAXIMIZE)
sizerControl = wx.GridBagSizer(hgap=4,vgap = 4)
lblTitle = wx.StaticText(panelControl, label="Pick Scores")
self.btnRoll = wx.Button(panelControl, label="Roll!")
sizerControl.Add(lblTitle, pos=(0,0),
flag=wx.ALIGN_CENTER|wx.TOP|wx.LEFT|wx.BOTTOM, border=5)
sizerControl.Add(self.btnRoll, pos=(0,1),
span=(1,5), flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=5)
rboxPick = ["Default", "Strength", "Dexterity", "Constitution",
"Intelligence", "Wisdom", "Charisma"
]
labels = ["One", "Two", "Three", "Four"]
#Create, layout and bind the RadioBoxes
for row, label in enumerate(labels):
lbl = wx.StaticText(panelControl)
rbox = wx.RadioBox(panelControl, label="Roll %s"%(label), choices=rboxPick)
self.Bind(wx.EVT_RADIOBOX, self.onRadioBox, rbox)
sizerControl.Add(rbox, pos=(row+1, 1), span=(1,5),
flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border=2)
sizerMain = wx.BoxSizer()
sizerMain.Add(sizerControl)
panelControl.SetSizerAndFit(sizerMain)
def onRadioBox(self, evt):
"""Event handler for RadioBox.."""
rbox = evt.GetEventObject()#Get a reference to the RadioBox
rboxLbl = rbox.GetLabel() #We can identify the RadioBox with its label
selection = rbox.GetSelection()
print rboxLbl
print selection
if rboxLbl == "Roll One":
#do something
pass
elif rboxLbl == "Roll Two":
#do something else
pass
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = GUI(None, -1, "")
frame.Show(1)
app.MainLoop()
If for some reason you need that empty StaticText that you have paired with each RadioBox, then I would probably just make that pair a rich compostite widget, with some methods for changing the label etc. Then use method 2 to create and update them. If you need to modify these widgets attributes outside of the event handler after they are created then I think your going need to keep references to them in some shape or form e.g method 1.
Here is a working example
import wx
import wx.lib.newevent
class LblRadBox(wx.Panel):
"""
Simple example of a composite widget
Add methods as required to improve functionality...
"""
def __init__(self, parent, stLbl="", rbLbl="", choices=[]):
wx.Panel.__init__(self, parent)
self.stLbl = wx.StaticText(self, label=stLbl)
self.rbox = wx.RadioBox(self, label=rbLbl, choices=choices)
sizer = wx.BoxSizer()
sizer.Add(self.stLbl)
sizer.Add(self.rbox)
self.SetSizerAndFit(sizer)
def SetSTLabel(self, lbl):
self.stLbl.SetLabel(lbl)
def GetLabel(self):
return self.rbox.GetLabel()
def GetSelection(self, lbl):
return self.rbox.GetSelection()
class GUI(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(700, 400))
panelControl = wx.Panel(self, 1, style=wx.MAXIMIZE)
sizerControl = wx.GridBagSizer(hgap=4,vgap = 4)
lblTitle = wx.StaticText(panelControl, label="Pick Scores")
self.btnRoll = wx.Button(panelControl, label="Roll!")
sizerControl.Add(lblTitle, pos=(0,0),
flag=wx.ALIGN_CENTER|wx.TOP|wx.LEFT|wx.BOTTOM, border=5)
sizerControl.Add(self.btnRoll, pos=(0,1),
span=(1,5), flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=5)
rboxPick = ["Default", "Strength", "Dexterity", "Constitution",
"Intelligence", "Wisdom", "Charisma"
]
labels = ["One", "Two", "Three", "Four"]
#Create, layout and bind the RadioBoxes
for row, label in enumerate(labels):
rbox = LblRadBox(panelControl, rbLbl="Roll %s"%(label), choices=rboxPick)
#if u want to be able to access the rboxes outside of onRadioBox()
#then add references of them to a dictionary like in method 1..
sizerControl.Add(rbox, pos=(row+1, 1), span=(1,5),
flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border=2)
panelControl.Bind(wx.EVT_RADIOBOX, self.onRadioBox)
sizerMain = wx.BoxSizer()
sizerMain.Add(sizerControl)
panelControl.SetSizerAndFit(sizerMain)
def onRadioBox(self, evt):
"""Event handler for RadioBox.."""
rbox = evt.GetEventObject()#Get a reference to the RadioBox
rboxLbl = rbox.GetLabel() #We can identify the RadioBox with its label
selection = rbox.GetSelection()
print rboxLbl
print selection
if rboxLbl == "Roll One":
#do something
pass
elif rboxLbl == "Roll Two":
#do something else
pass
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = GUI(None, -1, "")
frame.Show(1)
app.MainLoop()

They will be unique anyway, if you do not provide an id you can create with wx.NewId() wx will create one for you.
If you take care of creating (or retrieving) ids, and store them (in a list, in a dict, you choose), then you'll be able to get back to all single element to edit it.
ids = []
for lbl in ('Name', 'Surname', 'Address'):
st = wx.StaticText(panel)
tc = wx.TextControl(panel, label=lbl)
ids.append(tc.GetId())
Anyway you should be able to make your edits, that usually respond to user actions, only using events data without the need of storing any id.

Related

making textinput widget on the basis of items in list in a popup and reading the text in in it using kivy framework-

def get_credential_fields_for_popup_success(self, urlrequest, data):
# print(data)
# print(data['fields'])
s = list(data['fields'].values())
# print(s)
r = list(s[0].values())
# print(r)
c = list(r[0].values())
# print(c)
list_of_widget = []
for i in c[0]:
s = list(i.values())
list_of_widget.append(s)
real_list = []
for i in list_of_widget:
# print(i[0])
real_list.append(i[0])
self.widgets = real_list
# print(real_list)
def OpenPopupWindow(self):
self.layout = GridLayout(rows=len(self.widgets) + 1)
#count = 0
for i in range(0, len(self.widgets)):
self.arr = ['zero', 'one', 'two', 'three', 'four']
name = self.arr[i]
name= ObjectProperty(None)
print(name)
if i == 0 :
self.label = MDLabel(text = self.widgets[i])
self.name = TextInput(text="", multiline=False, hint_text=self.widgets[i])
#print(self.widget_name.text)
else :
self.label = MDLabel(text= self.widgets[i])
self.name = TextInput(text="", multiline=False, hint_text=self.widgets[i],password= True)
#print(self.widget_name.text)
self.layout.add_widget(self.label)
self.layout.add_widget(self.name)
#print(self.ids.layout)
#for i in range(0, len(self.widgets)):
# layout.add_widget()
self.button = Button(text="Close", size_hint=(0.2, 0.2), pos_hint={'center_x': 0.2, 'center_y': 0.2})
self.button.bind(on_press= self.cred_save)
self.layout.add_widget(self.button)
self.popup = Popup(title="test Popup", content=self.layout, size_hint=(None, None), size=(300, 400),
background="atlas://images/my_popup/white (2) (1)",
auto_dismiss=True)
self.popup.open()
#print(self.ids.1.text)
#print(self.ids.2.text)
def cred_save(self,obj):
#print('name'+self.ids.layout.ids.zero.text)
#print('name'+self.layout.one.text)
#print('name'+se lf.layout.two.text)
user_id = self.zero.text
password = self.one.text
pin = self.two.text
print(user_id)
print(password)
#self.popup.dismiss()
#print(self.ids.)
#self.popup.dismiss()
client_id = self.zero.text
Here , I am getting a list whose length will determine the textinput field in the popup on trigerring the OpenPopupWindow, in this function i made a popup. Inside it , made gridlayout nd binded input fields , and i have tried everything to get the text written in input fileds after pressing the button but i am not able to get the text written in other function which i have called on_press the button widget in popup.

wxpython: How can I fill a Notebook tab with a grid created elsewhere?

Right now I have a GUI built from wxpython that successfully pulls data from a .csv, populates a wx grid object, and then displays it in a new frame. I have also successfully got my main window to display some information in the notebook style. My goal is to make it so that when I run my program, one of the main page tabs contains the same populated grid as the window I previously made. The issue that keeps stumping me is that the grid creation and grid population (two separate things) are done in different classes in a different (but imported) local file. Additionally, the below code in my program context gives the AttributeError: 'TabPanel' object has no attribute 'con', which makes sense, but I don't
Is this not possible or is there something I am missing (and am I even being clear?)? Below is what I would guess would be relevant code. (Incorrect spacing for class and constructor lines just for convenience here.) Thank you very much!
Tab/Notebook:
class TabPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
self.sizer = wx.BoxSizer(wx.VERTICAL)
txtOne = wx.Panel(Employee.EmployeeViewAllFrame(self).show())
self.sizer.Add(txtOne, 0, wx.ALL , 50)
self.SetSizer(self.sizer)
class NotebookDemo(wx.Notebook):
def __init__(self, parent):
wx.Notebook.__init__(self, parent, id=wx.ID_ANY, style=
wx.BK_DEFAULT
#wx.BK_TOP
#wx.BK_BOTTOM
#wx.BK_LEFT
#wx.BK_RIGHT
)
# Create the first tab and add it to the notebook
tabOne = TabPanel(self)
tabOne.SetBackgroundColour("BLUE")
self.AddPage(tabOne, "Main")
# Create and add the second tab
tabTwo = TabPanel(self)
self.AddPage(tabTwo, "Employees")
# Create and add the third tab
self.AddPage(TabPanel(self), "Tasks")
Grid/Frame:
class empGrid(wx.grid.Grid):
def __init__(self, parent):
wx.grid.Grid.__init__(self,parent,size = (1500,1000))
self.SetDefaultCellOverflow(False)
self.EnableEditing(False)
self.EnableDragGridSize(False)
self.EnableDragRowSize(False)
self.EnableDragColSize(False)
self.grid = gridlib.Grid(panel2)
self.CreateGrid(TOTALEMPLOYEES, 12)
self.SetColLabelValue(0, "Name")
self.SetColSize(0, 200)
self.SetColLabelValue(1, "Grade")
self.SetColLabelValue(2, "NGID")
self.SetColLabelValue(3, "MyID")
self.SetColLabelValue(4, "Skillset1")
self.SetColSize(4, 110)
self.SetColLabelValue(5, "Skillset2")
self.SetColSize(5, 110)
self.SetColLabelValue(6, "SME")
self.SetColLabelValue(7, "Org")
self.SetColLabelValue(8, "Manager")
self.SetColSize(8, 125)
self.SetColLabelValue(9, "OfficePriority")
self.SetColSize(9, 165)
self.SetColLabelValue(10, "Comments")
self.SetColSize(10, 200)
self.SetColLabelValue(11, "Loan?")
#self.AutoSizeColumns(setAsMin=True)
class EmployeeViewAllFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent,title = 'View All Employees',size=(wx.EXPAND,wx.EXPAND))
self.currentid = ''
self.currentrow = 0
self.parent = parent
#Declare all panels
self.panelMain = wx.Panel(self,size = (1500, 1000))
self.panelSide = wx.Panel(self,size = (wx.EXPAND, 1000))
#self.panelTitle = wx.Panel(self,size = (1000,30))
#self.buttonPanel = wx.Panel(self)
self.buttonExit = wx.Button(self.panelSide, label="exit")
self.buttonExit.Bind(wx.EVT_BUTTON, self.OnExitButton)
cur = self.parent.con.cursor()
cur.execute("SELECT * FROM " + EMPLOYEETABLE + " ORDER BY Name;")
self.rows = cur.fetchall()#Load all the employees into self.rows and organize by name
self.employeenumber = len(self.rows) #Going to be the fetched number from the database
global TOTALEMPLOYEES
TOTALEMPLOYEES = self.employeenumber
#Set up all the column panels and place into an array to be modified
#self.empGrid = empGrid(self.panelMain)
self.empGrid = empGrid(EMP.MainWindow.panel2)
for i in xrange (0, TOTALEMPLOYEES):
self.empGrid.SetRowLabelValue(i, str(i+1))
for j in xrange (0,12):
self.empGrid.SetCellValue(i, j, str(self.rows[i][j]))
if i % 2 == 1:#if it is odd, change the color to make it easier on the eyes
self.empGrid.SetCellBackgroundColour(i, j, 'LIGHT BLUE') #JTEST
self.empGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.OnGridDoubleClick)
self.empGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_DCLICK, self.OnGridDoubleClickRight)
#Now do the same thing for the buttons
text = wx.StaticText(self.panelSide, label = "Double left click an employee to modify fields\n\n\n Double right click an employee to add a new employee task" , size = (wx.EXPAND,400))
sideSizer = wx.BoxSizer(wx.VERTICAL)
sideSizer.Add(text)
sideSizer.Add(self.buttonExit)
self.panelSide.SetSizer(sideSizer)
self.panelSide.Layout()
#Put them all together then display
displayEmployeeSizer = wx.BoxSizer(wx.VERTICAL)
displayEmployeeSizer.Add(self.empGrid) #JGRID
self.panelMain.SetSizer(displayEmployeeSizer)
self.panelMain.Layout()
viewEmployeeSizer = wx.BoxSizer(wx.HORIZONTAL)
#viewEmployeeSizer.Add(self.panelTitle,proportion=0)
viewEmployeeSizer.Add(self.panelMain,proportion=0)
viewEmployeeSizer.Add(self.panelSide,proportion = 0)
#viewEmployeeSizer.Add(self.buttonPanel, proportion=0, flag = wx.ALIGN_CENTER_HORIZONTAL)
#viewEmployeeSizer.Add(self.buttonExit, proportion = 0, flag = wx.ALIGN_CENTER_HORIZONTAL)
self.SetSizer(viewEmployeeSizer) #Set the panel size
#self.buttonPanel.Layout()
self.Layout()
self.Show()
You can't show the exact same widget in two different parents. Instead, you will need to create an instance of the empGrid when you create your standalone frame AND a different instance when you create the notebook.
When you instantiate the empGrid, you pass it the the notebook panel/page as its parent. When you create the frame, you will pass the frame (or its panel) as the parent.

(MainWindow) Object has no attribute (go) [WxPython]

I'm working on making a bind event on a button refer to a function, however i get the error explained in the title.
I already have another bind event working and coded, and as far as i can tell there is zero difference between the two syntactically.
def OnBtnSuperTesting(self, event):
class MainWindow(wx.Frame):
def updateList(self, e):
#########Defining some listbox possibilites
T89 = [large array was here]
T87AZ = [large array was here]
T89ZA = T89AZ[::-1]
T87ZA = T87AZ[::-1]
#############
if self.radioAtoZ.GetValue() == True:
if self.radioT89.GetValue() == True:
choices = T89AZ
else:
choices = T87AZ
elif self.radioZtoA.GetValue() == True:
if self.radioT89.GetValue() == True:
choices = T89ZA
else:
choices = T87ZA
else:
if self.radioT89.GetValue() == True:
choices = T89
else:
choices = T87
self.listbox.Set(choices)
def Generate(self, e):
#probably need a try except here
selection = self.listbox.GetString(self.listbox.GetSelection())
if self.radioT89 == True:
if selection == 'String name here':
self.pathname = 'pathname here (its coded)'
#Assume the indentation here is right, StackOverflow isn't fommating this nicely
self.lstCommands.AppendRows(1, 1)
item = self.lstCommands.GetNumberRows()-1
self.lstCommands.SetCellValue(item, 0, "Add Module")
self.lstCommands.SetCellValue(item, 1, self.pathname)
self.modifiedFlg = True
def __init__(self, parent, title):
self.dirname=''
wx.Frame.__init__(self, parent, title=title, size=(320,440))
self.SetBackgroundColour(wx.WHITE)
self.CenterOnScreen()
self.CreateStatusBar()
self.radioT89 = wx.RadioButton(self, -1, 'T89 only', pos = (2,0), style = wx.RB_GROUP)
self.radioT87 = wx.RadioButton(self, -1, 'T87 only', pos = (154, 0))
self.radioKeySort = wx.RadioButton(self, -1, 'Sort by Key', pos = (2,40), style = wx.RB_GROUP)
self.radioAtoZ = wx.RadioButton(self, -1, 'Sort Name A-Z', pos = (2,60))
self.radioZtoA = wx.RadioButton(self, -1, 'Sort Name Z-A', pos = (2,80))
self.checkCode = wx.CheckBox(self, -1, 'Generate Code', pos = (154,40))
self.checkBuild = wx.CheckBox(self, -1, 'Generate Build Report', pos = (154, 60))
self.ln = wx.StaticLine(self, -1, pos = (0,15), size = (300,3), style = wx.LI_HORIZONTAL)
self.ln2 = wx.StaticLine(self, -1, pos = (150,15), size = (3,100), style = wx.LI_VERTICAL)
self.radioT87.Bind(wx.EVT_RADIOBUTTON, self.updateList)
self.radioT89.Bind(wx.EVT_RADIOBUTTON, self.updateList)
self.radioKeySort.Bind(wx.EVT_RADIOBUTTON, self.updateList)
self.radioAtoZ.Bind(wx.EVT_RADIOBUTTON, self.updateList)
self.radioZtoA.Bind(wx.EVT_RADIOBUTTON, self.updateList)
self.go.Bind(wx.EVT_BUTTON, self.Generate)
self.go = wx.Button(self,-1, label = 'Go!', pos = (110, 325))
# Setting up the menu.
filemenu= wx.Menu()
menuAbout= filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File")
self.SetMenuBar(menuBar)
# Events.
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
self.SetAutoLayout(1)
self.Show()
def OnExit(self,e):
self.Close(True) # Close the frame.
app = wx.App(False)
frame = MainWindow(None, "Supervisory Testing")
app.MainLoop()
So, you can see that the def updateList is indented the same as def Generate. It also has the same parameters and the same bind event syntax, with the exception that one is a radio button and the other is a button. What am i doing wrong?
You are binding to self.go in the line before you have asigned wx.Button to self.go, move the bind to after the button is created.

wxPython - dynamially update a listctrl depending on input into a textctrl

does anyone of you have an example how to make the following possible:
I have a listctrl that displays > 600 items. Now I need to search in these items for a text the user inputs and update the list to only show the items containing this string.
So let us say the list contains "Hello", "Hi" and "Morning". The list displays all three items. Now the user types "h" into the textctrl, and the listctrl is narrowed down to "Hello" and "Hi". If the user instead types "o", and the list becomes "Hello" and "Morning".
Is this possible? Or is there any other convenient way to find an item in a listctrl? The build in "find as you type" is only of real use if you do exactly know what you search for - and in my case this will not really be the case...
Thanks, Woodpicker
The wxPython demo has a pretty good "type-ahead" filter built into it. Looking at the source code to Main.py they do it the "manual way", loop and rebuild the list. They are using a treeview but the ideas are sound:
def OnSearch(self, event=None):
value = self.filter.GetValue()
if not value:
self.RecreateTree()
return
wx.BeginBusyCursor()
for category, items in _treeList:
self.searchItems[category] = []
for childItem in items:
if SearchDemo(childItem, value):
self.searchItems[category].append(childItem)
wx.EndBusyCursor()
self.RecreateTree()
I like the ObjectListView wrapper better than the straight wx.ListCtrl. It includes the ability to filter items in the control as a feature of the widget. You can read about it here: http://objectlistview.sourceforge.net/python/features.html#filtering and here's the main page for the control: http://objectlistview.sourceforge.net/python/
Here is an example of filtering an UltimateListCtrl.
I know this is 2 years later but I've found other examples on stackoverflow really, really helpful.
I'm new to python/wxpython but hopefully it will be useful.
starting from http://www.blog.pythonlibrary.org/2011/11/02/wxpython-an-intro-to-the-ultimatelistctrl/
import wx
from wx.lib.agw import ultimatelistctrl as ULC
class ULC_Panel(wx.Panel):
""""""
def __init__(self, parent, col_headers=None, list_data=None, options=None, dlg=None,
selected_list=None):
"""Constructor"""
wx.Panel.__init__(self, parent)
self.options = options
self.show_only_selected = False
self.filter_string = ""
hsizer = wx.BoxSizer(wx.HORIZONTAL)
okayButton = wx.Button(self, wx.ID_OK, "OK")
okayButton.SetToolTip(wx.ToolTip("Click to close this dialog and use the selections"))
self.Bind(wx.EVT_BUTTON, self.OnOkayCanButton, okayButton)
hsizer.Add(okayButton, 0, wx.ALL, 5)
canButton = wx.Button(self, wx.ID_CANCEL, "Cancel")
canButton.SetToolTip(wx.ToolTip("Click to close this dialog and cancel selections"))
self.Bind(wx.EVT_BUTTON, self.OnOkayCanButton, canButton)
hsizer.Add(canButton, 0, wx.ALL, 5)
cb_show_only = wx.CheckBox(self, -1, "Show only selected items?")
cb_show_only.SetValue(self.show_only_selected)
cb_show_only.SetToolTip(wx.ToolTip("Click to show only selected rows"))
self.Bind(wx.EVT_CHECKBOX, self.EvtShowOnly, cb_show_only)
hsizer.Add(cb_show_only, 0, wx.ALL, 5)
self.stext = wx.StaticText(self, -1, "Filter: ", style=wx.ALIGN_LEFT)
self.filtr = wx.TextCtrl(self, -1, "", style=wx.ALIGN_LEFT)
self.Bind(wx.EVT_TEXT, self.OnFiltr, self.filtr)
fsizer = wx.BoxSizer(wx.HORIZONTAL)
fsizer.Add(self.stext, 0, wx.ALL)
fsizer.Add(self.filtr, 1, wx.EXPAND)
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont.SetWeight(wx.BOLD)
boldfont.SetPointSize(12)
self.ultimateList = ULC.UltimateListCtrl(self, agwStyle = wx.LC_REPORT
| wx.LC_VRULES | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT
| wx.LC_HRULES)
self.checkbox = [None] * len(list_data)
if selected_list != None:
self.selected = selected_list
else:
self.selected = [False] * len(list_data)
self.rows_max = len(list_data)
self.rows_current = -1
self.cols_max = len(col_headers)
self.cols_extra = 1
if options & ULC.ULC_MASK_CHECK:
self.cols_extra += 1
for i in xrange(self.cols_max+self.cols_extra):
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info._image = []
info._format = 0
info._kind = 1
width = 150
if i >= self.cols_extra:
info._text = col_headers[i-self.cols_extra]
elif i == 0:
info._text = "Row"
width = 35
elif i == 1 and options & ULC.ULC_MASK_CHECK:
info._text = "Select"
width = 50
self.ultimateList.InsertColumnInfo(i, info)
self.ultimateList.SetColumnWidth(i, width)
self.list_data = list_data
pos = self.populate_table("")
if pos != None:
self.sz = self.ultimateList.GetItemRect(pos)
self.width = self.sz[2] + self.sz[3]
self.height = self.sz[1]
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(hsizer, 0, wx.EXPAND)
sizer.Add(fsizer, 0, wx.EXPAND)
sizer.Add(self.ultimateList, 1, flag=wx.EXPAND)
self.SetSizer(sizer)
sizer.Fit(self)
def EvtShowOnly(self, event):
cb = event.GetEventObject()
val = cb.GetValue()
self.show_only_selected = val
pos = self.populate_table(self.filter_string)
print "show_only_selected val= ", val
def EvtCheckBox(self, event):
cb = event.GetEventObject()
id = event.GetId()
val = cb.GetValue()
self.selected[id] = val
print "id, val= ", id, val
def OnOkayCanButton(self, event):
id = event.GetId()
dlg.EndModal(id)
def myGetNeedWH(self):
return (self.width, self.height)
def myGetSelectedState(self):
return self.selected
def populate_table(self, str_in):
busy = wx.BusyCursor()
if str_in:
str_low = str_in.lower()
if self.options & ULC.ULC_MASK_CHECK:
# if we have widgets in the row then we have to delete 1 row
# at a time (or else it leaves some of the widgets)
i = self.rows_current
#print "i, self.rows_max= ", i, self.rows_max
while i >= 0:
#print "i= ", i
self.ultimateList.DeleteItem(i)
i -= 1
else:
self.ultimateList.DeleteAllItems()
row = -1
for i in xrange(len(self.list_data)):
tlwr = self.list_data[i][0].lower()
if not str_in or tlwr.find(str_low) >= 0:
if self.show_only_selected and self.selected[i] == False:
continue
row += 1
for j in xrange(self.cols_max+self.cols_extra):
if j == 0:
pos = self.ultimateList.InsertStringItem(row, str(row))
elif j == 1 and self.options & ULC.ULC_MASK_CHECK:
self.checkbox[i] = wx.CheckBox(self.ultimateList, id= i)
self.checkbox[i].SetValue(self.selected[i])
self.checkbox[i].SetToolTip(wx.ToolTip("Click to select this row"))
self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.checkbox[i])
self.ultimateList.SetItemWindow(pos, col=1, wnd=self.checkbox[i], expand=False)
else:
self.ultimateList.SetStringItem(row, j, self.list_data[i][j-self.cols_extra])
self.rows_current = row
return row
def OnFiltr(self, event):
str1 = event.GetString()
id = event.GetId()
#print "got txt_tval str[%s]= %s" % (id, str1)
self.filter_string = str1
pos = self.populate_table(str1)
event.Skip()
return
########################################################################
class FilterListDiag(wx.Dialog):
def __init__(self, parent, id, title, headers=None, data_table=None, options=None, selected_list=None):
wx.Dialog.__init__(self, parent, id, title="", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
options_in = options
self.panel = ULC_Panel(self, col_headers=headers, list_data=data_table, options=options_in,
dlg=self, selected_list=selected_list)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel, 1, wx.EXPAND)
self.SetSizer(sizer)
(self.width, self.height) = self.panel.myGetNeedWH()
def myGetNeedWH(self):
return (self.width, self.height)
def myGetSelectedState(self):
return self.panel.myGetSelectedState()
class TestFrame(wx.Frame):
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="MvP UltimateListCtrl Demo", size=(850,600))
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = TestFrame()
col_headers = ['col0', 'col1', 'col2']
list_data = [
["Newsboys", "Go", "Rock"],
["Puffy", "Bring It!", "Pop"],
["Family Force 5", "III", "Pop"],
["Me2", "III", "Pop"],
["Duffy", "III", "Pop"],
["Fluffy", "III", "Pop"],
]
# sel_data passes in a list of which rows are already selected
sel_data = [
False,
False,
False,
False,
True,
False,
]
opt=ULC.ULC_MASK_CHECK # just reusing this to indicate I want a column of checkboxes.
dlg = FilterListDiag(frame, -1, "hi", headers=col_headers, data_table=list_data, options=opt, selected_list=sel_data)
(w, h) = dlg.myGetNeedWH()
print w,h
dlg.SetSizeWH(w, 300)
val = dlg.ShowModal()
selected = dlg.myGetSelectedState()
print "okay, can, val= ", wx.ID_OK, wx.ID_CANCEL, val
dlg.Destroy()
print 'selected=', selected
I have something different.
I created a dictionary and updated every time there is an entry in the list ctrl. Now, when I have a search box, which changes the list ctrl with each input from the keyboard and I clear and re-update my dictionary. This way, I always have the latest index for the values.
Code is as below:
class ViewUserDialog(wx.Dialog):
def __init__(self):
title = 'View Users Records'
super().__init__(parent=None, size=(750, 600), title=title)
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.list_sizer = wx.BoxSizer(wx.VERTICAL)
self.row_obj_dict = {}
.
.
.
self.list_ctrl_View_User.InsertColumn(0, "ID", width=150)
self.list_ctrl_View_User.InsertColumn(1, "User Name", width=150)
for index, users in original_user_frame.iterrows():
self.list_ctrl_View_User.InsertItem(indexes, user_id_value)
self.list_ctrl_View_User.SetItem(indexes, 1, users.NAME)
user_objects.append(users)
self.row_obj_dict[indexes] = users
indexes += 1
Now on searching, and selecting an item in lstctrl, you will still get your results:
def on_view(self, event):
selection = self.list_ctrl_View_User.GetFocusedItem()
self.list_ctrl_View_User.ClearAll()
if selection >= 0:
user = self.row_obj_dict[selection]
print(user)
self.list_ctrl_View_User.InsertColumn(0, "ID", width=150)
self.list_ctrl_View_User.InsertColumn(1, "User Name", width=150)
for index, users in original_user_frame.iterrows():
self.list_ctrl_View_User.InsertItem(indexes, user_id_value)
self.list_ctrl_View_User.SetItem(indexes, 1, users.NAME)
This will always give the currect selected item from the lstctrl and update the list with every input.

Problems with multiple panels in a single notebook page

I am creating a program to calculate D&D scores. I have all the backend done, and I want to get the GUI done now.
What I am trying to do here is have a static panel for certain buttons (next, previous, ok, cancel, etc.). The panel is not cooperating.
I want to try to get it on the bottom right (where next/previous buttons traditionally are). This panel can go in the notebook sizer or in the sizer sizerMain I have made for everything else in step_1.
Let me know if you have any questions. I am very new to wxPython and I hope you can deal with my code... :)
Code is below:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import wx
class step_1(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, id=wx.ID_ANY)
# Create initial sizers and panels
## Main sizer, containing both panels
sizerMain = wx.BoxSizer(wx.VERTICAL)
## For the main control area
panelControl = wx.Panel(self,2)
sizerControl = wx.GridBagSizer(hgap = 4,vgap = 4)
## For buttons
panelBtn = wx.Panel(self,1)
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
# Add widgets
## Main content area
lblTitle = wx.StaticText(self,label = "Pick Scores")
sizerControl.Add(lblTitle,pos = (0,0),
flag = wx.ALIGN_CENTER|wx.TOP|wx.LEFT|wx.BOTTOM,
border = 5)
btnRoll = wx.Button(self,label = "Roll!")
sizerControl.Add(btnRoll,pos = (0,1),span = (1,5),
flag = wx.EXPAND|wx.ALL,border = 5)
### Radio boxes
#### Radio button tuple
rboxPick = ["Default","Strength","Dexterity","Constitution",
"Intelligence","Wisdom","Charisma"]
self.lblRoll1 = wx.StaticText(self,label = "0")
sizerControl.Add(self.lblRoll1,pos = (1,0),flag = wx.ALIGN_CENTER)
self.rboxRoll1 = wx.RadioBox(self,label = "Roll One",choices = rboxPick)
sizerControl.Add(self.rboxRoll1,pos = (1,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll2 = wx.StaticText(self,label = "0")
sizerControl.Add(self.lblRoll2,pos = (2,0),flag = wx.ALIGN_CENTER)
self.rboxRoll2 = wx.RadioBox(self,label = "Roll Two",choices = rboxPick)
sizerControl.Add(self.rboxRoll2,pos = (2,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll3 = wx.StaticText(self,label = "0")
sizerControl.Add(self.lblRoll3,pos = (3,0),flag = wx.ALIGN_CENTER)
self.rboxRoll3 = wx.RadioBox(self,label = "Roll Three",choices = rboxPick)
sizerControl.Add(self.rboxRoll3,pos = (3,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll4 = wx.StaticText(self,label = "0")
sizerControl.Add(self.lblRoll4,pos = (4,0),flag = wx.ALIGN_CENTER)
self.rboxRoll4 = wx.RadioBox(self,label = "Roll Four",choices = rboxPick)
sizerControl.Add(self.rboxRoll4,pos = (4,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll5 = wx.StaticText(self,label = "0")
sizerControl.Add(self.lblRoll5,pos = (5,0),flag = wx.ALIGN_CENTER)
self.rboxRoll5 = wx.RadioBox(self,label = "Roll Five",choices = rboxPick)
sizerControl.Add(self.rboxRoll5,pos = (5,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
self.lblRoll6 = wx.StaticText(self,label = "0")
sizerControl.Add(self.lblRoll6,pos = (6,0),flag = wx.ALIGN_CENTER)
self.rboxRoll6 = wx.RadioBox(self,label = "Roll Six",choices = rboxPick)
sizerControl.Add(self.rboxRoll6,pos = (6,1),span = (1,5),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 2)
### Instructions
self.tcLogger = wx.TextCtrl(self,style = wx.TE_MULTILINE)
sizerControl.Add(self.tcLogger,pos = (7,0),span = (1,6),
flag = wx.EXPAND|wx.LEFT|wx.RIGHT,border = 5)
self.tcLogger.AppendText("""Instructions
1. Click the "Roll!" button up top.
- Scores will be placed in the empty slots on the left side.
2. Look at the scores and decide where you want to put them.
3. Click the correct label for each score.
- Make sure you only assign one score to one ability.
4. Click "Assign" to finalize the assignment.""")
## Button area
self.btnPrev = wx.Button(self,label = "Previous",size = (90,28))
self.btnAssign = wx.Button(self,label = "Assign",size = (90,28))
self.btnNext = wx.Button(self,label = "Next",size = (90,28))
sizerBtn.Add(self.btnPrev)
sizerBtn.Add(self.btnAssign)
sizerBtn.Add(self.btnNext,flag = wx.RIGHT|wx.BOTTOM,border = 5)
self.btnNext.Disable()
self.btnPrev.Disable()
# Set and fit sizers, panels, etc.
## Growable rows and columns
sizerControl.AddGrowableCol(1)
sizerControl.AddGrowableRow(7)
## Finalize sizers and panels
panelControl.SetSizerAndFit(sizerControl)
panelBtn.SetSizerAndFit(sizerBtn)
### Final sizer to hold everything
sizerMain.Add(panelControl,2,wx.EXPAND|wx.ALIGN_TOP|wx.ALL,border = 5)
sizerMain.Add(panelBtn,1,wx.EXPAND|wx.ALIGN_BOTTOM|wx.RIGHT,border = 5)
self.SetAutoLayout(True)
self.SetSizerAndFit(sizerMain)
self.Layout()
# Bind events (as needed)
class step_2(wx.Panel):
def __init__(self, parent):
""""""
wx.Panel.__init__(self, parent, id=wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(txtOne, 0, wx.ALL, 5)
sizer.Add(txtTwo, 0, wx.ALL, 5)
self.SetSizer(sizer)
class step_3(wx.Panel):
def __init__(self, parent):
""""""
wx.Panel.__init__(self, parent, id=wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(txtOne, 0, wx.ALL, 5)
sizer.Add(txtTwo, 0, wx.ALL, 5)
self.SetSizer(sizer)
####
# create a button class here for later, don't worry about it now
####
class main_frame(wx.Frame):
"""Main Frame holding the main panel."""
def __init__(self,*args,**kwargs):
wx.Frame.__init__(self,*args,**kwargs)
# Build the menu bar
menuBar = wx.MenuBar()
menuFile = wx.Menu()
menuFileQuit = menuFile.Append(wx.ID_EXIT, text="&Quit")
#self.Bind(wx.EVT_MENU, self.OnQuit,menuFileQuit)
menuBar.Append(menuFile, "&File")
self.SetMenuBar(menuBar)
p = wx.Panel(self)
nb = wx.Notebook(p)
# create the page windows as children of the notebook
nbPage1 = step_1(nb)
nbPage2 = step_2(nb)
nbPage3 = step_3(nb)
# add the pages to the notebook with the label to show on the tab
nb.AddPage(nbPage1,"Page 1")
nb.AddPage(nbPage2,"Page 2")
nb.AddPage(nbPage3,"Page 3")
# finally, put the notebook in a sizer for the panel to manage the
# layout
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
p.SetSizer(sizer)
self.Center()
self.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = main_frame(None,-1,size = (1000,1000),title = "D&D Charcter Creator")
app.MainLoop()
You've got parenting problems!
For example, you want the widget self.lblRoll1 to be on the panelControl therefore you should make it a child of it.
e.g.
self.lblRoll1 = wx.StaticText(panelControl,label = "0")
This is your problem -it occurs throughout your code.
An indispensable tool for solving these type of issues is the Widget Inspection tool.
Also Id advise you to factor out the code for each panel into its own class (which would subclass wx.Panel). This will make it all much easier to read and maintain.

Categories