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 drag_dest_set_target_list(self, target_list):
80 if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
81 target_list = Gtk.TargetList.new(_construct_target_list(target_list))
82 super(Widget, self).drag_dest_set_target_list(target_list)
84 def drag_source_set_target_list(self, target_list):
85 if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
86 target_list = Gtk.TargetList.new(_construct_target_list(target_list))
87 super(Widget, self).drag_source_set_target_list(target_list)
90 Widget = override(Widget)
91 __all__.append('Widget')
94 class Container(Gtk.Container, Widget):
97 return len(self.get_children())
99 def __contains__(self, child):
100 return child in self.get_children()
103 return iter(self.get_children())
108 # alias for Python 2.x object protocol
109 __nonzero__ = __bool__
111 get_focus_chain = strip_boolean_result(Gtk.Container.get_focus_chain)
114 Container = override(Container)
115 __all__.append('Container')
118 class Editable(Gtk.Editable):
120 def insert_text(self, text, position):
121 return super(Editable, self).insert_text(text, -1, position)
123 get_selection_bounds = strip_boolean_result(Gtk.Editable.get_selection_bounds, fail_ret=())
126 Editable = override(Editable)
127 __all__.append("Editable")
130 class Action(Gtk.Action):
131 __init__ = deprecated_init(Gtk.Action.__init__,
132 arg_names=('name', 'label', 'tooltip', 'stock_id'),
133 category=PyGTKDeprecationWarning)
135 Action = override(Action)
136 __all__.append("Action")
139 class RadioAction(Gtk.RadioAction):
140 __init__ = deprecated_init(Gtk.RadioAction.__init__,
141 arg_names=('name', 'label', 'tooltip', 'stock_id', 'value'),
142 category=PyGTKDeprecationWarning)
144 RadioAction = override(RadioAction)
145 __all__.append("RadioAction")
148 class ActionGroup(Gtk.ActionGroup):
149 __init__ = deprecated_init(Gtk.ActionGroup.__init__,
151 category=PyGTKDeprecationWarning)
153 def add_actions(self, entries, user_data=None):
155 The add_actions() method is a convenience method that creates a number
156 of gtk.Action objects based on the information in the list of action
157 entry tuples contained in entries and adds them to the action group.
158 The entry tuples can vary in size from one to six items with the
159 following information:
161 * The name of the action. Must be specified.
162 * The stock id for the action. Optional with a default value of None
163 if a label is specified.
164 * The label for the action. This field should typically be marked
165 for translation, see the set_translation_domain() method. Optional
166 with a default value of None if a stock id is specified.
167 * The accelerator for the action, in the format understood by the
168 gtk.accelerator_parse() function. Optional with a default value of
170 * The tooltip for the action. This field should typically be marked
171 for translation, see the set_translation_domain() method. Optional
172 with a default value of None.
173 * The callback function invoked when the action is activated.
174 Optional with a default value of None.
176 The "activate" signals of the actions are connected to the callbacks and
177 their accel paths are set to <Actions>/group-name/action-name.
182 raise TypeError('entries must be iterable')
184 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
185 action = Action(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
186 if callback is not None:
187 if user_data is None:
188 action.connect('activate', callback)
190 action.connect('activate', callback, user_data)
192 self.add_action_with_accel(action, accelerator)
195 # using inner function above since entries can leave out optional arguments
198 def add_toggle_actions(self, entries, user_data=None):
200 The add_toggle_actions() method is a convenience method that creates a
201 number of gtk.ToggleAction objects based on the information in the list
202 of action entry tuples contained in entries and adds them to the action
203 group. The toggle action entry tuples can vary in size from one to seven
204 items with the following information:
206 * The name of the action. Must be specified.
207 * The stock id for the action. Optional with a default value of None
208 if a label is specified.
209 * The label for the action. This field should typically be marked
210 for translation, see the set_translation_domain() method. Optional
211 with a default value of None if a stock id is specified.
212 * The accelerator for the action, in the format understood by the
213 gtk.accelerator_parse() function. Optional with a default value of
215 * The tooltip for the action. This field should typically be marked
216 for translation, see the set_translation_domain() method. Optional
217 with a default value of None.
218 * The callback function invoked when the action is activated.
219 Optional with a default value of None.
220 * A flag indicating whether the toggle action is active. Optional
221 with a default value of False.
223 The "activate" signals of the actions are connected to the callbacks and
224 their accel paths are set to <Actions>/group-name/action-name.
230 raise TypeError('entries must be iterable')
232 def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
233 action = Gtk.ToggleAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
234 action.set_active(is_active)
235 if callback is not None:
236 if user_data is None:
237 action.connect('activate', callback)
239 action.connect('activate', callback, user_data)
241 self.add_action_with_accel(action, accelerator)
244 # using inner function above since entries can leave out optional arguments
247 def add_radio_actions(self, entries, value=None, on_change=None, user_data=None):
249 The add_radio_actions() method is a convenience method that creates a
250 number of gtk.RadioAction objects based on the information in the list
251 of action entry tuples contained in entries and adds them to the action
252 group. The entry tuples can vary in size from one to six items with the
253 following information:
255 * The name of the action. Must be specified.
256 * The stock id for the action. Optional with a default value of None
257 if a label is specified.
258 * The label for the action. This field should typically be marked
259 for translation, see the set_translation_domain() method. Optional
260 with a default value of None if a stock id is specified.
261 * The accelerator for the action, in the format understood by the
262 gtk.accelerator_parse() function. Optional with a default value of
264 * The tooltip for the action. This field should typically be marked
265 for translation, see the set_translation_domain() method. Optional
266 with a default value of None.
267 * The value to set on the radio action. Optional with a default
268 value of 0. Should be specified in applications.
270 The value parameter specifies the radio action that should be set
271 active. The "changed" signal of the first radio action is connected to
272 the on_change callback (if specified and not None) and the accel paths
273 of the actions are set to <Actions>/group-name/action-name.
278 raise TypeError('entries must be iterable')
282 def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
283 action = RadioAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=entry_value)
285 # FIXME: join_group is a patch to Gtk+ 3.0
286 # otherwise we can't effectively add radio actions to a
287 # group. Should we depend on 3.0 and error out here
288 # or should we offer the functionality via a compat
290 if hasattr(action, 'join_group'):
291 action.join_group(group_source)
293 if value == entry_value:
294 action.set_active(True)
296 self.add_action_with_accel(action, accelerator)
300 # using inner function above since entries can leave out optional arguments
301 action = _process_action(first_action, *e)
302 if first_action is None:
303 first_action = action
305 if first_action is not None and on_change is not None:
306 if user_data is None:
307 first_action.connect('changed', on_change)
309 first_action.connect('changed', on_change, user_data)
311 ActionGroup = override(ActionGroup)
312 __all__.append('ActionGroup')
315 class UIManager(Gtk.UIManager):
316 def add_ui_from_string(self, buffer):
317 if not isinstance(buffer, _basestring):
318 raise TypeError('buffer must be a string')
320 length = len(buffer.encode('UTF-8'))
322 return Gtk.UIManager.add_ui_from_string(self, buffer, length)
324 def insert_action_group(self, buffer, length=-1):
325 return Gtk.UIManager.insert_action_group(self, buffer, length)
327 UIManager = override(UIManager)
328 __all__.append('UIManager')
331 class ComboBox(Gtk.ComboBox, Container):
332 get_active_iter = strip_boolean_result(Gtk.ComboBox.get_active_iter)
334 ComboBox = override(ComboBox)
335 __all__.append('ComboBox')
339 __init__ = deprecated_init(Gtk.Box.__init__,
340 arg_names=('homogeneous', 'spacing'),
341 category=PyGTKDeprecationWarning)
344 __all__.append('Box')
347 class SizeGroup(Gtk.SizeGroup):
348 __init__ = deprecated_init(Gtk.SizeGroup.__init__,
350 deprecated_defaults={'mode': Gtk.SizeGroupMode.VERTICAL},
351 category=PyGTKDeprecationWarning)
353 SizeGroup = override(SizeGroup)
354 __all__.append('SizeGroup')
357 class MenuItem(Gtk.MenuItem):
358 __init__ = deprecated_init(Gtk.MenuItem.__init__,
359 arg_names=('label',),
360 category=PyGTKDeprecationWarning)
362 MenuItem = override(MenuItem)
363 __all__.append('MenuItem')
366 class Builder(Gtk.Builder):
368 def _extract_handler_and_args(obj_or_map, handler_name):
370 if isinstance(obj_or_map, collections.Mapping):
371 handler = obj_or_map.get(handler_name, None)
373 handler = getattr(obj_or_map, handler_name, None)
376 raise AttributeError('Handler %s not found' % handler_name)
379 if isinstance(handler, collections.Sequence):
380 if len(handler) == 0:
381 raise TypeError("Handler %s tuple can not be empty" % handler)
385 elif not _callable(handler):
386 raise TypeError('Handler %s is not a method, function or tuple' % handler)
390 def connect_signals(self, obj_or_map):
391 """Connect signals specified by this builder to a name, handler mapping.
393 Connect signal, name, and handler sets specified in the builder with
394 the given mapping "obj_or_map". The handler/value aspect of the mapping
395 can also contain a tuple in the form of (handler [,arg1 [,argN]])
396 allowing for extra arguments to be passed to the handler. For example:
398 .. code-block:: python
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 .. code-block:: python
518 dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)
520 will add "Open" and "Close" buttons to dialog.
529 for text, response in _button(args):
530 self.add_button(text, response)
532 raise TypeError('Must pass an even number of arguments')
534 Dialog = override(Dialog)
535 __all__.append('Dialog')
538 class MessageDialog(Gtk.MessageDialog, Dialog):
539 __init__ = deprecated_init(Gtk.MessageDialog.__init__,
540 arg_names=('parent', 'flags', 'message_type',
541 'buttons', 'message_format'),
542 deprecated_aliases={'text': 'message_format',
543 'message_type': 'type'},
544 category=PyGTKDeprecationWarning)
546 def format_secondary_text(self, message_format):
547 self.set_property('secondary-use-markup', False)
548 self.set_property('secondary-text', message_format)
550 def format_secondary_markup(self, message_format):
551 self.set_property('secondary-use-markup', True)
552 self.set_property('secondary-text', message_format)
554 MessageDialog = override(MessageDialog)
555 __all__.append('MessageDialog')
558 class ColorSelectionDialog(Gtk.ColorSelectionDialog):
559 __init__ = deprecated_init(Gtk.ColorSelectionDialog.__init__,
560 arg_names=('title',),
561 category=PyGTKDeprecationWarning)
563 ColorSelectionDialog = override(ColorSelectionDialog)
564 __all__.append('ColorSelectionDialog')
567 class FileChooserDialog(Gtk.FileChooserDialog):
568 __init__ = deprecated_init(Gtk.FileChooserDialog.__init__,
569 arg_names=('title', 'parent', 'action', 'buttons'),
570 category=PyGTKDeprecationWarning)
572 FileChooserDialog = override(FileChooserDialog)
573 __all__.append('FileChooserDialog')
576 class FontSelectionDialog(Gtk.FontSelectionDialog):
577 __init__ = deprecated_init(Gtk.FontSelectionDialog.__init__,
578 arg_names=('title',),
579 category=PyGTKDeprecationWarning)
581 FontSelectionDialog = override(FontSelectionDialog)
582 __all__.append('FontSelectionDialog')
585 class RecentChooserDialog(Gtk.RecentChooserDialog):
586 # Note, the "manager" keyword must work across the entire 3.x series because
587 # "recent_manager" is not backwards compatible with PyGObject versions prior to 3.10.
588 __init__ = deprecated_init(Gtk.RecentChooserDialog.__init__,
589 arg_names=('title', 'parent', 'recent_manager', 'buttons'),
590 deprecated_aliases={'recent_manager': 'manager'},
591 category=PyGTKDeprecationWarning)
593 RecentChooserDialog = override(RecentChooserDialog)
594 __all__.append('RecentChooserDialog')
597 class IconView(Gtk.IconView):
598 __init__ = deprecated_init(Gtk.IconView.__init__,
599 arg_names=('model',),
600 category=PyGTKDeprecationWarning)
602 get_item_at_pos = strip_boolean_result(Gtk.IconView.get_item_at_pos)
603 get_visible_range = strip_boolean_result(Gtk.IconView.get_visible_range)
604 get_dest_item_at_pos = strip_boolean_result(Gtk.IconView.get_dest_item_at_pos)
606 IconView = override(IconView)
607 __all__.append('IconView')
610 class ToolButton(Gtk.ToolButton):
611 __init__ = deprecated_init(Gtk.ToolButton.__init__,
612 arg_names=('stock_id',),
613 category=PyGTKDeprecationWarning)
615 ToolButton = override(ToolButton)
616 __all__.append('ToolButton')
619 class IMContext(Gtk.IMContext):
620 get_surrounding = strip_boolean_result(Gtk.IMContext.get_surrounding)
622 IMContext = override(IMContext)
623 __all__.append('IMContext')
626 class RecentInfo(Gtk.RecentInfo):
627 get_application_info = strip_boolean_result(Gtk.RecentInfo.get_application_info)
629 RecentInfo = override(RecentInfo)
630 __all__.append('RecentInfo')
633 class TextBuffer(Gtk.TextBuffer):
634 def _get_or_create_tag_table(self):
635 table = self.get_tag_table()
637 table = Gtk.TextTagTable()
638 self.set_tag_table(table)
642 def create_tag(self, tag_name=None, **properties):
643 """Creates a tag and adds it to the tag table of the TextBuffer.
646 Name of the new tag, or None
648 Keyword list of properties and their values
650 This is equivalent to creating a Gtk.TextTag and then adding the
651 tag to the buffer's tag table. The returned tag is owned by
652 the buffer's tag table.
654 If ``tag_name`` is None, the tag is anonymous.
656 If ``tag_name`` is not None, a tag called ``tag_name`` must not already
657 exist in the tag table for this buffer.
659 Properties are passed as a keyword list of names and values (e.g.
660 foreground='DodgerBlue', weight=Pango.Weight.BOLD)
666 tag = Gtk.TextTag(name=tag_name, **properties)
667 self._get_or_create_tag_table().add(tag)
670 def create_mark(self, mark_name, where, left_gravity=False):
671 return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
673 def set_text(self, text, length=-1):
674 Gtk.TextBuffer.set_text(self, text, length)
676 def insert(self, iter, text, length=-1):
677 if not isinstance(text, _basestring):
678 raise TypeError('text must be a string, not %s' % type(text))
680 Gtk.TextBuffer.insert(self, iter, text, length)
682 def insert_with_tags(self, iter, text, *tags):
683 start_offset = iter.get_offset()
684 self.insert(iter, text)
689 start = self.get_iter_at_offset(start_offset)
692 self.apply_tag(tag, start, iter)
694 def insert_with_tags_by_name(self, iter, text, *tags):
701 tag_obj = self.get_tag_table().lookup(tag)
703 raise ValueError('unknown text tag: %s' % tag)
704 tag_objs.append(tag_obj)
706 self.insert_with_tags(iter, text, *tag_objs)
708 def insert_at_cursor(self, text, length=-1):
709 if not isinstance(text, _basestring):
710 raise TypeError('text must be a string, not %s' % type(text))
712 Gtk.TextBuffer.insert_at_cursor(self, text, length)
714 get_selection_bounds = strip_boolean_result(Gtk.TextBuffer.get_selection_bounds, fail_ret=())
716 TextBuffer = override(TextBuffer)
717 __all__.append('TextBuffer')
720 class TextIter(Gtk.TextIter):
721 forward_search = strip_boolean_result(Gtk.TextIter.forward_search)
722 backward_search = strip_boolean_result(Gtk.TextIter.backward_search)
724 TextIter = override(TextIter)
725 __all__.append('TextIter')
728 class TreeModel(Gtk.TreeModel):
730 return self.iter_n_children(None)
735 # alias for Python 2.x object protocol
736 __nonzero__ = __bool__
738 def _getiter(self, key):
739 if isinstance(key, Gtk.TreeIter):
741 elif isinstance(key, int) and key < 0:
742 index = len(self) + key
744 raise IndexError("row index is out of bounds: %d" % key)
746 aiter = self.get_iter(index)
748 raise IndexError("could not find tree path '%s'" % key)
752 aiter = self.get_iter(key)
754 raise IndexError("could not find tree path '%s'" % key)
757 def _coerce_path(self, path):
758 if isinstance(path, Gtk.TreePath):
761 return TreePath(path)
763 def __getitem__(self, key):
764 aiter = self._getiter(key)
765 return TreeModelRow(self, aiter)
767 def __setitem__(self, key, value):
769 self.set_row(row.iter, value)
771 def __delitem__(self, key):
772 aiter = self._getiter(key)
776 return TreeModelRowIter(self, self.get_iter_first())
778 get_iter_first = strip_boolean_result(Gtk.TreeModel.get_iter_first)
779 iter_children = strip_boolean_result(Gtk.TreeModel.iter_children)
780 iter_nth_child = strip_boolean_result(Gtk.TreeModel.iter_nth_child)
781 iter_parent = strip_boolean_result(Gtk.TreeModel.iter_parent)
782 get_iter_from_string = strip_boolean_result(Gtk.TreeModel.get_iter_from_string,
783 ValueError, 'invalid tree path')
785 def get_iter(self, path):
786 path = self._coerce_path(path)
787 success, aiter = super(TreeModel, self).get_iter(path)
789 raise ValueError("invalid tree path '%s'" % path)
792 def iter_next(self, aiter):
793 next_iter = aiter.copy()
794 success = super(TreeModel, self).iter_next(next_iter)
798 def iter_previous(self, aiter):
799 prev_iter = aiter.copy()
800 success = super(TreeModel, self).iter_previous(prev_iter)
804 def _convert_row(self, row):
805 # TODO: Accept a dictionary for row
806 # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
807 if isinstance(row, str):
808 raise TypeError('Expected a list or tuple, but got str')
810 n_columns = self.get_n_columns()
811 if len(row) != n_columns:
812 raise ValueError('row sequence has the incorrect number of elements')
816 for cur_col, value in enumerate(row):
817 # do not try to set None values, they are causing warnings
820 result.append(self._convert_value(cur_col, value))
821 columns.append(cur_col)
822 return (result, columns)
824 def set_row(self, treeiter, row):
825 converted_row, columns = self._convert_row(row)
826 for column in columns:
829 continue # None means skip this row
831 self.set_value(treeiter, column, value)
833 def _convert_value(self, column, value):
834 '''Convert value to a GObject.Value of the expected type'''
836 if isinstance(value, GObject.Value):
838 return GObject.Value(self.get_column_type(column), value)
840 def get(self, treeiter, *columns):
841 n_columns = self.get_n_columns()
845 if not isinstance(col, int):
846 raise TypeError("column numbers must be ints")
848 if col < 0 or col >= n_columns:
849 raise ValueError("column number is out of range")
851 values.append(self.get_value(treeiter, col))
856 # Signals supporting python iterables as tree paths
858 def row_changed(self, path, iter):
859 return super(TreeModel, self).row_changed(self._coerce_path(path), iter)
861 def row_inserted(self, path, iter):
862 return super(TreeModel, self).row_inserted(self._coerce_path(path), iter)
864 def row_has_child_toggled(self, path, iter):
865 return super(TreeModel, self).row_has_child_toggled(self._coerce_path(path),
868 def row_deleted(self, path):
869 return super(TreeModel, self).row_deleted(self._coerce_path(path))
871 def rows_reordered(self, path, iter, new_order):
872 return super(TreeModel, self).rows_reordered(self._coerce_path(path),
876 TreeModel = override(TreeModel)
877 __all__.append('TreeModel')
880 class TreeSortable(Gtk.TreeSortable, ):
882 get_sort_column_id = strip_boolean_result(Gtk.TreeSortable.get_sort_column_id, fail_ret=(None, None))
884 def set_sort_func(self, sort_column_id, sort_func, user_data=None):
885 super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
887 def set_default_sort_func(self, sort_func, user_data=None):
888 super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
890 TreeSortable = override(TreeSortable)
891 __all__.append('TreeSortable')
894 class TreeModelSort(Gtk.TreeModelSort):
895 __init__ = deprecated_init(Gtk.TreeModelSort.__init__,
896 arg_names=('model',),
897 category=PyGTKDeprecationWarning)
899 TreeModelSort = override(TreeModelSort)
900 __all__.append('TreeModelSort')
903 class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
904 def __init__(self, *column_types):
905 Gtk.ListStore.__init__(self)
906 self.set_column_types(column_types)
908 def _do_insert(self, position, row):
910 row, columns = self._convert_row(row)
911 treeiter = self.insert_with_valuesv(position, columns, row)
913 treeiter = Gtk.ListStore.insert(self, position)
917 def append(self, row=None):
919 return self._do_insert(-1, row)
920 # gtk_list_store_insert() does not know about the "position == -1"
921 # case, so use append() here
923 return Gtk.ListStore.append(self)
925 def prepend(self, row=None):
926 return self._do_insert(0, row)
928 def insert(self, position, row=None):
929 return self._do_insert(position, row)
931 # FIXME: sends two signals; check if this can use an atomic
932 # insert_with_valuesv()
934 def insert_before(self, sibling, row=None):
935 treeiter = Gtk.ListStore.insert_before(self, sibling)
938 self.set_row(treeiter, row)
942 # FIXME: sends two signals; check if this can use an atomic
943 # insert_with_valuesv()
945 def insert_after(self, sibling, row=None):
946 treeiter = Gtk.ListStore.insert_after(self, sibling)
949 self.set_row(treeiter, row)
953 def set_value(self, treeiter, column, value):
954 value = self._convert_value(column, value)
955 Gtk.ListStore.set_value(self, treeiter, column, value)
957 def set(self, treeiter, *args):
959 def _set_lists(columns, values):
960 if len(columns) != len(values):
961 raise TypeError('The number of columns do not match the number of values')
962 for col_num, val in zip(columns, values):
963 if not isinstance(col_num, int):
964 raise TypeError('TypeError: Expected integer argument for column.')
965 self.set_value(treeiter, col_num, val)
968 if isinstance(args[0], int):
971 _set_lists(columns, values)
972 elif isinstance(args[0], (tuple, list)):
974 raise TypeError('Too many arguments')
975 _set_lists(args[0], args[1])
976 elif isinstance(args[0], dict):
977 columns = args[0].keys()
978 values = args[0].values()
979 _set_lists(columns, values)
981 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
983 ListStore = override(ListStore)
984 __all__.append('ListStore')
987 class TreeModelRow(object):
989 def __init__(self, model, iter_or_path):
990 if not isinstance(model, Gtk.TreeModel):
991 raise TypeError("expected Gtk.TreeModel, %s found" % type(model).__name__)
993 if isinstance(iter_or_path, Gtk.TreePath):
994 self.iter = model.get_iter(iter_or_path)
995 elif isinstance(iter_or_path, Gtk.TreeIter):
996 self.iter = iter_or_path
998 raise TypeError("expected Gtk.TreeIter or Gtk.TreePath, \
999 %s found" % type(iter_or_path).__name__)
1003 return self.model.get_path(self.iter)
1007 return self.get_next()
1011 return self.get_previous()
1015 return self.get_parent()
1018 next_iter = self.model.iter_next(self.iter)
1020 return TreeModelRow(self.model, next_iter)
1022 def get_previous(self):
1023 prev_iter = self.model.iter_previous(self.iter)
1025 return TreeModelRow(self.model, prev_iter)
1027 def get_parent(self):
1028 parent_iter = self.model.iter_parent(self.iter)
1030 return TreeModelRow(self.model, parent_iter)
1032 def __getitem__(self, key):
1033 if isinstance(key, int):
1034 if key >= self.model.get_n_columns():
1035 raise IndexError("column index is out of bounds: %d" % key)
1037 key = self._convert_negative_index(key)
1038 return self.model.get_value(self.iter, key)
1039 elif isinstance(key, slice):
1040 start, stop, step = key.indices(self.model.get_n_columns())
1042 for i in range(start, stop, step):
1043 alist.append(self.model.get_value(self.iter, i))
1046 raise TypeError("indices must be integers, not %s" % type(key).__name__)
1048 def __setitem__(self, key, value):
1049 if isinstance(key, int):
1050 if key >= self.model.get_n_columns():
1051 raise IndexError("column index is out of bounds: %d" % key)
1053 key = self._convert_negative_index(key)
1054 self.model.set_value(self.iter, key, value)
1055 elif isinstance(key, slice):
1056 start, stop, step = key.indices(self.model.get_n_columns())
1057 indexList = range(start, stop, step)
1058 if len(indexList) != len(value):
1060 "attempt to assign sequence of size %d to slice of size %d"
1061 % (len(value), len(indexList)))
1063 for i, v in enumerate(indexList):
1064 self.model.set_value(self.iter, v, value[i])
1066 raise TypeError("index must be an integer or slice, not %s" % type(key).__name__)
1068 def _convert_negative_index(self, index):
1069 new_index = self.model.get_n_columns() + index
1071 raise IndexError("column index is out of bounds: %d" % index)
1074 def iterchildren(self):
1075 child_iter = self.model.iter_children(self.iter)
1076 return TreeModelRowIter(self.model, child_iter)
1078 __all__.append('TreeModelRow')
1081 class TreeModelRowIter(object):
1083 def __init__(self, model, aiter):
1090 row = TreeModelRow(self.model, self.iter)
1091 self.iter = self.model.iter_next(self.iter)
1094 # alias for Python 2.x object protocol
1100 __all__.append('TreeModelRowIter')
1103 class TreePath(Gtk.TreePath):
1105 def __new__(cls, path=0):
1106 if isinstance(path, int):
1108 elif not isinstance(path, _basestring):
1109 path = ":".join(str(val) for val in path)
1112 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1114 return TreePath.new_from_string(path)
1116 raise TypeError("could not parse subscript '%s' as a tree path" % path)
1119 return self.to_string()
1121 def __lt__(self, other):
1122 return other is not None and self.compare(other) < 0
1124 def __le__(self, other):
1125 return other is not None and self.compare(other) <= 0
1127 def __eq__(self, other):
1128 return other is not None and self.compare(other) == 0
1130 def __ne__(self, other):
1131 return other is None or self.compare(other) != 0
1133 def __gt__(self, other):
1134 return other is None or self.compare(other) > 0
1136 def __ge__(self, other):
1137 return other is None or self.compare(other) >= 0
1140 return iter(self.get_indices())
1143 return self.get_depth()
1145 def __getitem__(self, index):
1146 return self.get_indices()[index]
1148 TreePath = override(TreePath)
1149 __all__.append('TreePath')
1152 class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
1153 def __init__(self, *column_types):
1154 Gtk.TreeStore.__init__(self)
1155 self.set_column_types(column_types)
1157 def _do_insert(self, parent, position, row):
1159 row, columns = self._convert_row(row)
1160 treeiter = self.insert_with_values(parent, position, columns, row)
1162 treeiter = Gtk.TreeStore.insert(self, parent, position)
1166 def append(self, parent, row=None):
1167 return self._do_insert(parent, -1, row)
1169 def prepend(self, parent, row=None):
1170 return self._do_insert(parent, 0, row)
1172 def insert(self, parent, position, row=None):
1173 return self._do_insert(parent, position, row)
1175 # FIXME: sends two signals; check if this can use an atomic
1176 # insert_with_valuesv()
1178 def insert_before(self, parent, sibling, row=None):
1179 treeiter = Gtk.TreeStore.insert_before(self, parent, sibling)
1182 self.set_row(treeiter, row)
1186 # FIXME: sends two signals; check if this can use an atomic
1187 # insert_with_valuesv()
1189 def insert_after(self, parent, sibling, row=None):
1190 treeiter = Gtk.TreeStore.insert_after(self, parent, sibling)
1193 self.set_row(treeiter, row)
1197 def set_value(self, treeiter, column, value):
1198 value = self._convert_value(column, value)
1199 Gtk.TreeStore.set_value(self, treeiter, column, value)
1201 def set(self, treeiter, *args):
1203 def _set_lists(columns, values):
1204 if len(columns) != len(values):
1205 raise TypeError('The number of columns do not match the number of values')
1206 for col_num, val in zip(columns, values):
1207 if not isinstance(col_num, int):
1208 raise TypeError('TypeError: Expected integer argument for column.')
1209 self.set_value(treeiter, col_num, val)
1212 if isinstance(args[0], int):
1215 _set_lists(columns, values)
1216 elif isinstance(args[0], (tuple, list)):
1218 raise TypeError('Too many arguments')
1219 _set_lists(args[0], args[1])
1220 elif isinstance(args[0], dict):
1221 columns = args[0].keys()
1222 values = args[0].values()
1223 _set_lists(columns, values)
1225 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}. No -1 termination is needed.')
1227 TreeStore = override(TreeStore)
1228 __all__.append('TreeStore')
1231 class TreeView(Gtk.TreeView, Container):
1232 __init__ = deprecated_init(Gtk.TreeView.__init__,
1233 arg_names=('model',),
1234 category=PyGTKDeprecationWarning)
1236 get_path_at_pos = strip_boolean_result(Gtk.TreeView.get_path_at_pos)
1237 get_visible_range = strip_boolean_result(Gtk.TreeView.get_visible_range)
1238 get_dest_row_at_pos = strip_boolean_result(Gtk.TreeView.get_dest_row_at_pos)
1240 def enable_model_drag_source(self, start_button_mask, targets, actions):
1241 target_entries = _construct_target_list(targets)
1242 super(TreeView, self).enable_model_drag_source(start_button_mask,
1246 def enable_model_drag_dest(self, targets, actions):
1247 target_entries = _construct_target_list(targets)
1248 super(TreeView, self).enable_model_drag_dest(target_entries,
1251 def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
1252 if not isinstance(path, Gtk.TreePath):
1253 path = TreePath(path)
1254 super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
1256 def set_cursor(self, path, column=None, start_editing=False):
1257 if not isinstance(path, Gtk.TreePath):
1258 path = TreePath(path)
1259 super(TreeView, self).set_cursor(path, column, start_editing)
1261 def get_cell_area(self, path, column=None):
1262 if not isinstance(path, Gtk.TreePath):
1263 path = TreePath(path)
1264 return super(TreeView, self).get_cell_area(path, column)
1266 def insert_column_with_attributes(self, position, title, cell, **kwargs):
1267 column = TreeViewColumn()
1268 column.set_title(title)
1269 column.pack_start(cell, False)
1270 self.insert_column(column, position)
1271 column.set_attributes(cell, **kwargs)
1273 TreeView = override(TreeView)
1274 __all__.append('TreeView')
1277 class TreeViewColumn(Gtk.TreeViewColumn):
1278 def __init__(self, title='',
1281 Gtk.TreeViewColumn.__init__(self, title=title)
1283 self.pack_start(cell_renderer, True)
1285 for (name, value) in attributes.items():
1286 self.add_attribute(cell_renderer, name, value)
1288 cell_get_position = strip_boolean_result(Gtk.TreeViewColumn.cell_get_position)
1290 def set_cell_data_func(self, cell_renderer, func, func_data=None):
1291 super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
1293 def set_attributes(self, cell_renderer, **attributes):
1294 Gtk.CellLayout.clear_attributes(self, cell_renderer)
1296 for (name, value) in attributes.items():
1297 Gtk.CellLayout.add_attribute(self, cell_renderer, name, value)
1300 TreeViewColumn = override(TreeViewColumn)
1301 __all__.append('TreeViewColumn')
1304 class TreeSelection(Gtk.TreeSelection):
1306 def select_path(self, path):
1307 if not isinstance(path, Gtk.TreePath):
1308 path = TreePath(path)
1309 super(TreeSelection, self).select_path(path)
1311 def get_selected(self):
1312 success, model, aiter = super(TreeSelection, self).get_selected()
1314 return (model, aiter)
1316 return (model, None)
1318 # for compatibility with PyGtk
1320 def get_selected_rows(self):
1321 rows, model = super(TreeSelection, self).get_selected_rows()
1322 return (model, rows)
1325 TreeSelection = override(TreeSelection)
1326 __all__.append('TreeSelection')
1329 class Button(Gtk.Button, Container):
1330 _init = deprecated_init(Gtk.Button.__init__,
1331 arg_names=('label', 'stock', 'use_stock', 'use_underline'),
1333 category=PyGTKDeprecationWarning,
1336 def __init__(self, *args, **kwargs):
1337 # Doubly deprecated initializer, the stock keyword is non-standard.
1338 # Simply give a warning that stock items are deprecated even though
1339 # we want to deprecate the non-standard keyword as well here from
1341 if 'stock' in kwargs and kwargs['stock']:
1342 warnings.warn('Stock items are deprecated. '
1343 'Please use: Gtk.Button.new_with_mnemonic(label)',
1344 PyGTKDeprecationWarning, stacklevel=2)
1345 new_kwargs = kwargs.copy()
1346 new_kwargs['label'] = new_kwargs['stock']
1347 new_kwargs['use_stock'] = True
1348 new_kwargs['use_underline'] = True
1349 del new_kwargs['stock']
1350 Gtk.Button.__init__(self, **new_kwargs)
1352 self._init(*args, **kwargs)
1354 Button = override(Button)
1355 __all__.append('Button')
1358 class LinkButton(Gtk.LinkButton):
1359 __init__ = deprecated_init(Gtk.LinkButton.__init__,
1360 arg_names=('uri', 'label'),
1361 category=PyGTKDeprecationWarning)
1363 LinkButton = override(LinkButton)
1364 __all__.append('LinkButton')
1367 class Label(Gtk.Label):
1368 __init__ = deprecated_init(Gtk.Label.__init__,
1369 arg_names=('label',),
1370 category=PyGTKDeprecationWarning)
1372 Label = override(Label)
1373 __all__.append('Label')
1376 class Adjustment(Gtk.Adjustment):
1377 _init = deprecated_init(Gtk.Adjustment.__init__,
1378 arg_names=('value', 'lower', 'upper',
1379 'step_increment', 'page_increment', 'page_size'),
1380 deprecated_aliases={'page_increment': 'page_incr',
1381 'step_increment': 'step_incr'},
1382 category=PyGTKDeprecationWarning,
1385 def __init__(self, *args, **kwargs):
1386 self._init(*args, **kwargs)
1388 # The value property is set between lower and (upper - page_size).
1389 # Just in case lower, upper or page_size was still 0 when value
1390 # was set, we set it again here.
1391 if 'value' in kwargs:
1392 self.set_value(kwargs['value'])
1394 Adjustment = override(Adjustment)
1395 __all__.append('Adjustment')
1398 class Table(Gtk.Table, Container):
1399 __init__ = deprecated_init(Gtk.Table.__init__,
1400 arg_names=('n_rows', 'n_columns', 'homogeneous'),
1401 deprecated_aliases={'n_rows': 'rows', 'n_columns': 'columns'},
1402 category=PyGTKDeprecationWarning)
1404 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):
1405 Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
1407 Table = override(Table)
1408 __all__.append('Table')
1411 class ScrolledWindow(Gtk.ScrolledWindow):
1412 __init__ = deprecated_init(Gtk.ScrolledWindow.__init__,
1413 arg_names=('hadjustment', 'vadjustment'),
1414 category=PyGTKDeprecationWarning)
1416 ScrolledWindow = override(ScrolledWindow)
1417 __all__.append('ScrolledWindow')
1420 class HScrollbar(Gtk.HScrollbar):
1421 __init__ = deprecated_init(Gtk.HScrollbar.__init__,
1422 arg_names=('adjustment',),
1423 category=PyGTKDeprecationWarning)
1425 HScrollbar = override(HScrollbar)
1426 __all__.append('HScrollbar')
1429 class VScrollbar(Gtk.VScrollbar):
1430 __init__ = deprecated_init(Gtk.VScrollbar.__init__,
1431 arg_names=('adjustment',),
1432 category=PyGTKDeprecationWarning)
1434 VScrollbar = override(VScrollbar)
1435 __all__.append('VScrollbar')
1438 class Paned(Gtk.Paned):
1439 def pack1(self, child, resize=False, shrink=True):
1440 super(Paned, self).pack1(child, resize, shrink)
1442 def pack2(self, child, resize=True, shrink=True):
1443 super(Paned, self).pack2(child, resize, shrink)
1445 Paned = override(Paned)
1446 __all__.append('Paned')
1449 class Arrow(Gtk.Arrow):
1450 __init__ = deprecated_init(Gtk.Arrow.__init__,
1451 arg_names=('arrow_type', 'shadow_type'),
1452 category=PyGTKDeprecationWarning)
1454 Arrow = override(Arrow)
1455 __all__.append('Arrow')
1458 class IconSet(Gtk.IconSet):
1459 def __new__(cls, pixbuf=None):
1460 if pixbuf is not None:
1461 warnings.warn('Gtk.IconSet(pixbuf) has been deprecated. Please use: '
1462 'Gtk.IconSet.new_from_pixbuf(pixbuf)',
1463 PyGTKDeprecationWarning, stacklevel=2)
1464 iconset = Gtk.IconSet.new_from_pixbuf(pixbuf)
1466 iconset = Gtk.IconSet.__new__(cls)
1469 IconSet = override(IconSet)
1470 __all__.append('IconSet')
1473 class Viewport(Gtk.Viewport):
1474 __init__ = deprecated_init(Gtk.Viewport.__init__,
1475 arg_names=('hadjustment', 'vadjustment'),
1476 category=PyGTKDeprecationWarning)
1478 Viewport = override(Viewport)
1479 __all__.append('Viewport')
1482 class TreeModelFilter(Gtk.TreeModelFilter):
1483 def set_visible_func(self, func, data=None):
1484 super(TreeModelFilter, self).set_visible_func(func, data)
1486 def set_value(self, iter, column, value):
1487 # Delegate to child model
1488 iter = self.convert_iter_to_child_iter(iter)
1489 self.get_model().set_value(iter, column, value)
1491 TreeModelFilter = override(TreeModelFilter)
1492 __all__.append('TreeModelFilter')
1494 if Gtk._version != '2.0':
1495 class Menu(Gtk.Menu):
1496 def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
1497 self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
1498 Menu = override(Menu)
1499 __all__.append('Menu')
1501 _Gtk_main_quit = Gtk.main_quit
1504 @override(Gtk.main_quit)
1505 def main_quit(*args):
1508 stock_lookup = strip_boolean_result(Gtk.stock_lookup)
1509 __all__.append('stock_lookup')
1511 initialized, argv = Gtk.init_check(sys.argv)
1512 sys.argv = list(argv)