Tkinter button command activates upon running program? [duplicate] - python

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 6 months ago.
I'm trying to make a build retrieval form, and seem to have issues with the buttons... I'm a novice at Python/tkinter GUI programming (and GUI programming in general) and borrowed the skeleton of a Hello World app, and sorta built off that.
In the code below, I've set the "command" option of my Browse button to call my class's internal get_dir() function when it's clicked. However, as soon as I attempt to run the app, the get_dir() function is called and I'm prompted to choose a directory. Any ideas why this happens, and what I can do to make it behave properly?
from Tkinter import *
import tkFont
from tkFileDialog import askdirectory
class App:
def __init__(self, master):
fontHead = tkFont.Font(family="Arial", size=10, weight=tkFont.BOLD)
fontBold = tkFont.Font(family="Arial", size=8, weight=tkFont.BOLD)
fontReg = tkFont.Font(family="Arial", size=8)
frameN = Frame(master)
frameN.grid(row=0,padx=5,pady=5)
frameXBH = Frame(frameN)
frameXBH.grid(row=0,columnspan=5,padx=5)
Canvas(frameXBH,borderwidth=0,relief="flat",height=1,width=20,background="#cccccc").grid(row=0)
Label(frameXBH, text="Xbox 360",font=fontBold,width=9).grid(row=0,column=1)
Canvas(frameXBH,borderwidth=0,relief="flat",height=1,width=440,background="#cccccc").grid(row=0,column=2,sticky="WE")
Label(frameN, text="Destination Path:",font=fontReg).grid(row=1,sticky="W")
xbPath = Entry(frameN,width=30,font=fontReg)
xbPath.grid(row=1,column=1,sticky="W")
xbBrowse = Button(frameN,text="Browse...",font=fontReg,command=self.get_dir(xbPath))
xbBrowse.grid(row=1,column=2,sticky="W")
xbRel = Checkbutton(frameN,text="Release",font=fontReg)
xbRel.grid(row=1,column=3,sticky="W")
xbShip = Checkbutton(frameN,text="Ship",font=fontReg)
xbShip.grid(row=1,column=4,sticky="W")
Canvas(frameN,borderwidth=1,relief="groove",width=550,height=0).grid(row=2,columnspan=5,pady=10)
# SAVE AND CANCEL
btnSave = Button(frameN,text="Save",width=10)
btnSave.grid(row=3,column=3,sticky="E")
btnCancel = Button(frameN,text="Cancel",width=10)
btnCancel.grid(row=3,column=4,sticky="W")
def get_dir(self,box):
tmp = askdirectory(mustexist=1,title="Please select a destination")
tmp = tmp.replace("/","\\")
box.delete(0,END)
box.insert(0,tmp)
root = Tk()
root.resizable(0,0)
app = App(root)
root.mainloop()

Make your event handler a lambda function, which calls your get_dir() with whatever arguments you want:
xbBrowse = Button(frameN, text="Browse...", font=fontReg, command=lambda : self.get_dir(xbPath))

In the above code:
xbBrowse = Button(frameN,text="Browse...",font=fontReg,command=self.get_dir(xbPath))
You are invoking the function already, you should be simply passing the function:
xbBrowse = Button(frameN,text="Browse...",font=fontReg,command=self.get_dir)

You need to pass a reference of your get_dir method
so change
xbBrowse = Button(frameN,text="Browse...",font=fontReg,command=self.get_dir(xbPath))
to
xbBrowse = Button(frameN,text="Browse...",font=fontReg, command=self.get_dir)
Then make your Entry widget an instance variable so that you can access it in your get_dir method.
e.g.
self.xbPath = Entry(frameN,width=30,font=fontReg)
Then your get_dir() method will look like:
def get_dir(self):
tmp = askdirectory(mustexist=1,title="Please select a destination")
tmp = tmp.replace("/","\\")
self.xbPath.delete(0,END)
self.xbPath.insert(0,tmp)

Related

How to use Tk button? [duplicate]

This question already has answers here:
How to pass arguments to a Button command in Tkinter?
(18 answers)
Closed 3 years ago.
I'm a beginner in Python and I'm trying to build some GUI to understand.
I want to pass to a button a function that requies a parameter, but when I launch the script, it does not
work.
I'm attaching a python file.
from tkinter import *
from tkinter import messagebox
window = Tk()
window.title("Hello World")
window.geometry('350x200')
def clicked(msg):
messagebox.showinfo("Message",msg)
text = Entry(window,width=100)
text.grid(column = 1, row = 0)
btn = Button(window,text = "Click me",command = clicked(text.get()))
btn.grid(column=5, row=1)
window.mainloop()
The following fix is required for your file:
When you assign the command parameter, it wont wait till your click on button, it will pop out the message box right after your tkinter application execution. (Thats what i experienced when i executed your code)
You have to use lambda here
So you can fix this by:
btn.bind('<Button-1>',lambda event: clicked('Your Text')) # Button-1 stands for left mouse click
More on bind() method:
https://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
This is the final code:
from tkinter import *
from tkinter import messagebox
window = Tk()
window.title("Hello World")
window.geometry('350x200')
def clicked(msg):
messagebox.showinfo("Message",msg)
text = Entry(window,width=100)
text.grid(column = 1, row = 0)
btn = Button(window,text = "Click me")
btn.bind('<Button-1>',lambda event: clicked(text.get()))
btn.grid(column=5, row=1)
window.mainloop()
The command will be executed when the code is interpreted, to avoid that you can use lambda to pass an argument to the function
command = lambda : clicked(text.get()))
Or you can use partial which will return a callable object that behaves like a function when it is called.
from functools import partial
...
command = partial(clicked, text.get()))

button won't repeat commands tkinter [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 6 years ago.
I'm new to GUIs and just learning how to use Tkinter. I wrote a python program to put the students in my class into pairs for my seating charts. The program itself runs fine; the problem is the tkinter gui.
I've tried to make the button generate and display a new set of pairs each time. Instead, the GUI will display the first set of pairs as soon as I open it, without even pressing the button. The button does nothing. If I run it in console, I don't get any error messages or anything printed to console.
At Button commands in Tkinter I saw an answer to do with using lambda. When I tried it, the label will initially be blank. It will display the pairs when the button is pressed the first time, as expected, but on subsequent presses it just makes the label blank again.
I'm not sure what the problem is. Can you help me? Here is the code:
from Tkinter import *
from SeatingChart import *
root = Tk()
class App:
def __init__(self, master):
self.win = Frame(master)
self.win.pack()
self.d = Label(self.win, text = "", width=140)
self.d.pack()
self.b = Button(self.win, text="Pair Up!", command=self.display_pairs(roster))
self.b.pack()
def display_pairs(self, l):
self.d.config(text=pair(roster))
app = App(root)
mainloop()
When creating the button, the command that you supply is the result of the display_pairs method (which is None because it doesn't return anything) rather than a reference to the method.
Try:
class App:
def __init__(self, master):
self.win = Frame(master)
self.win.pack()
self.d = Label(self.win, text = "", width=140)
# Maybe display an initial roster?
# self.d = Label(self.win, text = pair(roster), width=140)
self.d.pack()
self.b = Button(self.win, text="Pair Up!", command=self.display_pairs)
self.b.pack()
def display_pairs(self):
self.d.config(text=pair(roster))

Cannot type in Python Entry Widget

I've got an interesting problem with the tk Entry widget. If I run the following test code,
from Tkinter import *
root =Tk()
def pfunc(self):
print Input.get()
f=Frame(root)
f.pack()
Input=Entry(f)
#Input.bind("<Return>",pfunc)
Input.pack()
root.mainloop()
I can properly enter into the widget and print to console; however the following code, as part of a larger GUI, does not allow me to click in the Entry boxes at all.
self.Tlabel = Label(self.TempFrame, text="Temp")
self.Tlabel.pack( side = LEFT)
self.Tenter = Entry(self.TempFrame,width=10, bd =5)
self.Tenter.bind("<Return>",self.getFlux)
self.Tenter.pack (side=RIGHT)
self.Flabel = Label(self.FluxFrame, text="Flux")
self.Flabel.pack( side = LEFT)
self.Fenter = Entry(self.FluxFrame, width=10, bd =5)
self.Fenter.bind("<Return>",self.getTemp)
self.Fenter.pack(side = RIGHT)
def getFlux(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Fenter.set(fit_data[0]*np.exp(fit_data[1]*int(self.Tenter.get())))
else:
self.Fenter.set("Invalid")
def getTemp(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Tenter.set(np.log(float(self.Fenter.get())/fit_data[0])/fit_data[1])
else:
self.Tenter.set("Invalid")
Furthermore, if I run both codes on a separate windows PC I have the same problem. The only difference I can possibly think of is that I am using instance variables within a class; but it seems that other widgets are bound and working properly.
Basically, the bind method is passing "Return" as a parameter to getTemp. As another user suggested, just add another parameter to the function.
If you use bind method, callback is called with an event object. You should add event parameter to the method/function. (See Events and Bindings - An Introduction to Tkinter )
So, rpelcae following lines:
def getFlux(self, event):
...
def getTemp(self, event):
...
The first program work unintentionally. Its parameter name should be event, not self.

How to pause a script with a Tkinter event?

I'm creating a simple class named Dialog. I only want it to return when init method is already finished.
from tkinter import *
class Dialog:
def __set_data(self):
self.data = self.entry_.get()
self.top.destroy()
def __init__(self, prompt_text = "Prompt:", submit_text = "OK", text_size = 5, button_size = 5, main_window = Tk()):
main_window.withdraw()
top = self.top = Toplevel(main_window)
Label(top, text=prompt_text).pack()
self.entry_ = Entry(top)
self.entry_.pack(padx = text_size)
button = Button(top, text = submit_text, command = self.__set_data)
button.pack(pady=button_size)
def get_data(self):
return self.data
data = 0
a = Dialog();
print (a.get_data())
If you run this code, you will get the output 0. I want to the output only show up after the user input. How can I do this?
Firstly, I don't think that you really want to postpone the __init__ method returning. If you do that, your code will never get to tk.mainloop(), and will never actually appear on screen.
Instead, what you want to do is be notified when the data in the Entry widget changes. This is a pretty common thing to do in GUI toolkits; the way tkinter handles it is with Events and Bindings. You can read about them generally here.
One way of performing your particular task (showing the data in self.entry once it has been changed by the user) might be to use the method shown in this question.
Alternatively, you could add a command to your submit button (see here) and read the value of the entry in that method.

Text Input in Tkinter

Goal
I am trying to write a basic file which I can import in all other programs that will have a simple function that will take entry from the user and then return it.
Code
For that I have the following code:
class takeInput(object):
def __init__(self,requestMessage,parent):
self.string = ''
self.frame = Frame(parent)
self.frame.pack()
self.acceptInput(requestMessage)
def acceptInput(self,requestMessage):
r = self.frame
k = Label(r,text=requestMessage)
k.pack(side='left')
self.e = Entry(r,text='Name')
self.e.pack(side='left')
self.e.focus_set()
b = Button(r,text='okay',command=self.gettext)
b.pack(side='right')
def gettext(self):
self.string = self.e.get()
self.frame.destroy()
print self.string
def getString(self):
return self.string
def getText(requestMessage,parent):
global a
a = takeInput(requestMessage,parent)
return a.getString()
And I also added some script level code so as to test this:
root = Tk()
getText('enter your name',root)
var = a.getString()
print var
root.mainloop()
And what is really baffling me is that:
var does not have the value that I entered it has the empty string ''
a.string variable has the value that I entered and I checked this from the shell.
Also When I tried to assign the string returned from a.getString() to var in the shell, then it worked.
note I am new to Tkinter programming and dont fully understand how the mainloop() works. So maybe this is were the problem is. But I am not sure.
Specs
OS:Linux Mint 14
Python IDLE 2.7
Please help me out with this issue.
As other answers tell, you print var before entering the mainloop, that is, before your window is actually running, and your program is waiting for user input.
You could rely on tkSimpleDialog family to get your input:
import Tkinter
import tkSimpleDialog
root = Tkinter.Tk()
var = tkSimpleDialog.askstring("Name prompt", "enter your name")
print var
If you want to pursue your way, you could perform your print from the "ok" button callback (gettext in your case). You could also generate a virtual event when "ok" is pressed and bind to this event in your main program (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/virtual-events.html)
The flow of your code goes like this:
the main scope calls getText.
getText creates a takeInput object a.
the takeInput object initializes itself, creating Labels & buttons etc.
getText returns a.getString(), which returns self.string, which still has its default value, the empty string.
the main scope prints var, which is empty.
So far, all of this has taken place within the span of a few nanoseconds. The user hasn't even seen the window yet.
the main scope then calls root.mainloop(), which finally gives the user the opportunity to interact with the window. But it's too late. var has already been printed.
If you want getText to not return until the user has submitted his text, then mainloop has to occur inside getText, not after it.
from Tkinter import *
class takeInput(object):
def __init__(self,requestMessage):
self.root = Tk()
self.string = ''
self.frame = Frame(self.root)
self.frame.pack()
self.acceptInput(requestMessage)
def acceptInput(self,requestMessage):
r = self.frame
k = Label(r,text=requestMessage)
k.pack(side='left')
self.e = Entry(r,text='Name')
self.e.pack(side='left')
self.e.focus_set()
b = Button(r,text='okay',command=self.gettext)
b.pack(side='right')
def gettext(self):
self.string = self.e.get()
self.root.destroy()
def getString(self):
return self.string
def waitForInput(self):
self.root.mainloop()
def getText(requestMessage):
msgBox = takeInput(requestMessage)
#loop until the user makes a decision and the window is destroyed
msgBox.waitForInput()
return msgBox.getString()
var = getText('enter your name')
print "Var:", var
The problem is that your test routine already prints out the value of var before the dialog has been shown, let alone text being entered. (You can easily validate this by adding some print statements to your test code.) This is because the call to mainloop() is at the very end. Instead, you should call mainloop after creating the frame, but before reading and returning the input, e.g. it might go to your getText method:
def getText(requestMessage,parent):
a = takeInput(requestMessage,parent)
parent.mainloop()
return a.getString()
This still does not work really well, as you have to close the dialog (click the [x]-button) even after clicking on 'okay', and I am not sure how to fix this.
However, note that there already is a module for this, tkSimpleDialog, providing methods such as askstring(title, prompt) that show just such an input dialog. So you might either use those, or look at the source code (found in /usr/lib/python2.7/lib-tk or the like) to find out how it's done.
Here's a quick snippet:
import tkinter
from tkinter import simpledialog
root = tkinter.Tk()
# withdraw() will make the parent window disappear.
root.withdraw()
# shows a dialogue with a string input field
youtube_url = simpledialog.askstring('YouTube URL', 'Enter the youtube URL of the video', parent=root)
if str(youtube_url).startswith('http'):
pass
else:
pass

Categories

Resources