29

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()
Doktor J
  • 1,058
  • 1
  • 14
  • 33
  • @nbro I think you have your duplicate backwards, I asked this in 2010, the one you're flagging this a duplicate of was asked in 2011 -_- – Doktor J Mar 10 '17 at 16:03
  • The reason I marked this post as a duplicate of the other is because the other actually contains more info and that question is simpler.. – nbro Mar 10 '17 at 16:08
  • "I asked this in 2010, the one you're flagging this a duplicate of was asked in 2011" That [does not matter](https://meta.stackoverflow.com/questions/251938/should-i-flag-a-question-as-duplicate-if-it-has-received-better-answers) on Stack Overflow. – Karl Knechtel Sep 04 '22 at 05:37

3 Answers3

41

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))
Pichi Wuana
  • 732
  • 2
  • 9
  • 35
Kendall
  • 426
  • 5
  • 2
  • 14
    Can you explain what this is doing by adding the lambda part? I had this issue with the minimal application in the Tkinter docs, where I wanted to add text to the window when the button is pressed. I had the command=label.grid() adding a label when the it ran, and adding lamba fixed it. Why was the command without lambda being executed immediately? What is lambda keyword doing? – Matt Jun 23 '17 at 03:23
  • same here - i tried lambda and it works too. What did it do ? – Nidhin_toms Jan 18 '19 at 01:43
  • this answer explains it more: https://stackoverflow.com/questions/8269096/why-is-button-parameter-command-executed-when-declared – ProperVowel Jul 09 '21 at 15:26
  • Why is it so difficult to implement buttons in python? – SJ10 Jan 25 '22 at 05:13
20

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)
pyfunc
  • 65,343
  • 15
  • 148
  • 136
9

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)
volting
  • 16,773
  • 7
  • 36
  • 54
  • I intend to have more than one browse button/entry on the form though, so I need to have multiple buttons call get_dir() and pass it the appropriate Entry field... how would you do that? – Doktor J Sep 14 '10 at 14:05
  • @Doktor J: The conventional way to is to either have a separate handler for each event, so in your case a separate get_dir() method for each browse button or alternatively you could implement your browse button + entry widget +.. as a composite widget and have it automatically do all the stuff you want, i.e. get a path process it display it etc. Then its as simple as creating as many instances of your composite widgets as you want. – volting Sep 14 '10 at 14:38