title: Python3 Gtk+ How to obtain the following grid layout? (picture)
body: <p>I need a grid with different widgets for my main application window. Since I am quite new to Gtk, I decided to start off by creating a simple layout so I could understand better the logic behind.</p>
  
  <p>The following image is the layout I'm trying to obtain:</p>
  
  <p><a href="https://i.stack.imgur.com/sk14Q.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/sk14Q.png" alt="enter image description here"></a></p>
  
  <p>As you can see I already have a <strong>menubar</strong> <em>(Archivo | Preferencias | Ayuda)</em> and a <strong>toolbar</strong> <em>(Nuevo | Abrir | Deshacer | Pantalla completa)</em>.</p>
  
  <p>So, in the RED rectangle I'm trying to get a <strong>scrolledwindow</strong> (where imagine that I have a list of names of images in a big column), the GREEN line would be a <strong>separator</strong>, and finally the BLUE rectangle would be a widget to display the image when selecting an image of the scrolledwindow.</p>
  
  <p>The following image is the current output. Note that the scrolledwindow is right below the toolbar taking all the vertical space left and 99% of the width, and the 1% space of the width left is where the separator is being displayed on the right edge of the window (hence the white portion on the right side of the toolbar)</p>
  
  <p><a href="https://i.stack.imgur.com/yfOgt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/yfOgt.png" alt="enter image description here"></a></p>
  
  <p>Finally, this is the code of this UI, in case you want to run it. <strong>The grid is built in the Window class initializtion.</strong></p>
  
  <p><strong>application.py</strong></p>
  
  <pre><code># -*- encoding: utf-8 -*-
  
  # Author:
  # Diego Suárez García, d.suarez@udc.es
  
  # ## ## ## ## ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## ## ## ## # 
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ #
  #                                                                           #
  #   application.py :                                                        #
  #                                                                           #
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # 
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
  
  import json
  
  import sys
  
  import gi
  gi.require_version('Gtk', '3.0')
  from gi.repository import Gtk
  from gi.repository import Gio
  from gi.repository import Gdk
  
  # Constants
  MENU_FILE = "menubar.ui"
  
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ #
  # ~                             Window Class                              ~ #
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # 
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
  
  class Window(Gtk.ApplicationWindow):
  
      def __init__(self, app):
  
          # Main window initialization
          super(Window, self).__init__(title="FreeComet 1.0", application=app)
          self.set_default_size(800, 600)
  
          # The project filename
          self.project_filename = None
  
          # The grid to attach the toolbar
          grid = Gtk.Grid()
          # The toolbar
          toolbar = self.__create_toolbar()
          toolbar.set_hexpand(True)               # with extra horizontal space
  
          # The Toolbar Actions
          undo_action = Gio.SimpleAction.new("undo", None)
          undo_action.connect("activate", self.undo_callback)
          self.add_action(undo_action)
  
          fullscreen_action = Gio.SimpleAction.new("fullscreen", None)
          fullscreen_action.connect("activate", self.fullscreen_callback)
          self.add_action(fullscreen_action)
  
          new_action = Gio.SimpleAction.new("new-project", None)
          new_action.connect("activate", self.new_project_callback)
          self.add_action(new_action)
  
          open_project_action = Gio.SimpleAction.new("open-project", None)
          open_project_action.connect("activate", self.open_project_callback)
          self.add_action(open_project_action)
  
          # The scrolledwindow
          scrolled_window = Gtk.ScrolledWindow()
          scrolled_window.set_border_width(10)
          scrolled_window.set_vexpand(True)
          scrolled_window.set_hexpand(False)
          # there is always the scrollbar (otherwise: AUTOMATIC - only if needed
          # - or NEVER)
          scrolled_window.set_policy(
              Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
  
          # a horizontal separator
          hseparator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
          # a vertical separator
          vseparator = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
  
          # Build grid
          grid.add(toolbar)
          grid.attach(scrolled_window, 0, 1, 1, 1)
          grid.attach(vseparator, 1, 1, 1, 1)
          grid.attach(Gtk.Button(), 2, 1, 1, 1)
          # Add the grid to the window
          self.add(grid)
  
  
  
  
      def __create_toolbar(self):
  
          # Toolbar initialization (primary toolbar of the application)
          toolbar = Gtk.Toolbar()
          toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
  
          # Button for the 'new' action
          new_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_NEW)
          new_button.set_property("has-tooltip", True)
          new_button.connect("query-tooltip", self.new_project_tooltip_callback)
          new_button.set_is_important(True)
          toolbar.insert(new_button, 0)
          new_button.set_action_name("win.new-project")
  
          # Button for the 'open' action
          open_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_OPEN)
          open_button.set_property("has-tooltip", True)
          open_button.connect("query-tooltip", self.open_project_tooltip_callback)
          open_button.set_is_important(True)
          toolbar.insert(open_button, 1)
          open_button.set_action_name("win.open-project")
  
          # Button for the 'undo' action
          undo_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_UNDO)
          undo_button.set_property("has-tooltip", True)
          undo_button.connect("query-tooltip", self.undo_tooltip_callback)
          undo_button.set_is_important(True)
          toolbar.insert(undo_button, 2)
          undo_button.set_action_name("win.undo")
  
          # Button for the 'fullscreen/leave fullscreen' action
          self.fullscreen_button = Gtk.ToolButton.new_from_stock(
              Gtk.STOCK_FULLSCREEN)
          self.fullscreen_button.set_property("has-tooltip", True)
          self.fullscreen_button.connect("query-tooltip", 
              self.fullscreen_tooltip_callback)
          self.fullscreen_button.set_is_important(True)
          toolbar.insert(self.fullscreen_button, 3)
          self.fullscreen_button.set_action_name("win.fullscreen")
  
          # return the complete toolbar
          return toolbar
  
  
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ #
  # ~                               Callbacks                               ~ #
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # 
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
  
      #                                            #
      # #       Toolbar Tooltips Callbacks       # #
      #                                            #
  
      def new_project_tooltip_callback(self, widget, x, y, keyboard_mode, tooltip):
          tooltip.set_text("Crear un nuevo proyecto")
          tooltip.set_icon_from_stock("gtk-new", Gtk.IconSize.MENU)
          return True
  
      def open_project_tooltip_callback(self, widget, x, y, keyboard_mode, tooltip):
          tooltip.set_text("Abrir un proyecto existente")
          tooltip.set_icon_from_stock("gtk-open", Gtk.IconSize.MENU)
          return True
  
      def undo_tooltip_callback(self, widget, x, y, keyboard_mode, tooltip):
          tooltip.set_text("Deshacer la última acción")
          tooltip.set_icon_from_stock("gtk-undo", Gtk.IconSize.MENU)
          return True
  
      def fullscreen_tooltip_callback(self, widget, x, y, keyboard_mode, tooltip):
          tooltip.set_text("Modo pantalla completa")
          tooltip.set_icon_from_stock("gtk-fullscreen", Gtk.IconSize.MENU)
          return True
  
      #                                            #
      # #           Toolbar Callbacks            # #
      #                                            #
  
      def new_project_callback(self, action, parameter):
          print("You clicked \"New Project\".")
  
      def open_project_callback(self, action, parameter):
          # Create a filechooserdialog to open:
          # The arguments are: title of the window, parent_window, action,
          # (buttons, response)
          open_project_dialog = Gtk.FileChooserDialog("Seleccione un proyecto", self,
                                              Gtk.FileChooserAction.OPEN,
                                             (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                              Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))
  
          # Not only local files can be selected in the file selector
          open_project_dialog.set_local_only(False)
          # Connect the dialog with the callback function open_response_callback_response()
          open_project_dialog.connect("response", self.__open_project_callback_response)
          # Show the dialog
          open_project_dialog.show()
  
      def __open_project_callback_response(self, dialog, response_id):
  
          open_project_dialog = dialog
          if response_id == Gtk.ResponseType.ACCEPT:
              # Filename we get from the FileChooserDialog
              self.project_filename = open_project_dialog.get_filename()
  
          dialog.destroy()
  
          # Read project
          #data = json.load(self.project_filename)
  
      def undo_callback(self, action, parameter):
          print("You clicked \"Undo\".")
  
      def fullscreen_callback(self, action, parameter):
          # check if the state is the same as Gdk.WindowState.FULLSCREEN, which
          # is a bit flag
          is_fullscreen = self.get_window().get_state(
          ) &amp; Gdk.WindowState.FULLSCREEN != 0
          if not is_fullscreen:
              self.fullscreen_button.set_stock_id(Gtk.STOCK_LEAVE_FULLSCREEN)
              self.fullscreen()
          else:
              self.fullscreen_button.set_stock_id(Gtk.STOCK_FULLSCREEN)
              self.unfullscreen()
  
  
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ #
  # ~                           Application Class                           ~ #
  # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # 
  ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##    
  
  class Application(Gtk.Application):
  
      def __init__(self):
          super(Application, self).__init__()
  
      def do_activate(self):    
          self._win_main = Window(self)
          self._win_main.show_all()
  
      def do_startup(self):
          # FIRST THING TO DO: do_startup()
          Gtk.Application.do_startup(self)
  
          # The Application Menubar
          builder = Gtk.Builder()
          try:
              builder.add_from_file(MENU_FILE)
          except:
              print("ERROR: " + MENU_FILE +" not found")
              sys.exit()
          self.set_menubar(builder.get_object("menubar"))
  
          # [2] The Menubar Actions
          new_project_action = Gio.SimpleAction.new("new", None)
          new_project_action.connect("activate", self.new_project_callback)
          self.add_action(new_project_action)
  
          open_project_action = Gio.SimpleAction.new("open", None)
          open_project_action.connect("activate", self.open_project_callback)
          self.add_action(open_project_action)
  
          save_action = Gio.SimpleAction.new("save", None)
          save_action.connect("activate", self.save_callback)
          self.add_action(save_action)
  
          save_as_action = Gio.SimpleAction.new("save-as", None)
          save_as_action.connect("activate", self.save_as_callback)
          self.add_action(save_as_action)
  
          quit_action = Gio.SimpleAction.new("quit", None)
          quit_action.connect("activate", self.quit_callback)
          self.add_action(quit_action)
  
  
      #                                   #
      # #       Menubar Callbacks       # #
      #                                   #
  
      def new_project_callback(self, action, parameter):
          print("You clicked \"New\"")
  
      def open_project_callback(self, action, parameter):
          self._win_main.open_project_callback(action, parameter)
  
      def save_callback(self, action, parameter):
          print("You clicked \"Save\"") 
  
      def save_as_callback(self, action, parameter):
          print("You clicked \"Save as..\"") 
  
      def quit_callback(self, action, parameter):
          self.quit()  
  
  
  if __name__ == "__main__":
      app = Application()
      app.run(sys.argv)
  </code></pre>
  
  <p><strong>menubar.ui</strong></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  
  &lt;interface&gt;
    &lt;menu id="menubar"&gt;
      &lt;submenu&gt;
        &lt;attribute name="label" translatable="yes"&gt;_Archivo&lt;/attribute&gt;
        &lt;section&gt;
          &lt;item&gt;
            &lt;attribute name="label" translatable="yes"&gt;Nuevo&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.new&lt;/attribute&gt;
          &lt;/item&gt;
          &lt;item&gt;
            &lt;attribute name="label" translatable="yes"&gt;Abrir...&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.open&lt;/attribute&gt;
            &lt;attribute name="accel"&gt;&amp;lt;Primary&amp;gt;o&lt;/attribute&gt;
          &lt;/item&gt;
        &lt;/section&gt;
        &lt;section&gt;
          &lt;item&gt;
            &lt;attribute name ="label" translatable="yes"&gt;Guardar&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.save&lt;/attribute&gt;
            &lt;attribute name="accel"&gt;&amp;lt;Primary&amp;gt;s&lt;/attribute&gt;
          &lt;/item&gt;
          &lt;item&gt;
            &lt;attribute name ="label" translatable="yes"&gt;Guardar Como...&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.save-as&lt;/attribute&gt;
            &lt;!--&lt;attribute name="accel"&gt;&amp;lt;Primary&amp;gt;a&lt;/attribute&gt;--&gt;
          &lt;/item&gt;
        &lt;/section&gt;
        &lt;section&gt;
          &lt;item&gt;
            &lt;attribute name ="label" translatable="yes"&gt;Salir&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.quit&lt;/attribute&gt;
            &lt;attribute name="accel"&gt;&amp;lt;Primary&amp;gt;q&lt;/attribute&gt;
          &lt;/item&gt;
        &lt;/section&gt;
      &lt;/submenu&gt;
      &lt;submenu&gt;
        &lt;attribute name="label" translatable="yes"&gt;_Preferencias&lt;/attribute&gt;
        &lt;section&gt;
          &lt;item&gt;
            &lt;attribute name="label" translatable="yes"&gt;_Idioma&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.language&lt;/attribute&gt;
          &lt;/item&gt;
        &lt;/section&gt;
      &lt;/submenu&gt;
      &lt;submenu&gt;
        &lt;attribute name="label" translatable="yes"&gt;A_yuda&lt;/attribute&gt;
        &lt;section&gt;
          &lt;item&gt;
            &lt;attribute name="label" translatable="yes"&gt;Acerca de FreeComet&lt;/attribute&gt;
            &lt;attribute name="action"&gt;app.about&lt;/attribute&gt;
          &lt;/item&gt;
        &lt;/section&gt;
      &lt;/submenu&gt;
    &lt;/menu&gt;
  &lt;/interface&gt;
  </code></pre>