-1

In order to understand the difference in use of method calls, I wrote a MVC in python 2.7. Here is the code:

import Tkinter as tk

class Model(object):
    def __init__(self, *args, **kwargs):
        # dict
        self.data = {}
        # -- >values
        self.data["Value_One"] = tk.IntVar()
        self.data["Value_Two"] = tk.IntVar()
        self.data["Value_Three"] = tk.IntVar()
        # --> texts
        self.data["Text_Label_val_One"] = tk.StringVar()
        self.data["Text_Label_val_Two"] = tk.StringVar()
        self.data["Text_Label_val_Three"] = tk.StringVar()

class Control(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs) # init
        tk.Tk.wm_title(self, "Testing Grounds") # title

        self.model = Model()
        self.view = View(parent = self, controller = self)
        self.view.pack(fill = 'both', expand = True)

    def set_labels_text(self):
        self.model.data["Text_Label_val_One"].set("Value_One is set to: {0}".format(self.model.data["Value_One"].get()))
        self.model.data["Text_Label_val_Two"].set("Value_Two is set to: {0}".format(self.model.data["Value_Two"].get()))
        self.model.data["Text_Label_val_Three"].set("Value_Three is set to: {0}".format(self.model.data["Value_Three"].get()))

    def set_value_one(self):
        self.model.data["Value_One"].set(1)    

    def set_value_two(self):
        self.model.data["Value_Two"].set(2)

    def set_value_three(self):
        self.model.data["Value_Three"].set(3)

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

        self.buttons()
        self.labels()

    def buttons(self):
        set_v_one = tk.Button(self, text = "Set Value One To 1", command = lambda: self.controller.set_value_one())
        set_v_one.pack(fill = 'x', expand = True)

        set_v_two = tk.Button(self, text = "Set Value Two To 2", command = self.controller.set_value_two())
        set_v_two.pack(fill = 'x', expand = True)

        set_v_three = tk.Button(self, text = "Set Value Three To 3", command = self.controller.set_value_three)
        set_v_three.pack(fill = 'x', expand = True)

        update_lbl_two = tk.Button(self, text = "Update Labels", command = self.controller.set_labels_text)
        update_lbl_two.pack(fill = 'x')

    def labels(self):
        label_one = tk.Label(self, textvariable = self.controller.model.data["Value_One"])
        label_one.pack(fill = 'x', expand = True)

        label_two = tk.Label(self, textvariable = self.controller.model.data["Value_Two"])
        label_two.pack(fill = 'x', expand = True)

        label_three = tk.Label(self, textvariable = self.controller.model.data["Value_Three"])
        label_three.pack(fill = 'x', expand = True)        

        label_val_one = tk.Label(self, textvariable = self.controller.model.data["Text_Label_val_One"])
        label_val_one.pack(fill = 'x', expand = True)

        label_val_two = tk.Label(self, textvariable = self.controller.model.data["Text_Label_val_Two"])
        label_val_two.pack(fill = 'x', expand = True)

        label_val_three = tk.Label(self, textvariable = self.controller.model.data["Text_Label_val_Three"])
        label_val_three.pack(fill = 'x', expand = True)

if __name__ == "__main__":
    app = Control()
    app.mainloop()

If one executes it and hits the button to update the labels, the result is the following:

enter image description here

As one can see, model.self.data["Value_One"] is not set on startup, due to the use of lambda, which I thought to be a nameless function, which can only return one value and nothing more. Here it seems to inhibit the initial call of the method by the command line of button set_v_one.

In the case of model.self.data["Value_Two"], the value is updated on startup. I'd think thats because the function is called, when the command line of the button is read and the button created, due to the active call or initialization of the method via brakets (), because it does happen, even when one doesn't pack the button.

For model.self.data["Value_Three"], the value is also not updated on startup. This is, as I'd think, caused by the method set_value_three(self) of the controller bound to the command line, but not initialized, due to the lack of calling for it by using the brakets ().

After pressing the buttons set_v_one and set_v_three, the values get properly updated, as indicated by the corresponding labels label_one and label_three.

Even though I'm using these method calls a lot, I couldn't yet fully understand, how they work in detail. If someone could clarify this or point me to a good source I just haven't found yet, it would be much appreciated.

MrPadlog
  • 115
  • 1
  • 8
  • 2
    `self.method <- reference to the method`, `self.method() <- call the method`. You may as well use `self.method` if you are using `lambda:self.method()` as the lambda just makes it callable. – Padraic Cunningham Oct 10 '16 at 11:25

1 Answers1

2

All in all, there is no mystery here.

The command argument of the Button constructor takes a callback, i.e. a function that will be executed when the button is pressed. You do this for buttons 1 and 3. So when the corresponding buttons are clicked, the function (be it the lambda or the bound method) is called and the labels are updated. With button two, you actually execute the method that sets the value of the second label and assigning the command argument the result of that method call (which will be None). This is in violation of the API as far as I understand and should probably result in an error.

To sum it up, your confusion seems to stem from mixing up a function (an object that can be executed) and its invocation (the action of executing a function).

MrMobster
  • 1,851
  • 16
  • 25
  • So in this case `lambda` works as a wrapper, which points to the method, that shall be called, as long as there aren't more than one return value in the method, called by `lambda`? – MrPadlog Oct 10 '16 at 12:04
  • The lambda is just a shortcut for defining a function. In your case it is equivalent to `def myfun(): self.controller.set_value_one()` and then `command = myfun` – MrMobster Oct 10 '16 at 12:13