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 class Widget(Gtk.Widget):
77 translate_coordinates = strip_boolean_result(Gtk.Widget.translate_coordinates)
79 def render_icon(self, stock_id, size, detail=None):
80 return super(Widget, self).render_icon(stock_id, size, detail)
82 def drag_dest_set_target_list(self, target_list):
83 if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
84 target_list = Gtk.TargetList.new(_construct_target_list(target_list))
85 super(Widget, self).drag_dest_set_target_list(target_list)
87 def drag_source_set_target_list(self, target_list):
88 if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
89 target_list = Gtk.TargetList.new(_construct_target_list(target_list))
90 super(Widget, self).drag_source_set_target_list(target_list)
93 Widget = override(Widget)
94 __all__.append('Widget')
97 class Container(Gtk.Container, Widget):
100 return len(self.get_children())
102 def __contains__(self, child):
103 return child in self.get_children()
106 return iter(self.get_children())
111 # alias for Python 2.x object protocol
112 __nonzero__ = __bool__
114 get_focus_chain = strip_boolean_result(Gtk.Container.get_focus_chain)
117 Container = override(Container)
118 __all__.append('Container')
121 class Editable(Gtk.Editable):
123 def insert_text(self, text, position):
124 return super(Editable, self).insert_text(text, -1, position)
126 get_selection_bounds = strip_boolean_result(Gtk.Editable.get_selection_bounds, fail_ret=())
129 Editable = override(Editable)
130 __all__.append("Editable")
133 class Action(Gtk.Action):
134 __init__ = deprecated_init(Gtk.Action.__init__,
135 arg_names=('name', 'label', 'tooltip', 'stock_id'),
136 category=PyGTKDeprecationWarning)
138 Action = override(Action)
139 __all__.append("Action")
142 class RadioAction(Gtk.RadioAction):
143 __init__ = deprecated_init(Gtk.RadioAction.__init__,
144 arg_names=('name', 'label', 'tooltip', 'stock_id', 'value'),
145 category=PyGTKDeprecationWarning)
147 RadioAction = override(RadioAction)
148 __all__.append("RadioAction")
151 class ActionGroup(Gtk.ActionGroup):
152 __init__ = deprecated_init(Gtk.ActionGroup.__init__,
154 category=PyGTKDeprecationWarning)
156 def add_actions(self, entries, user_data=None):
158 The add_actions() method is a convenience method that creates a number
159 of gtk.Action objects based on the information in the list of action
160 entry tuples contained in entries and adds them to the action group.
161 The entry tuples can vary in size from one to six items with the
162 following information:
164 * The name of the action. Must be specified.
165 * The stock id for the action. Optional with a default value of None
166 if a label is specified.
167 * The label for the action. This field should typically be marked
168 for translation, see the set_translation_domain() method. Optional
169 with a default value of None if a stock id is specified.
170 * The accelerator for the action, in the format understood by the
171 gtk.accelerator_parse() function. Optional with a default value of
173 * The tooltip for the action. This field should typically be marked
174 for translation, see the set_translation_domain() method. Optional
175 with a default value of None.
176 * The callback function invoked when the action is activated.
177 Optional with a default value of None.
179 The "activate" signals of the actions are connected to the callbacks and
180 their accel paths are set to <Actions>/group-name/action-name.
185 raise TypeError('entries must be iterable')
187 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
188 action = Action(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
189 if callback is not None:
190 if user_data is None:
191 action.connect('activate', callback)
193 action.connect('activate', callback, user_data)
195 self.add_action_with_accel(action, accelerator)
198 # using inner function above since entries can leave out optional arguments
201 def add_toggle_actions(self, entries, user_data=None):
203 The add_toggle_actions() method is a convenience method that creates a
204 number of gtk.ToggleAction objects based on the information in the list
205 of action entry tuples contained in entries and adds them to the action
206 group. The toggle action entry tuples can vary in size from one to seven
207 items with the following information:
209 * The name of the action. Must be specified.
210 * The stock id for the action. Optional with a default value of None
211 if a label is specified.
212 * The label for the action. This field should typically be marked
213 for translation, see the set_translation_domain() method. Optional
214 with a default value of None if a stock id is specified.
215 * The accelerator for the action, in the format understood by the
216 gtk.accelerator_parse() function. Optional with a default value of
218 * The tooltip for the action. This field should typically be marked
219 for translation, see the set_translation_domain() method. Optional
220 with a default value of None.
221 * The callback function invoked when the action is activated.
222 Optional with a default value of None.
223 * A flag indicating whether the toggle action is active. Optional
224 with a default value of False.
226 The "activate" signals of the actions are connected to the callbacks and
227 their accel paths are set to <Actions>/group-name/action-name.
233 raise TypeError('entries must be iterable')
235 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
236 action = Gtk.ToggleAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
237 action.set_active(is_active)
238 if callback is not None:
239 if user_data is None:
240 action.connect('activate', callback)
242 action.connect('activate', callback, user_data)
244 self.add_action_with_accel(action, accelerator)
247 # using inner function above since entries can leave out optional arguments
250 def add_radio_actions(self, entries, value=None, on_change=None, user_data=None):
252 The add_radio_actions() method is a convenience method that creates a
253 number of gtk.RadioAction objects based on the information in the list
254 of action entry tuples contained in entries and adds them to the action
255 group. The entry tuples can vary in size from one to six items with the
256 following information:
258 * The name of the action. Must be specified.
259 * The stock id for the action. Optional with a default value of None
260 if a label is specified.
261 * The label for the action. This field should typically be marked
262 for translation, see the set_translation_domain() method. Optional
263 with a default value of None if a stock id is specified.
264 * The accelerator for the action, in the format understood by the
265 gtk.accelerator_parse() function. Optional with a default value of
267 * The tooltip for the action. This field should typically be marked
268 for translation, see the set_translation_domain() method. Optional
269 with a default value of None.
270 * The value to set on the radio action. Optional with a default
271 value of 0. Should be specified in applications.
273 The value parameter specifies the radio action that should be set
274 active. The "changed" signal of the first radio action is connected to
275 the on_change callback (if specified and not None) and the accel paths
276 of the actions are set to <Actions>/group-name/action-name.
281 raise TypeError('entries must be iterable')
285 def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
286 action = RadioAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=entry_value)
288 # FIXME: join_group is a patch to Gtk+ 3.0
289 # otherwise we can't effectively add radio actions to a
290 # group. Should we depend on 3.0 and error out here
291 # or should we offer the functionality via a compat
293 if hasattr(action, 'join_group'):
294 action.join_group(group_source)
296 if value == entry_value:
297 action.set_active(True)
299 self.add_action_with_accel(action, accelerator)
303 # using inner function above since entries can leave out optional arguments
304 action = _process_action(first_action, *e)
305 if first_action is None:
306 first_action = action
308 if first_action is not None and on_change is not None:
309 if user_data is None:
310 first_action.connect('changed', on_change)
312 first_action.connect('changed', on_change, user_data)
314 ActionGroup = override(ActionGroup)
315 __all__.append('ActionGroup')
318 class UIManager(Gtk.UIManager):
319 def add_ui_from_string(self, buffer):
320 if not isinstance(buffer, _basestring):
321 raise TypeError('buffer must be a string')
323 length = len(buffer.encode('UTF-8'))
325 return Gtk.UIManager.add_ui_from_string(self, buffer, length)
327 def insert_action_group(self, buffer, length=-1):
328 return Gtk.UIManager.insert_action_group(self, buffer, length)
330 UIManager = override(UIManager)
331 __all__.append('UIManager')
334 class ComboBox(Gtk.ComboBox, Container):
335 get_active_iter = strip_boolean_result(Gtk.ComboBox.get_active_iter)
337 ComboBox = override(ComboBox)
338 __all__.append('ComboBox')
342 __init__ = deprecated_init(Gtk.Box.__init__,
343 arg_names=('homogeneous', 'spacing'),
344 category=PyGTKDeprecationWarning)
347 __all__.append('Box')
350 class SizeGroup(Gtk.SizeGroup):
351 __init__ = deprecated_init(Gtk.SizeGroup.__init__,
353 deprecated_defaults={'mode': Gtk.SizeGroupMode.VERTICAL},
354 category=PyGTKDeprecationWarning)
356 SizeGroup = override(SizeGroup)
357 __all__.append('SizeGroup')
360 class MenuItem(Gtk.MenuItem):
361 __init__ = deprecated_init(Gtk.MenuItem.__init__,
362 arg_names=('label',),
363 category=PyGTKDeprecationWarning)
365 MenuItem = override(MenuItem)
366 __all__.append('MenuItem')
369 class Builder(Gtk.Builder):
371 def _extract_handler_and_args(obj_or_map, handler_name):
373 if isinstance(obj_or_map, collections.Mapping):
374 handler = obj_or_map.get(handler_name, None)
376 handler = getattr(obj_or_map, handler_name, None)
379 raise AttributeError('Handler %s not found' % handler_name)
382 if isinstance(handler, collections.Sequence):
383 if len(handler) == 0:
384 raise TypeError("Handler %s tuple can not be empty" % handler)
388 elif not _callable(handler):
389 raise TypeError('Handler %s is not a method, function or tuple' % handler)
393 def connect_signals(self, obj_or_map):
394 """Connect signals specified by this builder to a name, handler mapping.
396 Connect signal, name, and handler sets specified in the builder with
397 the given mapping "obj_or_map". The handler/value aspect of the mapping
398 can also contain a tuple in the form of (handler [,arg1 [,argN]])
399 allowing for extra arguments to be passed to the handler. For example:
400 builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)})
402 def _full_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map):
403 handler, args = self._extract_handler_and_args(obj_or_map, handler_name)
405 after = flags & GObject.ConnectFlags.AFTER
406 if connect_obj is not None:
408 gobj.connect_object_after(signal_name, handler, connect_obj, *args)
410 gobj.connect_object(signal_name, handler, connect_obj, *args)
413 gobj.connect_after(signal_name, handler, *args)
415 gobj.connect(signal_name, handler, *args)
417 self.connect_signals_full(_full_callback, obj_or_map)
419 def add_from_string(self, buffer):
420 if not isinstance(buffer, _basestring):
421 raise TypeError('buffer must be a string')
425 return Gtk.Builder.add_from_string(self, buffer, length)
427 def add_objects_from_string(self, buffer, object_ids):
428 if not isinstance(buffer, _basestring):
429 raise TypeError('buffer must be a string')
433 return Gtk.Builder.add_objects_from_string(self, buffer, length, object_ids)
435 Builder = override(Builder)
436 __all__.append('Builder')
439 # NOTE: This must come before any other Window/Dialog subclassing, to ensure
440 # that we have a correct inheritance hierarchy.
443 class Window(Gtk.Window):
444 __init__ = deprecated_init(Gtk.Window.__init__,
446 category=PyGTKDeprecationWarning)
448 Window = override(Window)
449 __all__.append('Window')
452 class Dialog(Gtk.Dialog, Container):
453 _old_arg_names = ('title', 'parent', 'flags', 'buttons', '_buttons_property')
454 _init = deprecated_init(Gtk.Dialog.__init__,
455 arg_names=('title', 'transient_for', 'flags',
456 'add_buttons', 'buttons'),
457 ignore=('flags', 'add_buttons'),
458 deprecated_aliases={'transient_for': 'parent',
459 'buttons': '_buttons_property'},
460 category=PyGTKDeprecationWarning)
462 def __init__(self, *args, **kwargs):
464 new_kwargs = kwargs.copy()
465 old_kwargs = dict(zip(self._old_arg_names, args))
466 old_kwargs.update(kwargs)
468 # Increment the warning stacklevel for sub-classes which implement their own __init__.
470 if self.__class__ != Dialog and self.__class__.__init__ != Dialog.__init__:
473 # buttons was overloaded by PyGtk but is needed for Gtk.MessageDialog
474 # as a pass through, so type check the argument and give a deprecation
475 # when it is not of type Gtk.ButtonsType
476 add_buttons = old_kwargs.get('buttons', None)
477 if add_buttons is not None and not isinstance(add_buttons, Gtk.ButtonsType):
478 warnings.warn('The "buttons" argument must be a Gtk.ButtonsType enum value. '
479 'Please use the "add_buttons" method for adding buttons. '
480 'See: https://wiki.gnome.org/PyGObject/InitializerDeprecations',
481 PyGTKDeprecationWarning, stacklevel=stacklevel)
482 if 'buttons' in new_kwargs:
483 del new_kwargs['buttons']
487 flags = old_kwargs.get('flags', 0)
489 warnings.warn('The "flags" argument for dialog construction is deprecated. '
490 'Please use initializer keywords: modal=True and/or destroy_with_parent=True. '
491 'See: https://wiki.gnome.org/PyGObject/InitializerDeprecations',
492 PyGTKDeprecationWarning, stacklevel=stacklevel)
494 if flags & Gtk.DialogFlags.MODAL:
495 new_kwargs['modal'] = True
497 if flags & Gtk.DialogFlags.DESTROY_WITH_PARENT:
498 new_kwargs['destroy_with_parent'] = True
500 self._init(*args, **new_kwargs)
503 self.add_buttons(*add_buttons)
505 action_area = property(lambda dialog: dialog.get_action_area())
506 vbox = property(lambda dialog: dialog.get_content_area())
508 def add_buttons(self, *args):
510 The add_buttons() method adds several buttons to the Gtk.Dialog using
511 the button data passed as arguments to the method. This method is the
512 same as calling the Gtk.Dialog.add_button() repeatedly. The button data
513 pairs - button text (or stock ID) and a response ID integer are passed
514 individually. For example:
516 dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)
518 will add "Open" and "Close" buttons to dialog.
527 for text, response in _button(args):
528 self.add_button(text, response)
530 raise TypeError('Must pass an even number of arguments')
532 Dialog = override(Dialog)
533 __all__.append('Dialog')
536 class MessageDialog(Gtk.MessageDialog, Dialog):
537 __init__ = deprecated_init(Gtk.MessageDialog.__init__,
538 arg_names=('parent', 'flags', 'message_type',
539 'buttons', 'message_format'),
540 deprecated_aliases={'text': 'message_format',
541 'message_type': 'type'},
542 category=PyGTKDeprecationWarning)
544 def format_secondary_text(self, message_format):
545 self.set_property('secondary-use-markup', False)
546 self.set_property('secondary-text', message_format)
548 def format_secondary_markup(self, message_format):
549 self.set_property('secondary-use-markup', True)
550 self.set_property('secondary-text', message_format)
552 MessageDialog = override(MessageDialog)
553 __all__.append('MessageDialog')
556 class ColorSelectionDialog(Gtk.ColorSelectionDialog):
557 __init__ = deprecated_init(Gtk.ColorSelectionDialog.__init__,
558 arg_names=('title',),
559 category=PyGTKDeprecationWarning)
561 ColorSelectionDialog = override(ColorSelectionDialog)
562 __all__.append('ColorSelectionDialog')
565 class FileChooserDialog(Gtk.FileChooserDialog):
566 __init__ = deprecated_init(Gtk.FileChooserDialog.__init__,
567 arg_names=('title', 'parent', 'action', 'buttons'),
568 category=PyGTKDeprecationWarning)
570 FileChooserDialog = override(FileChooserDialog)
571 __all__.append('FileChooserDialog')
574 class FontSelectionDialog(Gtk.FontSelectionDialog):
575 __init__ = deprecated_init(Gtk.FontSelectionDialog.__init__,
576 arg_names=('title',),
577 category=PyGTKDeprecationWarning)
579 FontSelectionDialog = override(FontSelectionDialog)
580 __all__.append('FontSelectionDialog')
583 class RecentChooserDialog(Gtk.RecentChooserDialog):
584 # Note, the "manager" keyword must work across the entire 3.x series because
585 # "recent_manager" is not backwards compatible with PyGObject versions prior to 3.10.
586 __init__ = deprecated_init(Gtk.RecentChooserDialog.__init__,
587 arg_names=('title', 'parent', 'recent_manager', 'buttons'),
588 deprecated_aliases={'recent_manager': 'manager'},
589 category=PyGTKDeprecationWarning)
591 RecentChooserDialog = override(RecentChooserDialog)
592 __all__.append('RecentChooserDialog')
595 class IconView(Gtk.IconView):
596 __init__ = deprecated_init(Gtk.IconView.__init__,
597 arg_names=('model',),
598 category=PyGTKDeprecationWarning)
600 get_item_at_pos = strip_boolean_result(Gtk.IconView.get_item_at_pos)
601 get_visible_range = strip_boolean_result(Gtk.IconView.get_visible_range)
602 get_dest_item_at_pos = strip_boolean_result(Gtk.IconView.get_dest_item_at_pos)
604 IconView = override(IconView)
605 __all__.append('IconView')
608 class ToolButton(Gtk.ToolButton):
609 __init__ = deprecated_init(Gtk.ToolButton.__init__,
610 arg_names=('stock_id',),
611 category=PyGTKDeprecationWarning)
613 ToolButton = override(ToolButton)
614 __all__.append('ToolButton')
617 class IMContext(Gtk.IMContext):
618 get_surrounding = strip_boolean_result(Gtk.IMContext.get_surrounding)
620 IMContext = override(IMContext)
621 __all__.append('IMContext')
624 class RecentInfo(Gtk.RecentInfo):
625 get_application_info = strip_boolean_result(Gtk.RecentInfo.get_application_info)
627 RecentInfo = override(RecentInfo)
628 __all__.append('RecentInfo')
631 class TextBuffer(Gtk.TextBuffer):
632 def _get_or_create_tag_table(self):
633 table = self.get_tag_table()
635 table = Gtk.TextTagTable()
636 self.set_tag_table(table)
640 def create_tag(self, tag_name=None, **properties):
642 @tag_name: name of the new tag, or None
643 @properties: keyword list of properties and their values
645 Creates a tag and adds it to the tag table of the TextBuffer.
646 Equivalent to creating a Gtk.TextTag and then adding the
647 tag to the buffer's tag table. The returned tag is owned by
648 the buffer's tag table.
650 If @tag_name is None, the tag is anonymous.
652 If @tag_name is not None, a tag called @tag_name must not already
653 exist in the tag table for this buffer.
655 Properties are passed as a keyword list of names and values (e.g.
656 foreground = 'DodgerBlue', weight = Pango.Weight.BOLD)
658 Return value: a new tag
661 tag = Gtk.TextTag(name=tag_name, **properties)
662 self._get_or_create_tag_table().add(tag)
665 def create_mark(self, mark_name, where, left_gravity=False):
666 return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
668 def set_text(self, text, length=-1):
669 Gtk.TextBuffer.set_text(self, text, length)
671 def insert(self, iter, text, length=-1):
672 if not isinstance(text, _basestring):
673 raise TypeError('text must be a string, not %s' % type(text))
675 Gtk.TextBuffer.insert(self, iter, text, length)
677 def insert_with_tags(self, iter, text, *tags):
678 start_offset = iter.get_offset()
679 self.insert(iter, text)
684 start = self.get_iter_at_offset(start_offset)
687 self.apply_tag(tag, start, iter)
689 def insert_with_tags_by_name(self, iter, text, *tags):
696 tag_obj = self.get_tag_table().lookup(tag)
698 raise ValueError('unknown text tag: %s' % tag)
699 tag_objs.append(tag_obj)
701 self.insert_with_tags(iter, text, *tag_objs)
703 def insert_at_cursor(self, text, length=-1):
704 if not isinstance(text, _basestring):
705 raise TypeError('text must be a string, not %s' % type(text))
707 Gtk.TextBuffer.insert_at_cursor(self, text, length)
709 get_selection_bounds = strip_boolean_result(Gtk.TextBuffer.get_selection_bounds, fail_ret=())
711 TextBuffer = override(TextBuffer)
712 __all__.append('TextBuffer')
715 class TextIter(Gtk.TextIter):
717 forward_search = strip_boolean_result(Gtk.TextIter.forward_search)
718 backward_search = strip_boolean_result(Gtk.TextIter.backward_search)
720 def begins_tag(self, tag=None):
721 return super(TextIter, self).begins_tag(tag)
723 def ends_tag(self, tag=None):
724 return super(TextIter, self).ends_tag(tag)
726 def toggles_tag(self, tag=None):
727 return super(TextIter, self).toggles_tag(tag)
729 TextIter = override(TextIter)
730 __all__.append('TextIter')
733 class TreeModel(Gtk.TreeModel):
735 return self.iter_n_children(None)
740 # alias for Python 2.x object protocol
741 __nonzero__ = __bool__
743 def _getiter(self, key):
744 if isinstance(key, Gtk.TreeIter):
746 elif isinstance(key, int) and key < 0:
747 index = len(self) + key
749 raise IndexError("row index is out of bounds: %d" % key)
751 aiter = self.get_iter(index)
753 raise IndexError("could not find tree path '%s'" % key)
757 aiter = self.get_iter(key)
759 raise IndexError("could not find tree path '%s'" % key)
762 def _coerce_path(self, path):
763 if isinstance(path, Gtk.TreePath):
766 return TreePath(path)
768 def __getitem__(self, key):
769 aiter = self._getiter(key)
770 return TreeModelRow(self, aiter)
772 def __setitem__(self, key, value):
774 self.set_row(row.iter, value)
776 def __delitem__(self, key):
777 aiter = self._getiter(key)
781 return TreeModelRowIter(self, self.get_iter_first())
783 get_iter_first = strip_boolean_result(Gtk.TreeModel.get_iter_first)
784 iter_children = strip_boolean_result(Gtk.TreeModel.iter_children)
785 iter_nth_child = strip_boolean_result(Gtk.TreeModel.iter_nth_child)
786 iter_parent = strip_boolean_result(Gtk.TreeModel.iter_parent)
787 get_iter_from_string = strip_boolean_result(Gtk.TreeModel.get_iter_from_string,
788 ValueError, 'invalid tree path')
790 def get_iter(self, path):
791 path = self._coerce_path(path)
792 success, aiter = super(TreeModel, self).get_iter(path)
794 raise ValueError("invalid tree path '%s'" % path)
797 def iter_next(self, aiter):
798 next_iter = aiter.copy()
799 success = super(TreeModel, self).iter_next(next_iter)
803 def iter_previous(self, aiter):
804 prev_iter = aiter.copy()
805 success = super(TreeModel, self).iter_previous(prev_iter)
809 def _convert_row(self, row):
810 # TODO: Accept a dictionary for row
811 # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
812 if isinstance(row, str):
813 raise TypeError('Expected a list or tuple, but got str')
815 n_columns = self.get_n_columns()
816 if len(row) != n_columns:
817 raise ValueError('row sequence has the incorrect number of elements')
821 for cur_col, value in enumerate(row):
822 # do not try to set None values, they are causing warnings
825 result.append(self._convert_value(cur_col, value))
826 columns.append(cur_col)
827 return (result, columns)
829 def set_row(self, treeiter, row):
830 converted_row, columns = self._convert_row(row)
831 for column in columns:
834 continue # None means skip this row
836 self.set_value(treeiter, column, value)
838 def _convert_value(self, column, value):
839 '''Convert value to a GObject.Value of the expected type'''
841 if isinstance(value, GObject.Value):
843 return GObject.Value(self.get_column_type(column), value)
845 def get(self, treeiter, *columns):
846 n_columns = self.get_n_columns()
850 if not isinstance(col, int):
851 raise TypeError("column numbers must be ints")
853 if col < 0 or col >= n_columns:
854 raise ValueError("column number is out of range")
856 values.append(self.get_value(treeiter, col))
860 def filter_new(self, root=None):
861 return super(TreeModel, self).filter_new(root)
864 # Signals supporting python iterables as tree paths
866 def row_changed(self, path, iter):
867 return super(TreeModel, self).row_changed(self._coerce_path(path), iter)
869 def row_inserted(self, path, iter):
870 return super(TreeModel, self).row_inserted(self._coerce_path(path), iter)
872 def row_has_child_toggled(self, path, iter):
873 return super(TreeModel, self).row_has_child_toggled(self._coerce_path(path),
876 def row_deleted(self, path):
877 return super(TreeModel, self).row_deleted(self._coerce_path(path))
879 def rows_reordered(self, path, iter, new_order):
880 return super(TreeModel, self).rows_reordered(self._coerce_path(path),
884 TreeModel = override(TreeModel)
885 __all__.append('TreeModel')
888 class TreeSortable(Gtk.TreeSortable, ):
890 get_sort_column_id = strip_boolean_result(Gtk.TreeSortable.get_sort_column_id, fail_ret=(None, None))
892 def set_sort_func(self, sort_column_id, sort_func, user_data=None):
893 super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
895 def set_default_sort_func(self, sort_func, user_data=None):
896 super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
898 TreeSortable = override(TreeSortable)
899 __all__.append('TreeSortable')
902 class TreeModelSort(Gtk.TreeModelSort):
903 __init__ = deprecated_init(Gtk.TreeModelSort.__init__,
904 arg_names=('model',),
905 category=PyGTKDeprecationWarning)
907 TreeModelSort = override(TreeModelSort)
908 __all__.append('TreeModelSort')
911 class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
912 def __init__(self, *column_types):
913 Gtk.ListStore.__init__(self)
914 self.set_column_types(column_types)
916 def _do_insert(self, position, row):
918 row, columns = self._convert_row(row)
919 treeiter = self.insert_with_valuesv(position, columns, row)
921 treeiter = Gtk.ListStore.insert(self, position)
925 def append(self, row=None):
927 return self._do_insert(-1, row)
928 # gtk_list_store_insert() does not know about the "position == -1"
929 # case, so use append() here
931 return Gtk.ListStore.append(self)
933 def prepend(self, row=None):
934 return self._do_insert(0, row)
936 def insert(self, position, row=None):
937 return self._do_insert(position, row)
939 # FIXME: sends two signals; check if this can use an atomic
940 # insert_with_valuesv()
942 def insert_before(self, sibling, row=None):
943 treeiter = Gtk.ListStore.insert_before(self, sibling)
946 self.set_row(treeiter, row)
950 # FIXME: sends two signals; check if this can use an atomic
951 # insert_with_valuesv()
953 def insert_after(self, sibling, row=None):
954 treeiter = Gtk.ListStore.insert_after(self, sibling)
957 self.set_row(treeiter, row)
961 def set_value(self, treeiter, column, value):
962 value = self._convert_value(column, value)
963 Gtk.ListStore.set_value(self, treeiter, column, value)
965 def set(self, treeiter, *args):
967 def _set_lists(columns, values):
968 if len(columns) != len(values):
969 raise TypeError('The number of columns do not match the number of values')
970 for col_num, val in zip(columns, values):
971 if not isinstance(col_num, int):
972 raise TypeError('TypeError: Expected integer argument for column.')
973 self.set_value(treeiter, col_num, val)
976 if isinstance(args[0], int):
979 _set_lists(columns, values)
980 elif isinstance(args[0], (tuple, list)):
982 raise TypeError('Too many arguments')
983 _set_lists(args[0], args[1])
984 elif isinstance(args[0], dict):
985 columns = args[0].keys()
986 values = args[0].values()
987 _set_lists(columns, values)
989 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
991 ListStore = override(ListStore)
992 __all__.append('ListStore')
995 class TreeModelRow(object):
997 def __init__(self, model, iter_or_path):
998 if not isinstance(model, Gtk.TreeModel):
999 raise TypeError("expected Gtk.TreeModel, %s found" % type(model).__name__)
1001 if isinstance(iter_or_path, Gtk.TreePath):
1002 self.iter = model.get_iter(iter_or_path)
1003 elif isinstance(iter_or_path, Gtk.TreeIter):
1004 self.iter = iter_or_path
1006 raise TypeError("expected Gtk.TreeIter or Gtk.TreePath, \
1007 %s found" % type(iter_or_path).__name__)
1011 return self.model.get_path(self.iter)
1015 return self.get_next()
1019 return self.get_previous()
1023 return self.get_parent()
1026 next_iter = self.model.iter_next(self.iter)
1028 return TreeModelRow(self.model, next_iter)
1030 def get_previous(self):
1031 prev_iter = self.model.iter_previous(self.iter)
1033 return TreeModelRow(self.model, prev_iter)
1035 def get_parent(self):
1036 parent_iter = self.model.iter_parent(self.iter)
1038 return TreeModelRow(self.model, parent_iter)
1040 def __getitem__(self, key):
1041 if isinstance(key, int):
1042 if key >= self.model.get_n_columns():
1043 raise IndexError("column index is out of bounds: %d" % key)
1045 key = self._convert_negative_index(key)
1046 return self.model.get_value(self.iter, key)
1047 elif isinstance(key, slice):
1048 start, stop, step = key.indices(self.model.get_n_columns())
1050 for i in range(start, stop, step):
1051 alist.append(self.model.get_value(self.iter, i))
1054 raise TypeError("indices must be integers, not %s" % type(key).__name__)
1056 def __setitem__(self, key, value):
1057 if isinstance(key, int):
1058 if key >= self.model.get_n_columns():
1059 raise IndexError("column index is out of bounds: %d" % key)
1061 key = self._convert_negative_index(key)
1062 self.model.set_value(self.iter, key, value)
1063 elif isinstance(key, slice):
1064 start, stop, step = key.indices(self.model.get_n_columns())
1065 indexList = range(start, stop, step)
1066 if len(indexList) != len(value):
1068 "attempt to assign sequence of size %d to slice of size %d"
1069 % (len(value), len(indexList)))
1071 for i, v in enumerate(indexList):
1072 self.model.set_value(self.iter, v, value[i])
1074 raise TypeError("index must be an integer or slice, not %s" % type(key).__name__)
1076 def _convert_negative_index(self, index):
1077 new_index = self.model.get_n_columns() + index
1079 raise IndexError("column index is out of bounds: %d" % index)
1082 def iterchildren(self):
1083 child_iter = self.model.iter_children(self.iter)
1084 return TreeModelRowIter(self.model, child_iter)
1086 __all__.append('TreeModelRow')
1089 class TreeModelRowIter(object):
1091 def __init__(self, model, aiter):
1098 row = TreeModelRow(self.model, self.iter)
1099 self.iter = self.model.iter_next(self.iter)
1102 # alias for Python 2.x object protocol
1108 __all__.append('TreeModelRowIter')
1111 class TreePath(Gtk.TreePath):
1113 def __new__(cls, path=0):
1114 if isinstance(path, int):
1116 elif not isinstance(path, _basestring):
1117 path = ":".join(str(val) for val in path)
1120 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1122 return TreePath.new_from_string(path)
1124 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1127 return self.to_string()
1129 def __lt__(self, other):
1130 return not other is None and self.compare(other) < 0
1132 def __le__(self, other):
1133 return not other is None and self.compare(other) <= 0
1135 def __eq__(self, other):
1136 return not other is None and self.compare(other) == 0
1138 def __ne__(self, other):
1139 return other is None or self.compare(other) != 0
1141 def __gt__(self, other):
1142 return other is None or self.compare(other) > 0
1144 def __ge__(self, other):
1145 return other is None or self.compare(other) >= 0
1148 return iter(self.get_indices())
1151 return self.get_depth()
1153 def __getitem__(self, index):
1154 return self.get_indices()[index]
1156 TreePath = override(TreePath)
1157 __all__.append('TreePath')
1160 class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
1161 def __init__(self, *column_types):
1162 Gtk.TreeStore.__init__(self)
1163 self.set_column_types(column_types)
1165 def _do_insert(self, parent, position, row):
1167 row, columns = self._convert_row(row)
1168 treeiter = self.insert_with_values(parent, position, columns, row)
1170 treeiter = Gtk.TreeStore.insert(self, parent, position)
1174 def append(self, parent, row=None):
1175 return self._do_insert(parent, -1, row)
1177 def prepend(self, parent, row=None):
1178 return self._do_insert(parent, 0, row)
1180 def insert(self, parent, position, row=None):
1181 return self._do_insert(parent, position, row)
1183 # FIXME: sends two signals; check if this can use an atomic
1184 # insert_with_valuesv()
1186 def insert_before(self, parent, sibling, row=None):
1187 treeiter = Gtk.TreeStore.insert_before(self, parent, sibling)
1190 self.set_row(treeiter, row)
1194 # FIXME: sends two signals; check if this can use an atomic
1195 # insert_with_valuesv()
1197 def insert_after(self, parent, sibling, row=None):
1198 treeiter = Gtk.TreeStore.insert_after(self, parent, sibling)
1201 self.set_row(treeiter, row)
1205 def set_value(self, treeiter, column, value):
1206 value = self._convert_value(column, value)
1207 Gtk.TreeStore.set_value(self, treeiter, column, value)
1209 def set(self, treeiter, *args):
1211 def _set_lists(columns, values):
1212 if len(columns) != len(values):
1213 raise TypeError('The number of columns do not match the number of values')
1214 for col_num, val in zip(columns, values):
1215 if not isinstance(col_num, int):
1216 raise TypeError('TypeError: Expected integer argument for column.')
1217 self.set_value(treeiter, col_num, val)
1220 if isinstance(args[0], int):
1223 _set_lists(columns, values)
1224 elif isinstance(args[0], (tuple, list)):
1226 raise TypeError('Too many arguments')
1227 _set_lists(args[0], args[1])
1228 elif isinstance(args[0], dict):
1229 columns = args[0].keys()
1230 values = args[0].values()
1231 _set_lists(columns, values)
1233 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
1235 TreeStore = override(TreeStore)
1236 __all__.append('TreeStore')
1239 class TreeView(Gtk.TreeView, Container):
1240 __init__ = deprecated_init(Gtk.TreeView.__init__,
1241 arg_names=('model',),
1242 category=PyGTKDeprecationWarning)
1244 get_path_at_pos = strip_boolean_result(Gtk.TreeView.get_path_at_pos)
1245 get_visible_range = strip_boolean_result(Gtk.TreeView.get_visible_range)
1246 get_dest_row_at_pos = strip_boolean_result(Gtk.TreeView.get_dest_row_at_pos)
1248 def enable_model_drag_source(self, start_button_mask, targets, actions):
1249 target_entries = _construct_target_list(targets)
1250 super(TreeView, self).enable_model_drag_source(start_button_mask,
1254 def enable_model_drag_dest(self, targets, actions):
1255 target_entries = _construct_target_list(targets)
1256 super(TreeView, self).enable_model_drag_dest(target_entries,
1259 def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
1260 if not isinstance(path, Gtk.TreePath):
1261 path = TreePath(path)
1262 super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
1264 def set_cursor(self, path, column=None, start_editing=False):
1265 if not isinstance(path, Gtk.TreePath):
1266 path = TreePath(path)
1267 super(TreeView, self).set_cursor(path, column, start_editing)
1269 def get_cell_area(self, path, column=None):
1270 if not isinstance(path, Gtk.TreePath):
1271 path = TreePath(path)
1272 return super(TreeView, self).get_cell_area(path, column)
1274 def insert_column_with_attributes(self, position, title, cell, **kwargs):
1275 column = TreeViewColumn()
1276 column.set_title(title)
1277 column.pack_start(cell, False)
1278 self.insert_column(column, position)
1279 column.set_attributes(cell, **kwargs)
1281 TreeView = override(TreeView)
1282 __all__.append('TreeView')
1285 class TreeViewColumn(Gtk.TreeViewColumn):
1286 def __init__(self, title='',
1289 Gtk.TreeViewColumn.__init__(self, title=title)
1291 self.pack_start(cell_renderer, True)
1293 for (name, value) in attributes.items():
1294 self.add_attribute(cell_renderer, name, value)
1296 cell_get_position = strip_boolean_result(Gtk.TreeViewColumn.cell_get_position)
1298 def set_cell_data_func(self, cell_renderer, func, func_data=None):
1299 super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
1301 def set_attributes(self, cell_renderer, **attributes):
1302 Gtk.CellLayout.clear_attributes(self, cell_renderer)
1304 for (name, value) in attributes.items():
1305 Gtk.CellLayout.add_attribute(self, cell_renderer, name, value)
1308 TreeViewColumn = override(TreeViewColumn)
1309 __all__.append('TreeViewColumn')
1312 class TreeSelection(Gtk.TreeSelection):
1314 def select_path(self, path):
1315 if not isinstance(path, Gtk.TreePath):
1316 path = TreePath(path)
1317 super(TreeSelection, self).select_path(path)
1319 def get_selected(self):
1320 success, model, aiter = super(TreeSelection, self).get_selected()
1322 return (model, aiter)
1324 return (model, None)
1326 # for compatibility with PyGtk
1328 def get_selected_rows(self):
1329 rows, model = super(TreeSelection, self).get_selected_rows()
1330 return (model, rows)
1333 TreeSelection = override(TreeSelection)
1334 __all__.append('TreeSelection')
1337 class Button(Gtk.Button, Container):
1338 _init = deprecated_init(Gtk.Button.__init__,
1339 arg_names=('label', 'stock', 'use_stock', 'use_underline'),
1341 category=PyGTKDeprecationWarning,
1344 def __init__(self, *args, **kwargs):
1345 # Doubly deprecated initializer, the stock keyword is non-standard.
1346 # Simply give a warning that stock items are deprecated even though
1347 # we want to deprecate the non-standard keyword as well here from
1349 if 'stock' in kwargs and kwargs['stock']:
1350 warnings.warn('Stock items are deprecated. '
1351 'Please use: Gtk.Button.new_with_mnemonic(label)',
1352 PyGTKDeprecationWarning, stacklevel=2)
1353 new_kwargs = kwargs.copy()
1354 new_kwargs['label'] = new_kwargs['stock']
1355 new_kwargs['use_stock'] = True
1356 new_kwargs['use_underline'] = True
1357 del new_kwargs['stock']
1358 Gtk.Button.__init__(self, **new_kwargs)
1360 self._init(*args, **kwargs)
1362 Button = override(Button)
1363 __all__.append('Button')
1366 class LinkButton(Gtk.LinkButton):
1367 __init__ = deprecated_init(Gtk.LinkButton.__init__,
1368 arg_names=('uri', 'label'),
1369 category=PyGTKDeprecationWarning)
1371 LinkButton = override(LinkButton)
1372 __all__.append('LinkButton')
1375 class Label(Gtk.Label):
1376 __init__ = deprecated_init(Gtk.Label.__init__,
1377 arg_names=('label',),
1378 category=PyGTKDeprecationWarning)
1380 Label = override(Label)
1381 __all__.append('Label')
1384 class Adjustment(Gtk.Adjustment):
1385 _init = deprecated_init(Gtk.Adjustment.__init__,
1386 arg_names=('value', 'lower', 'upper',
1387 'step_increment', 'page_increment', 'page_size'),
1388 deprecated_aliases={'page_increment': 'page_incr',
1389 'step_increment': 'step_incr'},
1390 category=PyGTKDeprecationWarning,
1393 def __init__(self, *args, **kwargs):
1394 self._init(*args, **kwargs)
1396 # The value property is set between lower and (upper - page_size).
1397 # Just in case lower, upper or page_size was still 0 when value
1398 # was set, we set it again here.
1399 if 'value' in kwargs:
1400 self.set_value(kwargs['value'])
1402 Adjustment = override(Adjustment)
1403 __all__.append('Adjustment')
1406 class Table(Gtk.Table, Container):
1407 __init__ = deprecated_init(Gtk.Table.__init__,
1408 arg_names=('n_rows', 'n_columns', 'homogeneous'),
1409 deprecated_aliases={'n_rows': 'rows', 'n_columns': 'columns'},
1410 category=PyGTKDeprecationWarning)
1412 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):
1413 Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
1415 Table = override(Table)
1416 __all__.append('Table')
1419 class ScrolledWindow(Gtk.ScrolledWindow):
1420 __init__ = deprecated_init(Gtk.ScrolledWindow.__init__,
1421 arg_names=('hadjustment', 'vadjustment'),
1422 category=PyGTKDeprecationWarning)
1424 ScrolledWindow = override(ScrolledWindow)
1425 __all__.append('ScrolledWindow')
1428 class HScrollbar(Gtk.HScrollbar):
1429 __init__ = deprecated_init(Gtk.HScrollbar.__init__,
1430 arg_names=('adjustment',),
1431 category=PyGTKDeprecationWarning)
1433 HScrollbar = override(HScrollbar)
1434 __all__.append('HScrollbar')
1437 class VScrollbar(Gtk.VScrollbar):
1438 __init__ = deprecated_init(Gtk.VScrollbar.__init__,
1439 arg_names=('adjustment',),
1440 category=PyGTKDeprecationWarning)
1442 VScrollbar = override(VScrollbar)
1443 __all__.append('VScrollbar')
1446 class Paned(Gtk.Paned):
1447 def pack1(self, child, resize=False, shrink=True):
1448 super(Paned, self).pack1(child, resize, shrink)
1450 def pack2(self, child, resize=True, shrink=True):
1451 super(Paned, self).pack2(child, resize, shrink)
1453 Paned = override(Paned)
1454 __all__.append('Paned')
1457 class Arrow(Gtk.Arrow):
1458 __init__ = deprecated_init(Gtk.Arrow.__init__,
1459 arg_names=('arrow_type', 'shadow_type'),
1460 category=PyGTKDeprecationWarning)
1462 Arrow = override(Arrow)
1463 __all__.append('Arrow')
1466 class IconSet(Gtk.IconSet):
1467 def __new__(cls, pixbuf=None):
1468 if pixbuf is not None:
1469 warnings.warn('Gtk.IconSet(pixbuf) has been deprecated. Please use: '
1470 'Gtk.IconSet.new_from_pixbuf(pixbuf)',
1471 PyGTKDeprecationWarning, stacklevel=2)
1472 iconset = Gtk.IconSet.new_from_pixbuf(pixbuf)
1474 iconset = Gtk.IconSet.__new__(cls)
1477 IconSet = override(IconSet)
1478 __all__.append('IconSet')
1481 class Viewport(Gtk.Viewport):
1482 __init__ = deprecated_init(Gtk.Viewport.__init__,
1483 arg_names=('hadjustment', 'vadjustment'),
1484 category=PyGTKDeprecationWarning)
1486 Viewport = override(Viewport)
1487 __all__.append('Viewport')
1490 class TreeModelFilter(Gtk.TreeModelFilter):
1491 def set_visible_func(self, func, data=None):
1492 super(TreeModelFilter, self).set_visible_func(func, data)
1494 def set_value(self, iter, column, value):
1495 # Delegate to child model
1496 iter = self.convert_iter_to_child_iter(iter)
1497 self.get_model().set_value(iter, column, value)
1499 TreeModelFilter = override(TreeModelFilter)
1500 __all__.append('TreeModelFilter')
1502 if Gtk._version != '2.0':
1503 class Menu(Gtk.Menu):
1504 def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
1505 self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
1506 Menu = override(Menu)
1507 __all__.append('Menu')
1509 _Gtk_main_quit = Gtk.main_quit
1512 @override(Gtk.main_quit)
1513 def main_quit(*args):
1516 stock_lookup = strip_boolean_result(Gtk.stock_lookup)
1517 __all__.append('stock_lookup')
1519 initialized, argv = Gtk.init_check(sys.argv)
1520 sys.argv = list(argv)