Reputation: 495
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:
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)
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"><Primary>o</attribute>
</item>
</section>
<section>
<item>
<attribute name ="label" translatable="yes">Guardar</attribute>
<attribute name="action">app.save</attribute>
<attribute name="accel"><Primary>s</attribute>
</item>
<item>
<attribute name ="label" translatable="yes">Guardar Como...</attribute>
<attribute name="action">app.save-as</attribute>
<!--<attribute name="accel"><Primary>a</attribute>-->
</item>
</section>
<section>
<item>
<attribute name ="label" translatable="yes">Salir</attribute>
<attribute name="action">app.quit</attribute>
<attribute name="accel"><Primary>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
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