1

What I'm trying to accomplish is a way of changing a variable in another class instance. I've been scratching my head trying to understand how (or if) this is possible.

How can one update the value of self.lblvar that is in the class MainWindow from the class SecondWindow?

This is what I'm working with to test my theory:

from tkinter import *

class MainWindow:
    def __init__(self, rootWin):
        self.rootWin = rootWin
        self.rootWin.geometry('400x200')

        self.mainMenu = Menu(self.rootWin)
        self.mainMenu.add_command(label = 'Open Second Window', command = self.openSecondWindow)

        self.lblvar = StringVar()
        self.lblvar.set('Change Me!')
        self.lbl = Label(rootWin, textvariable = self.lblvar)
        self.lbl.pack()

        self.rootWin.config(menu = self.mainMenu)

    def openSecondWindow(self):
        self.secondWin = Tk()
        self.secWin = SecondWindow(self)
        self.secondWin.mainloop()

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

        self.btn = Button(self, label = 'Change Label?', command = self.changeOther)
        self.btn.pack()

    def changeOther(self):
        self.parent.lblvar.set('Changed it!')

def main():
    root = Tk()
    mainWin = MainWindow(root)
    root.mainloop()

if __name__ == "__main__":
    main()

I'm a bit of a novice when it comes to Python classes, so any guidance and/or explanation regarding this would be appreciated!

Edit: Changed original question to a clearer question to aid further searches on the topic

TheJFo
  • 13
  • 1
  • 5
  • 2
    Sorry, but without more explanations about what you are trying to accomplish, your question is unclear. – Thierry Lathuille Nov 11 '19 at 17:01
  • Hey @ThierryLathuille! What I'm trying to accomplish is to create a secondary "options" window to make adjustments to the main window. In this instance, the second window is to only make a change to the main window's label object. – TheJFo Nov 11 '19 at 17:09
  • Are you looking for a Tkinter specific answer (i.e. can I change properties in other windows?), or weather or not this concept is viable in python in general? – Aleon Nov 11 '19 at 17:16
  • @Aleon that is correct. I'm trying to modify labels and other objects in another Tkinter window – TheJFo Nov 11 '19 at 17:35
  • @Aleon Tkinter or not the issues is one of class interactions. What the user is asking for is possible. Its just a matter of passing the instance of 'self' and the variable needing editing must be a class attribute. In the OP's example however they are doing things a little different than I would. I would inherit from Tk in the class directly and they are using more than one instance of `Tk` as this is not something you should ever do with tkinter. – Mike - SMT Nov 11 '19 at 18:01
  • I find this question is clear and should be re-opened. The OP is simple having a problem with interacting between 2 classes and trying to update the StringVar for a label in one window from another. Thought there are several things wrong with their code the question is clear enough to answer. – Mike - SMT Nov 11 '19 at 18:10
  • @Mike-SMT going by the title of the question you are correct, but if you look at the code itself (and OP's comments), OP already tried doing exactly that (access attribute of the parent), failed due to an issue *with how the user uses Tkinter*, and then wrongly attributed the error to how classes may or may not work in python. – Aleon Nov 11 '19 at 18:10
  • @Aleon the OP is on the right track though. The issue is really easy to fix. I have voted to reopen as it can be answered accurately with the code and question provided. – Mike - SMT Nov 11 '19 at 18:12
  • 1
    @Aleon the OP's could should work fine if they fix `openSecondWindow`. This method is not doing what they think it is. Though I would rewrite it to inherit from `Tk` and `Toplevel` to fix some other issues. – Mike - SMT Nov 11 '19 at 18:30
  • Thanks @Mike-SMT! I was wondering how the post could have been better posed to the community. It's my first time on this forum and - aside from my lack of understanding classes - I have sought answers here first. To understand how it should be utilized properly (or do what I'm expecting), should I pose this in a new question? – TheJFo Nov 12 '19 at 01:38
  • @TheJFo If you can formulate a clearer question the best thing to do is update this post and then let the vote for reopen take care of it. We are only 1 vote away from reopening and then I can post my answer :D – Mike - SMT Nov 12 '19 at 15:20

2 Answers2

0

Yes. It is entirely possible to modify the state of a class or an instance of a class from within another class. Unlike traditional OOP languages such as C++, Python enforces no access rules- everything is public.

class ClassA:
    def __init__(self, var_a):
        self.var_a = var_a

    def change_var_c(self, instance_b, new_value):
        instance_b.var_c = new_value

    def change_var_b(self, new_value):
        ClassB.VAR_B = new_value

class ClassB:
    VAR_B = 2
    def __init__(self, var_c):
        self.var_c = var_c


instance_a = ClassA(1)
instance_b = ClassB(3)

instance_a.change_var_c(instance_b, 4)
print(instance_b.var_c) # prints 4

instance_a.change_var_b(5)
print(ClassB.VAR_B) # prints 5

Nonetheless, this probably isn't something you should be doing. Modifying the internals of objects violates the principle of encapsulation. Classes should instead expose a public interface for modifying their "private" members.

mario_sunny
  • 1,412
  • 11
  • 29
  • "Unlike traditional OOP languages such as Java" interestingly, Python is a slightly *older* OOP language than Java. I guess you could say unlike C++, which would have been the equivalent at the time. But access modifiers aren't actually something that is characteristic of OOP languages. It's just a lot of people think "OOP" mean "Do it like Java". – juanpa.arrivillaga Nov 11 '19 at 17:10
  • How would a public interface be created in that instance? On the other hand, if I were to still want to go this route, should I define it only in the MainWindow class? – TheJFo Nov 11 '19 at 17:17
  • @TheJFo By public interface, I just mean a collection of methods on your `MainWindow` class. So you would define a `def set_label_text(self, text):` on your `MainWindow` class, for example, that would do `self.lblvar.set(text)`. Then a class with access to a `MainWindow` object could call this method to change the text of the label, ex. `main_window.set_label_text("New text")`. – mario_sunny Nov 11 '19 at 17:23
  • @mario_sunny, so would you suggest then placing this secondary window in the MainWindow class and call upon it when necessary then? It seems like calling to the other class is a roundabout way of accomplishing what I'm after. – TheJFo Nov 11 '19 at 17:44
0

So the minimal changes that are needed to get your code to work as exspected would be to change your class SecondWindow to accept 2 arguements, one for the top level window and one for the main window class as well as changing the method openSecondWindow to work with a Toplevel window instead of a 2nd instance of Tk as you should never use more than one instance of Tk() within a tkinter GUI. One minor issue is you use label = inside of your button and this should actually be text=. All so we can work with the class attribute.

Something like this:

def openSecondWindow(self):
    self.secondWin = Toplevel(self.rootWin)
    self.secWin = SecondWindow(self.secondWin, self)

class SecondWindow:
    def __init__(self, top, master):
        self.master = master

        self.btn = Button(top, text='Change Label?', command=self.changeOther)
        self.btn.pack()

    def changeOther(self):
        self.master.lblvar.set('Changed it!')

That said I would change a few things overall to improve readability and reduce the lines of code.

  1. import tkinter as tk instead of using *. This will help prevent overwriting of built in methods/other imports as well as make it clear what library you are working with in the code.

  2. Inherit from Tk() and Toplevel() in your class to make things easier to work with.

  3. Only use self. where it is needed. Some places in your code this is not needed.

See below reworked example and let me know if you have any questions:

import tkinter as tk


class MainWindow(tk.Tk):
    def __init__(self):
        # super is used to avoid referring to the base class explicitly.
        # to read more on this check out the link below.
        super().__init__()
        self.geometry('400x200')
        main_menu = tk.Menu(self)
        self.config(menu=main_menu)
        main_menu.add_command(label='Open Second Window', command=SecondWindow)
        self.lblvar = tk.StringVar(value='Change Me!')
        # No need to use `self.` on this label as we are working with the lblvar in the 2nd window and not the label.
        tk.Label(self, textvariable=self.lblvar).pack()


class SecondWindow(tk.Toplevel):
    def __init__(self):
        super().__init__()
        # No need to use `self.` on this button unless down the road you want to change this button in some way.
        tk.Button(self, text='Change Label?', command=self.change_other).pack()

    def change_other(self):
        # with inheriting like this in tkinter you do not need to define self.master as it is automatic here.
        # Other classes or methods may need it defined however. Just wanted to make sure you are aware of this.
        self.master.lblvar.set('Changed it!')


if __name__ == "__main__":
    MainWindow().mainloop()

Results:

enter image description here

More details on super().

Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • Wow! This is exactly what I'm looking for! I guess I need to look a bit further into how the classes that I create inherit from another class. I'm hoping to get a better grasp of this as I work on a few of my personal Python projects to obtain a good working knowledge. Thanks for the constructive criticism on my coding style to get a more readable version. – TheJFo Nov 14 '19 at 01:37
  • @TheJFo Yep glad to help. So what the class inheritance does is give your class all the attributes and methods of the class you are inheriting from. It is a useful tool for sure. – Mike - SMT Nov 14 '19 at 03:47