mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sample GTK+3 program to explore use of GMenuModel
This commit is contained in:
parent
33ed408e7a
commit
de065c56af
259
src/calibre/gui2/dbus_export/gtk.py
Normal file
259
src/calibre/gui2/dbus_export/gtk.py
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
# Demo program to explore the GTK DBus interface, which is only partially documented
|
||||||
|
# at https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
|
||||||
|
|
||||||
|
import sys, dbus, subprocess
|
||||||
|
from threading import Thread
|
||||||
|
from pprint import pformat
|
||||||
|
|
||||||
|
from gi.repository import Gtk, Gdk, GdkX11 # noqa
|
||||||
|
|
||||||
|
UI_INFO = """
|
||||||
|
<ui>
|
||||||
|
<menubar name='MenuBar'>
|
||||||
|
<menu action='FileMenu'>
|
||||||
|
<menu action='FileNew'>
|
||||||
|
<menuitem action='FileNewStandard' />
|
||||||
|
<menuitem action='FileNewFoo' />
|
||||||
|
<menuitem action='FileNewGoo' />
|
||||||
|
</menu>
|
||||||
|
<separator />
|
||||||
|
<menuitem action='FileQuit' />
|
||||||
|
</menu>
|
||||||
|
<menu action='EditMenu'>
|
||||||
|
<menuitem action='EditCopy' />
|
||||||
|
<menuitem action='EditPaste' />
|
||||||
|
<menuitem action='EditSomething' />
|
||||||
|
</menu>
|
||||||
|
<menu action='ChoicesMenu'>
|
||||||
|
<menuitem action='ChoiceOne'/>
|
||||||
|
<menuitem action='ChoiceTwo'/>
|
||||||
|
<separator />
|
||||||
|
<menuitem action='ChoiceThree'/>
|
||||||
|
</menu>
|
||||||
|
</menubar>
|
||||||
|
<toolbar name='ToolBar'>
|
||||||
|
<toolitem action='FileNewStandard' />
|
||||||
|
<toolitem action='FileQuit' />
|
||||||
|
</toolbar>
|
||||||
|
<popup name='PopupMenu'>
|
||||||
|
<menuitem action='EditCopy' />
|
||||||
|
<menuitem action='EditPaste' />
|
||||||
|
<menuitem action='EditSomething' />
|
||||||
|
</popup>
|
||||||
|
</ui>
|
||||||
|
"""
|
||||||
|
|
||||||
|
class MenuExampleWindow(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
Gtk.Window.__init__(self, application=app, title="Menu Example")
|
||||||
|
|
||||||
|
self.set_default_size(800, 600)
|
||||||
|
self.scroll = s = Gtk.ScrolledWindow()
|
||||||
|
s.set_hexpand(True), s.set_vexpand(True)
|
||||||
|
s.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||||
|
s.set_min_content_height(450)
|
||||||
|
self.label = la = Gtk.TextView()
|
||||||
|
la.set_text = la.get_buffer().set_text
|
||||||
|
s.add(la)
|
||||||
|
|
||||||
|
action_group = Gtk.ActionGroup("my_actions")
|
||||||
|
|
||||||
|
self.add_file_menu_actions(action_group)
|
||||||
|
self.add_edit_menu_actions(action_group)
|
||||||
|
self.add_choices_menu_actions(action_group)
|
||||||
|
|
||||||
|
uimanager = self.create_ui_manager()
|
||||||
|
uimanager.insert_action_group(action_group)
|
||||||
|
|
||||||
|
menubar = uimanager.get_widget("/MenuBar")
|
||||||
|
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
box.pack_start(menubar, False, False, 0)
|
||||||
|
|
||||||
|
toolbar = uimanager.get_widget("/ToolBar")
|
||||||
|
box.pack_start(toolbar, False, False, 0)
|
||||||
|
|
||||||
|
eventbox = Gtk.EventBox()
|
||||||
|
eventbox.connect("button-press-event", self.on_button_press_event)
|
||||||
|
box.pack_start(s, False, False, 0)
|
||||||
|
box.pack_start(eventbox, True, True, 0)
|
||||||
|
|
||||||
|
label = Gtk.Label("Right-click to see the popup menu.")
|
||||||
|
eventbox.add(label)
|
||||||
|
|
||||||
|
self.popup = uimanager.get_widget("/PopupMenu")
|
||||||
|
|
||||||
|
self.add(box)
|
||||||
|
|
||||||
|
def add_file_menu_actions(self, action_group):
|
||||||
|
action_filemenu = Gtk.Action("FileMenu", "File", None, None)
|
||||||
|
action_group.add_action(action_filemenu)
|
||||||
|
|
||||||
|
action_filenewmenu = Gtk.Action("FileNew", None, None, Gtk.STOCK_NEW)
|
||||||
|
action_group.add_action(action_filenewmenu)
|
||||||
|
|
||||||
|
action_new = Gtk.Action("FileNewStandard", "_New",
|
||||||
|
"Create a new file", Gtk.STOCK_NEW)
|
||||||
|
action_new.connect("activate", self.on_menu_file_new_generic)
|
||||||
|
action_group.add_action_with_accel(action_new, '<Ctrl>N')
|
||||||
|
|
||||||
|
action_group.add_actions([
|
||||||
|
("FileNewFoo", None, "New Foo", None, "Create new foo",
|
||||||
|
self.on_menu_file_new_generic),
|
||||||
|
("FileNewGoo", None, "_New Goo", None, "Create new goo",
|
||||||
|
self.on_menu_file_new_generic),
|
||||||
|
])
|
||||||
|
|
||||||
|
action_filequit = Gtk.Action("FileQuit", None, None, Gtk.STOCK_QUIT)
|
||||||
|
action_filequit.connect("activate", self.on_menu_file_quit)
|
||||||
|
action_group.add_action_with_accel(action_filequit, '<Ctrl>Q')
|
||||||
|
|
||||||
|
def add_edit_menu_actions(self, action_group):
|
||||||
|
action_group.add_actions([
|
||||||
|
("EditMenu", None, "Edit"),
|
||||||
|
("EditCopy", Gtk.STOCK_COPY, None, None, None,
|
||||||
|
self.on_menu_others),
|
||||||
|
("EditPaste", Gtk.STOCK_PASTE, None, None, None,
|
||||||
|
self.on_menu_others),
|
||||||
|
("EditSomething", None, "Something", "<control><alt>S", None,
|
||||||
|
self.on_menu_others)
|
||||||
|
])
|
||||||
|
|
||||||
|
def add_choices_menu_actions(self, action_group):
|
||||||
|
action_group.add_action(Gtk.Action("ChoicesMenu", "Choices", None,
|
||||||
|
None))
|
||||||
|
|
||||||
|
action_group.add_radio_actions([
|
||||||
|
("ChoiceOne", None, "One", None, None, 1),
|
||||||
|
("ChoiceTwo", None, "Two", None, None, 2)
|
||||||
|
], 1, self.on_menu_choices_changed)
|
||||||
|
|
||||||
|
three = Gtk.ToggleAction("ChoiceThree", "Three", None, None)
|
||||||
|
three.connect("toggled", self.on_menu_choices_toggled)
|
||||||
|
action_group.add_action(three)
|
||||||
|
|
||||||
|
def create_ui_manager(self):
|
||||||
|
uimanager = Gtk.UIManager()
|
||||||
|
|
||||||
|
# Throws exception if something went wrong
|
||||||
|
uimanager.add_ui_from_string(UI_INFO)
|
||||||
|
|
||||||
|
# Add the accelerator group to the toplevel window
|
||||||
|
accelgroup = uimanager.get_accel_group()
|
||||||
|
self.add_accel_group(accelgroup)
|
||||||
|
return uimanager
|
||||||
|
|
||||||
|
def on_menu_file_new_generic(self, widget):
|
||||||
|
print("A File|New menu item was selected.")
|
||||||
|
|
||||||
|
def on_menu_file_quit(self, widget):
|
||||||
|
app.quit()
|
||||||
|
|
||||||
|
def on_menu_others(self, widget):
|
||||||
|
print("Menu item " + widget.get_name() + " was selected")
|
||||||
|
|
||||||
|
def on_menu_choices_changed(self, widget, current):
|
||||||
|
print(current.get_name() + " was selected.")
|
||||||
|
|
||||||
|
def on_menu_choices_toggled(self, widget):
|
||||||
|
if widget.get_active():
|
||||||
|
print(widget.get_name() + " activated")
|
||||||
|
else:
|
||||||
|
print(widget.get_name() + " deactivated")
|
||||||
|
|
||||||
|
def on_button_press_event(self, widget, event):
|
||||||
|
# Check if right mouse button was preseed
|
||||||
|
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
|
||||||
|
self.popup.popup(None, None, None, None, event.button, event.time)
|
||||||
|
return True # event has been handled
|
||||||
|
|
||||||
|
def convert(v):
|
||||||
|
if isinstance(v, basestring):
|
||||||
|
return unicode(v)
|
||||||
|
if isinstance(v, dbus.Struct):
|
||||||
|
return tuple(convert(val) for val in v)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [convert(val) for val in v]
|
||||||
|
if isinstance(v, dict):
|
||||||
|
return {convert(k):convert(val) for k, val in v.iteritems()}
|
||||||
|
if isinstance(v, dbus.Boolean):
|
||||||
|
return bool(v)
|
||||||
|
if isinstance(v, (dbus.UInt32, dbus.UInt16)):
|
||||||
|
return int(v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
class MyApplication(Gtk.Application):
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
win = self.window = MenuExampleWindow(self)
|
||||||
|
win.show_all()
|
||||||
|
self.get_xprop_data()
|
||||||
|
Thread(target=self.print_dbus_data).start()
|
||||||
|
|
||||||
|
def get_xprop_data(self):
|
||||||
|
property_names = {GdkX11.x11_get_xatom_name(i) for i in xrange(691)}
|
||||||
|
property_names = {name for name in property_names if
|
||||||
|
name.startswith('_GTK') or name.startswith('_UNITY') or name.startswith('_GNOME')}
|
||||||
|
props = []
|
||||||
|
for name in sorted(property_names):
|
||||||
|
props.append(subprocess.check_output(['xprop', '-id', str(self.window.get_window().get_xid()), name]))
|
||||||
|
raw = props[-1]
|
||||||
|
if raw.startswith('_UNITY_OBJECT_PATH'):
|
||||||
|
self.object_path = eval(raw.split('=')[-1].strip())
|
||||||
|
elif raw.startswith('_GTK_UNIQUE_BUS_NAME'):
|
||||||
|
self.bus_name = eval(raw.split('=')[-1].strip())
|
||||||
|
self.xprop_data = '\n'.join(props)
|
||||||
|
|
||||||
|
def print(self, *args):
|
||||||
|
self.data.append(' '.join(map(str, args)))
|
||||||
|
|
||||||
|
def print_menu_start(self, bus, group=0, seen=None):
|
||||||
|
groups = set()
|
||||||
|
seen = seen or set()
|
||||||
|
seen.add(group)
|
||||||
|
print = self.print
|
||||||
|
print ('\nMenu description (Group %d)' % group)
|
||||||
|
for item in bus.call_blocking(self.bus_name, self.object_path, 'org.gtk.Menus', 'Start', 'au', ([group],)):
|
||||||
|
print ('Subscription group:', item[0])
|
||||||
|
print ('Menu number:', item[1])
|
||||||
|
for menu_item in item[2]:
|
||||||
|
menu_item = {unicode(k):convert(v) for k, v in menu_item.iteritems()}
|
||||||
|
if ':submenu' in menu_item:
|
||||||
|
groups.add(menu_item[':submenu'][0])
|
||||||
|
if ':section' in menu_item:
|
||||||
|
groups.add(menu_item[':section'][0])
|
||||||
|
print (pformat(menu_item))
|
||||||
|
for other_group in sorted(groups - seen):
|
||||||
|
self.print_menu_start(bus, other_group, seen)
|
||||||
|
|
||||||
|
def print_dbus_data(self):
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
self.data = []
|
||||||
|
self.get_actions_description(bus)
|
||||||
|
self.print_menu_start(bus)
|
||||||
|
self.data.append(self.xprop_data)
|
||||||
|
self.window.label.set_text('\n'.join(self.data))
|
||||||
|
|
||||||
|
def get_actions_description(self, bus):
|
||||||
|
print = self.print
|
||||||
|
print('\nActions description')
|
||||||
|
self.actions_desc = d = {}
|
||||||
|
for name, data in bus.call_blocking(self.bus_name, self.object_path, 'org.gtk.Actions', 'DescribeAll', '', ()).iteritems():
|
||||||
|
d[name] = {'enabled':convert(data[0]), 'param type': convert(data[1]), 'state':convert(data[2])}
|
||||||
|
print ('Name:', name)
|
||||||
|
print (pformat(d[name]))
|
||||||
|
|
||||||
|
def do_startup(self):
|
||||||
|
Gtk.Application.do_startup(self)
|
||||||
|
|
||||||
|
app = MyApplication(application_id='com.calibre-ebook.test-gtk')
|
||||||
|
sys.exit(app.run(sys.argv))
|
Loading…
x
Reference in New Issue
Block a user