views:

91

answers:

4

I'm writing a Python program with a GUI built with the Tkinter module. I'm using a class to define the GUI because it makes it easier to pass commands to buttons and makes the whole thing a bit easier to understand.

The actual initialization of my GUI takes about 150 lines of code. To make this easier to understand, I've written the __init__ function like so:

def __init__(self, root):
    self.root = root
    self._init_menu()
    self._init_connectbar()
    self._init_usertree()
    self._init_remotetree()
    self._init_bottom()

where _init_menu(), _init_connectbar(), and so on do all the initialization work. This makes my code easier to follow and prevents __init__ from getting too big.

However, this creates scope issues. Since an Entry widget that I defined in _init_connectbar() is in the function scope and is not a class attribute, I can't refer to it in other methods in the class.

I can make these problems go away by doing most of the initialization in __init__, but I'll lose the abstraction I got with my first method.

Should I expand __init__, or find another way to bring the widgets into class scope?

+1  A: 

You should look into the builder-pattern for this kind of stuff. If your GUI is complex, then there will be some complexity in describing it. Whether that is a complex function or a complex description in some file comes down to the same. You can just try to make it as readable and maintainable as possible, and in my experience the builder pattern really helps here.

Space_C0wb0y
+2  A: 

In my opinion, you should store the widgets as instance variables so that you can refer to them from any method. As in most programming languages, readability decreases when functions get too large, so your approach of splitting up the initialization code is a good idea.

When the class itself grows too large for one source file, you can also split up the class using mix-in classes (similar to having partial classes in C#).

For example:

class MainGuiClass(GuiMixin_FunctionalityA, GuiMixin_FunctionalityB):
    def __init__(self):
        GuiMixin_FunctionalityA.__init__(self)
        GuiMixin_FunctionalityB.__init__(self)

This comes in handy when the GUI consists of different functionalities (for instance a configuration tab, an execution tab or whatsoever).

AndiDog
Good answer, but you are being a little too cautious. Readability decreases for large functions in _all_ programming languages.
Muhammad Alkarouri
@Muhammad Alkarouri: In Brainfuck, readability seems constant independent of the function length. (But *yes*, you're right ;)
AndiDog
@AndiDog: Wow. Should have seen that one coming :)
Muhammad Alkarouri
@AndiDog: in Brainfuck, function reads you!
Rafe Kettler
+5  A: 

Either store some of those widget references in instance variables or return them (a minimal set mind you; you want to Reduce Coupling) and store them in local variables in __init__ before passing the relevant ones as arguments to your subsequent construction helpers. The latter is cleaner, but requires that things be decoupled enough that you can create an ordering that makes it possible.

Donal Fellows
Thanks, this is what I really should be doing. I guess I'll just have each `_init_something` return the widgets that it initializes.
Rafe Kettler
@Rafe: Just the “interesting” widgets. GUIs often have other things like labels and frames that aren't interesting; they're like the set that the interesting widgets (entries, listboxes, canvases, etc.) play their part on. (Of course, sometimes entries aren't really interesting and labels are, but that depends on the details of the GUI you're building…)
Donal Fellows
+1  A: 

Why don't you make your widgets that you need to refer to, instance variables. This is what I usaully do and seems to be quite a common approach.

e.g.

self.some_widget
volting
I suppose I could define each widget in `__init__` and then run methods to do the actual configuration and packing of each widget.
Rafe Kettler
Thats not exactly what I was suggesting. Although I do usually have a separate method to the do the Layout, you can still define your widgets in your `__init__widget() ` methods, but making your widgets instance variables means that you can access them else where.
volting