main window in tkinter app

W

William Gill

A short while ago someone posted that(unlike the examples) you should
use Tk as the base for your main window in tkinter apps, not Frame. Thus :

class MyMain(Frame):
def __init__(self, master):
self.root = master
self.master=master
self.createWidgets()
def createWidgets():
...
root = Tk()
app = MyMain(root)
app.master.title("Object Editor")
root.mainloop()

would become:

class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

When I try converting to this approach I run into a problem with the
__init__() method. It appears to go into an infinite loop in
tkinter.__getattr__().

If I omit __init__() I get a properly titled window, but must explicitly
call my createWidgets method from __main__.

class MyMain(Tk):
createWidgets()
...
...

app = MyMain()
app.title("My App")
app.createWidgets()
app.mainloop()

Am I missing something?

Bill
 
E

Eric Brunel

A short while ago someone posted that(unlike the examples) you should
use Tk as the base for your main window in tkinter apps, not Frame. Thus :

class MyMain(Frame):
def __init__(self, master):
self.root = master
self.master=master
self.createWidgets()
def createWidgets():
...
root = Tk()
app = MyMain(root)
app.master.title("Object Editor")
root.mainloop()

would become:

class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

When I try converting to this approach I run into a problem with the
__init__() method. It appears to go into an infinite loop in
tkinter.__getattr__().
[...]

I never ran into this problem. Can you please post a short script showing this behavior? Without knowing what you exactly do in your __init__ and createWidgets method, it's quite hard to figure out what happens...
 
W

William Gill

I never ran into this problem. ...

O.K. That, means I probably have something else wrong. I will need to
start with a 'clean slate' instead of trying to modify existing code.
It's getting to convoluted to follow anyway after all the cobbling I've
done.

If I get a repeat of the original problem I will post the code and the
exact error message, but at least now I know It SHOULD work.

Thanks

Bill,


Eric said:
A short while ago someone posted that(unlike the examples) you should
use Tk as the base for your main window in tkinter apps, not Frame.
Thus :

class MyMain(Frame):
def __init__(self, master):
self.root = master
self.master=master
self.createWidgets()
def createWidgets():
...
root = Tk()
app = MyMain(root)
app.master.title("Object Editor")
root.mainloop()

would become:

class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

When I try converting to this approach I run into a problem with the
__init__() method. It appears to go into an infinite loop in
tkinter.__getattr__().

[...]

I never ran into this problem. Can you please post a short script
showing this behavior? Without knowing what you exactly do in your
__init__ and createWidgets method, it's quite hard to figure out what
happens...
 
W

William Gill

O.K. I tried from scratch, and the following snippet produces an
infinite loop saying:

File "C:\Python24\lib\lib-tk\Tkinter.py", line 1647, in __getattr__
return getattr(self.tk, attr)

If I comment out the __init__ method, I get the titled window, and print
out self.var ('1')


import os
from Tkinter import *

class MyApp(Tk):
var=1
def __init__(self):
pass
def getval(self):
return self.var


app = MyApp()

app.title("An App")
print app.getval()
app.mainloop()


Eric said:
A short while ago someone posted that(unlike the examples) you should
use Tk as the base for your main window in tkinter apps, not Frame.
Thus :

class MyMain(Frame):
def __init__(self, master):
self.root = master
self.master=master
self.createWidgets()
def createWidgets():
...
root = Tk()
app = MyMain(root)
app.master.title("Object Editor")
root.mainloop()

would become:

class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

When I try converting to this approach I run into a problem with the
__init__() method. It appears to go into an infinite loop in
tkinter.__getattr__().

[...]

I never ran into this problem. Can you please post a short script
showing this behavior? Without knowing what you exactly do in your
__init__ and createWidgets method, it's quite hard to figure out what
happens...
 
W

William Gill

It also seems to operate the same with or without " app.mainloop()". Is
an explicit call to mainloop needed?

William said:
O.K. I tried from scratch, and the following snippet produces an
infinite loop saying:

File "C:\Python24\lib\lib-tk\Tkinter.py", line 1647, in __getattr__
return getattr(self.tk, attr)

If I comment out the __init__ method, I get the titled window, and print
out self.var ('1')


import os
from Tkinter import *

class MyApp(Tk):
var=1
def __init__(self):
pass
def getval(self):
return self.var


app = MyApp()

app.title("An App")
print app.getval()
app.mainloop()


Eric said:
A short while ago someone posted that(unlike the examples) you should
use Tk as the base for your main window in tkinter apps, not Frame.
Thus :

class MyMain(Frame):
def __init__(self, master):
self.root = master
self.master=master
self.createWidgets()
def createWidgets():
...
root = Tk()
app = MyMain(root)
app.master.title("Object Editor")
root.mainloop()

would become:

class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

When I try converting to this approach I run into a problem with the
__init__() method. It appears to go into an infinite loop in
tkinter.__getattr__().


[...]

I never ran into this problem. Can you please post a short script
showing this behavior? Without knowing what you exactly do in your
__init__ and createWidgets method, it's quite hard to figure out what
happens...
 
C

Christopher Subich

William said:
O.K. I tried from scratch, and the following snippet produces an
infinite loop saying:

File "C:\Python24\lib\lib-tk\Tkinter.py", line 1647, in __getattr__
return getattr(self.tk, attr)

If I comment out the __init__ method, I get the titled window, and print
out self.var ('1')


import os
from Tkinter import *

class MyApp(Tk):
var=1
def __init__(self):
pass
def getval(self):
return self.var


app = MyApp()

app.title("An App")
print app.getval()
app.mainloop()

You're not calling the parent's __init__ inside your derived class. I
would point out where the Python Tutorial points out that you should do
this, but it's not in the obvious place (Classes: Inheritance).

Python does -not- automagically call parent-class __init__s for derived
classes, you must do that explicitly. Changing the definition of your
class to the following works: var=1
def __init__(self):
Tk.__init__(self)
pass
def getval(self):
return self.var

It works when you comment out __init__ because of a quirk in Python's
name resolution. As you'd logically expect, if you don't define a
function in a derived class but call it (such as instance.method()), it
will call the method from the base class.

You just proved that this works for __init__ methods also. When you
didn't define __init__ for your derived class, MyApp() called
Tk.__init__(), which Does the Right Thing in terms of setting up all the
specific Tkinter-specific members.
 
W

William Gill

That does it!, thanks.

Thinking about it, when I created a derived class with an __init__
method, I overrode the base class's init. It should have been
intuitive that I needed to explicitly call baseclass.__init(self), it
wasn't. It might have hit me if the fault was related to someting in
baseclass.__init() not taking place, but the recursion loop didn't give
me a clue. Any idea why failing to init the base class caused the loop?


Bill
 
C

Christopher Subich

William said:
That does it!, thanks.

Thinking about it, when I created a derived class with an __init__
method, I overrode the base class's init. It should have been
intuitive that I needed to explicitly call baseclass.__init(self), it
wasn't. It might have hit me if the fault was related to someting in
baseclass.__init() not taking place, but the recursion loop didn't give
me a clue. Any idea why failing to init the base class caused the loop?

You never pasted the first part of the traceback:

File "<pyshell#204>", line 1, in -toplevel-
app.title('frob')
File "C:\Python24\Lib\lib-tk\Tkinter.py", line 1531, in wm_title
return self.tk.call('wm', 'title', self._w, string)
File "C:\Python24\Lib\lib-tk\Tkinter.py", line 1654, in __getattr__
return getattr(self.tk, attr)
File "C:\Python24\Lib\lib-tk\Tkinter.py", line 1654, in __getattr__
return getattr(self.tk, attr)

When you didn't call Tk.__init__(self), self.tk was never initialized.
Further, it's obvious from the traceback that Tk implements a
__getattr__ method; from the python docs:

__getattr__(self,name): Called when an attribute lookup has not found
the attribute in the usual places

Since self.tk doesn't exist, __getattr__(self,tk) was called. Without
looking at the TKinter source code, we can surmise that there's a
default behavior of "if self doesn't have it, return the relevant
attribute from within self.tk." The problem, of course, arises when
self.tk doesn't exist -- this causes the self.tk reference to call
self.__getattr__('tk'), which is recursive.

The infinite recursion, then, is a mild bug in TKinter that doesn't show
itself during normal use. The proper solution should be to replace the
self.tk call with either self.__dict__['tk'], or to make Tk a new-style
class and use object.__getattribute__(self,'tk'). (Note: there are
probably some fine details that I'm missing in this 'solution', so take
it with a potato-sized grain of salt. The general principle still
applies though.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top