Related
I am trying to couple wx.ListBox with wx.combo.Comboctrl. A sample code is below. For some reason, the items in the ListBox are not clickable/selectable. I wonder how I can make it work. Thanks!
EDIT: Missing code added
import wx, wx.combo
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="", size=(300, 100))
gbs = wx.GridBagSizer()
ComboCtrlBox = wx.combo.ComboCtrl(self)
ComboCtrlPopup = ListBoxComboPopup()
ComboCtrlBox.SetPopupControl(ComboCtrlPopup)
ComboCtrlPopup.ListBox.Append("Apple")
ComboCtrlPopup.ListBox.Append("Banana")
ComboCtrlPopup.ListBox.Append("Orange")
ComboCtrlPopup.ListBox.Bind(wx.EVT_LISTBOX, self.OnSelect) #ADDED
gbs.Add(ComboCtrlBox, pos = (0, 0), span = (1, 1), flag = wx.EXPAND|wx.ALL, border = 10)
gbs.AddGrowableCol(0)
self.SetSizer(gbs)
self.Layout()
def OnSelect(self, evt): #ADDED
print "HAHA"
class ListBoxComboPopup(wx.combo.ComboPopup):
def Init(self):
self.ItemList = []
def Create(self, parent):
self.ListBox = wx.ListBox(parent, -1, size = (-1, 20), choices = self.ItemList)
def GetControl(self):
return self.ListBox
def OnPopup(self):
pass
#-----------------------------------------------------------------------------#
if __name__ == '__main__':
APP = wx.App(False)
FRAME = MainFrame(None)
FRAME.Show()
APP.MainLoop()
You are missing a few things in your ListBoxComboPopup class needed to make it work well with the ComboCtrl. At a minimum you are missing some event binding and handler to catch selection events from the ListBox, and an implementation of the GetStringValue method which the combo will call to get the value to be displayed. Please see the ComboCtrl sample in the wxPython demo for more details and example code.
I am trying to create a web crawler based on specific user input. For example, the User Input I am trying to receive is from a ListBox and a text field. Once I have that information, I would like the user to click a button to start the search with the information collected.
This is where I have been getting problems. The EVT function doesn't recognize the listbox since its been linked to the Button evt. Is there a way to solve the problem? Can EVT information be shared with other EVTs?
Here is what I have so far:
import wx # for gui
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Title', size=(300,200))
tournLevel = ['$10,000', '$15,000', '$20,000', '$50,000','$75,000','$100,000']
levelBox = wx.ListBox(panel, -1, (40, 50), (90, 90), tournLevel)
levelBox.SetSelection(1) # default selection
checkButton = wx.Button(panel, label= "Check Now", pos = (150, 50), size = (90, 40))
self.Bind(wx.EVT_BUTTON, self.OnClick, checkButton)
def OnClick(self, event):
currLevel = event.GetSelection()
print(currLevel) # to test if GetSelection is working
if __name__ == '__main__':
app = wx.App()
frame = MyFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
I would be very happy if I could just get the button to recognize the ListBox results.
Thank you for your time!
You can just grab it from the listbox, you don't need it from the event. See below:
import wx # for gui
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Title', size=(300,200))
tournLevel = ['$10,000', '$15,000', '$20,000', '$50,000','$75,000','$100,000']
self.levelBox = wx.ListBox(panel, -1, (40, 50), (90, 90), tournLevel)
self.levelBox.SetSelection(1) # default selection
self.checkButton = wx.Button(panel, label= "Check Now", pos = (150, 50), size = (90, 40))
self.Bind(wx.EVT_BUTTON, self.OnClick, self.checkButton)
def OnClick(self, event):
currLevel = self.levelBox.GetSelection()
print(currLevel) # to test if GetSelection is working
if __name__ == '__main__':
app = wx.App()
frame = MyFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
More specifically, if you store levelBox as self.levelBox, it will be accessible inside the OnClick method as a MyFrame attribute. You can then use the GetSelection method for this object (not the event), which will get the current selection.
You can make levelBox into a property of the class by turning it into self.levelBox and accessing it that way as #brettb mentioned. However you can get a bit sneakier and do it using a lambda for your callback to pass the Listbox widget to the event handler:
import wx # for gui
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Title', size=(300,200))
panel = wx.Panel(self)
tournLevel = ['$10,000', '$15,000', '$20,000', '$50,000','$75,000','$100,000']
levelBox = wx.ListBox(panel, -1, (40, 50), (90, 90), tournLevel)
levelBox.SetSelection(1) # default selection
checkButton = wx.Button(panel, label= "Check Now", pos = (150, 50), size = (90, 40))
evt = lambda caller, widget=levelBox: self.OnClick(caller, widget)
checkButton.Bind(wx.EVT_BUTTON, evt)
def OnClick(self, event, widget):
currLevel = widget.GetSelection()
print(currLevel) # to test if GetSelection is working
print widget.GetString(currLevel)
if __name__ == '__main__':
app = wx.App()
frame = MyFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
Also note that you didn't have panel defined, so your original code doesn't work. See the following for more information:
http://wiki.wxpython.org/Passing%20Arguments%20to%20Callbacks
I have a listbox with a set of strings. The set of strings I want to display depends on which radio button is selected. I would like it such that while the user is interacting with the Form, if they ever change the radio button it will update the list box.
Here is my code (I'm leaving the array for t87 and t89 out because they are very long (assume they exist):
def OnBtnSuperTesting(self, event):
class MainWindow(wx.Frame):
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.Bind(wx.EVT_RADIOBUTTON, self.radioT89, self.updateList())
self.listbox = wx.ListBox(self, -1, pos = (0,120), size = (300,200), choices = T89, style = (wx.LB_SINGLE|wx.LB_HSCROLL))
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.
def updateList(self):
if self.radioT87.GetValue() == True:
choices = T87
self.listbox.Set(choices)
else:
choices = T89
self.listbox.Set(choices)
app = wx.App(False)
frame = MainWindow(None, "Supervisory Testing")
app.MainLoop()
When you create each radiobutton you can create a bind event. What this does (as you have implemented later on in your code) is execute a command function when the bind event occurs. In your case it would look like this:
self.Bind(wx.EVT_RADIOBUTTON,self.RadioButton,self.DoSomething)
Explanation:
wx.EVT_RADIOBUTTON
This is the event that is triggered when the user changes the Radiobutton's status. It may or may not have attributes.
self.RadioButton
This is the radiobutton which you would like to bind. In your case "self.radioAtoZ" or similar.
self.DoSomething
THis is the callback function. You can make it whatever you want such as:
def DoSomething(self):
if self.radioAtoZ.getStatus():
rearrangeNumbersFromAtoZ
print 'Re-arranged numbers from A to Z'
else:
etc.
EDIT:
self.RadioButton.Bind(EVT_RADIOBUTTON, self.DoSomething)
Your structure for self.DoSomething should be like this:
Class MainWindow:
def __init_(self, parent):
def DoSomething(self):
#dostuff
Also in response to your other comment:
when a function is called within a Bind event, it passes the event to the function by default. In addition, all functions have the "self" arg, thus 2 given args. You need to change the following:
def DoSomething(self, event):
#dostuff
I decided to rewrite the OP's code to demonstrate how to bind 2 RadioButton's to 2 different event handlers and update the ListBox:
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Radios!")
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.radioAtoZ = wx.RadioButton(panel, label='Sort Name A-Z',
style = wx.RB_GROUP)
self.radioAtoZ.Bind(wx.EVT_RADIOBUTTON, self.sortAZ)
sizer.Add(self.radioAtoZ, 0, wx.ALL|wx.EXPAND, 5)
self.radioZtoA = wx.RadioButton(panel, label='Sort Name Z-A')
self.radioZtoA.Bind(wx.EVT_RADIOBUTTON, self.sortZA)
sizer.Add(self.radioZtoA, 0, wx.ALL|wx.EXPAND, 5)
choices = ["aardvark", "zebra", "bat", "giraffe"]
self.listbox = wx.ListBox(panel, choices=choices)
sizer.Add(self.listbox, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.Show()
#----------------------------------------------------------------------
def sortAZ(self, event):
""""""
choices = self.listbox.GetStrings()
choices.sort()
self.listbox.SetItems(choices)
#----------------------------------------------------------------------
def sortZA(self, event):
""""""
choices = self.listbox.GetStrings()
choices.sort()
choices.reverse()
self.listbox.SetItems(choices)
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
You will want to take a look at the following wiki article on the differences of binding it this way versus the other:
http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind
Most of the time when you create a group of widgets that do different things, you bind them to different event handlers. If you want to bind all the RadioButtons to the same handler, then you'll probably need to name each widget with a unique name so that when they come to the handler, you can tell which button was pressed. Then you can use a series of if statements to decide what to do to the list box. Here's a tutorial that talks about that sort of thing:
http://www.blog.pythonlibrary.org/2011/09/20/wxpython-binding-multiple-widgets-to-the-same-handler/
I want to make a game in wxPython (no other modules) and I want to make it so that you can enter some values in popup screens before the game starts, and then the game will be drawn on a canvas which in turn is drawn on a panel, which is bound to the main game.
I made the gamescreen with all fancy stuff (works solo)
I made the input screens
But I cannot link them.
How do I start the game so it will open a dialog box, then on the closure of it open another one, and then open the game ?
I tried the following, but it will not open my canvas:
# makes a game by showing 2 dialogs
# after dialogs have been answered, starts the game by drawing the canvas.
# imports
import wx
import Speelveld3
# globals
SCRWIDTH = 950
SCRHEIGHT = 700
# dialogbox class
class MyDialog1(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent)
self.username = wx.TextCtrl(self)
self.okButton = wx.Button(self, wx.ID_OK, "OK")
class MyDialog2(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent)
self.canvasWidth = wx.TextCtrl(self)
self.okButton = wx.Button(self, wx.ID_OK, "OK")
# main class
class Game(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, title='My game', size=(SCRWIDTH, SCRHEIGHT))
self.username = ""
self.canvasWidth = 10
# hide the frame for now
self.Hide()
def OnInit(self):
#Make your dialogs
dlg1 = MyDialog1(self)
#if the user pressed "OK" (i.e. NOT "Cancel" or any other button you might add)
if dlg1.ShowModal() == wx.ID_OK:
#get the username from the dialog
self.username = dlg1.username.GetValue()
#clean up the dialog (AFTER you get the username)
dlg1.Destroy()
dlg2 = MyDialog2(self)
#if the user pressed "OK" (i.e. NOT "Cancel" or any other button you might add)
if dlg2.ShowModal() == wx.ID_OK:
#get the username from the dialog
self.canvasWidth = dlg2.canvasWidth.GetValue()
#clean up the dialog (AFTER you get the username)
dlg2.Destroy()
# Now that you have your settings, Make the gameboard
# THIS PART IS STILL BROKEN!
# I can paste the whole board class (structure of it is taken from the tetris tutorial)
# but that seems a bit much tbh...
self.gameBoard = Board.Board(self)
self.gameBoard = SetFocus()
self.gameBoard.start()
self.Centre()
self.Show(True) #show the frame
if __name__ == '__main__':
# how can I start the game here?
app = wx.App()
frame = Game()
board = Speelveld3.Speelveld(frame)
board.start()
frame.Show()
app.MainLoop()
You've double posted, and the lack of any wx.Dialog in your sample code suggests to me that you haven't even looked at a tutorial yet, but I will give you the benefit of the doubt.
First, if you want to return information from a dialog, the easiest way is to define a custom dialog. Define a new class that inherits from wx.Dialog and then set it up just like you would a normal panel or a frame. It seems to me that you will need two of these. They'll look something like this:
class MyDialog1(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent)
self.username = wx.TextCtrl(self) #this is where users will enter their username
self.okButton = wx.Button(self, wx.ID_OK, "OK") #Note that I'm using wx.ID_OK. This is important
Now, for the logic you want. Pretty much every object in wxPython that you actually see has the functions Show() and Hide() (API here). You don't want to show your frame until AFTER the dialogs are finished, so in your __init__(), call Hide(). I'm also initializing a variable, username, which is where I will store the data from my dialog.
class Game(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(SCRWIDTH, SCRHEIGHT))
self.username = ""
self.Hide() #don't show the frame just yet
#self.Hide() is the exact same as self.Show(False)
Now, for your dialogs. Like Mike Driscoll suggested, you call your dialogs BEFORE making your canvas. wx.Dialogs are launched using ShowModal(). By setting the ID of self.okButton to the constant wx.ID_OK, wxPython recognizes that the dialog should be closed after the button in clicked. You should also be aware of wx.ID_CANCEL.
def OnInit(self):
#Make your dialogs
dlg1 = MyDialog1(self)
if dlg1.ShowModal() == wx.ID_OK:
#if the user pressed "OK" (i.e. NOT "Cancel" or any other button you might add)
self.username = dlg1.username.GetValue() #get the username from the dialog
dlg1.Destroy() #clean up the dialog (AFTER you get the username)
#do this again for your second dialog
#Now that you have your settings, Make the gameboard
self.gameBoard = Board.Board(self)
self.gameBoard = SetFocus()
self.gameBoard.start()
self.Centre()
self.Show(True) #show the frame
In your OnInit you just need to call your dialogs and show them modally BEFORE you create your Board instance. Then it should work correctly.
EDIT (6-28-12): Here's some code:
import wx
########################################################################
class MyDlg(wx.Dialog):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Dialog.__init__(self, None, title="I'm a dialog!")
lbl = wx.StaticText(self, label="Hi from the panel's init!")
btn = wx.Button(self, id=wx.ID_OK, label="Close me")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL, 5)
self.SetSizer(sizer)
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
# show a custom dialog
dlg = MyDlg()
dlg.ShowModal()
dlg.Destroy()
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
pdc = wx.PaintDC(self)
try:
dc = wx.GCDC(pdc)
except:
dc = pdc
rect = wx.Rect(0,0, 100, 100)
for RGB, pos in [((178, 34, 34), ( 50, 90)),
(( 35, 142, 35), (110, 150)),
(( 0, 0, 139), (170, 90))
]:
r, g, b = RGB
penclr = wx.Colour(r, g, b, wx.ALPHA_OPAQUE)
brushclr = wx.Colour(r, g, b, 128) # half transparent
dc.SetPen(wx.Pen(penclr))
dc.SetBrush(wx.Brush(brushclr))
rect.SetPosition(pos)
dc.DrawRoundedRectangleRect(rect, 8)
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Example frame")
# show a MessageDialog
style = wx.OK|wx.ICON_INFORMATION
dlg = wx.MessageDialog(parent=None,
message="Hello from the frame's init",
caption="Information", style=style)
dlg.ShowModal()
dlg.Destroy()
# create panel
panel = MyPanel(self)
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()
I'm having a bit of trouble with a panel that has two wxPython TextCtrls in it. I want either an EVT_CHAR or EVT_KEY_UP handler bound to both controls, and I want to be able to tell which TextCtrl generated the event. I would think that event.Id would tell me this, but in the following sample code it's always 0. Any thoughts? I've only tested this on OS X.
This code simply checks that both TextCtrls have some text in them before enabling the Done button
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title,
wx.DefaultPosition, wx.Size(200, 150))
self.panel = BaseNameEntryPanel(self)
class BaseNameEntryPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.entry = wx.TextCtrl(self, wx.NewId())
self.entry2 = wx.TextCtrl(self, wx.NewId())
self.donebtn = wx.Button(self, wx.NewId(), "Done")
self.donebtn.Disable()
vsizer = wx.BoxSizer(wx.VERTICAL)
vsizer.Add(self.entry, 1, wx.EXPAND|wx.GROW)
vsizer.Add(self.entry2, 1, wx.EXPAND|wx.GROW)
vsizer.Add(self.donebtn, 1, wx.EXPAND|wx.GROW)
self.SetSizer(vsizer)
self.Fit()
self.entry.Bind(wx.EVT_KEY_UP, self.Handle)
self.entry2.Bind(wx.EVT_KEY_UP, self.Handle)
def Handle(self, event):
keycode = event.GetKeyCode()
print keycode, event.Id # <- event.Id is always 0!
def checker(entry):
return bool(entry.GetValue().strip())
self.donebtn.Enable(checker(self.entry) and checker(self.entry2))
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "Hello from wxPython")
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
You could try event.GetId() or event.GetEventObject() and see if either of these work.
Another approach to this is to use lambda or functools.partial to effectively pass a parameter to the handler. So, for example, sub in the lines below into your program:
self.entry.Bind(wx.EVT_KEY_UP, functools.partial(self.Handle, ob=self.entry))
self.entry2.Bind(wx.EVT_KEY_UP, functools.partial(self.Handle, ob=self.entry2))
def Handle(self, event, ob=None):
print ob
And then ob will be either entry or entry2 depending on which panel is clicked. But, of course, this shouldn't be necessary, and GetId and GetEventObject() should both work -- though I don't (yet) have a Mac to try these on.