demo: Use HeaderBar for main app window
[pygobject.git] / demo / demo.py
blobd67935d949a3bdf611af5a1d19e860aecdecbfff
1 #!/usr/bin/env python
2 # -*- Mode: Python; py-indent-offset: 4 -*-
3 # vim: tabstop=4 shiftwidth=4 expandtab
5 # Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 # USA
23 import codecs
24 import os
25 import sys
26 import textwrap
28 from gi.repository import GLib, GObject, Pango, GdkPixbuf, Gtk, Gio
30 try:
31 from gi.repository import GtkSource
32 GtkSource # PyFlakes
33 except ImportError:
34 GtkSource = None
37 DEMOROOTDIR = os.path.abspath(os.path.dirname(__file__))
38 DEMOCODEDIR = os.path.join(DEMOROOTDIR, 'demos')
39 sys.path.insert(0, DEMOROOTDIR)
42 class Demo(GObject.GObject):
43 __gtype_name__ = 'GtkDemo'
45 def __init__(self, title, module, filename):
46 super(Demo, self).__init__()
48 self.title = title
49 self.module = module
50 self.filename = filename
52 @classmethod
53 def new_from_file(cls, path):
54 relpath = os.path.relpath(path, DEMOROOTDIR)
55 packagename = os.path.dirname(relpath).replace(os.sep, '.')
56 modulename = os.path.basename(relpath)[0:-3]
58 package = __import__(packagename, globals(), locals(), [modulename], 0)
59 module = getattr(package, modulename)
61 try:
62 return cls(module.title, module, path)
63 except AttributeError as e:
64 raise AttributeError('(%s): %s' % (path, e.message))
67 class DemoTreeStore(Gtk.TreeStore):
68 __gtype_name__ = 'GtkDemoTreeStore'
70 def __init__(self, *args):
71 super(DemoTreeStore, self).__init__(str, Demo, Pango.Style)
73 self._parent_nodes = {}
75 for filename in self._list_dir(DEMOCODEDIR):
76 fullpath = os.path.join(DEMOCODEDIR, filename)
77 initfile = os.path.join(os.path.dirname(fullpath), '__init__.py')
79 if fullpath != initfile and os.path.isfile(initfile) and fullpath.endswith('.py'):
80 parentname = os.path.dirname(os.path.relpath(fullpath, DEMOCODEDIR))
82 if parentname:
83 parent = self._get_parent_node(parentname)
84 else:
85 parent = None
87 demo = Demo.new_from_file(fullpath)
88 self.append(parent, (demo.title, demo, Pango.Style.NORMAL))
90 def _list_dir(self, path):
91 demo_file_list = []
93 for filename in os.listdir(path):
94 fullpath = os.path.join(path, filename)
96 if os.path.isdir(fullpath):
97 demo_file_list.extend(self._list_dir(fullpath))
98 elif os.path.isfile(fullpath):
99 demo_file_list.append(fullpath)
101 return sorted(demo_file_list, key=str.lower)
103 def _get_parent_node(self, name):
104 if name not in self._parent_nodes.keys():
105 node = self.append(None, (name, None, Pango.Style.NORMAL))
106 self._parent_nodes[name] = node
108 return self._parent_nodes[name]
111 class GtkDemoApp(Gtk.Application):
112 __gtype_name__ = 'GtkDemoWindow'
114 def __init__(self):
115 super(GtkDemoApp, self).__init__(application_id='org.gnome.pygobject.gtkdemo')
117 # Use a GResource to hold the CSS files. Resource bundles are created by
118 # the glib-compile-resources program shipped with Glib which takes an xml
119 # file that describes the bundle, and a set of files that the xml
120 # references. These are combined into a binary resource bundle.
121 base_path = os.path.abspath(os.path.dirname(__file__))
122 resource_path = os.path.join(base_path, 'demos/data/demo.gresource')
123 resource = Gio.Resource.load(resource_path)
125 # FIXME: method register() should be without the underscore
126 # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
127 # Once the resource has been globally registered it can be used
128 # throughout the application.
129 resource._register()
131 def on_activate(self, app):
132 self.window = Gtk.ApplicationWindow.new(self)
133 self.window.set_title('PyGObject GTK+ Code Demos')
134 self.window.set_default_size(600, 400)
135 self.setup_default_icon()
137 self.header_bar = Gtk.HeaderBar(show_close_button=True,
138 subtitle='Foobar')
139 self.window.set_titlebar(self.header_bar)
141 stack = Gtk.Stack(transition_type=Gtk.StackTransitionType.SLIDE_LEFT_RIGHT,
142 homogeneous=True)
143 switcher = Gtk.StackSwitcher(stack=stack, halign=Gtk.Align.CENTER)
145 self.header_bar.set_custom_title(switcher)
147 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
148 homogeneous=False,
149 spacing=0)
150 self.window.add(hbox)
152 tree = self.create_tree()
153 hbox.pack_start(child=tree, expand=False, fill=False, padding=0)
154 hbox.pack_start(child=stack, expand=True, fill=True, padding=0)
156 text_widget, info_buffer = self.create_text_view()
157 stack.add_titled(text_widget, name='info', title='Info')
159 self.info_buffer = info_buffer
160 self.info_buffer.create_tag('title', font='Sans 18')
162 text_widget, self.source_buffer = self.create_source_view()
163 stack.add_titled(text_widget, name='source', title='Source')
165 self.window.show_all()
167 self.selection_cb(self.tree_view.get_selection(),
168 self.tree_view.get_model())
170 def find_file(self, base=''):
171 dir = os.path.join(DEMOCODEDIR, 'data')
172 logo_file = os.path.join(dir, 'gtk-logo-rgb.gif')
173 base_file = os.path.join(dir, base)
175 if (GLib.file_test(logo_file, GLib.FileTest.EXISTS) and
176 GLib.file_test(base_file, GLib.FileTest.EXISTS)):
177 return base_file
178 else:
179 filename = os.path.join(DEMOCODEDIR, base)
181 if GLib.file_test(filename, GLib.FileTest.EXISTS):
182 return filename
184 # can't find the file
185 raise IOError('Cannot find demo data file "%s"' % base)
187 def setup_default_icon(self):
188 filename = self.find_file('gtk-logo-rgb.gif')
189 pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
190 transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
191 list = []
192 list.append(transparent)
193 Gtk.Window.set_default_icon_list(list)
195 def selection_cb(self, selection, model):
196 sel = selection.get_selected()
197 if sel == ():
198 return
200 treeiter = sel[1]
201 title = model.get_value(treeiter, 0)
202 demo = model.get_value(treeiter, 1)
204 if demo is None:
205 return
207 # Split into paragraphs based on double newlines and use
208 # textwrap to strip out all other formatting whitespace
209 description = ''
210 for paragraph in demo.module.description.split('\n\n'):
211 description += '\n'.join(textwrap.wrap(paragraph, 99999))
212 description += '\n\n' # Add paragraphs back in
214 f = codecs.open(demo.filename, 'rU', 'utf-8')
215 code = f.read()
216 f.close()
218 # output and style the title
219 (start, end) = self.info_buffer.get_bounds()
220 self.info_buffer.delete(start, end)
221 (start, end) = self.source_buffer.get_bounds()
222 self.source_buffer.delete(start, end)
224 start = self.info_buffer.get_iter_at_offset(0)
225 end = start.copy()
226 self.info_buffer.insert(end, title)
227 start = end.copy()
228 start.backward_chars(len(title))
229 self.info_buffer.apply_tag_by_name('title', start, end)
230 self.info_buffer.insert(end, '\n')
232 # output the description
233 self.info_buffer.insert(end, description)
235 # output the code
236 start = self.source_buffer.get_iter_at_offset(0)
237 end = start.copy()
238 self.source_buffer.insert(end, code)
240 def row_activated_cb(self, view, path, col, store):
241 iter = store.get_iter(path)
242 demo = store.get_value(iter, 1)
244 if demo is not None:
245 store.set_value(iter, 2, Pango.Style.ITALIC)
246 try:
247 demo.module.main(self)
248 finally:
249 store.set_value(iter, 2, Pango.Style.NORMAL)
251 def create_tree(self):
252 tree_store = DemoTreeStore()
253 tree_view = Gtk.TreeView()
254 self.tree_view = tree_view
255 tree_view.set_model(tree_store)
256 selection = tree_view.get_selection()
257 selection.set_mode(Gtk.SelectionMode.BROWSE)
258 tree_view.set_size_request(200, -1)
260 cell = Gtk.CellRendererText()
261 column = Gtk.TreeViewColumn(title='Widget (double click for demo)',
262 cell_renderer=cell,
263 text=0,
264 style=2)
266 first_iter = tree_store.get_iter_first()
267 if first_iter is not None:
268 selection.select_iter(first_iter)
270 selection.connect('changed', self.selection_cb, tree_store)
271 tree_view.connect('row_activated', self.row_activated_cb, tree_store)
273 tree_view.append_column(column)
275 tree_view.expand_all()
276 tree_view.set_headers_visible(False)
277 scrolled_window = Gtk.ScrolledWindow(hadjustment=None,
278 vadjustment=None)
279 scrolled_window.set_policy(Gtk.PolicyType.NEVER,
280 Gtk.PolicyType.AUTOMATIC)
282 scrolled_window.add(tree_view)
284 label = Gtk.Label(label='Widget (double click for demo)')
286 box = Gtk.Notebook()
287 box.append_page(scrolled_window, label)
289 tree_view.grab_focus()
291 return box
293 def create_scrolled_window(self):
294 scrolled_window = Gtk.ScrolledWindow(hadjustment=None,
295 vadjustment=None)
296 scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
297 Gtk.PolicyType.AUTOMATIC)
298 scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
299 return scrolled_window
301 def create_text_view(self):
302 text_view = Gtk.TextView()
303 buffer = Gtk.TextBuffer()
305 text_view.set_buffer(buffer)
306 text_view.set_editable(False)
307 text_view.set_cursor_visible(False)
309 scrolled_window = self.create_scrolled_window()
310 scrolled_window.add(text_view)
312 text_view.set_wrap_mode(Gtk.WrapMode.WORD)
313 text_view.set_pixels_above_lines(2)
314 text_view.set_pixels_below_lines(2)
316 return scrolled_window, buffer
318 def create_source_view(self):
319 font_desc = Pango.FontDescription('monospace 11')
321 if GtkSource:
322 lang_mgr = GtkSource.LanguageManager()
323 lang = lang_mgr.get_language('python')
325 buffer = GtkSource.Buffer()
326 buffer.set_language(lang)
327 buffer.set_highlight_syntax(True)
329 view = GtkSource.View()
330 view.set_buffer(buffer)
331 view.set_show_line_numbers(True)
333 scrolled_window = self.create_scrolled_window()
334 scrolled_window.add(view)
336 else:
337 scrolled_window, buffer = self.create_text_view()
338 view = scrolled_window.get_child()
340 view.modify_font(font_desc)
341 view.set_wrap_mode(Gtk.WrapMode.NONE)
342 return scrolled_window, buffer
344 def run(self, argv):
345 self.connect('activate', self.on_activate)
346 return super(GtkDemoApp, self).run(argv)
349 def main(argv):
350 """Entry point for demo manager"""
351 app = GtkDemoApp()
352 return app.run(argv)
355 if __name__ == '__main__':
356 SystemExit(main(sys.argv))