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
26 from gi.repository import GObject
27 from ..overrides import override, strip_boolean_result, deprecated_init
28 from ..module import get_introspection_module
29 from gi import PyGIDeprecationWarning
31 if sys.version_info >= (3, 0):
33 _callable = lambda c: hasattr(c, '__call__')
35 _basestring = basestring
38 Gtk = get_introspection_module('Gtk')
42 if Gtk._version == '2.0':
43 warn_msg = "You have imported the Gtk 2.0 module. Because Gtk 2.0 \
44 was not designed for use with introspection some of the \
45 interfaces and API will fail. As such this is not supported \
46 by the pygobject development team and we encourage you to \
47 port your app to Gtk 3 or greater. PyGTK is the recomended \
48 python module to use with Gtk 2.0"
50 warnings.warn(warn_msg, RuntimeWarning)
53 class PyGTKDeprecationWarning(PyGIDeprecationWarning):
56 __all__.append('PyGTKDeprecationWarning')
59 def _construct_target_list(targets):
60 """Create a list of TargetEntry items from a list of tuples in the form (target, flags, info)
62 The list can also contain existing TargetEntry items in which case the existing entry
63 is re-used in the return list.
67 if not isinstance(entry, Gtk.TargetEntry):
68 entry = Gtk.TargetEntry.new(*entry)
69 target_entries.append(entry)
72 __all__.append('_construct_target_list')
75 def _extract_handler_and_args(obj_or_map, handler_name):
77 if isinstance(obj_or_map, collections.Mapping):
78 handler = obj_or_map.get(handler_name, None)
80 handler = getattr(obj_or_map, handler_name, None)
83 raise AttributeError('Handler %s not found' % handler_name)
86 if isinstance(handler, collections.Sequence):
88 raise TypeError("Handler %s tuple can not be empty" % handler)
92 elif not _callable(handler):
93 raise TypeError('Handler %s is not a method, function or tuple' % handler)
98 # Exposed for unit-testing.
99 __all__.append('_extract_handler_and_args')
102 def _builder_connect_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map):
103 handler, args = _extract_handler_and_args(obj_or_map, handler_name)
105 after = flags & GObject.ConnectFlags.AFTER
106 if connect_obj is not None:
108 gobj.connect_object_after(signal_name, handler, connect_obj, *args)
110 gobj.connect_object(signal_name, handler, connect_obj, *args)
113 gobj.connect_after(signal_name, handler, *args)
115 gobj.connect(signal_name, handler, *args)
118 class Widget(Gtk.Widget):
120 translate_coordinates = strip_boolean_result(Gtk.Widget.translate_coordinates)
122 def drag_dest_set_target_list(self, target_list):
123 if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
124 target_list = Gtk.TargetList.new(_construct_target_list(target_list))
125 super(Widget, self).drag_dest_set_target_list(target_list)
127 def drag_source_set_target_list(self, target_list):
128 if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
129 target_list = Gtk.TargetList.new(_construct_target_list(target_list))
130 super(Widget, self).drag_source_set_target_list(target_list)
132 def style_get_property(self, property_name, value=None):
134 prop = self.find_style_property(property_name)
136 raise ValueError('Class "%s" does not contain style property "%s"' %
137 (self, property_name))
138 value = GObject.Value(prop.value_type)
140 Gtk.Widget.style_get_property(self, property_name, value)
141 return value.get_value()
144 Widget = override(Widget)
145 __all__.append('Widget')
148 class Container(Gtk.Container, Widget):
151 return len(self.get_children())
153 def __contains__(self, child):
154 return child in self.get_children()
157 return iter(self.get_children())
162 # alias for Python 2.x object protocol
163 __nonzero__ = __bool__
165 get_focus_chain = strip_boolean_result(Gtk.Container.get_focus_chain)
167 def child_get_property(self, child, property_name, value=None):
169 prop = self.find_child_property(property_name)
171 raise ValueError('Class "%s" does not contain child property "%s"' %
172 (self, property_name))
173 value = GObject.Value(prop.value_type)
175 Gtk.Container.child_get_property(self, child, property_name, value)
176 return value.get_value()
178 def child_get(self, child, *prop_names):
179 """Returns a list of child property values for the given names."""
180 return [self.child_get_property(child, name) for name in prop_names]
182 def child_set(self, child, **kwargs):
183 """Set a child properties on the given child to key/value pairs."""
184 for name, value in kwargs.items():
185 name = name.replace('_', '-')
186 self.child_set_property(child, name, value)
189 Container = override(Container)
190 __all__.append('Container')
193 class Editable(Gtk.Editable):
195 def insert_text(self, text, position):
196 return super(Editable, self).insert_text(text, -1, position)
198 get_selection_bounds = strip_boolean_result(Gtk.Editable.get_selection_bounds, fail_ret=())
201 Editable = override(Editable)
202 __all__.append("Editable")
205 class Action(Gtk.Action):
206 __init__ = deprecated_init(Gtk.Action.__init__,
207 arg_names=('name', 'label', 'tooltip', 'stock_id'),
208 category=PyGTKDeprecationWarning)
210 Action = override(Action)
211 __all__.append("Action")
214 class RadioAction(Gtk.RadioAction):
215 __init__ = deprecated_init(Gtk.RadioAction.__init__,
216 arg_names=('name', 'label', 'tooltip', 'stock_id', 'value'),
217 category=PyGTKDeprecationWarning)
219 RadioAction = override(RadioAction)
220 __all__.append("RadioAction")
223 class ActionGroup(Gtk.ActionGroup):
224 __init__ = deprecated_init(Gtk.ActionGroup.__init__,
226 category=PyGTKDeprecationWarning)
228 def add_actions(self, entries, user_data=None):
230 The add_actions() method is a convenience method that creates a number
231 of gtk.Action objects based on the information in the list of action
232 entry tuples contained in entries and adds them to the action group.
233 The entry tuples can vary in size from one to six items with the
234 following information:
236 * The name of the action. Must be specified.
237 * The stock id for the action. Optional with a default value of None
238 if a label is specified.
239 * The label for the action. This field should typically be marked
240 for translation, see the set_translation_domain() method. Optional
241 with a default value of None if a stock id is specified.
242 * The accelerator for the action, in the format understood by the
243 gtk.accelerator_parse() function. Optional with a default value of
245 * The tooltip for the action. This field should typically be marked
246 for translation, see the set_translation_domain() method. Optional
247 with a default value of None.
248 * The callback function invoked when the action is activated.
249 Optional with a default value of None.
251 The "activate" signals of the actions are connected to the callbacks and
252 their accel paths are set to <Actions>/group-name/action-name.
257 raise TypeError('entries must be iterable')
259 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
260 action = Action(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
261 if callback is not None:
262 if user_data is None:
263 action.connect('activate', callback)
265 action.connect('activate', callback, user_data)
267 self.add_action_with_accel(action, accelerator)
270 # using inner function above since entries can leave out optional arguments
273 def add_toggle_actions(self, entries, user_data=None):
275 The add_toggle_actions() method is a convenience method that creates a
276 number of gtk.ToggleAction objects based on the information in the list
277 of action entry tuples contained in entries and adds them to the action
278 group. The toggle action entry tuples can vary in size from one to seven
279 items with the following information:
281 * The name of the action. Must be specified.
282 * The stock id for the action. Optional with a default value of None
283 if a label is specified.
284 * The label for the action. This field should typically be marked
285 for translation, see the set_translation_domain() method. Optional
286 with a default value of None if a stock id is specified.
287 * The accelerator for the action, in the format understood by the
288 gtk.accelerator_parse() function. Optional with a default value of
290 * The tooltip for the action. This field should typically be marked
291 for translation, see the set_translation_domain() method. Optional
292 with a default value of None.
293 * The callback function invoked when the action is activated.
294 Optional with a default value of None.
295 * A flag indicating whether the toggle action is active. Optional
296 with a default value of False.
298 The "activate" signals of the actions are connected to the callbacks and
299 their accel paths are set to <Actions>/group-name/action-name.
305 raise TypeError('entries must be iterable')
307 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
308 action = Gtk.ToggleAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
309 action.set_active(is_active)
310 if callback is not None:
311 if user_data is None:
312 action.connect('activate', callback)
314 action.connect('activate', callback, user_data)
316 self.add_action_with_accel(action, accelerator)
319 # using inner function above since entries can leave out optional arguments
322 def add_radio_actions(self, entries, value=None, on_change=None, user_data=None):
324 The add_radio_actions() method is a convenience method that creates a
325 number of gtk.RadioAction objects based on the information in the list
326 of action entry tuples contained in entries and adds them to the action
327 group. The entry tuples can vary in size from one to six items with the
328 following information:
330 * The name of the action. Must be specified.
331 * The stock id for the action. Optional with a default value of None
332 if a label is specified.
333 * The label for the action. This field should typically be marked
334 for translation, see the set_translation_domain() method. Optional
335 with a default value of None if a stock id is specified.
336 * The accelerator for the action, in the format understood by the
337 gtk.accelerator_parse() function. Optional with a default value of
339 * The tooltip for the action. This field should typically be marked
340 for translation, see the set_translation_domain() method. Optional
341 with a default value of None.
342 * The value to set on the radio action. Optional with a default
343 value of 0. Should be specified in applications.
345 The value parameter specifies the radio action that should be set
346 active. The "changed" signal of the first radio action is connected to
347 the on_change callback (if specified and not None) and the accel paths
348 of the actions are set to <Actions>/group-name/action-name.
353 raise TypeError('entries must be iterable')
357 def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
358 action = RadioAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=entry_value)
360 # FIXME: join_group is a patch to Gtk+ 3.0
361 # otherwise we can't effectively add radio actions to a
362 # group. Should we depend on 3.0 and error out here
363 # or should we offer the functionality via a compat
365 if hasattr(action, 'join_group'):
366 action.join_group(group_source)
368 if value == entry_value:
369 action.set_active(True)
371 self.add_action_with_accel(action, accelerator)
375 # using inner function above since entries can leave out optional arguments
376 action = _process_action(first_action, *e)
377 if first_action is None:
378 first_action = action
380 if first_action is not None and on_change is not None:
381 if user_data is None:
382 first_action.connect('changed', on_change)
384 first_action.connect('changed', on_change, user_data)
386 ActionGroup = override(ActionGroup)
387 __all__.append('ActionGroup')
390 class UIManager(Gtk.UIManager):
391 def add_ui_from_string(self, buffer):
392 if not isinstance(buffer, _basestring):
393 raise TypeError('buffer must be a string')
395 length = len(buffer.encode('UTF-8'))
397 return Gtk.UIManager.add_ui_from_string(self, buffer, length)
399 def insert_action_group(self, buffer, length=-1):
400 return Gtk.UIManager.insert_action_group(self, buffer, length)
402 UIManager = override(UIManager)
403 __all__.append('UIManager')
406 class ComboBox(Gtk.ComboBox, Container):
407 get_active_iter = strip_boolean_result(Gtk.ComboBox.get_active_iter)
409 ComboBox = override(ComboBox)
410 __all__.append('ComboBox')
414 __init__ = deprecated_init(Gtk.Box.__init__,
415 arg_names=('homogeneous', 'spacing'),
416 category=PyGTKDeprecationWarning)
419 __all__.append('Box')
422 class SizeGroup(Gtk.SizeGroup):
423 __init__ = deprecated_init(Gtk.SizeGroup.__init__,
425 deprecated_defaults={'mode': Gtk.SizeGroupMode.VERTICAL},
426 category=PyGTKDeprecationWarning)
428 SizeGroup = override(SizeGroup)
429 __all__.append('SizeGroup')
432 class MenuItem(Gtk.MenuItem):
433 __init__ = deprecated_init(Gtk.MenuItem.__init__,
434 arg_names=('label',),
435 category=PyGTKDeprecationWarning)
437 MenuItem = override(MenuItem)
438 __all__.append('MenuItem')
441 class Builder(Gtk.Builder):
442 def connect_signals(self, obj_or_map):
443 """Connect signals specified by this builder to a name, handler mapping.
445 Connect signal, name, and handler sets specified in the builder with
446 the given mapping "obj_or_map". The handler/value aspect of the mapping
447 can also contain a tuple in the form of (handler [,arg1 [,argN]])
448 allowing for extra arguments to be passed to the handler. For example:
450 .. code-block:: python
452 builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)})
454 self.connect_signals_full(_builder_connect_callback, obj_or_map)
456 def add_from_string(self, buffer):
457 if not isinstance(buffer, _basestring):
458 raise TypeError('buffer must be a string')
462 return Gtk.Builder.add_from_string(self, buffer, length)
464 def add_objects_from_string(self, buffer, object_ids):
465 if not isinstance(buffer, _basestring):
466 raise TypeError('buffer must be a string')
470 return Gtk.Builder.add_objects_from_string(self, buffer, length, object_ids)
472 Builder = override(Builder)
473 __all__.append('Builder')
476 # NOTE: This must come before any other Window/Dialog subclassing, to ensure
477 # that we have a correct inheritance hierarchy.
480 class Window(Gtk.Window):
481 __init__ = deprecated_init(Gtk.Window.__init__,
483 category=PyGTKDeprecationWarning)
485 Window = override(Window)
486 __all__.append('Window')
489 class Dialog(Gtk.Dialog, Container):
490 _old_arg_names = ('title', 'parent', 'flags', 'buttons', '_buttons_property')
491 _init = deprecated_init(Gtk.Dialog.__init__,
492 arg_names=('title', 'transient_for', 'flags',
493 'add_buttons', 'buttons'),
494 ignore=('flags', 'add_buttons'),
495 deprecated_aliases={'transient_for': 'parent',
496 'buttons': '_buttons_property'},
497 category=PyGTKDeprecationWarning)
499 def __init__(self, *args, **kwargs):
501 new_kwargs = kwargs.copy()
502 old_kwargs = dict(zip(self._old_arg_names, args))
503 old_kwargs.update(kwargs)
505 # Increment the warning stacklevel for sub-classes which implement their own __init__.
507 if self.__class__ != Dialog and self.__class__.__init__ != Dialog.__init__:
510 # buttons was overloaded by PyGtk but is needed for Gtk.MessageDialog
511 # as a pass through, so type check the argument and give a deprecation
512 # when it is not of type Gtk.ButtonsType
513 add_buttons = old_kwargs.get('buttons', None)
514 if add_buttons is not None and not isinstance(add_buttons, Gtk.ButtonsType):
515 warnings.warn('The "buttons" argument must be a Gtk.ButtonsType enum value. '
516 'Please use the "add_buttons" method for adding buttons. '
517 'See: https://wiki.gnome.org/PyGObject/InitializerDeprecations',
518 PyGTKDeprecationWarning, stacklevel=stacklevel)
519 if 'buttons' in new_kwargs:
520 del new_kwargs['buttons']
524 flags = old_kwargs.get('flags', 0)
526 warnings.warn('The "flags" argument for dialog construction is deprecated. '
527 'Please use initializer keywords: modal=True and/or destroy_with_parent=True. '
528 'See: https://wiki.gnome.org/PyGObject/InitializerDeprecations',
529 PyGTKDeprecationWarning, stacklevel=stacklevel)
531 if flags & Gtk.DialogFlags.MODAL:
532 new_kwargs['modal'] = True
534 if flags & Gtk.DialogFlags.DESTROY_WITH_PARENT:
535 new_kwargs['destroy_with_parent'] = True
537 self._init(*args, **new_kwargs)
540 self.add_buttons(*add_buttons)
542 action_area = property(lambda dialog: dialog.get_action_area())
543 vbox = property(lambda dialog: dialog.get_content_area())
545 def add_buttons(self, *args):
547 The add_buttons() method adds several buttons to the Gtk.Dialog using
548 the button data passed as arguments to the method. This method is the
549 same as calling the Gtk.Dialog.add_button() repeatedly. The button data
550 pairs - button text (or stock ID) and a response ID integer are passed
551 individually. For example:
553 .. code-block:: python
555 dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)
557 will add "Open" and "Close" buttons to dialog.
566 for text, response in _button(args):
567 self.add_button(text, response)
569 raise TypeError('Must pass an even number of arguments')
571 Dialog = override(Dialog)
572 __all__.append('Dialog')
575 class MessageDialog(Gtk.MessageDialog, Dialog):
576 __init__ = deprecated_init(Gtk.MessageDialog.__init__,
577 arg_names=('parent', 'flags', 'message_type',
578 'buttons', 'message_format'),
579 deprecated_aliases={'text': 'message_format',
580 'message_type': 'type'},
581 category=PyGTKDeprecationWarning)
583 def format_secondary_text(self, message_format):
584 self.set_property('secondary-use-markup', False)
585 self.set_property('secondary-text', message_format)
587 def format_secondary_markup(self, message_format):
588 self.set_property('secondary-use-markup', True)
589 self.set_property('secondary-text', message_format)
591 MessageDialog = override(MessageDialog)
592 __all__.append('MessageDialog')
595 class ColorSelectionDialog(Gtk.ColorSelectionDialog):
596 __init__ = deprecated_init(Gtk.ColorSelectionDialog.__init__,
597 arg_names=('title',),
598 category=PyGTKDeprecationWarning)
600 ColorSelectionDialog = override(ColorSelectionDialog)
601 __all__.append('ColorSelectionDialog')
604 class FileChooserDialog(Gtk.FileChooserDialog):
605 __init__ = deprecated_init(Gtk.FileChooserDialog.__init__,
606 arg_names=('title', 'parent', 'action', 'buttons'),
607 category=PyGTKDeprecationWarning)
609 FileChooserDialog = override(FileChooserDialog)
610 __all__.append('FileChooserDialog')
613 class FontSelectionDialog(Gtk.FontSelectionDialog):
614 __init__ = deprecated_init(Gtk.FontSelectionDialog.__init__,
615 arg_names=('title',),
616 category=PyGTKDeprecationWarning)
618 FontSelectionDialog = override(FontSelectionDialog)
619 __all__.append('FontSelectionDialog')
622 class RecentChooserDialog(Gtk.RecentChooserDialog):
623 # Note, the "manager" keyword must work across the entire 3.x series because
624 # "recent_manager" is not backwards compatible with PyGObject versions prior to 3.10.
625 __init__ = deprecated_init(Gtk.RecentChooserDialog.__init__,
626 arg_names=('title', 'parent', 'recent_manager', 'buttons'),
627 deprecated_aliases={'recent_manager': 'manager'},
628 category=PyGTKDeprecationWarning)
630 RecentChooserDialog = override(RecentChooserDialog)
631 __all__.append('RecentChooserDialog')
634 class IconView(Gtk.IconView):
635 __init__ = deprecated_init(Gtk.IconView.__init__,
636 arg_names=('model',),
637 category=PyGTKDeprecationWarning)
639 get_item_at_pos = strip_boolean_result(Gtk.IconView.get_item_at_pos)
640 get_visible_range = strip_boolean_result(Gtk.IconView.get_visible_range)
641 get_dest_item_at_pos = strip_boolean_result(Gtk.IconView.get_dest_item_at_pos)
643 IconView = override(IconView)
644 __all__.append('IconView')
647 class ToolButton(Gtk.ToolButton):
648 __init__ = deprecated_init(Gtk.ToolButton.__init__,
649 arg_names=('stock_id',),
650 category=PyGTKDeprecationWarning)
652 ToolButton = override(ToolButton)
653 __all__.append('ToolButton')
656 class IMContext(Gtk.IMContext):
657 get_surrounding = strip_boolean_result(Gtk.IMContext.get_surrounding)
659 IMContext = override(IMContext)
660 __all__.append('IMContext')
663 class RecentInfo(Gtk.RecentInfo):
664 get_application_info = strip_boolean_result(Gtk.RecentInfo.get_application_info)
666 RecentInfo = override(RecentInfo)
667 __all__.append('RecentInfo')
670 class TextBuffer(Gtk.TextBuffer):
671 def _get_or_create_tag_table(self):
672 table = self.get_tag_table()
674 table = Gtk.TextTagTable()
675 self.set_tag_table(table)
679 def create_tag(self, tag_name=None, **properties):
680 """Creates a tag and adds it to the tag table of the TextBuffer.
683 Name of the new tag, or None
685 Keyword list of properties and their values
687 This is equivalent to creating a Gtk.TextTag and then adding the
688 tag to the buffer's tag table. The returned tag is owned by
689 the buffer's tag table.
691 If ``tag_name`` is None, the tag is anonymous.
693 If ``tag_name`` is not None, a tag called ``tag_name`` must not already
694 exist in the tag table for this buffer.
696 Properties are passed as a keyword list of names and values (e.g.
697 foreground='DodgerBlue', weight=Pango.Weight.BOLD)
703 tag = Gtk.TextTag(name=tag_name, **properties)
704 self._get_or_create_tag_table().add(tag)
707 def create_mark(self, mark_name, where, left_gravity=False):
708 return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
710 def set_text(self, text, length=-1):
711 Gtk.TextBuffer.set_text(self, text, length)
713 def insert(self, iter, text, length=-1):
714 if not isinstance(text, _basestring):
715 raise TypeError('text must be a string, not %s' % type(text))
717 Gtk.TextBuffer.insert(self, iter, text, length)
719 def insert_with_tags(self, iter, text, *tags):
720 start_offset = iter.get_offset()
721 self.insert(iter, text)
726 start = self.get_iter_at_offset(start_offset)
729 self.apply_tag(tag, start, iter)
731 def insert_with_tags_by_name(self, iter, text, *tags):
738 tag_obj = self.get_tag_table().lookup(tag)
740 raise ValueError('unknown text tag: %s' % tag)
741 tag_objs.append(tag_obj)
743 self.insert_with_tags(iter, text, *tag_objs)
745 def insert_at_cursor(self, text, length=-1):
746 if not isinstance(text, _basestring):
747 raise TypeError('text must be a string, not %s' % type(text))
749 Gtk.TextBuffer.insert_at_cursor(self, text, length)
751 get_selection_bounds = strip_boolean_result(Gtk.TextBuffer.get_selection_bounds, fail_ret=())
753 TextBuffer = override(TextBuffer)
754 __all__.append('TextBuffer')
757 class TextIter(Gtk.TextIter):
758 forward_search = strip_boolean_result(Gtk.TextIter.forward_search)
759 backward_search = strip_boolean_result(Gtk.TextIter.backward_search)
761 TextIter = override(TextIter)
762 __all__.append('TextIter')
765 class TreeModel(Gtk.TreeModel):
767 return self.iter_n_children(None)
772 # alias for Python 2.x object protocol
773 __nonzero__ = __bool__
775 def _getiter(self, key):
776 if isinstance(key, Gtk.TreeIter):
778 elif isinstance(key, int) and key < 0:
779 index = len(self) + key
781 raise IndexError("row index is out of bounds: %d" % key)
783 aiter = self.get_iter(index)
785 raise IndexError("could not find tree path '%s'" % key)
789 aiter = self.get_iter(key)
791 raise IndexError("could not find tree path '%s'" % key)
794 def _coerce_path(self, path):
795 if isinstance(path, Gtk.TreePath):
798 return TreePath(path)
800 def __getitem__(self, key):
801 aiter = self._getiter(key)
802 return TreeModelRow(self, aiter)
804 def __setitem__(self, key, value):
806 self.set_row(row.iter, value)
808 def __delitem__(self, key):
809 aiter = self._getiter(key)
813 return TreeModelRowIter(self, self.get_iter_first())
815 get_iter_first = strip_boolean_result(Gtk.TreeModel.get_iter_first)
816 iter_children = strip_boolean_result(Gtk.TreeModel.iter_children)
817 iter_nth_child = strip_boolean_result(Gtk.TreeModel.iter_nth_child)
818 iter_parent = strip_boolean_result(Gtk.TreeModel.iter_parent)
819 get_iter_from_string = strip_boolean_result(Gtk.TreeModel.get_iter_from_string,
820 ValueError, 'invalid tree path')
822 def get_iter(self, path):
823 path = self._coerce_path(path)
824 success, aiter = super(TreeModel, self).get_iter(path)
826 raise ValueError("invalid tree path '%s'" % path)
829 def iter_next(self, aiter):
830 next_iter = aiter.copy()
831 success = super(TreeModel, self).iter_next(next_iter)
835 def iter_previous(self, aiter):
836 prev_iter = aiter.copy()
837 success = super(TreeModel, self).iter_previous(prev_iter)
841 def _convert_row(self, row):
842 # TODO: Accept a dictionary for row
843 # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
844 if isinstance(row, str):
845 raise TypeError('Expected a list or tuple, but got str')
847 n_columns = self.get_n_columns()
848 if len(row) != n_columns:
849 raise ValueError('row sequence has the incorrect number of elements')
853 for cur_col, value in enumerate(row):
854 # do not try to set None values, they are causing warnings
857 result.append(self._convert_value(cur_col, value))
858 columns.append(cur_col)
859 return (result, columns)
861 def set_row(self, treeiter, row):
862 converted_row, columns = self._convert_row(row)
863 for column in columns:
866 continue # None means skip this row
868 self.set_value(treeiter, column, value)
870 def _convert_value(self, column, value):
871 '''Convert value to a GObject.Value of the expected type'''
873 if isinstance(value, GObject.Value):
875 return GObject.Value(self.get_column_type(column), value)
877 def get(self, treeiter, *columns):
878 n_columns = self.get_n_columns()
882 if not isinstance(col, int):
883 raise TypeError("column numbers must be ints")
885 if col < 0 or col >= n_columns:
886 raise ValueError("column number is out of range")
888 values.append(self.get_value(treeiter, col))
893 # Signals supporting python iterables as tree paths
895 def row_changed(self, path, iter):
896 return super(TreeModel, self).row_changed(self._coerce_path(path), iter)
898 def row_inserted(self, path, iter):
899 return super(TreeModel, self).row_inserted(self._coerce_path(path), iter)
901 def row_has_child_toggled(self, path, iter):
902 return super(TreeModel, self).row_has_child_toggled(self._coerce_path(path),
905 def row_deleted(self, path):
906 return super(TreeModel, self).row_deleted(self._coerce_path(path))
908 def rows_reordered(self, path, iter, new_order):
909 return super(TreeModel, self).rows_reordered(self._coerce_path(path),
913 TreeModel = override(TreeModel)
914 __all__.append('TreeModel')
917 class TreeSortable(Gtk.TreeSortable, ):
919 get_sort_column_id = strip_boolean_result(Gtk.TreeSortable.get_sort_column_id, fail_ret=(None, None))
921 def set_sort_func(self, sort_column_id, sort_func, user_data=None):
922 super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
924 def set_default_sort_func(self, sort_func, user_data=None):
925 super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
927 TreeSortable = override(TreeSortable)
928 __all__.append('TreeSortable')
931 class TreeModelSort(Gtk.TreeModelSort):
932 __init__ = deprecated_init(Gtk.TreeModelSort.__init__,
933 arg_names=('model',),
934 category=PyGTKDeprecationWarning)
936 TreeModelSort = override(TreeModelSort)
937 __all__.append('TreeModelSort')
940 class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
941 def __init__(self, *column_types):
942 Gtk.ListStore.__init__(self)
943 self.set_column_types(column_types)
945 def _do_insert(self, position, row):
947 row, columns = self._convert_row(row)
948 treeiter = self.insert_with_valuesv(position, columns, row)
950 treeiter = Gtk.ListStore.insert(self, position)
954 def append(self, row=None):
956 return self._do_insert(-1, row)
957 # gtk_list_store_insert() does not know about the "position == -1"
958 # case, so use append() here
960 return Gtk.ListStore.append(self)
962 def prepend(self, row=None):
963 return self._do_insert(0, row)
965 def insert(self, position, row=None):
966 return self._do_insert(position, row)
968 # FIXME: sends two signals; check if this can use an atomic
969 # insert_with_valuesv()
971 def insert_before(self, sibling, row=None):
972 treeiter = Gtk.ListStore.insert_before(self, sibling)
975 self.set_row(treeiter, row)
979 # FIXME: sends two signals; check if this can use an atomic
980 # insert_with_valuesv()
982 def insert_after(self, sibling, row=None):
983 treeiter = Gtk.ListStore.insert_after(self, sibling)
986 self.set_row(treeiter, row)
990 def set_value(self, treeiter, column, value):
991 value = self._convert_value(column, value)
992 Gtk.ListStore.set_value(self, treeiter, column, value)
994 def set(self, treeiter, *args):
996 def _set_lists(columns, values):
997 if len(columns) != len(values):
998 raise TypeError('The number of columns do not match the number of values')
999 for col_num, val in zip(columns, values):
1000 if not isinstance(col_num, int):
1001 raise TypeError('TypeError: Expected integer argument for column.')
1002 self.set_value(treeiter, col_num, val)
1005 if isinstance(args[0], int):
1008 _set_lists(columns, values)
1009 elif isinstance(args[0], (tuple, list)):
1011 raise TypeError('Too many arguments')
1012 _set_lists(args[0], args[1])
1013 elif isinstance(args[0], dict):
1014 columns = args[0].keys()
1015 values = args[0].values()
1016 _set_lists(columns, values)
1018 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
1020 ListStore = override(ListStore)
1021 __all__.append('ListStore')
1024 class TreeModelRow(object):
1026 def __init__(self, model, iter_or_path):
1027 if not isinstance(model, Gtk.TreeModel):
1028 raise TypeError("expected Gtk.TreeModel, %s found" % type(model).__name__)
1030 if isinstance(iter_or_path, Gtk.TreePath):
1031 self.iter = model.get_iter(iter_or_path)
1032 elif isinstance(iter_or_path, Gtk.TreeIter):
1033 self.iter = iter_or_path
1035 raise TypeError("expected Gtk.TreeIter or Gtk.TreePath, \
1036 %s found" % type(iter_or_path).__name__)
1040 return self.model.get_path(self.iter)
1044 return self.get_next()
1048 return self.get_previous()
1052 return self.get_parent()
1055 next_iter = self.model.iter_next(self.iter)
1057 return TreeModelRow(self.model, next_iter)
1059 def get_previous(self):
1060 prev_iter = self.model.iter_previous(self.iter)
1062 return TreeModelRow(self.model, prev_iter)
1064 def get_parent(self):
1065 parent_iter = self.model.iter_parent(self.iter)
1067 return TreeModelRow(self.model, parent_iter)
1069 def __getitem__(self, key):
1070 if isinstance(key, int):
1071 if key >= self.model.get_n_columns():
1072 raise IndexError("column index is out of bounds: %d" % key)
1074 key = self._convert_negative_index(key)
1075 return self.model.get_value(self.iter, key)
1076 elif isinstance(key, slice):
1077 start, stop, step = key.indices(self.model.get_n_columns())
1079 for i in range(start, stop, step):
1080 alist.append(self.model.get_value(self.iter, i))
1083 raise TypeError("indices must be integers, not %s" % type(key).__name__)
1085 def __setitem__(self, key, value):
1086 if isinstance(key, int):
1087 if key >= self.model.get_n_columns():
1088 raise IndexError("column index is out of bounds: %d" % key)
1090 key = self._convert_negative_index(key)
1091 self.model.set_value(self.iter, key, value)
1092 elif isinstance(key, slice):
1093 start, stop, step = key.indices(self.model.get_n_columns())
1094 indexList = range(start, stop, step)
1095 if len(indexList) != len(value):
1097 "attempt to assign sequence of size %d to slice of size %d"
1098 % (len(value), len(indexList)))
1100 for i, v in enumerate(indexList):
1101 self.model.set_value(self.iter, v, value[i])
1103 raise TypeError("index must be an integer or slice, not %s" % type(key).__name__)
1105 def _convert_negative_index(self, index):
1106 new_index = self.model.get_n_columns() + index
1108 raise IndexError("column index is out of bounds: %d" % index)
1111 def iterchildren(self):
1112 child_iter = self.model.iter_children(self.iter)
1113 return TreeModelRowIter(self.model, child_iter)
1115 __all__.append('TreeModelRow')
1118 class TreeModelRowIter(object):
1120 def __init__(self, model, aiter):
1127 row = TreeModelRow(self.model, self.iter)
1128 self.iter = self.model.iter_next(self.iter)
1131 # alias for Python 2.x object protocol
1137 __all__.append('TreeModelRowIter')
1140 class TreePath(Gtk.TreePath):
1142 def __new__(cls, path=0):
1143 if isinstance(path, int):
1145 elif not isinstance(path, _basestring):
1146 path = ":".join(str(val) for val in path)
1149 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1151 return TreePath.new_from_string(path)
1153 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1155 def __init__(self, *args, **kwargs):
1156 super(TreePath, self).__init__()
1159 return self.to_string()
1161 def __lt__(self, other):
1162 return other is not None and self.compare(other) < 0
1164 def __le__(self, other):
1165 return other is not None and self.compare(other) <= 0
1167 def __eq__(self, other):
1168 return other is not None and self.compare(other) == 0
1170 def __ne__(self, other):
1171 return other is None or self.compare(other) != 0
1173 def __gt__(self, other):
1174 return other is None or self.compare(other) > 0
1176 def __ge__(self, other):
1177 return other is None or self.compare(other) >= 0
1180 return iter(self.get_indices())
1183 return self.get_depth()
1185 def __getitem__(self, index):
1186 return self.get_indices()[index]
1188 TreePath = override(TreePath)
1189 __all__.append('TreePath')
1192 class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
1193 def __init__(self, *column_types):
1194 Gtk.TreeStore.__init__(self)
1195 self.set_column_types(column_types)
1197 def _do_insert(self, parent, position, row):
1199 row, columns = self._convert_row(row)
1200 treeiter = self.insert_with_values(parent, position, columns, row)
1202 treeiter = Gtk.TreeStore.insert(self, parent, position)
1206 def append(self, parent, row=None):
1207 return self._do_insert(parent, -1, row)
1209 def prepend(self, parent, row=None):
1210 return self._do_insert(parent, 0, row)
1212 def insert(self, parent, position, row=None):
1213 return self._do_insert(parent, position, row)
1215 # FIXME: sends two signals; check if this can use an atomic
1216 # insert_with_valuesv()
1218 def insert_before(self, parent, sibling, row=None):
1219 treeiter = Gtk.TreeStore.insert_before(self, parent, sibling)
1222 self.set_row(treeiter, row)
1226 # FIXME: sends two signals; check if this can use an atomic
1227 # insert_with_valuesv()
1229 def insert_after(self, parent, sibling, row=None):
1230 treeiter = Gtk.TreeStore.insert_after(self, parent, sibling)
1233 self.set_row(treeiter, row)
1237 def set_value(self, treeiter, column, value):
1238 value = self._convert_value(column, value)
1239 Gtk.TreeStore.set_value(self, treeiter, column, value)
1241 def set(self, treeiter, *args):
1243 def _set_lists(columns, values):
1244 if len(columns) != len(values):
1245 raise TypeError('The number of columns do not match the number of values')
1246 for col_num, val in zip(columns, values):
1247 if not isinstance(col_num, int):
1248 raise TypeError('TypeError: Expected integer argument for column.')
1249 self.set_value(treeiter, col_num, val)
1252 if isinstance(args[0], int):
1255 _set_lists(columns, values)
1256 elif isinstance(args[0], (tuple, list)):
1258 raise TypeError('Too many arguments')
1259 _set_lists(args[0], args[1])
1260 elif isinstance(args[0], dict):
1261 columns = args[0].keys()
1262 values = args[0].values()
1263 _set_lists(columns, values)
1265 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
1267 TreeStore = override(TreeStore)
1268 __all__.append('TreeStore')
1271 class TreeView(Gtk.TreeView, Container):
1272 __init__ = deprecated_init(Gtk.TreeView.__init__,
1273 arg_names=('model',),
1274 category=PyGTKDeprecationWarning)
1276 get_path_at_pos = strip_boolean_result(Gtk.TreeView.get_path_at_pos)
1277 get_visible_range = strip_boolean_result(Gtk.TreeView.get_visible_range)
1278 get_dest_row_at_pos = strip_boolean_result(Gtk.TreeView.get_dest_row_at_pos)
1280 def enable_model_drag_source(self, start_button_mask, targets, actions):
1281 target_entries = _construct_target_list(targets)
1282 super(TreeView, self).enable_model_drag_source(start_button_mask,
1286 def enable_model_drag_dest(self, targets, actions):
1287 target_entries = _construct_target_list(targets)
1288 super(TreeView, self).enable_model_drag_dest(target_entries,
1291 def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
1292 if not isinstance(path, Gtk.TreePath):
1293 path = TreePath(path)
1294 super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
1296 def set_cursor(self, path, column=None, start_editing=False):
1297 if not isinstance(path, Gtk.TreePath):
1298 path = TreePath(path)
1299 super(TreeView, self).set_cursor(path, column, start_editing)
1301 def get_cell_area(self, path, column=None):
1302 if not isinstance(path, Gtk.TreePath):
1303 path = TreePath(path)
1304 return super(TreeView, self).get_cell_area(path, column)
1306 def insert_column_with_attributes(self, position, title, cell, **kwargs):
1307 column = TreeViewColumn()
1308 column.set_title(title)
1309 column.pack_start(cell, False)
1310 self.insert_column(column, position)
1311 column.set_attributes(cell, **kwargs)
1313 TreeView = override(TreeView)
1314 __all__.append('TreeView')
1317 class TreeViewColumn(Gtk.TreeViewColumn):
1318 def __init__(self, title='',
1321 Gtk.TreeViewColumn.__init__(self, title=title)
1323 self.pack_start(cell_renderer, True)
1325 for (name, value) in attributes.items():
1326 self.add_attribute(cell_renderer, name, value)
1328 cell_get_position = strip_boolean_result(Gtk.TreeViewColumn.cell_get_position)
1330 def set_cell_data_func(self, cell_renderer, func, func_data=None):
1331 super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
1333 def set_attributes(self, cell_renderer, **attributes):
1334 Gtk.CellLayout.clear_attributes(self, cell_renderer)
1336 for (name, value) in attributes.items():
1337 Gtk.CellLayout.add_attribute(self, cell_renderer, name, value)
1340 TreeViewColumn = override(TreeViewColumn)
1341 __all__.append('TreeViewColumn')
1344 class TreeSelection(Gtk.TreeSelection):
1346 def select_path(self, path):
1347 if not isinstance(path, Gtk.TreePath):
1348 path = TreePath(path)
1349 super(TreeSelection, self).select_path(path)
1351 def get_selected(self):
1352 success, model, aiter = super(TreeSelection, self).get_selected()
1354 return (model, aiter)
1356 return (model, None)
1358 # for compatibility with PyGtk
1360 def get_selected_rows(self):
1361 rows, model = super(TreeSelection, self).get_selected_rows()
1362 return (model, rows)
1365 TreeSelection = override(TreeSelection)
1366 __all__.append('TreeSelection')
1369 class Button(Gtk.Button, Container):
1370 _init = deprecated_init(Gtk.Button.__init__,
1371 arg_names=('label', 'stock', 'use_stock', 'use_underline'),
1373 category=PyGTKDeprecationWarning,
1376 def __init__(self, *args, **kwargs):
1377 # Doubly deprecated initializer, the stock keyword is non-standard.
1378 # Simply give a warning that stock items are deprecated even though
1379 # we want to deprecate the non-standard keyword as well here from
1381 if 'stock' in kwargs and kwargs['stock']:
1382 warnings.warn('Stock items are deprecated. '
1383 'Please use: Gtk.Button.new_with_mnemonic(label)',
1384 PyGTKDeprecationWarning, stacklevel=2)
1385 new_kwargs = kwargs.copy()
1386 new_kwargs['label'] = new_kwargs['stock']
1387 new_kwargs['use_stock'] = True
1388 new_kwargs['use_underline'] = True
1389 del new_kwargs['stock']
1390 Gtk.Button.__init__(self, **new_kwargs)
1392 self._init(*args, **kwargs)
1394 Button = override(Button)
1395 __all__.append('Button')
1398 class LinkButton(Gtk.LinkButton):
1399 __init__ = deprecated_init(Gtk.LinkButton.__init__,
1400 arg_names=('uri', 'label'),
1401 category=PyGTKDeprecationWarning)
1403 LinkButton = override(LinkButton)
1404 __all__.append('LinkButton')
1407 class Label(Gtk.Label):
1408 __init__ = deprecated_init(Gtk.Label.__init__,
1409 arg_names=('label',),
1410 category=PyGTKDeprecationWarning)
1412 Label = override(Label)
1413 __all__.append('Label')
1416 class Adjustment(Gtk.Adjustment):
1417 _init = deprecated_init(Gtk.Adjustment.__init__,
1418 arg_names=('value', 'lower', 'upper',
1419 'step_increment', 'page_increment', 'page_size'),
1420 deprecated_aliases={'page_increment': 'page_incr',
1421 'step_increment': 'step_incr'},
1422 category=PyGTKDeprecationWarning,
1425 def __init__(self, *args, **kwargs):
1426 self._init(*args, **kwargs)
1428 # The value property is set between lower and (upper - page_size).
1429 # Just in case lower, upper or page_size was still 0 when value
1430 # was set, we set it again here.
1431 if 'value' in kwargs:
1432 self.set_value(kwargs['value'])
1434 Adjustment = override(Adjustment)
1435 __all__.append('Adjustment')
1438 class Table(Gtk.Table, Container):
1439 __init__ = deprecated_init(Gtk.Table.__init__,
1440 arg_names=('n_rows', 'n_columns', 'homogeneous'),
1441 deprecated_aliases={'n_rows': 'rows', 'n_columns': 'columns'},
1442 category=PyGTKDeprecationWarning)
1444 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):
1445 Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
1447 Table = override(Table)
1448 __all__.append('Table')
1451 class ScrolledWindow(Gtk.ScrolledWindow):
1452 __init__ = deprecated_init(Gtk.ScrolledWindow.__init__,
1453 arg_names=('hadjustment', 'vadjustment'),
1454 category=PyGTKDeprecationWarning)
1456 ScrolledWindow = override(ScrolledWindow)
1457 __all__.append('ScrolledWindow')
1460 class HScrollbar(Gtk.HScrollbar):
1461 __init__ = deprecated_init(Gtk.HScrollbar.__init__,
1462 arg_names=('adjustment',),
1463 category=PyGTKDeprecationWarning)
1465 HScrollbar = override(HScrollbar)
1466 __all__.append('HScrollbar')
1469 class VScrollbar(Gtk.VScrollbar):
1470 __init__ = deprecated_init(Gtk.VScrollbar.__init__,
1471 arg_names=('adjustment',),
1472 category=PyGTKDeprecationWarning)
1474 VScrollbar = override(VScrollbar)
1475 __all__.append('VScrollbar')
1478 class Paned(Gtk.Paned):
1479 def pack1(self, child, resize=False, shrink=True):
1480 super(Paned, self).pack1(child, resize, shrink)
1482 def pack2(self, child, resize=True, shrink=True):
1483 super(Paned, self).pack2(child, resize, shrink)
1485 Paned = override(Paned)
1486 __all__.append('Paned')
1489 class Arrow(Gtk.Arrow):
1490 __init__ = deprecated_init(Gtk.Arrow.__init__,
1491 arg_names=('arrow_type', 'shadow_type'),
1492 category=PyGTKDeprecationWarning)
1494 Arrow = override(Arrow)
1495 __all__.append('Arrow')
1498 class IconSet(Gtk.IconSet):
1499 def __new__(cls, pixbuf=None):
1500 if pixbuf is not None:
1501 warnings.warn('Gtk.IconSet(pixbuf) has been deprecated. Please use: '
1502 'Gtk.IconSet.new_from_pixbuf(pixbuf)',
1503 PyGTKDeprecationWarning, stacklevel=2)
1504 iconset = Gtk.IconSet.new_from_pixbuf(pixbuf)
1506 iconset = Gtk.IconSet.__new__(cls)
1509 def __init__(self, *args, **kwargs):
1510 return super(IconSet, self).__init__()
1512 IconSet = override(IconSet)
1513 __all__.append('IconSet')
1516 class Viewport(Gtk.Viewport):
1517 __init__ = deprecated_init(Gtk.Viewport.__init__,
1518 arg_names=('hadjustment', 'vadjustment'),
1519 category=PyGTKDeprecationWarning)
1521 Viewport = override(Viewport)
1522 __all__.append('Viewport')
1525 class TreeModelFilter(Gtk.TreeModelFilter):
1526 def set_visible_func(self, func, data=None):
1527 super(TreeModelFilter, self).set_visible_func(func, data)
1529 def set_value(self, iter, column, value):
1530 # Delegate to child model
1531 iter = self.convert_iter_to_child_iter(iter)
1532 self.get_model().set_value(iter, column, value)
1534 TreeModelFilter = override(TreeModelFilter)
1535 __all__.append('TreeModelFilter')
1537 if Gtk._version != '2.0':
1538 class Menu(Gtk.Menu):
1539 def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
1540 self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
1541 Menu = override(Menu)
1542 __all__.append('Menu')
1544 _Gtk_main_quit = Gtk.main_quit
1547 @override(Gtk.main_quit)
1548 def main_quit(*args):
1551 stock_lookup = strip_boolean_result(Gtk.stock_lookup)
1552 __all__.append('stock_lookup')
1554 initialized, argv = Gtk.init_check(sys.argv)
1555 sys.argv = list(argv)