Tkinter button stays pressed even after running callback function

65 Views Asked by At

I am trying to code an GUI APP using python Tkinter that i came across the problem, my main question is what make the button in the following code stays pressed(or goes down) after closing file explorer opened as the result of pushing open button.

I read the the questions this and this but i am not still able to figure out the reason behind this behaviour.

Here is a minimal working code to illustrate my problem and my code structure.

import tkinter as tk
import tkinter.filedialog as fd


class OpenSection:

    def __init__(self, parent):
        self.parent = parent

    def initFrame(self):
        self.frmFrame = tk.Frame(self.parent)

    def createComponenets(self):
        self.btnOpenFile = tk.Button(self.frmFrame, text="OPEN")

    def loacateComponents(self):
        self.btnOpenFile.pack(side="left")

    def constructFrame(self):
        self.createComponenets()
        self.loacateComponents()

    def makeObjAvailable(self):
        self.frmFrame.btnOpenFile = self.btnOpenFile

    def createSection(self):
        self.initFrame()
        self.constructFrame()
        self.makeObjAvailable()
        return self.frmFrame


class OpenSectionFunc():

    def askOpenFileNameCmd(self):
        path = fd.askopenfilename(initialdir="/", title="Select Your File",filetypes=
                                         (("Excel files", "*.xlsx"), ("Excel Macro enabled files", "*.xlsm")))
        # return "break"


class MainWindow:

    def __init__(self, title, dimension):
        self.mainWinodw = tk.Tk()
        self.mainWinodw.title(title)
        self.mainWinodw.geometry(dimension)

    def createComponents(self):
        self.frmOpenSection = OpenSection(self.mainWinodw).createSection()

    def locateComponents(self):
        self.frmOpenSection.pack(side="top", expand=0, fill="x")


    def constructMainWindow(self):
        self.createComponents()
        self.locateComponents()

    def start(self):
        self.mainWinodw.mainloop()


mainWinodw = MainWindow("tk", "400x600")
mainWinodw.constructMainWindow()

mainWinodw.frmOpenSection.btnOpenFile.bind("<Button-1>", lambda event:OpenSectionFunc().askOpenFileNameCmd())


mainWinodw.start()

I know uncommenting the last line of askOpenFileNameCmd(self) would solve the problem(How?) but first of all i would like to get an clear explanation of the reason behind the problem, then i would like to ask is that is using return "break" to solve the problem an acceptable solution or the root of the problem is me structuring my code badly and it is better for me to rewrite my code, because i like to find more logical solution so i do not have to deal with such problems if my code grows.

1

There are 1 best solutions below

0
On BEST ANSWER

When you bind <Button-1> to a button it seems to break the default button press behaviour. Returning "break" from an event handler prevents Tkinter from propagating the event to other handlers. In your case, this prevents the default button click handler from running which fixes the problem. Alternatively, you can use the command attribute of the button as that is the intended way to bind a button click to a button.

As for the structure of your code, I think it could be made cleaner by subclassing the tkinter widgets directly and removing some unnecessary methods. I would do something like this:

import tkinter as tk
import tkinter.filedialog as fd

class OpenSection(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        
        self.createComponents()
        self.locateComponents()

    def createComponents(self):
        self.btnOpenFile = tk.Button(self, text="OPEN", command = self.askOpenFileNameCmd)

    def locateComponents(self):
        self.btnOpenFile.pack(side = "left")

    def askOpenFileNameCmd(self):
        path = fd.askopenfilename(initialdir="/", title="Select Your File",filetypes=
                                         (("Excel files", "*.xlsx"), ("Excel Macro enabled files", "*.xlsm")))
        

class MainWindow(tk.Tk):
    def __init__(self, title, dimension):
        tk.Tk.__init__(self)
        self.title(title)
        self.geometry(dimension)
        
        self.createComponents()
        self.locateComponents()

    def createComponents(self):
        self.frmOpenSection = OpenSection(self)

    def locateComponents(self):
        self.frmOpenSection.pack(side="top", expand=0, fill="x")


app = MainWindow("tk", "400x600")
app.mainloop()

OpenSection now subclasses tk.Frame, so OpenSection will work like a frame does. The initFrame, constructFrame and createSection methods have all been combined into the __init__ method because they didn't really need to be separate. The button now uses the command attribute instead of bind and the button command function has been moved into the same class as the button so it's easier to see what's going on. MainWindow has been cleaned up similarly.