Tkinter grid layout

R

Richard Lewis

Hi there,

I've got a tree control in Tkinter (using the ESRF Tree module) but I
can't get it to layout how I want it.

I'd like to have it so that it streches north/south (anchored to the top
and bottom), is of a fixed width and is anchored to the left hand side.
Here's my code (its derived from one of the examples from the ESRF web
site):

class MainWindow(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.document = # new DOM document
self.create_site_list()

def create_site_list(self):
self.list_model = ListModel(self.document)
# ListModel class returns the DOM outline as a simple Python data
structure

self.site_list = Tree.Tree(master=self,\
root_id="root",\
root_label="Site",\
get_contents_callback=self.get_list_item,\
# get_list_item uses the list_model to build list nodes
width=300)

self.site_list.grid(row=0, column=0, sticky=N+SW)

self.grid_rowconfigure(0, weight=1)

vsb = Scrollbar(self, orient=VERTICAL)
vsb.grid(row=0, column=1, sticky=NS)
self.site_list.configure(yscrollcommand=vsb.set)
vsb.configure(command=self.site_list.yview)

hsb = Scrollbar(self, orient=HORIZONTAL)
hsb.grid(row=1, column=0, sticky=EW+S)
self.site_list.configure(xscrollcommand=hsb.set)
hsb.configure(command=self.site_list.xview)

self.site_list.focus_set()


This code makes it centred in the east/west direction and a constant
height anchored to the top. I guess its the sticky values I need to play
with (?) but I can't find the right combination. I've given the code
with the ones that seem logically correct (to me!)

I expect this is probably quite trivial for someone who knows what
they're doing.

Any ideas?

Cheers,
Richard
 
E

Eric Brunel

Hi there,

I've got a tree control in Tkinter (using the ESRF Tree module) but I
can't get it to layout how I want it.

I'd like to have it so that it streches north/south (anchored to the top
and bottom), is of a fixed width and is anchored to the left hand side.
Here's my code (its derived from one of the examples from the ESRF web
site):

class MainWindow(Frame):
[snip code]

First of all, it is not a good idea to make your so-called window inherit from Frame: a Frame is not a window in tk, but a generic container. Creating a Frame without a container window will automatically initialize the tk toolkit, creating a default window which will become the default parent for widgets where you don't specify one. According to the well-known "explicit is better than implicit" principle, if a MainWindow instance are actually the main window for your application, you'd really better inherit from the Tkinter class for the main window, which is Tk. Doing it this way has a lot of advantages over what you do; for example, if you later have to create a menu bar for your application, you will be able to do it in the MainWindow class. If you inherit from Frame, you won't get any access to the actual window, so you won't be able to create a menu in it.

This may seems to be unrelated to your problem, but it's not: by creating a Frame, you introduce one more unneeded container. So, in some code you don't show here, you have to insert the MainWindow instance into its parent window via a call to its pack or grid method. Since the code you show seems to be correct, I guess the problem is in this call to pack or grid, which probably does not tell the Frame how to behave when its parent window is resized, causing it to get the default behaviour, which is "do nothing at all".

To be sure this actually is the problem, try to replace the line:
Frame.__init__(self, master)
in MainWindow.__init__ by:
Frame.__init__(self, master, bg='blue', bd=2)
This way, a blue border will appear around the frame, allowing you to see how it grows.
Then run your application, and resize the window. You should see that the frame does not grow when the window grows, explaining why your tree deos not grow (in fact, it would grow if its container did; but its container doesn't...)

So you should either make your MainWindow class inherit from Tk, which eliminates the unneeded container and the problems it may cause, or make sure the pack or grid on your MainWindow instance actually tells the container to grow with its container. With pack, it's quite easy: just do myWindow.pack(fill=BOTH, expand=1). With grid, it's a bit more complicated, since you will have to configure the grid on the container.

But basically, my advice would be:
- Never make your windows inherit from Frame; make them inherit from Tk for the main window or from Toplevel for all other ones
- When you have resize problems, always check the whole widget hierarchy from the actual window down to the widget showing the problem. The cause is very often not on the widget itself, but on one of its containers.

HTH
 
R

Richard Lewis

Hi there,

I've got a tree control in Tkinter (using the ESRF Tree module) but I
can't get it to layout how I want it.

I'd like to have it so that it streches north/south (anchored to the top
and bottom), is of a fixed width and is anchored to the left hand side.
Here's my code (its derived from one of the examples from the ESRF web
site):

class MainWindow(Frame):
[snip code]

First of all, it is not a good idea to make your so-called window inherit
from Frame: a Frame is not a window in tk, but a generic container.
[....]
But basically, my advice would be:
- Never make your windows inherit from Frame; make them inherit from Tk
for the main window or from Toplevel for all other ones
- When you have resize problems, always check the whole widget hierarchy
from the actual window down to the widget showing the problem. The cause
is very often not on the widget itself, but on one of its containers.
And very sound advice it turns out to be! I've changed my MainWindow
class to inherit from Tk and now all the layout works fine.

Thanks very much for your help!

Cheers,
Richard
 
W

William Gill

Excuse me for intruding, but I followed examples and ended up with a
similar architecture:

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

Erick, are you saying it should be modified to something like :

from Tkinter import *
class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

Thanks,
Bill

Eric said:
Hi there,

I've got a tree control in Tkinter (using the ESRF Tree module) but I
can't get it to layout how I want it.

I'd like to have it so that it streches north/south (anchored to the top
and bottom), is of a fixed width and is anchored to the left hand side.
Here's my code (its derived from one of the examples from the ESRF web
site):

class MainWindow(Frame):

[snip code]

First of all, it is not a good idea to make your so-called window
inherit from Frame: a Frame is not a window in tk, but a generic
container. Creating a Frame without a container window will
automatically initialize the tk toolkit, creating a default window which
will become the default parent for widgets where you don't specify one.
According to the well-known "explicit is better than implicit"
principle, if a MainWindow instance are actually the main window for
your application, you'd really better inherit from the Tkinter class for
the main window, which is Tk. Doing it this way has a lot of advantages
over what you do; for example, if you later have to create a menu bar
for your application, you will be able to do it in the MainWindow class.
If you inherit from Frame, you won't get any access to the actual
window, so you won't be able to create a menu in it.

This may seems to be unrelated to your problem, but it's not: by
creating a Frame, you introduce one more unneeded container. So, in some
code you don't show here, you have to insert the MainWindow instance
into its parent window via a call to its pack or grid method. Since the
code you show seems to be correct, I guess the problem is in this call
to pack or grid, which probably does not tell the Frame how to behave
when its parent window is resized, causing it to get the default
behaviour, which is "do nothing at all".

To be sure this actually is the problem, try to replace the line:
Frame.__init__(self, master)
in MainWindow.__init__ by:
Frame.__init__(self, master, bg='blue', bd=2)
This way, a blue border will appear around the frame, allowing you to
see how it grows.
Then run your application, and resize the window. You should see that
the frame does not grow when the window grows, explaining why your tree
deos not grow (in fact, it would grow if its container did; but its
container doesn't...)

So you should either make your MainWindow class inherit from Tk, which
eliminates the unneeded container and the problems it may cause, or make
sure the pack or grid on your MainWindow instance actually tells the
container to grow with its container. With pack, it's quite easy: just
do myWindow.pack(fill=BOTH, expand=1). With grid, it's a bit more
complicated, since you will have to configure the grid on the container.

But basically, my advice would be:
- Never make your windows inherit from Frame; make them inherit from Tk
for the main window or from Toplevel for all other ones
- When you have resize problems, always check the whole widget hierarchy
from the actual window down to the widget showing the problem. The cause
is very often not on the widget itself, but on one of its containers.

HTH
 
R

Richard Lewis

Excuse me for intruding, but I followed examples and ended up with a
similar architecture:

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

Erick, are you saying it should be modified to something like :

from Tkinter import *
class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()
This is what I've got now, and it works. I don't think I've seen many
examples which inherited from Tk, but it certainly solved my problem.
And I see the logic in it: Tk is the main window of an application,
while Frame is, as Eric said, just a generic container.

Cheers,
Richard
 
E

Eric Brunel

Excuse me for intruding, but I followed examples and ended up with a
similar architecture:

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

Erick, are you saying it should be modified to something like :

from Tkinter import *
class MyMain(Tk):
...
...
app = MyMain()
app.title("My App")
app.mainloop()

Well, basically, that's what I'm saying; but your example is a bit better than the OP's.

Basically, the problem is that an instance of MyMain will just be some kind of graphical component that can be inserted into basically anything. So nothing prevents me to do for example:

root = Tk()
Label(root, text='This is my application').pack(side=TOP)
frm = Frame(root)
frm.pack(side=TOP)
app = MyMain(frm)
# You don't show where you pack or grid your MayMain instance in its
# parent, so I'm doing it explicitely here...
app.pack()
root.mainloop()

So the container for MyMain is a Tk instance in your example, and a Frame instance in mine. And it works, because it's basically the use case for Frame sub-classes: they can be pack'ed or grid'ed or place'd into anything.

So MyMain cannot make any assumption on its container (self.master in your first example), since it can be any valid container. So there are many things that you just can't do in MyMain, because you don't have a window. For example, you can't set the window title, or define a menu bar, since all these are defined via methods only available on windows, i.e. Tk or Toplevel instances.

Basically, you did this right, since you call app.master.title(...) in the main script, where you know that app.master is a Tk instance. But what can be the reason to do it *outside* MyMain? Isn't it the window itself that knows what title it should have?

So in your second version, you can do:

from Tkinter import *
class MyMain(Tk):
Tk.__init__(self)
self.title("My App")
...
....
app = MyMain()
app.mainloop()

And this always works: since an instance of MyMain is an instance of Tk, you do have a window on which to call title. Note that this cannot be safely done in your first version, since self.master may not have a title method (see my example above).

So you should really stick to the following rules:
- If you write a class defining the main window for your application, make it inherit from Tkinter.Tk
- If you write a class defining a "secondary" window for your application, make it inherit from Tkinter.Toplevel
- If you write a class defining a graphical component that can be placed anywhere in a GUI, make it inherit from Tkinter.Frame
This is basically just a matter of good OO programming: you should choose the most accurate super-classes for the classes you define, or someday, you'll run into problems...

HTH
 
C

Christopher Subich

Eric said:
So you should either make your MainWindow class inherit from Tk, which
eliminates the unneeded container and the problems it may cause, or make
sure the pack or grid on your MainWindow instance actually tells the
container to grow with its container. With pack, it's quite easy: just
do myWindow.pack(fill=BOTH, expand=1). With grid, it's a bit more
complicated, since you will have to configure the grid on the container.

To expand on this, the grid-method uses a few calls that aren't
immediately obvious. Specifically, the containing object must have row
and columnconfigure called on them:

This creats a window containing a text widget above an entry widget.
Both will resize horizontally to fill the entire window, and the text
widget will resize vertically.
 

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

No members online now.

Forum statistics

Threads
474,260
Messages
2,571,308
Members
47,965
Latest member
BruceVesse

Latest Threads

Top