1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 # Copyright (C) 2009 Johan Dahlin <johan@gnome.org>
5 # 2010 Simon van der Linden <svdlinden@src.gnome.org>
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
23 from gi.repository import GObject
24 from ..overrides import override
25 from ..module import get_introspection_module
26 from gi import PyGIDeprecationWarning
28 if sys.version_info >= (3, 0):
30 _callable = lambda c: hasattr(c, '__call__')
32 _basestring = basestring
35 Gtk = get_introspection_module('Gtk')
39 if Gtk._version == '2.0':
41 warn_msg = "You have imported the Gtk 2.0 module. Because Gtk 2.0 \
42 was not designed for use with introspection some of the \
43 interfaces and API will fail. As such this is not supported \
44 by the pygobject development team and we encourage you to \
45 port your app to Gtk 3 or greater. PyGTK is the recomended \
46 python module to use with Gtk 2.0"
48 warnings.warn(warn_msg, RuntimeWarning)
51 class Widget(Gtk.Widget):
53 def translate_coordinates(self, dest_widget, src_x, src_y):
54 success, dest_x, dest_y = super(Widget, self).translate_coordinates(
55 dest_widget, src_x, src_y)
57 return (dest_x, dest_y,)
59 def render_icon(self, stock_id, size, detail=None):
60 return super(Widget, self).render_icon(stock_id, size, detail)
62 Widget = override(Widget)
63 __all__.append('Widget')
66 class Container(Gtk.Container, Widget):
69 return len(self.get_children())
71 def __contains__(self, child):
72 return child in self.get_children()
75 return iter(self.get_children())
80 # alias for Python 2.x object protocol
81 __nonzero__ = __bool__
83 def get_focus_chain(self):
84 success, widgets = super(Container, self).get_focus_chain()
88 Container = override(Container)
89 __all__.append('Container')
92 class Editable(Gtk.Editable):
94 def insert_text(self, text, position):
95 pos = super(Editable, self).insert_text(text, -1, position)
99 def get_selection_bounds(self):
100 success, start_pos, end_pos = super(Editable, self).get_selection_bounds()
102 return (start_pos, end_pos,)
106 Editable = override(Editable)
107 __all__.append("Editable")
110 class Action(Gtk.Action):
111 def __init__(self, name, label, tooltip, stock_id, **kwds):
112 Gtk.Action.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, **kwds)
114 Action = override(Action)
115 __all__.append("Action")
118 class RadioAction(Gtk.RadioAction):
119 def __init__(self, name, label, tooltip, stock_id, value, **kwds):
120 Gtk.RadioAction.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=value, **kwds)
122 RadioAction = override(RadioAction)
123 __all__.append("RadioAction")
126 class ActionGroup(Gtk.ActionGroup):
127 def __init__(self, name, **kwds):
128 super(ActionGroup, self).__init__(name=name, **kwds)
130 def add_actions(self, entries, user_data=None):
132 The add_actions() method is a convenience method that creates a number
133 of gtk.Action objects based on the information in the list of action
134 entry tuples contained in entries and adds them to the action group.
135 The entry tuples can vary in size from one to six items with the
136 following information:
138 * The name of the action. Must be specified.
139 * The stock id for the action. Optional with a default value of None
140 if a label is specified.
141 * The label for the action. This field should typically be marked
142 for translation, see the set_translation_domain() method. Optional
143 with a default value of None if a stock id is specified.
144 * The accelerator for the action, in the format understood by the
145 gtk.accelerator_parse() function. Optional with a default value of
147 * The tooltip for the action. This field should typically be marked
148 for translation, see the set_translation_domain() method. Optional
149 with a default value of None.
150 * The callback function invoked when the action is activated.
151 Optional with a default value of None.
153 The "activate" signals of the actions are connected to the callbacks and
154 their accel paths are set to <Actions>/group-name/action-name.
159 raise TypeError('entries must be iterable')
161 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
162 action = Action(name, label, tooltip, stock_id)
163 if callback is not None:
164 if user_data is None:
165 action.connect('activate', callback)
167 action.connect('activate', callback, user_data)
169 self.add_action_with_accel(action, accelerator)
172 # using inner function above since entries can leave out optional arguments
175 def add_toggle_actions(self, entries, user_data=None):
177 The add_toggle_actions() method is a convenience method that creates a
178 number of gtk.ToggleAction objects based on the information in the list
179 of action entry tuples contained in entries and adds them to the action
180 group. The toggle action entry tuples can vary in size from one to seven
181 items with the following information:
183 * The name of the action. Must be specified.
184 * The stock id for the action. Optional with a default value of None
185 if a label is specified.
186 * The label for the action. This field should typically be marked
187 for translation, see the set_translation_domain() method. Optional
188 with a default value of None if a stock id is specified.
189 * The accelerator for the action, in the format understood by the
190 gtk.accelerator_parse() function. Optional with a default value of
192 * The tooltip for the action. This field should typically be marked
193 for translation, see the set_translation_domain() method. Optional
194 with a default value of None.
195 * The callback function invoked when the action is activated.
196 Optional with a default value of None.
197 * A flag indicating whether the toggle action is active. Optional
198 with a default value of False.
200 The "activate" signals of the actions are connected to the callbacks and
201 their accel paths are set to <Actions>/group-name/action-name.
207 raise TypeError('entries must be iterable')
209 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
210 action = Gtk.ToggleAction(name, label, tooltip, stock_id)
211 action.set_active(is_active)
212 if callback is not None:
213 if user_data is None:
214 action.connect('activate', callback)
216 action.connect('activate', callback, user_data)
218 self.add_action_with_accel(action, accelerator)
221 # using inner function above since entries can leave out optional arguments
224 def add_radio_actions(self, entries, value=None, on_change=None, user_data=None):
226 The add_radio_actions() method is a convenience method that creates a
227 number of gtk.RadioAction objects based on the information in the list
228 of action entry tuples contained in entries and adds them to the action
229 group. The entry tuples can vary in size from one to six items with the
230 following information:
232 * The name of the action. Must be specified.
233 * The stock id for the action. Optional with a default value of None
234 if a label is specified.
235 * The label for the action. This field should typically be marked
236 for translation, see the set_translation_domain() method. Optional
237 with a default value of None if a stock id is specified.
238 * The accelerator for the action, in the format understood by the
239 gtk.accelerator_parse() function. Optional with a default value of
241 * The tooltip for the action. This field should typically be marked
242 for translation, see the set_translation_domain() method. Optional
243 with a default value of None.
244 * The value to set on the radio action. Optional with a default
245 value of 0. Should be specified in applications.
247 The value parameter specifies the radio action that should be set
248 active. The "changed" signal of the first radio action is connected to
249 the on_change callback (if specified and not None) and the accel paths
250 of the actions are set to <Actions>/group-name/action-name.
255 raise TypeError('entries must be iterable')
259 def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
260 action = RadioAction(name, label, tooltip, stock_id, entry_value)
262 # FIXME: join_group is a patch to Gtk+ 3.0
263 # otherwise we can't effectively add radio actions to a
264 # group. Should we depend on 3.0 and error out here
265 # or should we offer the functionality via a compat
267 if hasattr(action, 'join_group'):
268 action.join_group(group_source)
270 if value == entry_value:
271 action.set_active(True)
273 self.add_action_with_accel(action, accelerator)
277 # using inner function above since entries can leave out optional arguments
278 action = _process_action(first_action, *e)
279 if first_action is None:
280 first_action = action
282 if first_action is not None and on_change is not None:
283 if user_data is None:
284 first_action.connect('changed', on_change)
286 first_action.connect('changed', on_change, user_data)
288 ActionGroup = override(ActionGroup)
289 __all__.append('ActionGroup')
292 class UIManager(Gtk.UIManager):
293 def add_ui_from_string(self, buffer):
294 if not isinstance(buffer, _basestring):
295 raise TypeError('buffer must be a string')
299 return Gtk.UIManager.add_ui_from_string(self, buffer, length)
301 def insert_action_group(self, buffer, length=-1):
302 return Gtk.UIManager.insert_action_group(self, buffer, length)
304 UIManager = override(UIManager)
305 __all__.append('UIManager')
308 class ComboBox(Gtk.ComboBox, Container):
310 def get_active_iter(self):
311 success, aiter = super(ComboBox, self).get_active_iter()
315 ComboBox = override(ComboBox)
316 __all__.append('ComboBox')
320 def __init__(self, homogeneous=False, spacing=0, **kwds):
321 super(Box, self).__init__(**kwds)
322 self.set_homogeneous(homogeneous)
323 self.set_spacing(spacing)
326 __all__.append('Box')
329 class SizeGroup(Gtk.SizeGroup):
330 def __init__(self, mode=Gtk.SizeGroupMode.VERTICAL):
331 super(SizeGroup, self).__init__(mode=mode)
333 SizeGroup = override(SizeGroup)
334 __all__.append('SizeGroup')
337 class MenuItem(Gtk.MenuItem):
338 def __init__(self, label=None, **kwds):
340 super(MenuItem, self).__init__(label=label, **kwds)
342 super(MenuItem, self).__init__(**kwds)
344 MenuItem = override(MenuItem)
345 __all__.append('MenuItem')
348 class Builder(Gtk.Builder):
350 def connect_signals(self, obj_or_map):
351 def _full_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map):
353 if isinstance(obj_or_map, dict):
354 handler = obj_or_map.get(handler_name, None)
356 handler = getattr(obj_or_map, handler_name, None)
359 raise AttributeError('Handler %s not found' % handler_name)
361 if not _callable(handler):
362 raise TypeError('Handler %s is not a method or function' % handler_name)
364 after = flags & GObject.ConnectFlags.AFTER
365 if connect_obj is not None:
367 gobj.connect_object_after(signal_name, handler, connect_obj)
369 gobj.connect_object(signal_name, handler, connect_obj)
372 gobj.connect_after(signal_name, handler)
374 gobj.connect(signal_name, handler)
376 self.connect_signals_full(_full_callback, obj_or_map)
378 def add_from_string(self, buffer):
379 if not isinstance(buffer, _basestring):
380 raise TypeError('buffer must be a string')
384 return Gtk.Builder.add_from_string(self, buffer, length)
386 def add_objects_from_string(self, buffer, object_ids):
387 if not isinstance(buffer, _basestring):
388 raise TypeError('buffer must be a string')
392 return Gtk.Builder.add_objects_from_string(self, buffer, length, object_ids)
394 Builder = override(Builder)
395 __all__.append('Builder')
398 # NOTE: This must come before any other Window/Dialog subclassing, to ensure
399 # that we have a correct inheritance hierarchy.
402 class Window(Gtk.Window):
403 def __init__(self, type=Gtk.WindowType.TOPLEVEL, **kwds):
404 # type is a construct-only property; if it is already set (e. g. by
405 # GtkBuilder), do not try to set it again and just ignore it
407 self.get_property('type')
408 Gtk.Window.__init__(self, **kwds)
410 Gtk.Window.__init__(self, type=type, **kwds)
412 Window = override(Window)
413 __all__.append('Window')
416 class Dialog(Gtk.Dialog, Container):
423 _buttons_property=None,
426 # buttons is overloaded by PyGtk so we have to do the same here
427 # this breaks some subclasses of Dialog so add a _buttons_property
428 # keyword to work around this
429 if _buttons_property is not None:
430 kwds['buttons'] = _buttons_property
432 Gtk.Dialog.__init__(self, **kwds)
434 self.set_title(title)
436 self.set_transient_for(parent)
437 if flags & Gtk.DialogFlags.MODAL:
439 if flags & Gtk.DialogFlags.DESTROY_WITH_PARENT:
440 self.set_destroy_with_parent(True)
442 # NO_SEPARATOR has been removed from Gtk 3
443 if hasattr(Gtk.DialogFlags, "NO_SEPARATOR") and (flags & Gtk.DialogFlags.NO_SEPARATOR):
444 self.set_has_separator(False)
446 warnings.warn("Gtk.DialogFlags.NO_SEPARATOR has been depricated since Gtk+-3.0", PyGIDeprecationWarning)
448 if buttons is not None:
449 self.add_buttons(*buttons)
451 action_area = property(lambda dialog: dialog.get_action_area())
452 vbox = property(lambda dialog: dialog.get_content_area())
454 def add_buttons(self, *args):
456 The add_buttons() method adds several buttons to the Gtk.Dialog using
457 the button data passed as arguments to the method. This method is the
458 same as calling the Gtk.Dialog.add_button() repeatedly. The button data
459 pairs - button text (or stock ID) and a response ID integer are passed
460 individually. For example:
462 >>> dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)
464 will add "Open" and "Close" buttons to dialog.
473 for text, response in _button(args):
474 self.add_button(text, response)
476 raise TypeError('Must pass an even number of arguments')
478 Dialog = override(Dialog)
479 __all__.append('Dialog')
482 class MessageDialog(Gtk.MessageDialog, Dialog):
486 message_type=Gtk.MessageType.INFO,
487 buttons=Gtk.ButtonsType.NONE,
492 kwds['text'] = message_format
494 # type keyword is used for backwards compat with PyGTK
497 warnings.warn("The use of the keyword type as a parameter of the Gtk.MessageDialog constructor has been depricated. Please use message_type instead.", PyGIDeprecationWarning)
498 message_type = kwds.pop('type')
500 Gtk.MessageDialog.__init__(self,
501 _buttons_property=buttons,
502 message_type=message_type,
507 def format_secondary_text(self, message_format):
508 self.set_property('secondary-use-markup', False)
509 self.set_property('secondary-text', message_format)
511 def format_secondary_markup(self, message_format):
512 self.set_property('secondary-use-markup', True)
513 self.set_property('secondary-text', message_format)
515 MessageDialog = override(MessageDialog)
516 __all__.append('MessageDialog')
519 class AboutDialog(Gtk.AboutDialog):
520 def __init__(self, **kwds):
521 Gtk.AboutDialog.__init__(self, **kwds)
523 AboutDialog = override(AboutDialog)
524 __all__.append('AboutDialog')
527 class ColorSelectionDialog(Gtk.ColorSelectionDialog):
528 def __init__(self, title=None, **kwds):
529 Gtk.ColorSelectionDialog.__init__(self, title=title, **kwds)
531 ColorSelectionDialog = override(ColorSelectionDialog)
532 __all__.append('ColorSelectionDialog')
535 class FileChooserDialog(Gtk.FileChooserDialog):
539 action=Gtk.FileChooserAction.OPEN,
542 Gtk.FileChooserDialog.__init__(self,
548 FileChooserDialog = override(FileChooserDialog)
549 __all__.append('FileChooserDialog')
552 class FontSelectionDialog(Gtk.FontSelectionDialog):
553 def __init__(self, title=None, **kwds):
554 Gtk.FontSelectionDialog.__init__(self, title=title, **kwds)
556 FontSelectionDialog = override(FontSelectionDialog)
557 __all__.append('FontSelectionDialog')
560 class RecentChooserDialog(Gtk.RecentChooserDialog):
568 Gtk.RecentChooserDialog.__init__(self,
569 recent_manager=manager,
575 RecentChooserDialog = override(RecentChooserDialog)
576 __all__.append('RecentChooserDialog')
579 class IconView(Gtk.IconView):
581 def __init__(self, model=None, **kwds):
582 Gtk.IconView.__init__(self, model=model, **kwds)
584 def get_item_at_pos(self, x, y):
585 success, path, cell = super(IconView, self).get_item_at_pos(x, y)
589 def get_visible_range(self):
590 success, start_path, end_path = super(IconView, self).get_visible_range()
592 return (start_path, end_path,)
594 def get_dest_item_at_pos(self, drag_x, drag_y):
595 success, path, pos = super(IconView, self).get_dest_item_at_pos(drag_x, drag_y)
599 IconView = override(IconView)
600 __all__.append('IconView')
603 class ToolButton(Gtk.ToolButton):
605 def __init__(self, stock_id=None, **kwds):
606 Gtk.ToolButton.__init__(self, stock_id=stock_id, **kwds)
608 ToolButton = override(ToolButton)
609 __all__.append('ToolButton')
612 class IMContext(Gtk.IMContext):
614 def get_surrounding(self):
615 success, text, cursor_index = super(IMContext, self).get_surrounding()
617 return (text, cursor_index,)
619 IMContext = override(IMContext)
620 __all__.append('IMContext')
623 class RecentInfo(Gtk.RecentInfo):
625 def get_application_info(self, app_name):
626 success, app_exec, count, time = super(RecentInfo, self).get_application_info(app_name)
628 return (app_exec, count, time,)
630 RecentInfo = override(RecentInfo)
631 __all__.append('RecentInfo')
634 class TextBuffer(Gtk.TextBuffer):
635 def _get_or_create_tag_table(self):
636 table = self.get_tag_table()
638 table = Gtk.TextTagTable()
639 self.set_tag_table(table)
643 def create_tag(self, tag_name=None, **properties):
645 @tag_name: name of the new tag, or None
646 @properties: keyword list of properties and their values
648 Creates a tag and adds it to the tag table of the TextBuffer.
649 Equivalent to creating a Gtk.TextTag and then adding the
650 tag to the buffer's tag table. The returned tag is owned by
651 the buffer's tag table.
653 If @tag_name is None, the tag is anonymous.
655 If @tag_name is not None, a tag called @tag_name must not already
656 exist in the tag table for this buffer.
658 Properties are passed as a keyword list of names and values (e.g.
659 foreground = 'DodgerBlue', weight = Pango.Weight.BOLD)
661 Return value: a new tag
664 tag = Gtk.TextTag(name=tag_name, **properties)
665 self._get_or_create_tag_table().add(tag)
668 def create_mark(self, mark_name, where, left_gravity=False):
669 return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
671 def set_text(self, text, length=-1):
672 Gtk.TextBuffer.set_text(self, text, length)
674 def insert(self, iter, text, length=-1):
675 if not isinstance(text, _basestring):
676 raise TypeError('text must be a string, not %s' % type(text))
678 Gtk.TextBuffer.insert(self, iter, text, length)
680 def insert_with_tags(self, iter, text, *tags):
681 start_offset = iter.get_offset()
682 self.insert(iter, text)
687 start = self.get_iter_at_offset(start_offset)
690 self.apply_tag(tag, start, iter)
692 def insert_with_tags_by_name(self, iter, text, *tags):
699 tag_obj = self.get_tag_table().lookup(tag)
701 raise ValueError('unknown text tag: %s' % tag)
702 tag_objs.append(tag_obj)
704 self.insert_with_tags(iter, text, *tag_objs)
706 def insert_at_cursor(self, text, length=-1):
707 if not isinstance(text, _basestring):
708 raise TypeError('text must be a string, not %s' % type(text))
710 Gtk.TextBuffer.insert_at_cursor(self, text, length)
712 def get_selection_bounds(self):
713 success, start, end = super(TextBuffer, self).get_selection_bounds()
719 TextBuffer = override(TextBuffer)
720 __all__.append('TextBuffer')
723 class TextIter(Gtk.TextIter):
725 def forward_search(self, string, flags, limit):
726 success, match_start, match_end = super(TextIter, self).forward_search(string,
729 return (match_start, match_end)
733 def backward_search(self, string, flags, limit):
734 success, match_start, match_end = super(TextIter, self).backward_search(string,
737 return (match_start, match_end)
741 def begins_tag(self, tag=None):
742 return super(TextIter, self).begins_tag(tag)
744 def ends_tag(self, tag=None):
745 return super(TextIter, self).ends_tag(tag)
747 def toggles_tag(self, tag=None):
748 return super(TextIter, self).toggles_tag(tag)
750 TextIter = override(TextIter)
751 __all__.append('TextIter')
754 class TreeModel(Gtk.TreeModel):
756 return self.iter_n_children(None)
761 # alias for Python 2.x object protocol
762 __nonzero__ = __bool__
764 def _getiter(self, key):
765 if isinstance(key, Gtk.TreeIter):
767 elif isinstance(key, int) and key < 0:
768 index = len(self) + key
770 raise IndexError("row index is out of bounds: %d" % key)
772 aiter = self.get_iter(index)
774 raise IndexError("could not find tree path '%s'" % key)
778 aiter = self.get_iter(key)
780 raise IndexError("could not find tree path '%s'" % key)
783 def __getitem__(self, key):
784 aiter = self._getiter(key)
785 return TreeModelRow(self, aiter)
787 def __setitem__(self, key, value):
789 self.set_row(row.iter, value)
791 def __delitem__(self, key):
792 aiter = self._getiter(key)
796 return TreeModelRowIter(self, self.get_iter_first())
798 def get_iter(self, path):
799 if not isinstance(path, Gtk.TreePath):
800 path = TreePath(path)
802 success, aiter = super(TreeModel, self).get_iter(path)
804 raise ValueError("invalid tree path '%s'" % path)
807 def get_iter_first(self):
808 success, aiter = super(TreeModel, self).get_iter_first()
812 def get_iter_from_string(self, path_string):
813 success, aiter = super(TreeModel, self).get_iter_from_string(path_string)
815 raise ValueError("invalid tree path '%s'" % path_string)
818 def iter_next(self, aiter):
819 next_iter = aiter.copy()
820 success = super(TreeModel, self).iter_next(next_iter)
824 def iter_previous(self, aiter):
825 prev_iter = aiter.copy()
826 success = super(TreeModel, self).iter_previous(prev_iter)
830 def iter_children(self, aiter):
831 success, child_iter = super(TreeModel, self).iter_children(aiter)
835 def iter_nth_child(self, parent, n):
836 success, child_iter = super(TreeModel, self).iter_nth_child(parent, n)
840 def iter_parent(self, aiter):
841 success, parent_iter = super(TreeModel, self).iter_parent(aiter)
845 def _convert_row(self, row):
846 # TODO: Accept a dictionary for row
847 # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
848 if isinstance(row, str):
849 raise TypeError('Expected a list or tuple, but got str')
851 n_columns = self.get_n_columns()
852 if len(row) != n_columns:
853 raise ValueError('row sequence has the incorrect number of elements')
857 for cur_col, value in enumerate(row):
858 # do not try to set None values, they are causing warnings
861 result.append(self._convert_value(cur_col, value))
862 columns.append(cur_col)
863 return (result, columns)
865 def set_row(self, treeiter, row):
866 converted_row, columns = self._convert_row(row)
867 for column in columns:
870 continue # None means skip this row
872 self.set_value(treeiter, column, value)
874 def _convert_value(self, column, value):
878 # we may need to convert to a basic type
879 type_ = self.get_column_type(column)
880 if type_ == GObject.TYPE_STRING:
881 if isinstance(value, str):
883 elif sys.version_info < (3, 0):
884 if isinstance(value, unicode):
885 value = value.encode('UTF-8')
887 raise ValueError('Expected string or unicode for column %i but got %s%s' % (column, value, type(value)))
889 raise ValueError('Expected a string for column %i but got %s' % (column, type(value)))
890 elif type_ == GObject.TYPE_FLOAT or type_ == GObject.TYPE_DOUBLE:
891 if isinstance(value, float):
894 raise ValueError('Expected a float for column %i but got %s' % (column, type(value)))
895 elif type_ == GObject.TYPE_LONG or type_ == GObject.TYPE_INT:
896 if isinstance(value, int):
898 elif sys.version_info < (3, 0):
899 if isinstance(value, long):
902 raise ValueError('Expected an long for column %i but got %s' % (column, type(value)))
904 raise ValueError('Expected an integer for column %i but got %s' % (column, type(value)))
905 elif type_ == GObject.TYPE_BOOLEAN:
907 if sys.version_info < (3, 0):
908 cmp_classes.append(long)
910 if isinstance(value, tuple(cmp_classes)):
913 raise ValueError('Expected a bool for column %i but got %s' % (column, type(value)))
915 # use GValues directly to marshal to the correct type
916 # standard object checks should take care of validation
917 # so we don't have to do it here
918 value_container = GObject.Value()
919 value_container.init(type_)
920 if type_ == GObject.TYPE_CHAR:
921 value_container.set_char(value)
922 value = value_container
923 elif type_ == GObject.TYPE_UCHAR:
924 value_container.set_uchar(value)
925 value = value_container
926 elif type_ == GObject.TYPE_UNICHAR:
928 if sys.version_info < (3, 0):
929 cmp_classes.append(unicode)
931 if isinstance(value, tuple(cmp_classes)):
932 value = ord(value[0])
934 value_container.set_uint(value)
935 value = value_container
936 elif type_ == GObject.TYPE_UINT:
937 value_container.set_uint(value)
938 value = value_container
939 elif type_ == GObject.TYPE_ULONG:
940 value_container.set_ulong(value)
941 value = value_container
942 elif type_ == GObject.TYPE_INT64:
943 value_container.set_int64(value)
944 value = value_container
945 elif type_ == GObject.TYPE_UINT64:
946 value_container.set_uint64(value)
947 value = value_container
948 elif type_ == GObject.TYPE_PYOBJECT:
949 value_container.set_boxed(value)
950 value = value_container
954 def get(self, treeiter, *columns):
955 n_columns = self.get_n_columns()
959 if not isinstance(col, int):
960 raise TypeError("column numbers must be ints")
962 if col < 0 or col >= n_columns:
963 raise ValueError("column number is out of range")
965 values.append(self.get_value(treeiter, col))
969 def filter_new(self, root=None):
970 return super(TreeModel, self).filter_new(root)
972 TreeModel = override(TreeModel)
973 __all__.append('TreeModel')
976 class TreeSortable(Gtk.TreeSortable, ):
978 def get_sort_column_id(self):
979 success, sort_column_id, order = super(TreeSortable, self).get_sort_column_id()
981 return (sort_column_id, order,)
985 def set_sort_func(self, sort_column_id, sort_func, user_data=None):
986 super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
988 def set_default_sort_func(self, sort_func, user_data=None):
989 super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
991 TreeSortable = override(TreeSortable)
992 __all__.append('TreeSortable')
995 class TreeModelSort(Gtk.TreeModelSort):
996 def __init__(self, model, **kwds):
997 Gtk.TreeModelSort.__init__(self, model=model, **kwds)
999 TreeModelSort = override(TreeModelSort)
1000 __all__.append('TreeModelSort')
1003 class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
1004 def __init__(self, *column_types):
1005 Gtk.ListStore.__init__(self)
1006 self.set_column_types(column_types)
1008 def _do_insert(self, position, row):
1010 row, columns = self._convert_row(row)
1011 treeiter = self.insert_with_valuesv(position, columns, row)
1013 treeiter = Gtk.ListStore.insert(self, position)
1017 def append(self, row=None):
1019 return self._do_insert(-1, row)
1020 # gtk_list_store_insert() does not know about the "position == -1"
1021 # case, so use append() here
1023 return Gtk.ListStore.append(self)
1025 def prepend(self, row=None):
1026 return self._do_insert(0, row)
1028 def insert(self, position, row=None):
1029 return self._do_insert(position, row)
1031 # FIXME: sends two signals; check if this can use an atomic
1032 # insert_with_valuesv()
1034 def insert_before(self, sibling, row=None):
1035 treeiter = Gtk.ListStore.insert_before(self, sibling)
1038 self.set_row(treeiter, row)
1042 # FIXME: sends two signals; check if this can use an atomic
1043 # insert_with_valuesv()
1045 def insert_after(self, sibling, row=None):
1046 treeiter = Gtk.ListStore.insert_after(self, sibling)
1049 self.set_row(treeiter, row)
1053 def set_value(self, treeiter, column, value):
1054 value = self._convert_value(column, value)
1055 Gtk.ListStore.set_value(self, treeiter, column, value)
1057 def set(self, treeiter, *args):
1059 def _set_lists(columns, values):
1060 if len(columns) != len(values):
1061 raise TypeError('The number of columns do not match the number of values')
1062 for col_num, val in zip(columns, values):
1063 if not isinstance(col_num, int):
1064 raise TypeError('TypeError: Expected integer argument for column.')
1065 self.set_value(treeiter, col_num, val)
1068 if isinstance(args[0], int):
1071 _set_lists(columns, values)
1072 elif isinstance(args[0], (tuple, list)):
1074 raise TypeError('Too many arguments')
1075 _set_lists(args[0], args[1])
1076 elif isinstance(args[0], dict):
1077 columns = args[0].keys()
1078 values = args[0].values()
1079 _set_lists(columns, values)
1081 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
1083 ListStore = override(ListStore)
1084 __all__.append('ListStore')
1087 class TreeModelRow(object):
1089 def __init__(self, model, iter_or_path):
1090 if not isinstance(model, Gtk.TreeModel):
1091 raise TypeError("expected Gtk.TreeModel, %s found" % type(model).__name__)
1093 if isinstance(iter_or_path, Gtk.TreePath):
1094 self.iter = model.get_iter(iter_or_path)
1095 elif isinstance(iter_or_path, Gtk.TreeIter):
1096 self.iter = iter_or_path
1098 raise TypeError("expected Gtk.TreeIter or Gtk.TreePath, \
1099 %s found" % type(iter_or_path).__name__)
1103 return self.model.get_path(self.iter)
1107 return self.get_next()
1111 return self.get_previous()
1115 return self.get_parent()
1118 next_iter = self.model.iter_next(self.iter)
1120 return TreeModelRow(self.model, next_iter)
1122 def get_previous(self):
1123 prev_iter = self.model.iter_previous(self.iter)
1125 return TreeModelRow(self.model, prev_iter)
1127 def get_parent(self):
1128 parent_iter = self.model.iter_parent(self.iter)
1130 return TreeModelRow(self.model, parent_iter)
1132 def __getitem__(self, key):
1133 if isinstance(key, int):
1134 if key >= self.model.get_n_columns():
1135 raise IndexError("column index is out of bounds: %d" % key)
1137 key = self._convert_negative_index(key)
1138 return self.model.get_value(self.iter, key)
1139 elif isinstance(key, slice):
1140 start, stop, step = key.indices(self.model.get_n_columns())
1142 for i in range(start, stop, step):
1143 alist.append(self.model.get_value(self.iter, i))
1146 raise TypeError("indices must be integers, not %s" % type(key).__name__)
1148 def __setitem__(self, key, value):
1149 if isinstance(key, int):
1150 if key >= self.model.get_n_columns():
1151 raise IndexError("column index is out of bounds: %d" % key)
1153 key = self._convert_negative_index(key)
1154 self.model.set_value(self.iter, key, value)
1155 elif isinstance(key, slice):
1156 start, stop, step = key.indices(self.model.get_n_columns())
1157 indexList = range(start, stop, step)
1158 if len(indexList) != len(value):
1160 "attempt to assign sequence of size %d to slice of size %d"
1161 % (len(value), len(indexList)))
1163 for i, v in enumerate(indexList):
1164 self.model.set_value(self.iter, v, value[i])
1166 raise TypeError("index must be an integer or slice, not %s" % type(key).__name__)
1168 def _convert_negative_index(self, index):
1169 new_index = self.model.get_n_columns() + index
1171 raise IndexError("column index is out of bounds: %d" % index)
1174 def iterchildren(self):
1175 child_iter = self.model.iter_children(self.iter)
1176 return TreeModelRowIter(self.model, child_iter)
1178 __all__.append('TreeModelRow')
1181 class TreeModelRowIter(object):
1183 def __init__(self, model, aiter):
1190 row = TreeModelRow(self.model, self.iter)
1191 self.iter = self.model.iter_next(self.iter)
1194 # alias for Python 2.x object protocol
1200 __all__.append('TreeModelRowIter')
1203 class TreePath(Gtk.TreePath):
1205 def __new__(cls, path=0):
1206 if isinstance(path, int):
1208 elif isinstance(path, tuple):
1209 path = ":".join(str(val) for val in path)
1212 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1214 return TreePath.new_from_string(path)
1216 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1219 return self.to_string()
1221 def __lt__(self, other):
1222 return not other is None and self.compare(other) < 0
1224 def __le__(self, other):
1225 return not other is None and self.compare(other) <= 0
1227 def __eq__(self, other):
1228 return not other is None and self.compare(other) == 0
1230 def __ne__(self, other):
1231 return other is None or self.compare(other) != 0
1233 def __gt__(self, other):
1234 return other is None or self.compare(other) > 0
1236 def __ge__(self, other):
1237 return other is None or self.compare(other) >= 0
1240 return iter(self.get_indices())
1243 return self.get_depth()
1245 def __getitem__(self, index):
1246 return self.get_indices()[index]
1248 TreePath = override(TreePath)
1249 __all__.append('TreePath')
1252 class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
1254 def __init__(self, *column_types):
1255 Gtk.TreeStore.__init__(self)
1256 self.set_column_types(column_types)
1258 def _do_insert(self, parent, position, row):
1260 row, columns = self._convert_row(row)
1261 treeiter = self.insert_with_values(parent, position, columns, row)
1263 treeiter = Gtk.TreeStore.insert(self, parent, position)
1267 def append(self, parent, row=None):
1268 return self._do_insert(parent, -1, row)
1270 def prepend(self, parent, row=None):
1271 return self._do_insert(parent, 0, row)
1273 def insert(self, parent, position, row=None):
1274 return self._do_insert(parent, position, row)
1276 # FIXME: sends two signals; check if this can use an atomic
1277 # insert_with_valuesv()
1279 def insert_before(self, parent, sibling, row=None):
1280 treeiter = Gtk.TreeStore.insert_before(self, parent, sibling)
1283 self.set_row(treeiter, row)
1287 # FIXME: sends two signals; check if this can use an atomic
1288 # insert_with_valuesv()
1290 def insert_after(self, parent, sibling, row=None):
1291 treeiter = Gtk.TreeStore.insert_after(self, parent, sibling)
1294 self.set_row(treeiter, row)
1298 def set_value(self, treeiter, column, value):
1299 value = self._convert_value(column, value)
1300 Gtk.TreeStore.set_value(self, treeiter, column, value)
1302 def set(self, treeiter, *args):
1304 def _set_lists(columns, values):
1305 if len(columns) != len(values):
1306 raise TypeError('The number of columns do not match the number of values')
1307 for col_num, val in zip(columns, values):
1308 if not isinstance(col_num, int):
1309 raise TypeError('TypeError: Expected integer argument for column.')
1310 self.set_value(treeiter, col_num, val)
1313 if isinstance(args[0], int):
1316 _set_lists(columns, values)
1317 elif isinstance(args[0], (tuple, list)):
1319 raise TypeError('Too many arguments')
1320 _set_lists(args[0], args[1])
1321 elif isinstance(args[0], dict):
1322 columns = args[0].keys()
1323 values = args[0].values()
1324 _set_lists(columns, values)
1326 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
1328 TreeStore = override(TreeStore)
1329 __all__.append('TreeStore')
1332 class TreeView(Gtk.TreeView, Container):
1334 def __init__(self, model=None):
1335 Gtk.TreeView.__init__(self)
1337 self.set_model(model)
1339 def get_path_at_pos(self, x, y):
1340 success, path, column, cell_x, cell_y = super(TreeView, self).get_path_at_pos(x, y)
1342 return (path, column, cell_x, cell_y,)
1344 def get_visible_range(self):
1345 success, start_path, end_path = super(TreeView, self).get_visible_range()
1347 return (start_path, end_path,)
1349 def get_dest_row_at_pos(self, drag_x, drag_y):
1350 success, path, pos = super(TreeView, self).get_dest_row_at_pos(drag_x, drag_y)
1354 def _construct_target_list(self, targets):
1355 # FIXME: this should most likely be part of Widget or a global helper
1359 entry = Gtk.TargetEntry.new(*t)
1360 target_entries.append(entry)
1361 return target_entries
1363 def enable_model_drag_source(self, start_button_mask, targets, actions):
1364 target_entries = self._construct_target_list(targets)
1365 super(TreeView, self).enable_model_drag_source(start_button_mask,
1369 def enable_model_drag_dest(self, targets, actions):
1370 target_entries = self._construct_target_list(targets)
1371 super(TreeView, self).enable_model_drag_dest(target_entries,
1374 def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
1375 if not isinstance(path, Gtk.TreePath):
1376 path = TreePath(path)
1377 super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
1379 def set_cursor(self, path, column=None, start_editing=False):
1380 if not isinstance(path, Gtk.TreePath):
1381 path = TreePath(path)
1382 super(TreeView, self).set_cursor(path, column, start_editing)
1384 def get_cell_area(self, path, column=None):
1385 if not isinstance(path, Gtk.TreePath):
1386 path = TreePath(path)
1387 return super(TreeView, self).get_cell_area(path, column)
1389 def insert_column_with_attributes(self, position, title, cell, **kwargs):
1390 column = TreeViewColumn()
1391 column.set_title(title)
1392 column.pack_start(cell, False)
1393 self.insert_column(column, position)
1394 column.set_attributes(cell, **kwargs)
1396 TreeView = override(TreeView)
1397 __all__.append('TreeView')
1400 class TreeViewColumn(Gtk.TreeViewColumn):
1401 def __init__(self, title='',
1404 Gtk.TreeViewColumn.__init__(self, title=title)
1406 self.pack_start(cell_renderer, True)
1408 for (name, value) in attributes.items():
1409 self.add_attribute(cell_renderer, name, value)
1411 def cell_get_position(self, cell_renderer):
1412 success, start_pos, width = super(TreeViewColumn, self).cell_get_position(cell_renderer)
1414 return (start_pos, width,)
1416 def set_cell_data_func(self, cell_renderer, func, func_data=None):
1417 super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
1419 def set_attributes(self, cell_renderer, **attributes):
1420 Gtk.CellLayout.clear_attributes(self, cell_renderer)
1422 for (name, value) in attributes.items():
1423 Gtk.CellLayout.add_attribute(self, cell_renderer, name, value)
1426 TreeViewColumn = override(TreeViewColumn)
1427 __all__.append('TreeViewColumn')
1430 class TreeSelection(Gtk.TreeSelection):
1432 def select_path(self, path):
1433 if not isinstance(path, Gtk.TreePath):
1434 path = TreePath(path)
1435 super(TreeSelection, self).select_path(path)
1437 def get_selected(self):
1438 success, model, aiter = super(TreeSelection, self).get_selected()
1440 return (model, aiter)
1442 return (model, None)
1444 # for compatibility with PyGtk
1446 def get_selected_rows(self):
1447 rows, model = super(TreeSelection, self).get_selected_rows()
1448 return (model, rows)
1451 TreeSelection = override(TreeSelection)
1452 __all__.append('TreeSelection')
1455 class Button(Gtk.Button, Container):
1456 def __init__(self, label=None, stock=None, use_stock=False, use_underline=False, **kwds):
1460 use_underline = True
1461 Gtk.Button.__init__(self, label=label, use_stock=use_stock,
1462 use_underline=use_underline, **kwds)
1463 Button = override(Button)
1464 __all__.append('Button')
1467 class LinkButton(Gtk.LinkButton):
1468 def __init__(self, uri, label=None, **kwds):
1469 Gtk.LinkButton.__init__(self, uri=uri, label=label, **kwds)
1471 LinkButton = override(LinkButton)
1472 __all__.append('LinkButton')
1475 class Label(Gtk.Label):
1476 def __init__(self, label=None, **kwds):
1477 Gtk.Label.__init__(self, label=label, **kwds)
1479 Label = override(Label)
1480 __all__.append('Label')
1483 class Adjustment(Gtk.Adjustment):
1484 def __init__(self, *args, **kwds):
1485 arg_names = ('value', 'lower', 'upper',
1486 'step_increment', 'page_increment', 'page_size')
1487 new_args = dict(zip(arg_names, args))
1488 new_args.update(kwds)
1490 # PyGTK compatiblity
1491 if 'page_incr' in new_args:
1492 new_args['page_increment'] = new_args.pop('page_incr')
1493 if 'step_incr' in new_args:
1494 new_args['step_increment'] = new_args.pop('step_incr')
1495 Gtk.Adjustment.__init__(self, **new_args)
1497 # The value property is set between lower and (upper - page_size).
1498 # Just in case lower, upper or page_size was still 0 when value
1499 # was set, we set it again here.
1500 if 'value' in new_args:
1501 self.set_value(new_args['value'])
1503 Adjustment = override(Adjustment)
1504 __all__.append('Adjustment')
1507 class Table(Gtk.Table, Container):
1508 def __init__(self, rows=1, columns=1, homogeneous=False, **kwds):
1509 if 'n_rows' in kwds:
1510 rows = kwds.pop('n_rows')
1512 if 'n_columns' in kwds:
1513 columns = kwds.pop('n_columns')
1515 Gtk.Table.__init__(self, n_rows=rows, n_columns=columns, homogeneous=homogeneous, **kwds)
1517 def attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, xpadding=0, ypadding=0):
1518 Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
1520 Table = override(Table)
1521 __all__.append('Table')
1524 class ScrolledWindow(Gtk.ScrolledWindow):
1525 def __init__(self, hadjustment=None, vadjustment=None, **kwds):
1526 Gtk.ScrolledWindow.__init__(self, hadjustment=hadjustment, vadjustment=vadjustment, **kwds)
1528 ScrolledWindow = override(ScrolledWindow)
1529 __all__.append('ScrolledWindow')
1532 class HScrollbar(Gtk.HScrollbar):
1533 def __init__(self, adjustment=None, **kwds):
1534 Gtk.HScrollbar.__init__(self, adjustment=adjustment, **kwds)
1536 HScrollbar = override(HScrollbar)
1537 __all__.append('HScrollbar')
1540 class VScrollbar(Gtk.VScrollbar):
1541 def __init__(self, adjustment=None, **kwds):
1542 Gtk.VScrollbar.__init__(self, adjustment=adjustment, **kwds)
1544 VScrollbar = override(VScrollbar)
1545 __all__.append('VScrollbar')
1548 class Paned(Gtk.Paned):
1549 def pack1(self, child, resize=False, shrink=True):
1550 super(Paned, self).pack1(child, resize, shrink)
1552 def pack2(self, child, resize=True, shrink=True):
1553 super(Paned, self).pack2(child, resize, shrink)
1555 Paned = override(Paned)
1556 __all__.append('Paned')
1559 class Arrow(Gtk.Arrow):
1560 def __init__(self, arrow_type, shadow_type, **kwds):
1561 Gtk.Arrow.__init__(self, arrow_type=arrow_type,
1562 shadow_type=shadow_type,
1565 Arrow = override(Arrow)
1566 __all__.append('Arrow')
1569 class IconSet(Gtk.IconSet):
1570 def __new__(cls, pixbuf=None):
1571 if pixbuf is not None:
1572 iconset = Gtk.IconSet.new_from_pixbuf(pixbuf)
1574 iconset = Gtk.IconSet.__new__(cls)
1577 IconSet = override(IconSet)
1578 __all__.append('IconSet')
1581 class Viewport(Gtk.Viewport):
1582 def __init__(self, hadjustment=None, vadjustment=None, **kwds):
1583 Gtk.Viewport.__init__(self, hadjustment=hadjustment,
1584 vadjustment=vadjustment,
1587 Viewport = override(Viewport)
1588 __all__.append('Viewport')
1591 class TreeModelFilter(Gtk.TreeModelFilter):
1592 def set_visible_func(self, func, data=None):
1593 super(TreeModelFilter, self).set_visible_func(func, data)
1595 TreeModelFilter = override(TreeModelFilter)
1596 __all__.append('TreeModelFilter')
1598 if Gtk._version != '2.0':
1599 class Menu(Gtk.Menu):
1600 def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
1601 self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
1602 Menu = override(Menu)
1603 __all__.append('Menu')
1605 _Gtk_main_quit = Gtk.main_quit
1608 @override(Gtk.main_quit)
1609 def main_quit(*args):
1612 _Gtk_stock_lookup = Gtk.stock_lookup
1615 @override(Gtk.stock_lookup)
1616 def stock_lookup(*args):
1617 success, item = _Gtk_stock_lookup(*args)
1623 initialized, argv = Gtk.init_check(sys.argv)
1624 sys.argv = list(argv)
1626 raise RuntimeError("Gtk couldn't be initialized")