I'm a beginner at Python and I don't know what to set command to so I can open one of the links in the class list (Sorry if I am calling it the wrong thing. Please include what to call it in your answer.) For example, if I wanted to open Slopes link, what would I type in the command for button_slope?
import webbrowser
from tkinter import *
from tkinter import ttk
root = Tk()
style = ttk.Style()
style.configure("TButton",
font="Serif 15",
padding=10)
class GameLibrary:
def __init__(self, game, link):
self.game = game
self.link = link
games = [
GameLibrary("Slope", "https://www.y8.com/games/slope"),
GameLibrary("Punch Boxing Championship", "https://www.y8.com/games/punch_boxing_championship"),
]
main_frame = Frame(root)
main_frame.pack()
main_frame.grid(row=0, columnspan=4)
button_slope = ttk.Button(main_frame, text='Slope', command='what do i type here').grid(row=1, column=0)
root.mainloop()
command should be set to a callback function that executes when the button is pressed. For instance.
def callback():
print "click!"
button_slope = ttk.Button(main_frame, text='Slope', command=callback)
button_slope.grid(row=1, column=0)
Will print click! when you click the button. You would want to take whatever action is appropriate for your program.
Related
I am a Python beginning self-learner, running on MacOS.
I'm making a program with a text parser GUI in tkinter, where you type a command in a Entry widget, and hit a Button widget, which triggers my parse() funct, ect, printing the results to a Text widget, text-adventure style.
> Circumvent the button
I can't let you do that, Dave.
I'm trying to find a way to get rid of the need to haul the mouse over to the Button every time the user issues a command, but this turned out harder than I thought.
I'm guessing the correct code looks like self.bind('<Return>', self.parse())? But I don't even know where to put it. root, __init__, parse(), and create_widgets() don't want it.
To be clear, the only reason anyone should hit enter in the prog is to trigger parse(), so it doesn't need to be espoused to the Entry widget specifically. Anywhere it works is fine.
In response to 7stud, the basic format:
from tkinter import *
import tkinter.font, random, re
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master, ...)
self.grid()
self.create_widgets()
self.start()
def parse(self):
...
def create_widgets(self):
...
self.submit = Button(self, text= "Submit Command.", command= self.parse, ...)
self.submit.grid(...)
root = Tk()
root.bind('<Return>', self.parse)
app = Application(root)
root.mainloop()
Try running the following program. You just have to be sure your window has the focus when you hit Return--to ensure that it does, first click the button a couple of times until you see some output, then without clicking anywhere else hit Return.
import tkinter as tk
root = tk.Tk()
root.geometry("300x200")
def func(event):
print("You hit return.")
root.bind('<Return>', func)
def onclick():
print("You clicked the button")
button = tk.Button(root, text="click me", command=onclick)
button.pack()
root.mainloop()
Then you just have tweak things a little when making both the button click and hitting Return call the same function--because the command function needs to be a function that takes no arguments, whereas the bind function needs to be a function that takes one argument(the event object):
import tkinter as tk
root = tk.Tk()
root.geometry("300x200")
def func(event):
print("You hit return.")
def onclick(event=None):
print("You clicked the button")
root.bind('<Return>', onclick)
button = tk.Button(root, text="click me", command=onclick)
button.pack()
root.mainloop()
Or, you can just forgo using the button's command argument and instead use bind() to attach the onclick function to the button, which means the function needs to take one argument--just like with Return:
import tkinter as tk
root = tk.Tk()
root.geometry("300x200")
def func(event):
print("You hit return.")
def onclick(event):
print("You clicked the button")
root.bind('<Return>', onclick)
button = tk.Button(root, text="click me")
button.bind('<Button-1>', onclick)
button.pack()
root.mainloop()
Here it is in a class setting:
import tkinter as tk
class Application(tk.Frame):
def __init__(self):
self.root = tk.Tk()
self.root.geometry("300x200")
tk.Frame.__init__(self, self.root)
self.create_widgets()
def create_widgets(self):
self.root.bind('<Return>', self.parse)
self.grid()
self.submit = tk.Button(self, text="Submit")
self.submit.bind('<Button-1>', self.parse)
self.submit.grid()
def parse(self, event):
print("You clicked?")
def start(self):
self.root.mainloop()
Application().start()
Another alternative is to use a lambda:
ent.bind("<Return>", (lambda event: name_of_function()))
Full code:
from tkinter import *
from tkinter.messagebox import showinfo
def reply(name):
showinfo(title="Reply", message = "Hello %s!" % name)
top = Tk()
top.title("Echo")
Label(top, text="Enter your name:").pack(side=TOP)
ent = Entry(top)
ent.bind("<Return>", (lambda event: reply(ent.get())))
ent.pack(side=TOP)
btn = Button(top,text="Submit", command=(lambda: reply(ent.get())))
btn.pack(side=LEFT)
top.mainloop()
As you can see, creating a lambda function with an unused variable "event" solves the problem.
I found one good thing about using bind is that you get to know the trigger event: something like: "You clicked with event = [ButtonPress event state=Mod1 num=1 x=43 y=20]" due to the code below:
self.submit.bind('<Button-1>', self.parse)
def parse(self, trigger_event):
print("You clicked with event = {}".format(trigger_event))
Comparing the following two ways of coding a button click:
btn = Button(root, text="Click me to submit", command=(lambda: reply(ent.get())))
btn = Button(root, text="Click me to submit")
btn.bind('<Button-1>', (lambda event: reply(ent.get(), e=event)))
def reply(name, e = None):
messagebox.showinfo(title="Reply", message = "Hello {0}!\nevent = {1}".format(name, e))
The first one is using the command function which doesn't take an argument, so no event pass-in is possible.
The second one is a bind function which can take an event pass-in and print something like "Hello Charles! event = [ButtonPress event state=Mod1 num=1 x=68 y=12]"
We can left click, middle click or right click a mouse which corresponds to the event number of 1, 2 and 3, respectively. Code:
btn = Button(root, text="Click me to submit")
buttonClicks = ["<Button-1>", "<Button-2>", "<Button-3>"]
for bc in buttonClicks:
btn.bind(bc, lambda e : print("Button clicked with event = {}".format(e.num)))
Output:
Button clicked with event = 1
Button clicked with event = 2
Button clicked with event = 3
This worked for me while assigning buttons to functions.
Create a function to set buttons to other functions:
def enter(event=clr):
but_ch['command'] = clr
Then bind the function to the root and invoke:
root.bind('<Return>', lambda enter: but_ch.invoke())
Hi I am pretty new to tkinter and have being trying to create a button that opens a window then a have a button in the new window the gives a message when pressed. I ran into the problem that the only whay I could get it to recognise the function I wrote was to write it inside the function that opens the second window. I don't know if I have being searching for the wrong things but I can't find how to do this properly. Can someone help me out Here is my code
from tkinter import *
master = Tk()
master.title("frame control")
def win():
window2 = Toplevel()
def open():
stamp = Label(window2, text="Staped").pack()
lab2 = Button(window2,text = "yo ",command = open).pack()
lab1 = Button(master,text = " open a new window" , command = win).pack()
mainloop()
This is your code but with best practises:
import tkinter as tk
def create_stamp():
stamp = tk.Label(window2, text="Stamp")
stamp.pack()
def create_second_win():
global window2
window2 = tk.Toplevel(root)
lab2 = tk.Button(window2, text="Click me", command=create_stamp)
lab2.pack()
root = tk.Tk()
root.title("Frame control")
button = tk.Button(root, text="Open a new window", command=create_second_win)
button.pack()
root.mainloop()
I made window2 a global variable so that I can access it from create_stamp. Generally it is discouraged to use from ... import *. As #Matiiss said, sometimes you can have problems with global variables if you don't keep track of the variable names that you used.
If you want to avoid using global variables and want to use classes, look at this:
import tkinter as tk
class App:
def __init__(self):
self.stamps = []
self.root = tk.Tk()
self.root.title("Frame control")
self.button = tk.Button(self.root, text="Open a new window", command=self.create_second_win)
self.button.pack()
def create_stamp(self):
stamp = tk.Label(self.window2, text="Stamp")
stamp.pack()
self.stamps.append(stamp)
def create_second_win(self):
self.window2 = tk.Toplevel(self.root)
self.lab2 = tk.Button(self.window2, text="Click me", command=self.create_stamp)
self.lab2.pack()
def mainloop(self):
self.root.mainloop()
if __name__ == "__main__":
app = App()
app.mainloop()
As #Matiiss mentioned it would be more organised if you move the second window to its own class. For bigger projects it is a must but in this case you don't have to.
I am currently working on a little just for fun project, which pretends it´s generating something and then shows a specific message, but I have a question: As soon as I press the button on the screen it is showing a progressbar, that is what I want it to do, but if I press the button again it just shows the same thing again and again, is there any way to prevent the program from printing the Starting the generate text and the progressbar multiple times?
Here´s the code:
# my little import area
import tkinter as tk
from tkinter import ttk
# Initialization
win = tk.Tk()
win.title("StackOverflow")
# Window Size
win.resizable(False, False)
win.minsize(750,500)
# Button clicked command
def buttonclicked():
tk.Label(win, text="Starting to generate...").pack()
pb.pack()
pb.start(500)
#Widgets
headerlabel = tk.Label(win, text="StackOverFlow Question")
generatebutton = tk.Button(win, text="Generate", command=buttonclicked)
pb = ttk.Progressbar(win, orient="horizontal", length=250, mode="determinate")
#Positioning
headerlabel.pack()
generatebutton.pack()
win.mainloop()
You can put
global generatebutton
generatebutton.config(state='disabled')
in your buttonclicked function (which is stupid because the global keyword is usually SO BAD to use, it turns your code into nightmare), or use OOP to your advantage. You can also use win.destroy() or generatebutton.destroy().
Here is that more OOP-intensive code example:
import tkinter as tk
from tkinter import ttk
class Joke:
def __init__(self):
self.win = tk.Tk()
self.win.title("StackOverflow")
self.win.resizable(False, False)
self.win.minsize(750,500)
headerlabel = tk.Label(self.win, text="StackOverFlow Question")
self.generatebutton = tk.Button(self.win, text="Generate", command=self.buttonclicked)
self.pb = ttk.Progressbar(self.win, orient="horizontal", length=250, mode="determinate")
headerlabel.pack()
self.generatebutton.pack()
self.win.mainloop()
def buttonclicked(self):
tk.Label(self.win, text="Starting to generate...").pack()
self.pb.pack()
self.pb.start(500)
self.generatebutton.config(state='disabled')
Joke()
Hope that's helpful!
I've been writing this program using the Tkinter module in Python3.6.2. There are buttons to to open a list of teams and a list of competitors and I'd prefer to keep the outputs in one window, however if I click the "Teams" button followed by the "Individuals" button, the two elements open one after the other.
I'm hoping to add a button that resets the window (removing the outputs, leaving just the buttons), but I haven't found any solutions online that work with my program.
from tkinter import *
import tkinter as tk
bgcol = "#0B8CFF"
class MainMenu:
def __init__(self, master):
self.master = master
master.title("Scoring System GUI")
master.geometry("500x750+75+60")
self.label = Label(master, text="GUI")
self.label.pack()
self.team_button = Button(master, text="Teams", command=self.openTeams)
self.team_button.pack()
def openTeams(self):
self.label = Label(text="Team #1:")
self.label.pack()
team1 = open("team1.csv", "r")
message = team1.read()
text = Text(root, width = "50", height = "6", fg = "#000000")
text.pack()
text.insert(END, message)
redteam.close()
Here's a photo of the current output:
You have to clear the Text widget before inserting new strings. Just insert the following line before your text.insert(...) statement:
text.delete('1.0', END)
By the way, if you only want to display a list and not edit it (as I guess is the case here) a Listbox widget if often a better choice than a Text widget.
To do that you need to create another method inside your class and parse it to another button as callback command.
def reset_func(self):
self.text.delete('1.0', END)
with this method inside when you parse it as command to your button it will clear the content in text widget .
Is there any way in Python/tkinter to access child elements referring by their variable names, but from an other function?
For example in VBA, it is possible to directly refer to an element of an other window by its name.
For example if I have two windows, UserForm1 and UserForm2 I can change the text value of Label1 of UserForm2 by clicking a button on UserForm1.
Private Sub CommandButton1_Click()
UserForm2.Label1.Caption = "Changed"
End Sub
In tkinter I have found the winfo_children() to access child elements. Is there any way to access them by their names?
See my sample code below:
import tkinter
from tkinter import *
#----------------------------------------------------------------------
def new(parent_window):
""""""
parent_window.withdraw()
global main_window
global new_window
new_window = tkinter.Tk()
new_window.title("My App - New")
label1 = tkinter.Label(new_window, text="NEW")
label1.grid(row=0,column=0,columnspan=3,pady=10,padx=10, sticky="nsw")
b1 = tkinter.Button(new_window, text="Change It", command=lambda: showdashboard(new_window))
b1.grid(row=4,column=1,padx=20,pady=10,sticky="nwse")
b2 = tkinter.Button(new_window, text="Quit", command=lambda: quit())
b2.grid(row=5,column=1,padx=20,pady=10,sticky="nwse")
#----------------------------------------------------------------------
def dashboard(parent_window):
""""""
parent_window.withdraw()
global main_window
global dashboard_window
dashboard_window = tkinter.Tk()
dashboard_window.title("My App - Dashboard")
label1 = tkinter.Label(dashboard_window, text="Dashboard")
label1.grid(row=0,column=0,columnspan=3,pady=10,padx=10, sticky="nsw")
b1 = tkinter.Button(dashboard_window, text="New", command=lambda: new(dashboard_window))
b1.grid(row=4,column=1,padx=20,pady=10,sticky="nwse")
#----------------------------------------------------------------------
def showdashboard(parent_window):
""""""
parent_window.withdraw()
dashboard_window.update()
dashboard_window.deiconify()
#This way it works <<<<<<<<<<<<<<------!!!!!!!
byID=dashboard_window.winfo_children()
byID[0].config(text="change the value")
#But I am looking for somethin like this <<<<<<<<<<<<<<------????
dashboard_window.label1.config(text="changed the value")
#----------------------------------------------------------------------
main_window=tkinter.Tk()
main_window.title("MyApp")
label = tkinter.Label(main_window, text="My App")
label.grid(row=0,column=0,pady=10,padx=10,sticky="nwse")
b1 = tkinter.Button(main_window, text="Dashboard", command=lambda:dashboard(main_window))
b1.grid(row=1,column=0,padx=20,pady=10,sticky="nwse")
main_window.mainloop()
winfo_children() returns an instance of the class associated with the type of widget along with the name that tkinter assigned to the actual tk object.
This means that yes, we can refer to the name of widget, although I'm not sure what advantage this would really give you other than not needing to assign the label to a variable.
from tkinter import *
root = Tk()
Label(root, text="Label1").pack()
label2 = Label(root, name="name", text="Label2")
label2.pack()
print(root.winfo_children())
print(root.nametowidget('.!label'))
print(str(label2))
Button(root, text="Delete label2", command=lambda: root.nametowidget(".name").destroy()).pack()
The above will result in two Label widgets and a Button widget appearing in the window. The first Label is not stored in a variable and yet we can quite happily call it inside the print statement. The second is stored in a variable but you can see that in the command of the Button we don't refer to the variable but the name attribute of the Label.
Bryan Oakley has a fantastic answer here explaining this.
I'm not sure what you mean by names in:
"Is there any way in Python/tkinter to access child elements referring by their names?"
You can access widgets simply by their object references:
# Procedural
import tkinter as tk
def change():
object_reference['text'] = "Changed!"
root = tk.Tk()
object_reference = tk.Label(root, text="This is a label for root-window")
object_reference.pack()
another_window = tk.Toplevel(root)
btn_in_other_window = tk.Button(another_window, text="Change")
btn_in_other_window['command'] = change
btn_in_other_window.pack()
root.mainloop()
or if they were to be defined with more of an object-oriented approach, you can make use of the .(dot) notation:
#oop
import tkinter as tk
class App1(tk.Toplevel):
def __init__(self, master):
super().__init__()
self.label = tk.Label(self, text="This is App1's label")
self.label.pack()
class App2(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.button = tk.Button(self, text="This is a App2's button")
self.button.pack()
def change(label):
label['text'] = "Changed!"
if __name__ == '__main__':
root = tk.Tk()
root.withdraw()
app1 = App1(root)
app2 = App2(root)
app2.button['command'] = lambda label=app1.label: change(label)
root.mainloop()
As an alternative, it is possible to get a list of the children widgets of the element window with:
root.update()
lista = list(root.children.values())
Then it is possible to refer to the list elements and do whatever with the widget itself. For example to get the width of the first widget do print(lista[0].winfo_width()).
Warning: I am not sure that the list contains the elements in the same order that the widgets appear in the script, although for me it worked in this order. Hopping someone will write in the comments.