tkinter buttons through a loop, how do i identify which was clicked? - python-3.x

I created a loop to create a button for each element in a list. How do I identify which button is clicked and then assign a command to each one, which will create a new page with the same name as the button clicked?.
yvalue = 200
for i in sdecks:
deckbutton1=Button(fcpage,text=i,width=21,bd=2,fg="ghost white",bg="orchid1")
deckbutton1.place(x=105, y=yvalue)
yvalue = yvalue + 20

Either I don't get you question or this (adapted from here) should answer your question:
from functools import partial
import tkinter as tk
def create_new_page(name_of_page):
print("Creating new page with name=\"%s\"" % name_of_page)
root = tk.Tk()
for i in range(5):
# Create the command using partial
command = partial(create_new_page, i)
button = tk.Button(root, text="Button number"+str(i+1), command=command)
button.pack()
root.mainloop()

Related

Tkinter create multiple buttons in a loop and change text of clicked ones

I try to access/change button attributes for buttons created in a loop.
My idea was to collect the buttons in a list so that i can access every single button. Is there a better way?
At the moment i try to use the buttons-command to change the text of the clicked button. In the "action"-function i get the error-code "list index out of range" when i try to run the code!?
Since i am a newbie in python and tkinter hours over hours passed so far to find a solution without success.
Every idea would be very appreciated.
I used quite the same code without creating a list. The code was working but when i clicked a button only the last created button changed the text. Could it be that somehow i have to use the "StringVar"-function or "textvariable"?
import tkinter as tk
window = tk.Tk()
window.geometry("300x150")
window.title("Tic Tac Toe")
def action(i):
btns[i].configure(text = 'X')
btn_nr = -1
btns = []
for x in range(1,4):
for y in range(1,4):
btn_nr += 1
print(btn_nr)
btns.append(tk.Button(text='-', command = action(int(btn_nr))))
btns[int(btn_nr)].grid(row=x, column=y)
exit_button = tk.Button(text='Exit Game', command=window.destroy)
exit_button.grid(row=4, column=1, columnspan=15)
window.mainloop()
You were almost there. See below matter being resolved as you need a lambda to pass the btn_nr to the function action. By the way there is no need for int()
import tkinter as tk
window = tk.Tk()
window.geometry("300x150")
window.title("Tic Tac Toe")
def action(button):
if btns[button].cget('text') == '-':
btns[button].configure(text='X')
else:
btns[button].configure(text='-')
btn_nr = -1
btns = []
for x in range(1, 4):
for y in range(1, 4):
btn_nr += 1
print(btn_nr)
btns.append(tk.Button(text='-', command=lambda x=btn_nr: action(x)))
btns[btn_nr].grid(row=x, column=y)
exit_button = tk.Button(text='Exit Game', command=window.destroy)
exit_button.grid(row=4, column=1, columnspan=15)
window.mainloop()
I changed the action function a little to make it toggle between 'X' and '-'.

Changing or removing command linked to a button after clicking

Playing around with Tkinter and ran into some trouble.
I have 3 buttons:
Button1 will open my specified website using Selenium.
Button3 will carry out my function on the website.
Button2 will close the program.
Problem: I want to keep button1 from opening multiple windows/websites by
either removing/hiding it after it has been clicked or changing the
command
that will be performed by it.
After some googling I found some options and tried the following:
| pack_forget()
| pack_destroy()
| btn1.destroy()
All of the above does not seem to be doing anything. What am I missing?
import selenium
from selenium import webdriver
from time import sleep
import time
import tkinter
from tkinter import *
main = Tk()
def Primary():
txt = mtxt.get()
New = Label(main, text=""+txt, fg='Blue').pack(side=BOTTOM)
btn1 = Button(Bot, text='OK', command=Primary,
fg='Green').pack_destroy() #For some reason this does not work
return
def Secondary():
global main
main.destroy()
def Tres():
txt = mtxt.get()
New = Label(main, text=""+txt, fg='Blue').pack(side=BOTTOM)
return
main.geometry('350x400+500+300')
main.title('Title')
mtxt = StringVar()
Top = Frame().pack()
Bot = Frame().pack(side=BOTTOM)
Lbl = Label(main, text='TEXT').pack()
Lbl2 = Label(main, text='TEXT').pack()
Enter = Entry(main, textvariable=mtxt).pack()
btn1 = Button(Bot, text='OK', command=Primary, fg='Green').pack()
btn2 = Button(Bot, text='Cancel', command=Secondary,
fg='Red').pack(side=BOTTOM)
btn3 = Button(Bot, text='Next', command=Tres, fg='Green').pack()
main.mainloop()
Expected result:
On initial click btn1 performs 'command=Primary'
After initial click btn1 performs 'command=Tres'
or
after initial click btn1 is destroyed/hidden and btn3 takes it's place.

How to store button name, from a loop, when clicked on?

Trying to store the name of the buttons that are clicked on, in a list (orderList). These buttons were also generated from a list (menuList) by a FOR loop. Whenever any button is clicked, it stores the last value only.
menuList = ['egg', 'bacon', 'bread']
orderList = []
def makeOrder(arg):
orderList.append(arg)
print (orderList)
for btn in mealList:
ttk.Button(mainframe, text=btn, command=lambda: makeOrder(btn)).grid(column=1, row=5)
I just want to get each button to store it's name inside the blank list (orderList)
This is due to late binding of python which you can read more about it here. In short, you can work it out by modifying your lambda function:
from tkinter import ttk
import tkinter as tk
menuList = ['egg', 'bacon', 'bread']
mealList = ["breakfast","lunch","dinner"]
orderList = []
root = tk.Tk()
mainframe = tk.Frame(root)
mainframe.pack()
for num,btn in enumerate(mealList):
ttk.Button(mainframe, text=btn,command=lambda x=btn: orderList.append(x)).grid(column=num, row=5)
tk.Button(mainframe,text="result",command=lambda: print (orderList)).grid(row=6,column=0)
root.mainloop()

TKinter auto updating label from urllib

I'm trying make auto updating label from url.
I want to make something like pager. When file on server is changed, label should changes too. with button I can download it manually but I want to automate it. Where I'm making mistake?
from tkinter import *
import urllib.request
import time
root = Tk()
check = ""
#functions
def auto():
time.sleep(5) #becouse I don't want kill server
page = "http://howan.pl/pychal/plik.txt"
g = urllib.request.urlopen(page)
data = g.read()
g.close()
return (str(data, encoding='utf-8'))
def click():
page = "http://howan.pl/pychal/plik.txt"
g = urllib.request.urlopen(page)
data = g.read()
g.close()
label.config(text=str(data, encoding='utf-8'))
#Widgets
label = Label(root, text="zer0")
button = Button(root, text="hey", command= click)
if auto() == check:
check = auto
label.config(text=check)
print(auto())
label.pack()
button.pack()
root.mainloop()
To automate it you need to make a function that does the work, and then use root.after() to call that function on a regular basis. Since you have all the work in "click" already, you could just add:
def auto_click():
click()
root.after(5000, auto_click) # call this function again in 5,000 ms (5 seconds)
auto_click() # start the autoclick loop.

Tkinter - Changing label text via another function

I know that it's often best practice to write Tkinter GUI code using object-oriented programming (OOP), but I'm trying to keep things simple because I'm new to Python.
I have written the following code to create a simple GUI:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
def ChangeLabelText():
MyLabel.config(text = 'You pressed the button!')
def main():
Root = Tk()
MyLabel = ttk.Label(Root, text = 'The button has not been pressed.')
MyLabel.pack()
MyButton = ttk.Button(Root, text = 'Press Me', command = ChangeLabelText)
MyButton.pack()
Root.mainloop()
if __name__ == "__main__": main()
The GUI looks like this.
I thought the text in the GUI (MyLabel) would change to "You pressed the button!" when the button is clicked, but I get the following error when I click the button:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\elsey\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:/Users/elsey/Documents/question code.py", line 6, in ChangeLabelText
MyLabel.config(text = 'You pressed the button!')
NameError: name 'MyLabel' is not defined
What am I doing wrong? Any guidance would be appreciated.
MyLabel is local to main() so the way you can not access it that way from ChangeLabelText().
If you do not want to change the design of your program, then you will need to change the definition of ChangeLabelText() like what follows:
def ChangeLabelText(m):
m.config(text = 'You pressed the button!')
And withing main() you will need to pass MyLabel as an argument to ChangeLabelText().
But again, you will have a problem if you code this command = ChangeLabelText(MyLabel) when you declare and define MyButton because the program will execute directly the body of ChangeLabelText() at the start and you will not have the desired result.
To resolve this later problem, you will have to use (and may be read about) lambda
Full program
So your program becomes:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
def ChangeLabelText(m):
m.config(text = 'You pressed the button!')
def main():
Root = Tk()
MyLabel = ttk.Label(Root, text = 'The button has not been pressed.')
MyLabel.pack()
MyButton = ttk.Button(Root, text = 'Press Me', command = lambda: ChangeLabelText(MyLabel))
MyButton.pack()
Root.mainloop()
if __name__ == "__main__":
main()
Demo
Before clicking:
After clicking:
Are your sure you don't want to do it as a class (i think it makes the code a bit more clean as your project grows)? Here is a way to accomplish what you'e looking for:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
class myWindow:
def __init__(self, master):
self.MyLabel = ttk.Label(root, text = 'The button has not been pressed.')
self.MyLabel.pack()
self.MyButton = ttk.Button(root, text = 'Press Me', command = self.ChangeLabelText)
self.MyButton.pack()
def ChangeLabelText(self, event=None):
self.MyLabel.config(text = 'You pressed the button!')
if __name__ == "__main__":
root = Tk()
mainWindow = myWindow(root)
root.mainloop()
In a Mac, is looks like this before pressing the button:
And when you press it:
But basically, in order to be able to change the text in a Label or a button, you need to ensure it has an active reference. In this case, we are doing it by creating the window as a class and referencing the widgets in the form self. widget_name = widget().
but I'm trying to keep things simple because I'm new to Python Hopefully this helps in understanding that classes are the simple way, otherwise you have to jump through hoops and manually keep track of many variables. Also, the Python Style Guide suggests that CamelCase is used for class names and lower_case_with_underlines for variables and functions. https://www.python.org/dev/peps/pep-0008/
from tkinter import *
from tkinter import ttk
class ChangeLabel():
def __init__(self):
root = Tk()
self.my_label = ttk.Label(root, text = 'The button has not been pressed.')
self.my_label.pack()
## not necessary to keep a reference to this button
## because it is not referenced anywhere else
ttk.Button(root, text = 'Press Me',
command = self.change_label_text).pack()
root.mainloop()
def change_label_text(self):
self.my_label.config(text = 'You pressed the button!')
if __name__ == "__main__":
CL=ChangeLabel()

Resources