deko
deko

Reputation: 495

Python3 Gtk+ How to obtain the following grid layout? (picture)

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.

The following image is the layout I'm trying to obtain:

enter image description here

As you can see I already have a menubar (Archivo | Preferencias | Ayuda) and a toolbar (Nuevo | Abrir | Deshacer | Pantalla completa).

So, in the RED rectangle I'm trying to get a scrolledwindow (where imagine that I have a list of names of images in a big column), the GREEN line would be a separator, and finally the BLUE rectangle would be a widget to display the image when selecting an image of the scrolledwindow.

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)

enter image description here

Finally, this is the code of this UI, in case you want to run it. The grid is built in the Window class initializtion.

application.py

# -*- encoding: utf-8 -*-

# Author:
# Diego Suárez García, [email protected]

# ## ## ## ## ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## ## ## ## # 
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ #
#                                                                           #
#   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(
        ) & 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)

menubar.ui

<?xml version="1.0" encoding="UTF-8"?>

<interface>
  <menu id="menubar">
    <submenu>
      <attribute name="label" translatable="yes">_Archivo</attribute>
      <section>
        <item>
          <attribute name="label" translatable="yes">Nuevo</attribute>
          <attribute name="action">app.new</attribute>
        </item>
        <item>
          <attribute name="label" translatable="yes">Abrir...</attribute>
          <attribute name="action">app.open</attribute>
          <attribute name="accel">&lt;Primary&gt;o</attribute>
        </item>
      </section>
      <section>
        <item>
          <attribute name ="label" translatable="yes">Guardar</attribute>
          <attribute name="action">app.save</attribute>
          <attribute name="accel">&lt;Primary&gt;s</attribute>
        </item>
        <item>
          <attribute name ="label" translatable="yes">Guardar Como...</attribute>
          <attribute name="action">app.save-as</attribute>
          <!--<attribute name="accel">&lt;Primary&gt;a</attribute>-->
        </item>
      </section>
      <section>
        <item>
          <attribute name ="label" translatable="yes">Salir</attribute>
          <attribute name="action">app.quit</attribute>
          <attribute name="accel">&lt;Primary&gt;q</attribute>
        </item>
      </section>
    </submenu>
    <submenu>
      <attribute name="label" translatable="yes">_Preferencias</attribute>
      <section>
        <item>
          <attribute name="label" translatable="yes">_Idioma</attribute>
          <attribute name="action">app.language</attribute>
        </item>
      </section>
    </submenu>
    <submenu>
      <attribute name="label" translatable="yes">A_yuda</attribute>
      <section>
        <item>
          <attribute name="label" translatable="yes">Acerca de FreeComet</attribute>
          <attribute name="action">app.about</attribute>
        </item>
      </section>
    </submenu>
  </menu>
</interface>

Upvotes: 1

Views: 825

Answers (1)

theGtknerd
theGtknerd

Reputation: 3745

This is a quickly put together .ui file you might want to look at. As a sidenote, I put all the widgets I can into the .ui file, beacuse I can view the hierarchy in Glade.

Instead of using a grid (which works best for multiple rows and multiple columns of widgets), I have used a Gtk.Box to put the different rows of widgets in, and then used a Gtk.Paned to separate the scrolled window on the left from the image viewer on the right. A Gtk.Paned has the added bonus of having a draggable divider.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkApplicationWindow">
    <property name="can_focus">False</property>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_File</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-new</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-save</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-save-as</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkSeparatorMenuItem">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-quit</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Edit</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-cut</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-copy</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-paste</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-delete</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_View</property>
                <property name="use_underline">True</property>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Help</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkToolbar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="is_important">True</property>
                <property name="action_name">win.new-project</property>
                <property name="label" translatable="yes">toolbutton</property>
                <property name="use_underline">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkPaned">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="position">100</property>
            <property name="position_set">True</property>
            <child>
              <object class="GtkScrolledWindow">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="shadow_type">in</property>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="resize">False</property>
                <property name="shrink">True</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Upvotes: 1

Related Questions