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())
Related
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 trying to get the following code to work where event calls a function to clear an entry box. Can someone tell me what I am doing wrong. I am not too familiar with Tkinter.
import tkinter as tk
from tkinter import *
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Create Trusted Facts")
self.pack(fill=BOTH, expand=True)
frame2 = Frame(self)
frame2.pack(fill=X)
reqSumLbl = Label(frame2, text="Request Summary", width=22)
reqSumLbl.pack(side='left', padx=5, pady=5)
reqSumBox = Entry(frame2, width=100, bg="White", fg="lightgrey", borderwidth=1)
reqSumBox.insert(0, "Enter the Request Summary here")
reqSumBox.pack(fill=X, padx=50, pady=5, expand=True)
reqSumBox.bind("<Button-1>", self.clear_reqSumBox)
def clear_reqSumBox(self, reqSumBox):
reqSumBox.delete(0, END)
reqSumBox.config(fg="black")
global SummaryText
SummaryText = reqSumBox.get()
def main():
root = Tk()
root.geometry("500x550+350+50")
app = Example()
root.mainloop()
if __name__ == '__main__':
main()
reqSumBox.bind("<Button-1>", self.clear_reqSumBox)
When binding any event to a function, it automatically needs to take in a parameter called event, there are 2 ways to fix your code.
1.
reqSumBox.bind("<Button-1>", lambda event: self.clear_reqSumBox)
Make lambda function which takes in event and calls function.
2.
def reqSumBox(self, reqSumBox, event=None)
Add optional event parameter in reqSumBox function.
I personally use the first one.
First of all, why do you have two imports at the start of your Python script at they're both the same library, choose one it's incorrect.
About your question, it fails because you didn't provide the clicked object, it provided you as the first argument of the bind function the Event that happened.
I recommend you make your object a part of your current working class (Class Example), like that:
import tkinter as tk
from tkinter import *
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Create Trusted Facts")
self.pack(fill=BOTH, expand=True)
frame2 = Frame(self)
frame2.pack(fill=X)
reqSumLbl = Label(frame2, text="Request Summary", width=22)
reqSumLbl.pack(side='left', padx=5, pady=5)
# Check my changes here.
self.reqSumBox = Entry(frame2, width=100, bg="White", fg="lightgrey", borderwidth=1)
self.reqSumBox.insert(0, "Enter the Request Summary here")
self.reqSumBox.pack(fill=X, padx=50, pady=5, expand=True)
self.reqSumBox.bind("<Button-1>", self.clear_reqSumBox)
# Changed the argument name to "event".
def clear_reqSumBox(self, event):
self.reqSumBox.delete(0, END)
self.reqSumBox.config(fg="black")
def main():
root = Tk()
root.geometry("500x550+350+50")
app = Example()
root.mainloop()
if __name__ == '__main__':
main()
Check where I comment and analyze this code.
The callback of bind() requires an argument which is the event object. So modify the callback function definition as below:
def clear_reqSumBox(self, event):
# get the widget triggering this event
reqSumBox = event.widget
reqSumBox.delete(0, END)
reqSumBox.config(fg="black")
# after that reqSumBox.get() will return empty string
global SummaryText
# SummaryText = "" will have same result of below line
SummaryText = reqSumBox.get()
However the entry will be cleared whenever you click on the entry. Is it really what you want?
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'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.
I am just trying to make a non-graphic game in Tkinter but am having trouble with the entry widget. How can I wait for "<Return>" to be pressed before printing something?
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
f = Frame(master, width=500, height=300)
f.pack(fill=X, expand=True)
mt = StringVar()
menubar = Menu(master)
menubar.add_command(label="New Game", command=self.new_game)
menubar.add_command(label="Continue Game", command=self.continue_game)
master.config(menu=menubar)
maintext = Label(master, fg="blue", textvariable=mt)
maintext.pack(side=BOTTOM)
mt.set("")
self.e1 = Entry(master)
self.e1.pack()
self.e1.bind("<Return>")
self.e1.lower()
global mt
def new_game(self):
mt.set("What do you want your username to be?")
self.e1.lift()
#wait for <Return> to be pressed
mt.set("Welcome " + self.e1.get())
def continue_game(self):
mt.set("Type your username in.")
root = Tk()
app = App(root)
root.mainloop()
I want it to be when I click "New Game" on the top menubar, it shows the entry box and waits for me to type something in, and then click enter. THEN it prints out "Welcome"+(what the person types in). What actually happens is when I click New game, it immediately just prints "Welcome".
By print, I mean aet the Label at the bottom to something else, which is reffered to by "mt.set".
In GUI programs you don't wait* for something to happen, you respond to events. So, to call a function when the user presses return you would do something like:
self.e1.bind("<Return>", self._on_return)
The above will call the function _on_return when the user presses the return key.
In your specific code you don't really need a StringVar. For example, you could do this:
def __init__(self, master):
...
self.maintext = Label(master, fg="blue")
...
def _on_return(self, event):
self.maintext.configure(text="Welcome, %s" % self.e1.get())
* strictly speaking, you can wait for things, but that's not the right way to write GUI programs except under specific circumstances, such as waiting for a dialog to be dismissed.