This question already has answers here:
How to pass arguments to a Button command in Tkinter?
(18 answers)
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 4 years ago.
Im trying to get this Listbox in tkinter to update from a sqlite3 database when a button is clicked. I actually had it working using root.after(1000, function) method but the cursor kept resetting every time it was updated.
At the moment I can't get the listbox to populate at the button click.
I would love any help. Thanks!
root=Tk()
ListBox1 = Listbox(root)
ListBox1.grid(row=0, column=0 , sticky='nsew')
def ListUpdate():
listlist = []
#populates list from sql database in a for loop
return listlist
def ListPopulate(listlist):
ListBox1.delete(0, END)
for i in range(0,len(listlist[i]):
ListBox1.insert(END, listlist[i])
ButtonUpdate = Button(root, text='Update', command=ListPopulate(listlist))
ButtonUpdate.grid(row=5, column=6, sticky='nsew')
You're using command wrong. Replace:
Button(..., command=ListPopulate(listlist))
with:
Button(..., command=lambda l=ListUpdate(): ListPopulate(l))
Your command= argument for your Button calls ListPopulate(listlist) when the button is created and assigns its result (which is None, since you don't return anything from that function) as the command. You have therefore told the Button object that it has no command.
You're naturally going to run into problems passing the list to all the places it needs to go in an event-driven system. One approach is to make the list ListList a global variable. Define it outside all your functions at the top of the script, with your other variables:
ListBox1 = Listbox(root)
ListBox1.grid(row=0, column=0 , sticky='nsew')
listlist = []
Next, change the first line of ListUpdate() to use a slice assignment to empty out the existing list object, so you don't need to declare the object global in that function.
def ListUpdate():
listlist[:] = []
Then change ListPopulate() to take no arguments, since it's going to use the global listlist.
def ListPopulate():
Finally, write your Button() constructor to pass ListPopulate rather than calling it.
ButtonUpdate = Button(root, text='Update', command=ListPopulate)
Globals are generally bad practice. What you probably should do is subclass Listbox and give it an attribute that holds the list. Then your two functions would be methods of that class. (In fact, you probably want to update the displayed list on screen every time you change its contents... so you should have one method, not two.) I'll leave that as an exercise for when you learn object-oriented programming.
Related
This question already has an answer here:
Tkinter assign button command in a for loop with lambda [duplicate]
(1 answer)
Closed 7 months ago.
I used for loop to render 15 buttons, and each button invokes the same function to do something. My question is how to determine which button is clicked?
My code snippet is like below:
for number in range(1, 16):
ttk.Button(bottom_frame, text='Read', command=read_one).grid(column=4, row=number, padx=5, pady=5)
I want to reuse the function read_one() for every button, but don't know how to determine which button is clicked. Any comment is appreciated!
Here's my test code: https://pastebin.com/fWyyNVw7
Since the command callback doesn't get passed any parameters by default (like the calling control), there's no easy option.
However, you could use something like this:
for number in range(1, 16):
ttk.Button(bottom_frame, text='Read', command=lambda number=number: read_one(number)
).grid(column=4, row=number, padx=5, pady=5)
That way, read_one will be called with the number passed. Note that you'd need your read_one function to deal with it, e.g.:
def read_one(number):
# do something with number, since it tells you what button was pushed
...
When I run the following code, I receive the following error: NameError: name 'barE' is not defined.
Does Tkinter's Entry work from one function to another? I have a global window and frame in this program and it passes the user's input without error using the same syntax as below.
def newSearchForm():
window2=Tk()
window2.geometry("650x400")
frame = Frame(window2)
frame.pack(side="top", expand=True, fill="both")
barcode=Label(frame,text="Please Enter The Barcode", font='time 15')
barcode.place(x=10,y=90)
barE=Entry(frame)
barE.place(x=250,y=90)
isbn=Label(frame,text="Please Enter The ISBN", font='time 15')
isbn.place(x=10,y=130)
isbE=Entry(frame)
isbE.place(x=250,y=130)
repeatSearchButton=Button(frame,text="Enter", command=newSearch,width=12,bg='gray')
repeatSearchButton.place(x=150,y=170)
window.mainloop()
def newSearch():
uB = barE.get()
uI = isbE.get()
carlSearch(uB, uI)
itemTitle=workspace.find_element_by_xpath('//*[#id="mainContent"]/div[2]/div[2]/div[2]/div[1]/div[1]/div').text
ingramSearch()
fantasticFictionSearch(itemTitle)
outputMetadata()
I tried using the lambda command to explicitly pass the variables and that didn't work.
I tried using anexising window and that didn't work, so I destroyed it and created a new one.
I would suggest you to use text variable. You can create a text variable by StringVar and assign it in the entry widget.
For more details on why to use text variable see Should I use Entry's .get() or its textvariable's for Tkinter in Python?
Usage:
barE_var=StringVar()
barE=Entry(frame,textvariable=barE_var)
barE.place(x=250,y=90)
To get value use barE_var.get().
Does Tkinter's Entry work from one function to another?
It does work, but have to use global keyword to access them.
def newSearch():
global barE_var
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
How to pass arguments to a Button command in Tkinter?
(18 answers)
Closed 6 months ago.
how to change values in buttons done in a loop?
I mean a different text, a command for each of the resulting buttons.
the program counts files in a given folder, then this value is passed to the loop that displays the appropriate number of buttons.
here is the sample text:
files = os.listdir(".")
files_len = len(files) - 1
print(files_len)
def add():
return
for i in range(files_len):
b = Button(win, text="", command=add).grid()
You don't really need to change the Button's text, because you can just put the filename in when they're created (since you can determine this while doing so).
To change other aspects of an existing Button widget, like its command callback function, you can use the universal config() method on one after it's created. See Basic Widget Methods.
With code below, doing something like that would require a call similar to this: buttons[i].config(command=new_callback).
To avoid having to change the callback function after a Button is created, you might be able to define its callback function inside the same loop that creates the widget itself.
However it's also possible to define a single generic callback function outside the creation loop that's passed an argument that tells it which Button is causing it to be called — which is what the code below (now) does.
from pathlib import Path
import tkinter as tk
win = tk.Tk()
dirpath = Path("./datafiles") # Change as needed.
buttons = []
def callback(index):
text = buttons[index].cget("text")
print(f"buttons[{index}] with text {text!r} clicked")
for entry in dirpath.iterdir():
if entry.is_file():
button = tk.Button(win, text=entry.stem,
command=lambda index=len(buttons): callback(index))
button.grid()
buttons.append(button)
win.mainloop()
Try this. By making a list.
files = os.listdir(".")
files_len = len(files) - 1
print(files_len)
def add():
return
b=[]
for i in range(files_len):
b[i] = Button(win, text="", command=add).grid(row=i)
This question already has answers here:
Why is my Tk button being pressed automatically?
(2 answers)
Closed 8 years ago.
I'm using Python and TkInter to build a GUI where a certain command is called when a button is pressed. The relevant code looks like this:
class Main(Frame):
def __init__(self, parent):
self.generate_noise_button = Button(root, text="Add noise and save", command=self.generate_noise())
...
def generate_noise(self):
header.generate_noise()
If I place a breakpoint in the generate_noise() method and run the program, the breakpoint is instantly hit despite never being called or the button being pressed. Furthermore whenever I actually do press the button the method is never called making the button completely useless. Why?
self.generate_noise_button = Button(root, text="Add noise and save", command=self.generate_noise())
When you provide a command argument, you're not supposed to add parentheses to the end.
self.generate_noise_button = Button(root, text="Add noise and save", command=self.generate_noise)
You want the command to refer to the method object, not the value that the method returns after it is called.
I have a number of test files in a single directory. I'm trying to write a GUI to allow me to select and run one of them.
So, I have a loop that scans the directory and creates buttons:
for fnm in glob.glob ('Run*.py'):
tstName = fnm[3:-3] # Discard fixed part of filename
btn = Button (self, text=tstName,
command=lambda: self.test(tstName))
btn.grid (row=rowNum, column=0, pady=2)
rowNum += 1
This creates my GUI correctly, with buttons labelled say, A and B but when I press on the button labelled A it passes B to the test method.
I've looked around and found this question How can I pass arguments to Tkinter button's callback command? but the answer doesn't go on to use the same variable name, with a different value, to configure another widget. (In fact, by tying the variable name to the widget name it almost implies that the technique won't work in this case, as I've found.)
I'm very new to Python, but am quite familiar with creating this kind of GUI using Tcl/TK and I recognise this problem - the value of tstName is being passed when I press the button, but I want it to pass the value the variable had when I created it. I know how I'd fix that in Tcl/Tk - I'd define a command string using [list] at creation time which would capture the value of the variable.
How do I do the same in Python?
You need to bind the current value of tstName at the time you define the button. The way you're doing it, the value of tstName will whatever it is at the time you press the button.
To bind the value at the time that you create the button, use the value of tstName as the default value of a keyword parameter to the lambda, like so:
btn = Button(..., command=lambda t=tstName: self.test(t))