5

I have a small problem with my code.

There are two classes. First one creates a window with a Options button. Upon clicking the button, the second class is called and creates another window with an Ok button. Let's say there is also a checkbox, which changes the background color to black or something like that. After clicking the button, whatever changes were made in the options are stored into a file and the second window is closed.

All of this works fine. My problem is that now I need to call method update_init from the first class that will apply those changes to the MainWindow. The code below shows my first solution to this problem, but from what I understand, by using second mainloop I create second thread, which should be avoided.

class MainWindow:

    def __init__(self, master):
        self.master = master
        self.options_btn = tk.Button(self.master, text="Options", command=self.open_options)
        self.options_btn.pack()
        self.options_window = None

    def open_options(self):
        options_master = tk.Toplevel()
        self.options_window = OptionsWindow(options_master)
        options_master.mainloop()
        lst = meta_load()   # loads changes from a file
        self.update_init(lst)

    def update_init(self, lst):
        #code


class OptionsWindow:

    def __init__(self, master):
        self.master = master
        self.ok_btn = tk.Button(self.master, text="OK", command=self.update_meta)
        self.ok_btn.pack()

    def update_meta(self):
        meta_save(12)  # saves changes into a file
        self.master.destroy()

main_master = tk.Tk()
main_master.minsize(width=1280, height=720)
b = MainWindow(main_master)
main_master.mainloop()

My second solution was to just put both classes into one, but the code is quite messy if I do so. Can I somehow call the method update_init (which is in the MainWindow class) from the OptionsWindow class without initializing new MainWindow class window? Or is there any other way to deal with this? I would appreciate any help.

I am sorry if this is too specific, I've tried to make it as general as possible, but it's a very specific problem and I couldn't find much information about it anywhere on the internet.

Mike235
  • 51
  • 2
  • 3
    calling mainloop twice doesn't create a second thread. It's all the same thread. It's just another loop, and there's almost never a good reason to call it more than once. – Bryan Oakley Apr 07 '17 at 12:02
  • Is there any code in `update_init` that depends on variables associated with an instance of `MainWindow` (ie: does it use `self.`)? – Bryan Oakley Apr 07 '17 at 12:03
  • Yes, there is. Update_init method applies the changes made in options to the main window, so for example there is font option. When I change it, update_init will call self.textbox_font = x, which will change the font in a text widget. – Mike235 Apr 07 '17 at 13:06
  • 1
    In that case, you can't call a method on that class without instantiating that class first. If you don't instantiate it, `self` isn't defined and thus `self.textbox_font` will be undefined. – Bryan Oakley Apr 07 '17 at 13:09
  • BTW, you have one mainloop because `TopLevel` widget requires no loop at all. It's another child widget of your `main_master` application! – CommonSense Apr 07 '17 at 13:34
  • Yes, I thought so. I just hoped there might be some kind of a workaround. In the example I provided it works because the method open_options loops on the mainloop line and once the OptionsWindow is closed it continues, calling the update_init method. Which is exactly something I need, but as you said, I am not sure about using the loop more than once. – Mike235 Apr 07 '17 at 13:47
  • 1
    CommonSense: Yes I am aware of that. That example shows my solution to my problem, but I had to use second mainloop, which is why I am not sure about using it, as from what I read, it's not advised to use more than one loop. – Mike235 Apr 07 '17 at 13:53
  • Good question, but you do not need an intermediate file for that – Billal Begueradj May 01 '17 at 16:28

1 Answers1

0

In general you can call a class method from anywhere you want and pass anything to it without initialisation of that class's instance, thanks to objective nature of python, but beware of self dependencies! Although, I don't think that's a good practice.

class A:
    def __init__(self):
        self.foo = 'foo'

    def return_foo(self):
        return self.foo


class B:
    def __init__(self):
        self.bar = 'bar'
        print('Ha-ha Im inited!')

    def return_bar(self):
        try:
            return self.bar
        except AttributeError:
            return 'bar'


def test():
    a = A()
    # b = B()
    return_bar = getattr(B, 'return_bar', None)
    if callable(return_bar):
        print('%s%s' % (a.return_foo(), return_bar(None)))


test()

Links:

CommonSense
  • 4,232
  • 2
  • 14
  • 38
  • 1
    I am afraid this wouldn't help, because I would somehow have to trigger the method test, but if I could do that, I could trigger the method update_init instead. My problem is that once I click the Ok button, the OptionsWidnow class widnow is closed and the method update_init should be called. But because update_meta method (which ends the OptionsWIdnows class) is outside the MainWindow class, I can't call update_init. – Mike235 Apr 07 '17 at 13:42
  • 1
    @Mike235, ok, I think that I understand your idea. You can pass the reference of existing `MainWindow` to`OptionsWindow` and call `update_meta` and `update_init` on `destroy` event. Is that an option? – CommonSense Apr 07 '17 at 14:58
  • 1
    Yes, this sounds exactly like what I am looking for, although I am not 100% sure how to do it at the moment. But now that I know what I am looking for I should be able to figure it out on my own. Thank you very much. – Mike235 Apr 07 '17 at 15:27
  • 1
    @Mike235, Yeah, just pass instance alongside with parent toplevel `self.options_window = OptionsWindow(self, options_master)` and read about events (e.g. ``) and protocols [here](http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm). My "in general" answer was about totally separated classes, however it's not your case! Cheers! – CommonSense Apr 07 '17 at 15:46
  • I had no idea I can pass class as an argument to another class. This is much simpler solution than I expected. Now I feel a bit silly :D. I've changed my code and everything now works perfectly without the second mainloop. Thanks again! – Mike235 Apr 07 '17 at 15:58