-2

This is a minimal example I made to show the problem, and it is extracted from a large project so please forgive the naming. So basically I have an GUI that looks like this:

enter image description here

the connect button and BE\RP... buttons belongs to a frame (control_container), which is like a navigator or tab selector that should always show up, and the info button belongs to another frame (container), which, when you click on BE\RP... buttons, should change to those corresponding frame class, and it does. what confused me is that when clicking the connect button, it should call function connect and do a print. However, it doesn't work: when you click on it, simply nothing happened. But I do know that the program recognize the connect function since it would complain if you delete the function. For contrast, if you click on info on StartPage, it works just fine and print. This is really strange to me.

import tkinter as tk
from tkinter import ttk
from tkinter import *



class emcAutoApp(tk.Tk):

    def __init__(self, *args, **kwargs):

        tk.Tk.__init__(self, *args, **kwargs)


        self.control_container = tk.Frame(self)
        self.control_container.pack(side="top", fill="both", expand = True)


        container = tk.Frame(self, width=768, height=576, bg="")
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)     
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (StartPage, BE, RP, PreScan, RSE):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column = 0, sticky='nsew')

        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()

    def get_page(self, page_class):
        return self.frames[page_class]


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)

        self.frame_controller = controller

        #control frame starts here
        control_frame = ttk.Frame(self.frame_controller.control_container)
        control_frame.pack(side='top')

        chamber_frame = Frame(control_frame,
                           borderwidth=5,   
                           relief=RIDGE,
                           width=200
                          )
        chamber_frame.pack(side=TOP, expand=YES, fill=X)

        chamber_frame_1 = Frame(chamber_frame,
                           borderwidth=1,   
                           relief=RIDGE,
                           width=100
                          )
        chamber_frame_1.pack(side=LEFT, expand=YES, fill=X)

        chamber_frame_2 = Frame(chamber_frame,
                           borderwidth=1,   
                           relief=RIDGE,
                           width=100
                          )
        chamber_frame_2.pack(side=LEFT, expand=YES, fill=X)


        connect_button = ttk.Button(chamber_frame_2, text="connect", command=lambda: self.connect)
        connect_button.pack()

        tab_frame = Frame(control_frame,
                           borderwidth=5,   
                           relief=RIDGE,
                           width=500
                          )
        tab_frame.pack(side=TOP, expand=YES, fill=X)

        tab_frame_1 = Frame(tab_frame,
                           borderwidth=1,   
                           relief=RIDGE,
                           width=100
                          )
        tab_frame_1.pack(side=LEFT, expand=YES, fill=X)

        tab_frame_2 = Frame(tab_frame,
                           borderwidth=1,   
                           relief=RIDGE,
                           width=100
                          )
        tab_frame_2.pack(side=LEFT, expand=YES, fill=X)

        tab_frame_3 = Frame(tab_frame,
                           borderwidth=1,   
                           relief=RIDGE,
                           width=100
                          )
        tab_frame_3.pack(side=LEFT, expand=YES, fill=X)

        tab_frame_4 = Frame(tab_frame,
                           borderwidth=1,   
                           relief=RIDGE,
                           width=100
                          )
        tab_frame_4.pack(side=LEFT, expand=YES, fill=X)

        BE_button = ttk.Button(tab_frame_1, text="BE", 
                            command=lambda: self.frame_controller.show_frame(BE))
        BE_button.pack()

        RP_button = ttk.Button(tab_frame_2, text="RP", 
                            command=lambda: self.frame_controller.show_frame(RP))
        RP_button.pack()

        PreScan_button = ttk.Button(tab_frame_3, text="PreScan", 
                            command=lambda: self.frame_controller.show_frame(PreScan))
        PreScan_button.pack()

        RSE_button = ttk.Button(tab_frame_4, text="RSE", 
                            command=lambda: self.frame_controller.show_frame(RSE))
        RSE_button.pack()


        infobutton = ttk.Button(self, text = "info", command = self.info)
        infobutton.pack()


    def info(self):
        print("info")

    def connect(self):
        print("connected")

class BE(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)


        self.frame_controller = controller

class RP(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)


        self.frame_controller = controller

class PreScan(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)


        self.frame_controller = controller

class RSE(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)


        self.frame_controller = controller


if __name__ == "__main__":

    #=== GUI ===#
    LARGE_FONT = ("Verdana", 12)
    NORM_FRONT = ("Verdana", 10)

    app = emcAutoApp()
    app.mainloop()
Hansong Li
  • 417
  • 2
  • 11
  • 26
  • Please make the example smaller. There is a lot of code there unrelated to making a button work. See [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) – Bryan Oakley Apr 19 '17 at 22:45
  • This perhaps: `command=lambda: self.connect` .... that doesn't call connect. Likely `command=self.connect` will do. Or `command=lambda: self.connect('other stuff')` if you meant to add parameters. – tdelaney Apr 19 '17 at 22:45

1 Answers1

1

lambda: self.connect doesn't call connect. In this case there is no need for lambda, just directly reference the function. As a general rule of thumb, buttons should always be tied directly to functions rather than using lambda

connect_button = ttk.Button(..., command=self.connect)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you! Just a follow up, if I call function with arguments with a button, would command=self.connect(arg1, arg2) work? – Hansong Li Apr 20 '17 at 13:49
  • @HansongLi: no. That's when you might want to use `lambda` or `functools.partial`. The important thing here is to not memorize rules, but understand what is actually required. You must provide a _callable_ for the `command`. http://stackoverflow.com/questions/111234/what-is-a-callable-in-python – Bryan Oakley Apr 20 '17 at 13:57
  • Thanks a lot! I will look into it. – Hansong Li Apr 20 '17 at 14:35