Tkinter - Same event for multiple buttons - events

Using Tkinter, I have many buttons. I would like the same callback function to be triggered every time any of the buttons pressed. How can I find out which button was pressed ?
def call(p1):
# Which Button was pressed?
pass
for i in range (50):
B1 = Button(master, text = '...', width = 2)
B1.grid(row = i*20, column = 60)
B1.bind('<Button-1>',call)
B2 = Button(master, text = '...', width = 2)
B2.grid(row = i*20, column = 60)
B2.bind('<Button-1>',call)

Using a list to reference the dynamically created buttons and lambda to store a reference to the index of the button object. You can determine which button was clicked. In the below examples I use .cget("text") on the button object to demonstrate accessing the button widget.
import tkinter as tk
root = tk.Tk()
root.minsize(200, 200)
btn_list = [] # List to hold the button objects
def onClick(idx):
print(idx) # Print the index value
print(btn_list[idx].cget("text")) #Print the text for the selected button
for i in range(10):
# Lambda command to hold reference to the index matched with range value
b = tk.Button(root, text = 'Button #%s' % i, command = lambda idx = i: onClick(idx))
b.grid(row = i, column = 0)
btn_list.append(b) # Append the button to a list
root.mainloop()
Alternatively you can use bind and then access the widget from the event object generated.
import tkinter as tk
root = tk.Tk()
root.minsize(200, 200)
def onClick(event):
btn = event.widget # event.widget is the widget that called the event
print(btn.cget("text")) #Print the text for the selected button
for i in range(10):
b = tk.Button(root, text = 'Button #%s' % i)
b.grid(row = i, column = 0)
# Bind to left click which generates an event object
b.bind("<Button-1>", onClick)
root.mainloop()

#Steven Summers' first example seems most clear to me, but I think doing it without the list is even clearer.
The way I understood the question, you not only want to know which button was clicked but you also want each button to call one other, undescribed function (universal in my example below). In that case, you can use the very handy
combine_funcs (see: Have multiple commands when button is pressed) to call two functions from one widget.
Here is my code. Instead of a list, I simply have a string that is changed and printed with each click.
import tkinter as tk
root = tk.Tk()
root.minsize(200, 200)
buttonVal = ''
def combine_funcs(*funcs):
def combined_func(*args, **kwargs):
for f in funcs:
f(*args, **kwargs)
return combined_func
def universal():
print 'Universal function is called'
def button_check(buttonName):
buttonVal = buttonName
print buttonVal # Or whatever you want to do with the button info
for i in range(10):
B1 = tk.Button(root, text = 'Button #%s' % i, command = combine_funcs(universal, lambda buttonName = 'Button #%s' % i:button_check(buttonName)))
B1.grid(row = i, column = 0)
root.mainloop()

Use lambda:
B1 = Button(master, text = '...', width = 2, command = lambda: call('B1') )
And so on...

This might not be the simplest solution, but it is the only one I could come up with.
from Tkinter import *
master = Tk()
L = []
def call(p1):
for i in range(len(L)):
if str(L[i]) == str(p1.widget):
print 'Button' + str(i)
break
for i in range (50):
exec("Button" + str(i) + " = Button(master, text = '...', width = 2)")
exec("Button" + str(i) + ".grid(row = i*20, column = 60)")
exec("Button" + str(i) + ".bind('<Button-1>',call)")
s = 'L.append(str(Button' + str(i) + '))'
exec(s)

Related

setText() and addItem() of lineEdit and QlistWidget respectively do not show the contents

I want to display the value stored in the varible "total" to the QlineEdit and add value of the variable 'points' to the QlistWidget when the button is clicked.
In the following block of code, i used "self.lineEdit.setText(str(points))" and "self.LW2.addItem(self.points)" to set text to QlineEdit and QlistWidget respectively. Still, the values won't show up.
def display(self):
sqlC = self.databaseConn()
#self.playersList = []
self.pointlist = []
self.total = 0
self.economic_rate=0
if "----SELECT TEAM----" == self.CB1.currentText():
self.msgbox("Warning","Select a team to evaluate")
else:
for x in range(self.LW1.count()):
player=self.LW1.item(x).text()
#print(player)
sqlC.execute("SELECT Scored,Faced,Fours,Sixes,Bowled,Wkts,Catches,Stumping,RO,Given FROM Match WHERE Player = ?",(player,))
score=sqlC.fetchall()
points = FantasyCricket.FantasyCricket.BattingBowlingPoints.batting(score)
points += FantasyCricket.FantasyCricket.BattingBowlingPoints.bowling(score)
self.pointlist.append(points)
#print(points)
self.total += points
#print(self.total)
self.LW2.addItem(str(points))
self.lineEdit.setText(str(self.total))
P.S- the values are displayed correctly on the Shell using print()

Tkinter Scroll Window Does Not Scroll

Probably an easy question for a Tkinter veteran but why can't I scroll in my window?
If you run this code and add multiple data ports with the button, you will see what I mean.
Can anyone modify my code to make the scroll window work please?
I am not very well versed in Tkinter yet as I am with other similar tools in other languages so I'm sure there's going to be a lot to edit here.
from tkinter import *
fields = 'Buffer Depth \n(# of max-sized packets)', 'Payload Size \n(Max=1500bytes) ', 'Duplicate Quantity'
entries =[]
root = Tk()
root.title(string= 'Buffer Testing Tool')#create window title
root.geometry('500x750')#main window dimensions
root.configure(bg= 'crimson')
canvas = Canvas(root, height = 1000)
scroll_y = Scrollbar(root, orient="vertical", command=canvas.yview)
frame = Frame()
options = ["Transmit", "Receive"]
def makeform(root, fields):#this will organize and align the labels of the entries plus the entry fields
numberCheck =0
for field in fields:
row = Frame(canvas)
if (numberCheck==0):
firstES = Label(row, text= "Data Port 1", anchor = 'center', fg = "blue")#first ES label
firstES.pack()
DPtype = Label(row, text = 'Type', width=19, anchor = 'nw')
DPtype.pack(side= LEFT)
clicked= StringVar()
clicked.set(options[0])
drop = OptionMenu(row, clicked, *options)
drop.pack(side = LEFT, padx=5, pady= 5)
numberCheck=1
row.pack(side=TOP, fill=X, padx=5, pady= 5)
entries.append((field, clicked))
rowNew= Frame(canvas)
lab = Label(rowNew, width=20, text=field, anchor = 'w')
lab.pack(side=LEFT)
ent = Entry(rowNew)
ent.pack(side=RIGHT, expand = YES, fill=X)
rowNew.pack(side=TOP, fill=X, padx=5, pady= 5)
entries.append((field, ent))
else:
lab = Label(row, width=20, text=field, anchor = 'w')
ent = Entry(row)
row.pack(side=TOP, fill=X, padx=5, pady= 5)
lab.pack(side=LEFT)
ent.pack(side=RIGHT, expand = YES, fill=X)
entries.append((field, ent))
#return entries
def addEntryField():
next_row = int(len(entries)/4)+1
numberCheck =0
for field in fields:
row = Frame(canvas)
if (numberCheck==0):
nextES = Label(row, text= ('Data Port ' +str(next_row)), anchor = 'center', fg = "blue")
nextES.pack()
DPtype = Label(row, text = 'Type', width=19, anchor = 'nw')
DPtype.pack(side= LEFT)
clicked= StringVar()
clicked.set(options[0])
drop = OptionMenu(row, clicked, *options)
drop.pack(side = LEFT, padx=5, pady= 5)
numberCheck=1
row.pack(side=TOP, fill=X, padx=5, pady= 5)
entries.append((field, clicked))
rowNew= Frame(canvas)
lab = Label(rowNew, width=20, text=field, anchor = 'w')
lab.pack(side=LEFT)
ent = Entry(rowNew)
ent.pack(side=RIGHT, expand = YES, fill=X)
rowNew.pack(side=TOP, fill=X, padx=5, pady= 5)
entries.append((field, ent))
else:
lab = Label(row, width=20, text=field, anchor = 'w')
ent = Entry(row)
row.pack(side=TOP, fill=X, padx=5, pady= 5)
lab.pack(side=LEFT)
ent.pack(side=RIGHT, expand = YES, fill=X)
entries.append((field, ent))
#return entries
if __name__=='__main__':
b1 = Button(root, text = 'Generate XML File', command=root.quit, anchor = 'center')
b1.pack(side= BOTTOM, padx=5, pady=5)
b2 = Button(root, text = 'Run TTE-Build', command=root.quit, anchor = 'center')
b2.pack(side= BOTTOM, padx=5, pady=5)
b3 = Button(root, text = 'Run TTE-Plan', command=root.quit, anchor = 'center')
b3.pack(side= BOTTOM, padx=5, pady=5)
addEntryButton = Button(root, text= 'Add Data Port', fg = "Blue", command = addEntryField)
addEntryButton.pack(side= BOTTOM, anchor = 'n', padx=5, pady= 5)
ents = makeform(root, fields)#the creation of the window happens here
root.bind('<Return>', (lambda event, e=ents: fetch(e)))#after hitting 'Enter' this will collect the entered data from the user
canvas.create_window(0, 0, anchor= 'nw', window=frame)
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox("all"), yscrollcommand=scroll_y.set)
canvas.pack(fill='both', expand=True, side='left')
scroll_y.pack(fill='y', side='right')
root.mainloop()
def fetch(entries):#this will print what what the entry fields are named plus what is written down in the entry fields (regardless of the # of fields)
for entry in entries:
field = entry[0]
text = entry[1].get()#collect the entry for the fields
print('%s: "%s"' % (field, text))
def Message1():
XMLdone = Label(root, text= "XML File Generated!!")
XMLdone.pack()
def Message2():
XMLdone = Label(root, text= "TTE-Plan has been completed!!")
XMLdone.pack()
def Message3():
XMLdone = Label(root, text= "TTE-Build has been completed!!")
XMLdone.pack()
#Note: The Payload Size must always be greater than or equal to 18 bytes less than the corresponding VL.
There are several problems with the code. The first is that the frame you're adding to the canvas isn't a child of the canvas, but it needs to be. You should also give this window a more descriptive name than just frame.
So, create the frame like this:
canvas = Canvas(root, height = 1000)
scroll_y = Scrollbar(root, orient="vertical", command=canvas.yview)
inner_frame = Frame(canvas)
canvas.create_window(0, 0, anchor= 'nw', window=inner_frame)
The second problem is that you're not updating the scrollregion of the canvas with the inner frame changes. This is typically done by adding a binding to the <Configure> event of the inner frame:
inner_frame.bind("<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all")))
The third problem is that you're adding rows to the canvas rather than to the frame. You need to make your rows children of this inner frame.
for field in fields:
row = Frame(inner_frame)
...
rowNew= Frame(inner_frame)
...

GUI plot refreshing incorrectly on wxpython panel

Ok, so I've been tasked with creating a VERY simple GUI at work (I'm an intern). The task is to eventually connect to a machine and process real data, but right now I'm working on randomly generated sine data with noise. I've chose to work in Python 3.0, and use wxpython to create my GUI components.
As I want everything to appear on the same window, I'm using panels (hence wx.lib.plot.PlotCanvas rather than something like matplotlib.pyplot)
The problem that I have is that over time, the plot seems to 'expand' off of the panel. This is temporarily solved when I manually resize the window, but resumes again immediately after (you need to run the code to see what I mean).
Expansion over time in panel
Another problem (that has bugged me since I have started writing the code) is that sometimes when I resize the window (manually) or minimize it and then maximize it again, the timer randomly starts and stops.
I have tried all sorts of things (changing padding in sizers, extra arguments, changing time between refreshes GetBestSize()) but I believe that I simply don't understand wxpython well enough to identify where the problem is
I would really appreciate any help you can shed on either of these problems (I don't know, they might even be linked to each other).
FYI: I am not an experienced coder, and my code is not finished (I have more functions to code, but I feel like I should resolve this first). I have constructed this code by looking at different techniques from various tutorials and websites like stackoverflow, so I know it's not formatted well and could definitely be made more efficient. Also, I have removed some parts just to be safe about confidentiality - nothing important, just strings in messages.
PS: If you do have an easier way to do the whole plot/update thing that doesn't have this problem (preferably still in wx) I would be thrilled to hear that as well
And here's my code:
EDIT: Solved the expanding problem by using self.p2.SetSize((W+0,L+0)) instead of (self.p2.GetBestSize())
EDIT: Made transitions much smoother by just regenerating data and redrawing it on existing canvas in the evt_timer function (instead of recreating the whole canvas, which gave a blink-y appearance if you know what I mean)
import wx
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
import wx.lib.plot as plot
import time
import os
wildcard = "Text File (*.txt)|*.txt|"\
"Picture (*.png)|*.png|"\
"All files (*.*)|*.*"#This wildcard shows the options for file endings in the "SAVE" tab - see OnSave(self,event)
wildcard2 = "Picture (*.png)|*.png|"\
"Text File (*.txt)|*.txt|"\
"All files (*.*)|*.*"
class PlotCanvas(plot.PlotCanvas):
def __init__(self,parent,id,size,accepted):
"""
This randomly generates sine data (with noise) and plots it to a panel.
Incorporated as a separate class instead of instatiating it as a plot.PlotCanvas object
to overcome an issue of the size of the plot in the panel.
"""
plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_SUNKEN,size = size)
N = 100 # number of data points
self.t = np.linspace(0, 4*np.pi, N)
f = 1.15247 # Optional!! Advised not to use
self.data = 3.0*np.sin(f*self.t+0.001) + 0.5 + np.random.randn(N) # create artificial data with noise
guess_mean = np.mean(self.data)
guess_phase = 0
guess_freq = 1
guess_amp = 1
optimize_func = lambda x: x[0]*np.sin(x[1]*self.t+x[2]) + x[3] - self.data
est_amp, est_freq, est_phase, est_mean = leastsq(optimize_func, [guess_amp, guess_freq, guess_phase, guess_mean])[0]
fine_t = np.arange(0,max(self.t),0.1)
data_fit=est_amp*np.sin(est_freq*fine_t+est_phase)+est_mean
multiplier = 1
dataset1 = [(x,[d for d in self.data][[td for td in self.t].index(x)])for x in [td for td in self.t]]
fitdata1 = [(x,[df for df in data_fit][[tf for tf in fine_t].index(x)]) for x in [tf for tf in fine_t]]
dataset =[(x,y*multiplier) for (x,y) in dataset1]
fitdata = [(x,y*multiplier) for (x,y) in fitdata1]
self.data = dataset
self.data2 = fitdata
line = plot.PolyLine(self.data,legend = 'random',colour = 'light blue', width =2)
line2 = plot.PolyLine(self.data2,legend = 'sineline',colour ='black',width =2)
a = []
if "D" in accepted:
a.append(line)
if "S" in accepted:
a.append(line2)
if "G" in accepted:
pass
if "L" in accepted:
pass
gc = plot.PlotGraphics(a,'Line Graph','X','Y')
xmin = self.t[0]-0.01*(self.t[-1]-self.t[0])
xmax = self.t[-1]+0.01*(self.t[-1]-self.t[0])
self.Draw(gc,xAxis=(xmin,xmax),yAxis=(min([x[1] for x in dataset])-0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset])),
max([x[1] for x in dataset])+0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset]))))
#self.showLegend = True
#self.enableZoom = True
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
class Frame(wx.Frame):
"""
This is the main class. In it, we declare the separate panels, canvas, menubar, buttons and sizers.
"""
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
self.CurrentDirectory = os.getcwd()
self.timer=wx.Timer(self)#Instantiating the timer
self.count=0
self.Bind(wx.EVT_TIMER,self.evt_timer)#Binding it to itself so that it is always triggered
self.Bind(wx.EVT_PAINT,self.paint)
menubar = wx.MenuBar()
fileMenu = wx.Menu() #Creating the Menubar at the top
#Creating 3 menus: fileMenu,fit,and help
save = wx.Menu()
z = wx.MenuItem(save,wx.ID_ANY,'Save Raw Data\tCtrl+D')
self.Bind(wx.EVT_MENU,self.OnSave,z)
save.Append(z)
z= wx.MenuItem(save,wx.ID_ANY,'Save Image\tCtrl+I')
self.Bind(wx.EVT_MENU,self.OnSaveImage,z)
save.Append(z)
fileMenu.AppendSubMenu(save,'&Save')
fileMenu.AppendSeparator()
z = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W')
self.Bind(wx.EVT_MENU, self.OnQuit, z)
fileMenu.Append(z)
fit = wx.Menu()#Making a check menu
self.gaussian = fit.Append(wx.ID_ANY,'Gaussian',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleGaussian,self.gaussian)
fit.Check(self.gaussian.GetId(),False)
self.sine = fit.Append(wx.ID_ANY,'Sine',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleSine,self.sine)
fit.Check(self.sine.GetId(),False)
self.linear = fit.Append(wx.ID_ANY,'Linear',kind=wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleLinear,self.linear)
fit.Check(self.linear.GetId(),False)
help = wx.Menu()
z = wx.MenuItem(help,wx.ID_ANY,'&About\tCtrl+H')
self.Bind(wx.EVT_MENU,self.OnHelp,z)
help.Append(z)
menubar.Append(fileMenu, '&File')
menubar.Append(fit, '&Fit')
menubar.Append(help, '&Help')#adding menus to menubar
self.SetMenuBar(menubar)#formatting the frame with menubar
self.sp = wx.SplitterWindow(self)#Splitting the window into 2 panels
self.p1 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For buttons and user events
self.p2 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For display of the plot
self.sp.SplitVertically(self.p1,self.p2,300)
sizer = wx.GridBagSizer(3, 3)#Versatile sizer for layout of first panel self.p1
bitmappath = self.CurrentDirectory + "\\BITMAPS"
bmp = wx.Bitmap(bitmappath+"\\SAVE.BMP",wx.BITMAP_TYPE_BMP)
self.saveBtn = wx.BitmapButton(self.p1,wx.ID_ANY,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))
self.Bind(wx.EVT_BUTTON,self.OnSave,self.saveBtn)
sizer.Add(self.saveBtn, (0, 0), wx.DefaultSpan, wx.ALL,5)
bmp = wx.Bitmap(bitmappath +"\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn = wx.BitmapButton(self.p1,-1,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))# A button that starts and stops the plotting
self.startBtn.startval = "START"
self.Bind(wx.EVT_BUTTON,self.paint,self.startBtn)
sizer.Add(self.startBtn, (0, 1), wx.DefaultSpan,wx.ALL,5)
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),["D"])
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p1.SetSizerAndFit(sizer)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(W,L)
self.Maximize(True)
self.Centre()
self.Show()
############### event methods ###########
def paint(self,event):
"""
Updates the canvas based on the value of the startbtn(not the image). Bound to self.timer.
"""
bitmappath = self.CurrentDirectory + "\\BITMAPS"
if self.startBtn.startval == "START":
self.timer.Start(1)# increase the value for more time
bmp = wx.Bitmap(bitmappath + "\\STOP.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "STOP"
elif self.startBtn.startval == "STOP":
self.timer.Stop()
bmp = wx.Bitmap(bitmappath+ "\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "START"
def evt_timer(self,event):
self.count +=1
if self.count== 10:# By increasing count (or the number in self.timer.Start()) you can increase the interval between updates
#self.p2.canvas.Clear()
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
a = ["D"]
if self.sine.IsChecked():
a.append("S")
elif self.linear.IsChecked():
a.append("L")
elif self.gaussian.IsChecked():
a.append("G")
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),a)
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(self.p2.GetBestSize())
self.count=0 # reset the count
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
def OnSave(self,event):#Triggered by menubar and button
try:
rawdata = self.p2.canvas.data
raw_X =[x[0] for x in rawdata]
raw_Y =[x[1] for x in rawdata]
dlg = wx.FileDialog(#Code for this from http://www.blog.pythonlibrary.org
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
f = open(path+".txt","w+")
for i in range(len(raw_X)):
f.write(str(raw_X[i])+"\t"+str(raw_Y[i])+"\n")
f.close()
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:#Catch error when user closes save window without selecting any directory or filename
pass
def OnSaveImage(self,event):
try:
rawdata = self.p2.canvas.data
raw_X = [x[0] for x in rawdata]
raw_Y = [x[1] for x in rawdata]
dlg = wx.FileDialog(
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard2, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
fig1 = plt.figure()
plt.plot(raw_X,raw_Y)
plt.title("Raw Data")
fig1.savefig(path+".png")
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:
pass
def OnMultiply(self,e):
try:
factor = self.x.GetValue()
factor = float(factor)
self.IntegrationTime = factor
except ValueError as e:
self.Dialog(None,str(e),"W")
def OnQuit(self, e):
self.Close()
def OnHelp(self,e):
self.Dialog(None,"N/A","I")
def ToggleSine(self,e):
pass
def ToggleLinear(self,e):
self.Dialog(None,"Not added yet","W")
def ToggleGaussian(self,e):
self.Dialog(None,"Not added yet","W")
if __name__ =="__main__":
app=wx.App()
Frame(None,-1,"N/A")
app.MainLoop()

.get() not able to get results of checkboxes

See the following snippet of code:
def choose_ID():
import ttk
global single_ID
id = BooleanVar()
toplevel = Toplevel()
label1 = Label(toplevel, text = "Choose a User ID.", width = 40).pack(anchor=W, pady=5)
for items in range(len(single_ID)):
id = Checkbutton(toplevel, text=single_ID[items], variable=single_ID[items])
id.pack(anchor=W, padx=5)
single_run_but = Button(toplevel, text = "Run", width=10, height=1, command=run_command).pack(anchor=S, pady=5)
id.get()
Its purpose is to open a popup window with a number of checkboxes (the number could be anything from 1 to 100) containing user id's that have been passed to the function from earlier in the script.
The problem is with the id.get() line at the end. When it runs it errors, saying "Checkbutton instance has no attribute 'get'"
What do I need to change to be able to note which of the checkboxes have been checked (it could be only one or multiple boxes)?
Many thanks,
Chris.
The last time you set id is in the for loop. During this, you set id to be a checkbutton, which does not have the get() method.
What you want to do is use the get() method on an IntVar that is associated with the checkbutton through the variable attribute. You can keep references to these variables in a list. I've made a small example of how to dynamically create checkbuttons and still be able to get their values.
from Tkinter import *
def run_command():
selected_ids = []
for i, id_var in enumerate(id_var_list):
if id_var.get():
selected_ids.append(id_list[i])
print selected_ids
root = Tk()
Label(root, text = "Choose a User ID.", width = 40).pack(anchor=W, pady=5)
id_list = ['ID1', 'ID2', 'ID100']
id_checkbutton_list = []
id_var_list = []
for item in id_list:
id_var = IntVar()
id_checkbutton = Checkbutton(root, text=item, variable=id_var)
id_checkbutton.pack(anchor=W, padx=5)
id_var_list.append(id_var)
id_checkbutton_list.append(id_checkbutton)
Button(root, text = "Run", width=10, height=1, command=run_command).pack(anchor=S, pady=5)
root.mainloop()

wxPython: binding a dynamically created spinner to an event

I used the code below to create several spinners with a for loop.
Now, I can't figure out how to bind an event so that I know which spinner is being modified so that I can put the spinner value into the right variable.
If I can figure out which spinner is calling the handler I could map it to the correct variable.
Any thoughts? Is this even possible?
Thanks in advance!
import wx
class spinnerFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent,id, "Spinner Frame", size = (300,200))
#constants
spnr_sz = (50,-1)
names = ('name1','name2','name3','name4','name5','name6')
sizer = wx.GridBagSizer(5, 5)
# TEXT FONT EXAMPLE
# m_text = wx.StaticText(panel, -1, "Hello World!")
# m_text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
# m_text.SetSize(m_text.GetBestSize())
#temp
sizer = wx.GridBagSizer(5, 5)
row = 0
for n in names:
row += 1
my_label = wx.StaticText(self, -1, n)
spinner = wx.SpinCtrl(self, -1, size = spnr_sz, min = 0, initial = 10 )
self.Bind(wx.EVT_SPINCTRL, self.OnCompute, spinner)
sizer.Add(my_label, (row,0))
sizer.Add(spinner, (row,1))
sizer.AddGrowableRow(7)
sizer.AddGrowableCol(4)
self.SetSizerAndFit(sizer)
self.Centre()
def OnCompute(self,event):
# a = spinner.GetValue()
# ????
if __name__=='__main__':
app = wx.App(True) # was False
frame = wx.Frame(None)
frame = spinnerFrame (parent=None, id = -1)
frame.Show()
app.MainLoop()
Yes, this is possible. The easiest way I know is to associate your unique name with each call to Bind, either by giving the spinner a name or using partial functions (or lambdas, but lambdas can end up being messy), and then check for the name in the handler. Examples of how to use the name are given in this previous SO question.

Resources