how to change values ​in buttons done in a loop Tkinter [duplicate] - python

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)

Related

Tkinter fails to pass Entry data between two functions

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

If I created widgets in one function, how can I access them in another function using Python Tkinter?

this is my first project using Tkinter so please excuse me if the question is very simple to solve.
Depending on what option the user chooses from a dropdown, I call a function that creates and places certain widgets (e.g. an entry) on a frame. Then, when another button is pushed I want to access the text inside this entry. However, this seems to be giving me errors (saying that the widget is undefined) because I want to access a widget which I create when a function is called.
An obvious solution I see is to create all possible widgets I want to use outside the function, and only place them when the function is called. This seems quite sloppy and creates many more issues. Is there another fix?
Thanks in advance!
This is the function where I create and place the widgets on the frame.
def loadBook():
print("book is loaded")
#Authors
labelAuth1 = tk.Label(frame, text="Author 1 Name:")
entryAuth1 = tk.Entry(frame)
labelAuth1.place(relwidth=0.23, relheight=0.08, rely=0.1)
entryAuth1.place(relheight=0.08, relwidth=0.18, relx=0.3, rely=0.1)
This is a snippet of a function which uses input from the entry widget I created above:
def isBook():
if len(entryAuthSur1.get())==0:
pass
else:
bookString = ""
bookString += entryAuthSur1.get()
When the second function executes, I get a runtime error that entryAuthSur1 is not defined.
All variables inside functions are local. That means that it is deleted after the function call ends. As your variable (entryAuth1) wasn't global so it only existed inside the function and was deleted when the loadBook function ended. This is the working code:
import tkinter as tk
# Making a window for the widgets
root = tk.Tk()
def loadBook():
global entryAuth1 # make the entry global so all functions can access it
print("book is loaded")
#Authors
labelAuth1 = tk.Label(root, text="Author 1 Name:")
entryAuth1 = tk.Entry(root)
# I will check if the user presses the enter key to run the second function
entryAuth1.bind("<Return>", lambda e: isBook())
labelAuth1.pack()
entryAuth1.pack()
def isBook():
global entryAuth1 # make the entry global so all functions can access it
# Here you had `entryAuthSur1` but I guess it is the same as `entryAuth1`
if len(entryAuth1.get())==0:
pass
else:
bookString = ""
bookString += entryAuth1.get()
print(bookString) # I am going to print the result to the screen
# Call the first function
loadBook()
root.mainloop()

If I produce a Menubutton with checkbuttons created with a list, how can I get the name of the chosen button? [duplicate]

This question already has answers here:
How to pass arguments to a Button command in Tkinter?
(18 answers)
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 months ago.
Ok yes this question is difficultly worded, here is why:
I want to create a menubutton with checkbuttons, created by a list.
tflist = tf.liste
for tf in tflist:
n1mb.menu.add_checkbutton(label=tf,command=n1change)
n1mb is the menubutton
n1change is a function
def n1change():
n1mb['text'] = tf
What I want to do, is that the menubutton displays the checked button.
The problem with the shown code is, that if I choose a button, the menubutton is labeled with the last item in the list, not the chosen one.
When the checkbuttons are created, you loop through tflist. When this loop is done, tf has the value of the last item in tflist. If you never change tf afterwards, seting n1mb['text'] = tf will always set the text to the last value of tflist.
What you want instead is to "bake-in" the text that you want to set into the command that you set. You can give additional arguments ti a command by using a lambda anonymous function. When you do this in a loop you need to bind the variable in the lambda call. So your command could look like:
command=lambda text=tf: n1change(text)
However, this behavior doesn't really make sense with checkbuttons, since you can enable and disable them all individually. I'm guessing you actually want radiobuttons, for which only one is activa at all times. A complete example would be:
import tkinter as tk
root = tk.Tk()
v = tk.IntVar()
n1mb = tk.Menubutton(root, text="condiments", relief=tk.RAISED)
n1mb.grid()
def n1change(tf):
n1mb['text'] = tf
n1mb.menu = tk.Menu(n1mb, tearoff=0)
n1mb["menu"] = n1mb.menu
tflist = ['a', 'b', 'c']
for tf in tflist:
n1mb.menu.add_radiobutton(label=tf, command=lambda text=tf: n1change(text))
n1mb.pack()
root.mainloop()

Calling a list in another function - Python [duplicate]

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.

Why do i get 2 tkinter windows from the start? [duplicate]

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 5 years ago.
I tried to create a window with a button that creates another window.
m = Tk()
def new(a,b):
r = Tk()
r.geometry(str(a) + "x" + str(b) + "+0+0")
b = Button(m, text="Click", command=new(100,300)).place(x=0,y=0)
m.mainloop()
Instead of getting a window with a button i get two without clicking the button.
The two windows.png
What did i do wrong?
You're calling new as you construct the Button (technically, before you construct the Button since new must finish running so its return value can be passed as the command argument), not passing it as a callback to invoke on click.
You need to pass a (no argument) callable as the command without calling it, e.g. using a lambda to wrap your new call and thereby defer it until the lambda is called:
b = Button(m, text="Click", command=lambda: new(100,300)).place(x=0,y=0)
Inside of your Button call you're called the new function. That function is creating a new instance of Tk. This is why you have two windows opening.
Assuming you want to run the geometry operation on the first Tk instance, just pass the Tk object into your new function.
You can do it like so:
from tkinter import *
m = Tk()
def new(a, b, r):
r.geometry(str(a) + "x" + str(b) + "+0+0")
b = Button(m, text="Click", command=new(100, 300, m)).place(x=0, y=0)
m.mainloop()

Categories

Resources