PyGObject: Gtk.MenuBar
MenuBar
GTK+ mette a disposizione 2 diversi tipi di menu, Gtk.MenuBar
e Gtk.Toolbar.
Gtk.MenuBar è la barra dei menu standard che contiene una o più istanze di Gtk.MenuItem,
o una delle sue sottoclassi.
Il widget Gtk.Toolbar invece, è la toolbar classica contenente i bottoni che
permettono un accesso rapido alle funzioni più comuni gestite da una applicazione, ad esempio
“new document”, “print page”, “undo”. Contiene una o più istanze di Gtk.ToolItem,
o una delle sue sottoclassi.
Le properties principali sono:
Name | Type | Flags | Short Description |
---|---|---|---|
child-pack-direction | Gtk.PackDirection | r/w/en | La direzione di impacchettamento dei child nella menubar |
pack-direction Gtk.PackDirection | r/w/en | La direzione di impacchettamento della menubar |
Metodi
Oltre ai soliti getter e setter relativi alle properties dell’oggetto Gtk.MenuBar,
i principali metodi sono:
new()
Crea un nuovo oggetto Gtk.MenuBar
new_from_model(model)
Crea un nuovo oggetto Gtk.MenuBar e lo popola con gli elementi e i sottomenu ricavati dal model.
Gli elementi del menu creati sono connessi alle actions trovate nel Gtk.ApplicationWindow
al quale la menubar appartiene.
Parametri:
model: l’oggetto Gio.MenuModel dal quale estrapoliamo gli elementi
per popolare la menubar;
get_child_pack_direction()
Ritorna l’oggetto Gtk.PackDirection che rappresenta come i widget debbano essere
impacchettati all’interno di un child, nella menubar.
get_pack_direction()
Ritorna l’oggetto Gtk.PackDirection che rappresenta come gli elementi debbano
essere impacchettati nella menubar.
set_child_pack_direction(child_pack_dir)
Setta il modo in cui i widget debbano essere impacchettati all’interno di un child, nella menubar.
Parametri:
child_pack_dir: l’oggetto Gtk.PackDirection che indica il senso
di impacchettamento;
set_pack_direction(pack_dir)
Setta il modo in cui gli elementi debbano essere impacchettati nella menubar.
Parametri:
pack_dir: l’oggetto Gtk.PackDirection che indica il senso
di impacchettamento;
Actions
Le Actions rappresentano le operazioni che un utente può eseguire. Si crea una action creando
un’istanza della classe Gio.SimpleAction,
la si connette al segnale “activate”, cosicchè possa essere invocata una callback di riferimento.
Le actions, per comodità di organizzazione, possono essere mappate in widget
Gio.ActionMap, che permettono di mappare le diverse actions,
appartenenti a diversi gruppi, con l’utilizzo di un prefisso identificativo (ad esempio
“app.action_name” o “win.action_name”).
Come si crea il menu di una applicazione?
Si può effettuare in due modi, o creando un’istanza di Gio.Menu
e aggiungendo ad essa i vari oggetti Gio.MenuItem,
o leggendo da una stringa XML e costruendo il menu tramite Gtk.Builder.
Gio.Menu
Gio.Menu è una semplice implementazione di Gio.MenuModel ovvero un oggetto
rappresenta il contenuto di un menu, con una lista ordinata di elementi, che a loro volta
rappresentano le voci del menu. Ognuno di questi elementi è associato ad una action, che viene
attivata attraverso l’elemento stesso. L’oggetto Gio.Menu viene di fatto popolato da istanze di
Gio.MenuItem (gli elementi della lista).
Lo stesso Gio.Menu mette a disposizione metodi scorciatoia molto comodi che permettono di inserire
direttamente gli elementi, senza prima creare le istanze di Gio.MenuItem.
Ad esempio per inserire un elemento standard, possiamo usare il metodo Gio.Menu.insert(),
per aggiungere una sezione, il metodo Gio.Menu.insert_section(), mentre per
inserire un sottomenu, il metodo Gio.Menu.insert_submenu().
L’iter più esplicito consiste invece nel:
1. Si crea l’istanza Gio.Menu che rappresenta il menu model del sistema di menu;
>>> menu_model = Gio.Menu.new()
2. Si crea l’istanza di Gio.Menu, che rappresenta la voce di menu nella menubar,
cioè “File” (sottomenu del menumodel) e la si aggiunge al menu model;
>>> menu_model = Gio.Menu.new() >>> menu_model.append_submenu('File', menu_file)
3. Si crea l’istanza di Gio.MenuItem, che rappresenta il primo elemento del menu
“File”, cioè “New” e la si aggiunge al menu “File”;
>>> file_new = Gio.MenuItem.new('New', 'win.FileNew') >>> menu_file.append_item(file_new)
4. Si crea la Action che corrisponderà alla voce di menu che abbiamo creato, e
che si attiverà cliccando sulla voce stessa;
>>> action_file = Gio.SimpleAction.new(name='FileNew', parameter_type=None)
5. Si aggiunge la action all’istanza di Gtk.ApplicationWindow con il metodo
add_action(action);
6. Si connette la action alla callback desiderata;
Ecco un codeice di esempio:
#!/usr/bin/env python3 import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from gi.repository import Gio class AppWindow(Gtk.ApplicationWindow): def __init__(self): super().__init__() self.set_default_size(200, 100) # actions act_file = Gio.SimpleAction.new(name='FileNew', parameter_type=None) act_quit = Gio.SimpleAction.new(name='FileQuit', parameter_type=None) act_info = Gio.SimpleAction.new(name='AboutInfo', parameter_type=None) self.add_action(act_file) self.add_action(act_quit) self.add_action(act_info) # model menu menu_model = Gio.Menu.new() # 1st menu menu_file = Gio.Menu.new() file_new = Gio.MenuItem.new('New', 'win.FileNew') file_quit = Gio.MenuItem.new('Quit', 'win.FileQuit') menu_file.append_item(file_new) menu_file.append_item(file_quit) # 2nd menu menu_about = Gio.Menu.new() about_info = Gio.MenuItem.new('Info', 'win.AboutInfo') menu_about.append_item(about_info) menu_model.append_submenu('File', menu_file) menu_model.append_submenu('About', menu_about) menu_bar = Gtk.MenuBar.new_from_model(menu_model) # layout layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) layout.pack_start(menu_bar, False, False, 0) self.add(layout) # bindings act_file.connect('activate', self.on_menu) act_quit.connect('activate', self.on_quit) act_info.connect('activate', self.on_menu) self.connect('destroy', Gtk.main_quit) def on_menu(self, action, value): print("INFO: menu '%s'" % action.props.name) @staticmethod def on_quit(action, param): Gtk.main_quit() if __name__ == '__main__': win = AppWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
Gtk.Builder e XML
La stessa cosa si ottiene anche via Gtk.Builder.
Gtk.Builder è un oggetto che legge le descrizioni di una UI da un testo e ne istanzia gli oggetti
descritti in esso.
A grandi linee, all’interno del testo, gli oggetti del menu saranno così identificati:
UI ovvero l’interfaccia utente del menu: sarà compresa tra i tag
<interface></interface>
;
menu_model ovvero l’oggetto menu genitore: sarà compreso tra i tag
<menu></menu>
;
sub_menu ovvero le voci dei menu: saranno comprese tra i tag
<submenu></submenu>
;
item ovvero gli elementi interni alle voci di menu: saranno compresi tra i tag
<item></item>
;
attribute ovvero l’attributo dell’istanza che verrà creata dal buider: sarà
compreso tra i tag
<attribute></attribute>
;
Nel nostro caso precedente il testo che rappresenta il menu della app, sarà:
<interface> <menu id='MenuModel'> <submenu> <attribute name='label'>File</attribute> <item> <attribute name='label'>New</attribute> <attribute name='action'>win.FileNew</attribute> </item> <item> <attribute name='label'>Quit</attribute> <attribute name='action'>win.FileQuit</attribute> </item> </submenu> <submenu> <attribute name='label'>About</attribute> <item> <attribute name='label'>Info</attribute> <attribute name='action'>win.AboutInfo</attribute> </item> </submenu> </menu> </interface>
Basterà quindi creare il builder con il class_method Gtk.Builder.new_from_string(string, length):
>>> import gi ... gi.require_version('Gtk', '3.0') ... from gi.repository import Gtk, Gio >> UI_INFO = """ ... <interface> ... ... </interface> ... """ >>> builder = Gtk.Builder.new_from_string(UI_INFO, -1)
Come anticipato, il builder istanzia gli oggetti descritti nella stringa e permette di recuperarli
con il metodo get_object(string):
>>> builder.get_object('MenuModel') <Gio.Menu object at 0x0000000004cb7580 (GMenu at 0x000000000406f870)> >>> menu_model = builder.get_object('MenuModel') >>> menu_model.get_n_items() 2
Una volta creato il menu con il builder, il resto del codice non cambia:
#!/usr/bin/env python3 import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio UI_INFO = """ <interface> <menu id='MenuModel'> <submenu> <attribute name='label'>File</attribute> <item> <attribute name='label'>New</attribute> <attribute name='action'>win.FileNew</attribute> </item> <item> <attribute name='label'>Quit</attribute> <attribute name='action'>win.FileQuit</attribute> </item> </submenu> <submenu> <attribute name='label'>About</attribute> <item> <attribute name='label'>Info</attribute> <attribute name='action'>win.AboutInfo</attribute> </item> </submenu> </menu> </interface> """ class AppWindow(Gtk.ApplicationWindow): def __init__(self): super().__init__() self.set_default_size(200, 100) builder = Gtk.Builder.new_from_string(UI_INFO, -1) act_file = Gio.SimpleAction.new(name='FileNew', parameter_type=None) act_quit = Gio.SimpleAction.new(name='FileQuit', parameter_type=None) act_info = Gio.SimpleAction.new(name='AboutInfo', parameter_type=None) self.add_action(act_file) self.add_action(act_quit) self.add_action(act_info) menu_model = builder.get_object('MenuModel') menu_bar = Gtk.MenuBar.new_from_model(menu_model) # layout self.layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.layout.pack_start(menu_bar, False, False, 0) self.add(self.layout) act_file.connect('activate', self.on_menu) act_quit.connect('activate', self.on_quit) act_info.connect('activate', self.on_menu) self.connect('destroy', Gtk.main_quit) def on_menu(self, action, value): print("INFO: menu '%s'" % action.props.name) @staticmethod def on_quit(action, param): Gtk.main_quit() if __name__ == '__main__': win = AppWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
Sottomenu di menu
Con lo stesso principio possiamo creare sottomenu di menu, ovvero elementi di menu, che a loro
volta contengono altri elementi sotto di essi.
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio MENUBAR_INFO = """ <interface> <menu id='MenuModel'> <submenu> <attribute name='label'>File</attribute> <submenu> <attribute name="label">New</attribute> <section> <item> <attribute name="label">Text File</attribute> <attribute name="action">win.NewTextFile</attribute> </item> <item> <attribute name="label">Doc File</attribute> <attribute name="action">win.NewDocFile</attribute> </item> <item> <attribute name="label">Python File</attribute> <attribute name="action">win.NewPyFile</attribute> </item> </section> </submenu> <item> <attribute name='label'>Quit</attribute> <attribute name='action'>win.FileQuit</attribute> </item> </submenu> <submenu> <attribute name='label'>About</attribute> <item> <attribute name='label'>Info</attribute> <attribute name='action'>win.AboutInfo</attribute> </item> </submenu> </menu> </interface> """ class AppWindow(Gtk.ApplicationWindow): def __init__(self): super().__init__() self.set_default_size(200, 100) builder = Gtk.Builder.new_from_string(MENUBAR_INFO, -1) act_text = Gio.SimpleAction.new(name='NewTextFile', parameter_type=None) act_doc = Gio.SimpleAction.new(name='NewDocFile', parameter_type=None) act_py = Gio.SimpleAction.new(name='NewPyFile', parameter_type=None) act_quit = Gio.SimpleAction.new(name='FileQuit', parameter_type=None) act_info = Gio.SimpleAction.new(name='AboutInfo', parameter_type=None) self.add_action(act_text) self.add_action(act_doc) self.add_action(act_py) self.add_action(act_quit) self.add_action(act_info) menu_model = builder.get_object('MenuModel') menu_bar = Gtk.MenuBar.new_from_model(menu_model) # layout self.layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.layout.pack_start(menu_bar, False, False, 0) self.add(self.layout) act_text.connect('activate', self.on_file) act_doc.connect('activate', self.on_file) act_py.connect('activate', self.on_file) act_quit.connect('activate', self.on_quit) act_info.connect('activate', self.on_menu) self.connect('destroy', Gtk.main_quit) def on_menu(self, action, value): print("INFO: menu '%s'" % action.props.name) def on_file(self, action, value): print("INFO: menu '%s'" % action.props.name) @staticmethod def on_quit(action, param): Gtk.main_quit() if __name__ == '__main__': win = AppWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
Menu con RadioButton e ToggleButton
Nelle voci di menu possono essere contemplati anche elementi con stato, come ad esempio i
radiobutton e i togglebutton.
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib, Gio MENUBAR_INFO = """ <?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="menumodel"> <submenu> <attribute name="label">Settings</attribute> <section> <item> <attribute name="label">millimeters</attribute> <attribute name="action">win.state</attribute> <attribute name="target">mm</attribute> </item> <item> <attribute name="label">inches</attribute> <attribute name="action">win.state</attribute> <attribute name="target">inches</attribute> </item> </section> <section> <item> <attribute name="label">Convert</attribute> <attribute name="action">win.convert</attribute> </item> </section> </submenu> </menu> </interface> """ class AppWindow(Gtk.ApplicationWindow): def __init__(self): super().__init__(title="MenuBar Example") self.set_default_size(200, 200) act_state = Gio.SimpleAction.new_stateful( name="state", parameter_type=GLib.VariantType.new('s'), state=GLib.Variant.new_string('inches')) act_convert = Gio.SimpleAction.new_stateful( name="convert", parameter_type=None, state=GLib.Variant.new_boolean(False)) self.add_action(act_state) self.add_action(act_convert) # bindings act_state.connect("activate", self.on_state) act_convert.connect("activate", self.on_convert) builder = Gtk.Builder.new_from_string(MENUBAR_INFO, -1) menu_model = builder.get_object('menumodel') menu_bar = Gtk.MenuBar.new_from_model(menu_model) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) box.pack_start(menu_bar, False, False, 0) self.add(box) def on_state(self, action, parameter): print("INFO: measure is set to '%s'" % parameter.get_string()) action.set_state(parameter) def on_convert(self, action, parameter): action.set_state(GLib.Variant.new_boolean(not action.get_state())) result = "enabled" if action.get_state().get_boolean() else "disabled" print("INFO: <Convert> is %s" % result) if __name__ == '__main__': win = AppWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
ToolBar
Con lo stesso principio si crea anche una ToolBar.
Una volta costruita la stringa XML la si dà in pasto al Gtk.Builder e si crea la toolbar:
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio TOOLBAR_INFO = """ <?xml version="1.0" encoding="UTF-8"?> <interface> <!-- interface-requires gtk+ 3.0 --> <object class="GtkToolbar" id="toolbar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="hexpand">True</property> <property name="show_arrow">False</property> <child> <object class="GtkToolButton" id="new_button"> <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_action_appearance">False</property> <property name="is_important">True</property> <property name="action_name">win.new</property> <property name="label" translatable="yes">New</property> <property name="use_underline">True</property> <property name="stock_id">gtk-new</property> </object> <packing> <property name="expand">False</property> <property name="homogeneous">True</property> </packing> </child> <child> <object class="GtkToolButton" id="open_button"> <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_action_appearance">False</property> <property name="is_important">True</property> <property name="action_name">win.open</property> <property name="label" translatable="yes">Open</property> <property name="use_underline">True</property> <property name="stock_id">gtk-open</property> </object> <packing> <property name="expand">False</property> <property name="homogeneous">True</property> </packing> </child> </object> </interface> """ class AppWindow(Gtk.ApplicationWindow): def __init__(self): super().__init__(title="Toolbar Example") self.set_default_size(250, 100) builder = Gtk.Builder.new_from_string(TOOLBAR_INFO, -1) act_new = Gio.SimpleAction.new(name='new', parameter_type=None) act_open = Gio.SimpleAction.new(name='open', parameter_type=None) self.add_action(act_new) self.add_action(act_open) tool_bar = builder.get_object('toolbar') box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) box.pack_start(tool_bar, False, False, 0) self.add(box) # bindings act_new.connect('activate', self.on_menu) act_open.connect('activate', self.on_menu) self.connect('destroy', Gtk.main_quit) def on_menu(self, action, value): print("INFO: menu '%s'" % action.props.name) if __name__ == '__main__': win = AppWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
Gtk.ApplicationWindow e Gtk.Application
Se invece di utilizzare solo un oggetto Gtk.ApplicationWindow, come negli esempi precedenti,
volessimo strutturare la nostra applicazione, utilizzando, sia una window Gtk.ApplicationWindow,
sia un oggetto Gtk.Application, avremmo la possibità di distinguere le actions, in base al loro
prefisso. Le actions inerenti la window dell’applicazione, come negli esempi precedenti, hanno
prefisso “win.” e vanno create, aggiunte alla lista delle actions e connesse, all’interno di
Gtk.ApplicationWindowe, mentre le altre vengono istanziate e connesse all’interno di
Gtk.Application e avranno prefisso “app.”.
Il codice subirà qualche variante, ad esempio il builder verrà creato dentro Gtk.Application.
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio import sys MENUBAR_INFO = """ <interface> <menu id='MenuModel'> <submenu> <attribute name='label'>File</attribute> <item> <attribute name='label'>New</attribute> <attribute name='action'>app.FileNew</attribute> </item> <item> <attribute name='label'>Quit</attribute> <attribute name='action'>app.FileQuit</attribute> </item> </submenu> <submenu> <attribute name='label'>About</attribute> <item> <attribute name='label'>Info</attribute> <attribute name='action'>win.AboutInfo</attribute> </item> </submenu> </menu> </interface> """ class AppWindow(Gtk.ApplicationWindow): def __init__(self, title, app): super().__init__(title=title, application=app) self.set_default_size(200, 100) act_info = Gio.SimpleAction.new(name='AboutInfo', parameter_type=None) self.add_action(act_info) act_info.connect('activate', self.on_menu) self.connect('destroy', Gtk.main_quit) def on_menu(self, action, value): print("INFO: menu '%s'" % action.props.name) class GApplication(Gtk.Application): def __init__(self): super().__init__() def do_activate(self): win = AppWindow(title="MenuBar Example", app=self) win.show_all() def do_startup(self): # PRIMA AZIONE DA FARE: invocare do_startup() Gtk.Application.do_startup(self) builder = Gtk.Builder.new_from_string(MENUBAR_INFO, -1) menu_model = builder.get_object('MenuModel') self.set_menubar(menu_model) act_file = Gio.SimpleAction.new(name='FileNew', parameter_type=None) act_quit = Gio.SimpleAction.new(name='FileQuit', parameter_type=None) self.add_action(act_file) self.add_action(act_quit) act_file.connect('activate', self.on_new) act_quit.connect('activate', self.on_quit) def on_new(self, action, value): print("INFO: menu '%s'" % action.props.name) def on_quit(self, action, parameter): sys.exit() if __name__ == '__main__': app = GApplication() exit_status = app.run(sys.argv) sys.exit(exit_status)
Accelerator and mnemonic
Le Labels possono contenere mnemonics. Gli Mnemonics sono gli underscores nella
label prima del carattere che vogliamo utilizzare come scorciatoia da tastiera.
Ad esempio “_File”, ci indica che la “F” sarà il carattere mnemonic del menu File.
Premendo ALT, gli mnemonic diventeranno visibili. Premendo Alt+F il menu File, si aprirà.
Gli Accelerators possono essere aggiunti esplicitamente nella stringa XML che
definisce la UI. Per fare questo basta aggiungere un attributo “accel” all’item.
Nota:
<Primary>q
creerà la sequenza Ctrl+Q, dove “Primary” si riferisce al tasto Ctrl.
l’XML dell’item diventerà quindi:
<item> <attribute name='label'>_Quit</attribute> <attribute name='action'>app.FileQuit</attribute> <attribute name="accel"><Primary>q</attribute> </item>
link di riferimento:
Commenti recenti