0

I'm still trying to figure out how to separate my UI from my programme logic. I'm fairly new to Python and maybe you can help me out.

I was trying to separate my UI from my programme hoping that I might be able to switch from tkinter to some other GUI later on. So I was thinking to have my main programme doing stuff and calling a tk Frame for displaying my data.

So, this is my class for displaying and it works fine:

import tkinter as tk

class TestClass(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.label1 = tk.Label(text="Label1")
        self.label1.pack()
        self.changeLabel1("change from within") # <-- this works 

    def changeLabel1(self, changeText):
        self.label1.config(text=changeText)
        self.update_idletasks()

    def writeSomething(self, outputText):
        print(outputText)

And I made myself some test programme to instantiate the class:

# starter for TestClass
import TestClass
x = TestClass.TestClass()
x.mainloop()

x.writeSomething("Test") <-- nothing happens
x.changeLabel1("Test") <-- nothing happens

I put those calls to the functions after the mainloop just to show that I'm not able to change something after the mainloop has been called.

When I'm trying to change the label1 with the changeLabel1 function it works from within the class but not from my starter class. I get some output from writeSomething(text) but only when I close the window. And there is an error message which reads:

_tkinter.TclError: invalid command name ".!label"

(It is actually longer, only the last line of the traceback.)

Searching brought me to the conclusion that it might have something to do with the mainloop() but I'm not sure how to handle this problem.

What is best practice? Do I call the mainloop in my tkinter class or in my test file which calls the tkinter class?

Is this a way to separate UI and logic or am I getting something wrong and should it be done the other way around (calling the programme logic from within the UI class)?

I hope I made myself clear enough...

2
  • put x.mainloop() at the very end of your program. Commented Mar 13, 2018 at 14:01
  • OK, sorry, I should have made clear that I put those two calls to functions of my UI class to show that I can't make changes to my label after the mainloop is called. Commented Mar 13, 2018 at 14:04

1 Answer 1

3

In a typical GUI the main loop is responsible for handling user events (by dispatching them to your event handler callbacks). In your snippet, code after the call to x.mainloop() is only executed when the main window is closed and the loop exited.

So yes, the obvious answer is that it's the UI code (the event handlers callbacks) that is responsible for calling the "logic" code (which would be better named "domain model" code - there is some "logic" in the UI too) - which is mostly obvious when you think about it: your domain model code should know nothing about the UI so you can use it with different UIs (GUI, text based, web, command line script etc).

If you want your UI to update itself in reaction to things happening in your domain model layer (which is a common requirement for any non-trivial GUI app), your best bet is the good old Model-View-Controller pattern (the real thing, not the web derivative), where the "model" is your domain model (or some wrapper around it), the "controler" are your event handlers callbacks and the "view" is your UI components (or some wrapper around them). The key here is the use of the Observer pattern which let the UI subscribe to events sent by the model.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! I guess this is the difference between pro and amateur ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.