10.4. Layout Container

The Layout container is similar to the Fixed container except that it implements an infinite (where infinity is less than 2^32) scrolling area. The X window system has a limitation where windows can be at most 32767 pixels wide or tall. The Layout container gets around this limitation by doing some exotic stuff using window and bit gravities, so that you can have smooth scrolling even when you have many child widgets in your scrolling area.

A Layout container is created using:
 
layout = GtkLayout()
layout = GtkLayout(hadjustment, vadjustment)

As you can see, you can optionally specify the Adjustment objects that the Layout widget will use for its scrolling. If you don't specify the Adjustment objects, new ones will be created.

You can add and move widgets in the Layout container using the following two methods:
 
layout.put(widget, x, y)

layout.move(widget, x, y)

The size of the Layout container can be set using the next method:
 
layout.set_size(width, height)

Layout containers are one of the very few widgets in the GTK widget set that actively repaint themselves on screen as they are changed using the above methods (the vast majority of widgets queue requests which are then processed when control returns to the mainloop() function).

When you want to make a large number of changes to a Layout container, you can use the following two methods to disable and re-enable this repainting functionality:
 
layout.freeze()

layout.thaw()

The final four methods for use with Layout widgets are for manipulating the horizontal and vertical adjustment widgets:
 
hadj = layout.get_hadjustment()

vadj = layout.get_vadjustment()

layout.set_hadjustment(adjustment)

layout.set_vadjustment(adjustment)

The layout.py example program creates three buttons and puts them in a layout widget. when a button is clicked, it is moved to a random location in the layout. Figure 10.3 illustrates the starting display of the program:

Figure 10.3 Layout Example

The layout.py source code is:
 
     1   #!/usr/bin/env python
    2   
    3   # example layout.py
    4   
    5   import gtk
    6   import whrandom
    7   
    8   class LayoutExample:
    9       def OnWindowDeleteEvent(self, widget, event):
   10           # return false so that window will be destroyed
   11           return gtk.FALSE
   12   
   13       def OnWindowDestroy(self, widget, *data):
   14           # exit main loop
   15           gtk.mainquit()
   16   
   17       def OnButtonClicked(self, button):
   18           # move the button
   19           # Note: it probably isn't necessary to freeze the layout widget here,
   20           # but better safe than sorry
   21           self.layout.freeze()
   22           self.layout.move(button, whrandom.randint(0,500),
   23                            whrandom.randint(0,500))
   24           self.layout.thaw()
   25   
   26       def __init__(self):
   27           # create the top level window
   28           window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
   29           window.set_title("Layout Example")
   30           window.set_default_size(300, 300)
   31           window.connect("delete-event", self.OnWindowDeleteEvent)
   32           window.connect("destroy", self.OnWindowDestroy)
   33           # create the table and pack into the window
   34           table = gtk.GtkTable(2, 2, gtk.FALSE)
   35           window.add(table)
   36           # create the layout widget and pack into the table
   37           self.layout = gtk.GtkLayout(None, None)
   38           self.layout.set_size(600, 600)
   39           table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
   40                        gtk.FILL|gtk.EXPAND, 0, 0)
   41           # create the scrollbars and pack into the table
   42           vScrollbar = gtk.GtkVScrollbar(None)
   43           table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,
   44                        gtk.FILL|gtk.SHRINK, 0, 0)
   45           hScrollbar = gtk.GtkHScrollbar(None)
   46           table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,
   47                        gtk.FILL|gtk.SHRINK, 0, 0) 
   48           # tell the scrollbars to use the layout widget's adjustments
   49           vAdjust = self.layout.get_vadjustment()
   50           vScrollbar.set_adjustment(vAdjust)
   51           hAdjust = self.layout.get_hadjustment()
   52           hScrollbar.set_adjustment(hAdjust)
   53           # create 3 buttons and put them into the layout widget
   54           button = gtk.GtkButton("Press Me")
   55           button.connect("clicked", self.OnButtonClicked)
   56           self.layout.put(button, 0, 0)
   57           button = gtk.GtkButton("Press Me")
   58           button.connect("clicked", self.OnButtonClicked)
   59           self.layout.put(button, 100, 0)
   60           button = gtk.GtkButton("Press Me")
   61           button.connect("clicked", self.OnButtonClicked)
   62           self.layout.put(button, 200, 0)
   63           # show all the widgets
   64           window.show_all()
   65   
   66   def main():
   67       # enter the main loop
   68       gtk.mainloop()
   69       return 0
   70   
   71   if __name__ == "__main__":
   72       LayoutExample()
   73       main()