0

I'm having a question related to functions in a class. My goal is to write a program with a Visualization class, which handles all the GUI related stuff with tkinter and another class called SensorHandling, which will mainly test sensors whether they work or not and return the result, which will then be displayed in Visualization. I'm having problems with my GUI class.

I have two different functions inside the class Visualization, namely StartPage and TestingHead_Page. I pass root in order for them to be able to modify my application directly. Now, I want to display the TestingHead_Page with my button bTestingHead, but I get an error that TestingHead_Page is not defined.

How do I go about this problem? Criticism is always appreciated as I want to learn.

import tkinter as tk
from tkinter import ttk

import serial
import platform
import time

LARGEFONT = ("Verdana", 35)

class Visualization:

    def StartPage(self, root):

        lTitle= ttk.Label(root, text ="Title", font = LARGEFONT)
        lTitle.grid(row = 0, column = 1, sticky = "N", padx = 10, pady = 10)
        lTitle.config(font = ('Calibri', 32, 'bold'))

        lTesting = ttk.Label(root, text = "Testing", font = ('calibri', 20))
        lTesting.grid(row = 1, column = 0, padx = 10, pady = 10)

        bTestingHead = ttk.Button(root, text = "Testing Head", command = self.TestingHead_Page(root))
        bTestingHead.grid(row = 2 , column = 0, padx = 20, pady = 20)

        exit = tk.PhotoImage(file = "exit_icon.png")
        bExit = ttk.Button(root, text = "exit", image = exit, command = root.destroy)
        bExit.image = exit
        bExit.place(relx = 1.0, rely = 0.0, anchor = 'ne')

    def TestingHead_Page(self, root):

        lTitle = ttk.Label(root, text = "Testing Head")
        lTitle.grid(row = 0, column = 0, sticky = "W", padx = 10, pady = 10)

        bStartPage = ttk.Button(root, text = "Start Page", command = "")
        bStartPage.grid(row = 0, column = 1, sticky = "E", padx = 10, pady = 10)

        exit = tk.PhotoImage(file = "exit_icon.png")
        bExit = ttk.Button(root, text = "exit", image = exit, command = root.destroy)
        bExit.image = exit
        bExit.place(relx = 1.0, rely = 0.0, anchor = 'ne')

    if __name__ == "__main__":

        root = tk.Tk()
        guiref = StartPage(self, root)
        tk.mainloop()

#class SensorHandling():
Dimilo
  • 23
  • 4
  • Looks like you tried to take a popular way of displaying multiple pages in tkinter (accepted answer to [Switch between two frames in tkinter](https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter)) which involves making each "page" a separate **class**, and nested them inside your `Visualization`. While that's probably doable, you've done it wrong by converting each page class into a **method** of that class. Besides that, nesting classes is seldom done in Python because there's little to gain by doing so and they're somewhat of a pain to work with. – martineau Dec 23 '20 at 18:39
  • I chose this approach because it seemed easier to work with if I have only one class with all GUI related stuff and one class doing the work for me. Isn't it less of a pain if I do it this way or as you said by making each page a separate class? – Dimilo Dec 23 '20 at 19:10
  • I would suggest making each page class a separate class (as they are in the example I mentioned) but put them all into separate module which gets `import`ed into the script that defines your `Visualization` class — which can then use them as necessary. You could also put them and the `Visualization` class together in one script and then have your main code import that module to do that part of your application. – martineau Dec 23 '20 at 19:15
  • Thanks for the recommendations. But I didn't get one part of your answer. You suggest making each page a separate class but still put them into separate modules? – Dimilo Dec 23 '20 at 19:28
  • I meant make each page a separate `class` and put them all (together) in a separate module in `import` that one thing. – martineau Dec 23 '20 at 19:32
  • Alright, thanks! I appreciate it! – Dimilo Dec 23 '20 at 19:50

1 Answers1

1

In python, you need to pass the current instance, referenced through the self keyword, explicitly in the class's methods:

def StartPage(self, root):
    ...

def TestingHead_Page(self, root):
    ...

While inside the class, you would call them like this:

self.StartPage(root)
self.TestingHead_Page(root)

Read up on classes in Python.

Also, from the code, it seems like TestingHead_Page should return some value since you try to capture that in command = TestingHead_Page(root), so you need to fix that as well.

Jarvis
  • 8,494
  • 3
  • 27
  • 58
  • Thanks for the answer. I did think about this too, however, when I pass self as an argument I get a NameError "name 'self' is not defined", what am I missing? – Dimilo Dec 23 '20 at 18:32
  • Edit the code with self added and let us know which line gives the error. @Dimilo Outside the class, you would call these methods like `guiref.StartPage(root)`, etc. btw. – Jarvis Dec 23 '20 at 18:33
  • Alright, I did edit the code and line 64 gives the error wher I call StartPage for the first time: 'guiref = StartPage(self, root)' – Dimilo Dec 23 '20 at 18:40
  • You don't pass `self` outside the class. You don't do anything with the keyword `self` outside the class, it has no meaning. When you call like `guiref.StartPage(root)`, `self` is passed implicitly. Read up on the `self` keyword in Python. @Dimilo – Jarvis Dec 23 '20 at 18:41
  • I understand that you don't use `self` outside a class, but it is in the class `Visualization` together with the if statement, isn't it? – Dimilo Dec 23 '20 at 18:48
  • I missed that, you need to move that if statement outside the class. It won't work otherwise. It's not part of your class or any method. @Dimilo – Jarvis Dec 23 '20 at 18:51
  • Alright, got it to work now. Thank you very much for your help. I really appreciate it. I got a different problem now but I'll try to debug that myself anyway. I have one last question, is there no way to create root and run the mainloop inside a class? – Dimilo Dec 23 '20 at 18:59
  • You can do it in `__init__` (constructor) of your class. Also, please mark the answer as accepted it the issue is resolved. @Dimilo – Jarvis Dec 23 '20 at 19:00
  • If you do it in the `Visualization` class' `__init__()` method and make it an instance attribute: i.e. `self.root = tk.Tk()` then you won't need to explicitly pass it as an argument to each method when you call them — they can each just refer to it via `self.root`. – martineau Dec 28 '20 at 16:35