class Diary(ProxyDelegate): def __init__(self): self.entries = ObjectList([ Column("title", width=120, sorted=True), Column("period", width=80), Column("text", expand=True, visible=False) ]) ProxyDelegate.__init__(self, DiaryEntry(), ['title', 'period', 'text', 'chars', 'words'], gladefile="diary2.ui", delete_handler=self.quit_if_last) self.hbox.pack_start(self.entries, True, True, 0) self.entries.show() self.entries.grab_focus() self.set_editable(False) def set_editable(self, editable): self.leftbox.set_sensitive(editable) self.remove.set_sensitive(editable) def proxy_updated(self, *args): self.entries.update(self.model) def on_add__clicked(self, button): entry = DiaryEntry() entry.title = 'Untitled' self.entries.append(entry, select=True) self.set_editable(True) def on_remove__clicked(self, button): entry = self.entries.get_selected() if entry: self.entries.remove(entry, select=True) self.set_editable(len(self.entries) >= 1) if not len(self.entries): self.set_model(None) def on_text__content_changed(self, text): self.proxy.update_many(("chars", "words")) self.entries.update(self.model) def on_entries__selection_changed(self, entries, instance): if instance: self.set_model(instance) self.title.grab_focus()
class Diary(ProxyDelegate): def __init__(self): self.entries = ObjectList([Column("title", width=120, sorted=True), Column("period", width=80), Column("text", expand=True, visible=False)]) ProxyDelegate.__init__(self, DiaryEntry(), ['title', 'period', 'text', 'chars', 'words'], gladefile="diary2.ui", delete_handler=self.quit_if_last) self.hbox.pack_start(self.entries, True, True, 0) self.entries.show() self.entries.grab_focus() self.set_editable(False) def set_editable(self, editable): self.leftbox.set_sensitive(editable) self.remove.set_sensitive(editable) def proxy_updated(self, *args): self.entries.update(self.model) def on_add__clicked(self, button): entry = DiaryEntry() entry.title = 'Untitled' self.entries.append(entry, select=True) self.set_editable(True) def on_remove__clicked(self, button): entry = self.entries.get_selected() if entry: self.entries.remove(entry, select=True) self.set_editable(len(self.entries) >= 1) if not len(self.entries): self.set_model(None) def on_text__content_changed(self, text): self.proxy.update_many(("chars", "words")) self.entries.update(self.model) def on_entries__selection_changed(self, entries, instance): if instance: self.set_model(instance) self.title.grab_focus()
class DataTests(unittest.TestCase): """In all this tests we use the same configuration for a list""" def setUp(self): self.win = gtk.Window() self.win.set_default_size(400, 400) self.list = ObjectList([Column('name'), Column('age')]) self.win.add(self.list) refresh_gui() def tearDown(self): self.win.destroy() del self.win def testAddingOneInstance(self): # we should have two columns now self.assertEqual(2, len(self.list.get_columns())) person = Person('henrique', 21) self.list.append(person) refresh_gui() # usually you don't use the model directly, but tests are all about # breaking APIs, right? self.assertEqual(self.list[0], person) self.assertEqual(self.list[0].name, 'henrique') self.assertEqual(self.list[0].age, 21) # we still have to columns, right? self.assertEqual(2, len(self.list.get_columns())) def testAddingAObjectList(self): global persons self.list.add_list(persons) refresh_gui() self.assertEqual(len(self.list), len(persons)) def testAddingABunchOfInstances(self): global persons for person in persons: self.list.append(person) refresh_gui() self.assertEqual(len(self.list), len(persons)) def testRemovingOneInstance(self): global persons self.list.add_list(persons) refresh_gui() # we are going to remove Kiko person = persons[2] self.list.remove(person) self.assertEqual(len(self.list), len(persons) - 1) # now let's remove something that is not on the list #new_person = Person('Evandro', 24) #self.assertRaises(ValueError, self.list.remove, new_person) # note that even a new person with the same values as a person # in the list is not considered to be in the list #existing_person = Person('Gustavo', 25) #self.assertRaises(ValueError, self.list.remove, # existing_person) def testClearObjectList(self): global persons self.list.add_list(persons) refresh_gui() self.list.clear() self.assertEqual(len(self.list), 0) def testUpdatingOneInstance(self): global persons self.list.add_list(persons) refresh_gui() persons[0].age = 29 self.list.update(persons[0]) refresh_gui() # Do we have the same number of instances that we had before ? self.assertEqual(len(self.list), len(persons)) # Trying to find our updated instance in the list self.assertEqual(self.list[0].age, 29) # let's be evil new_person = Person('Nando', 32) self.assertRaises(ValueError, self.list.update, new_person) def testContains(self): global persons self.list.add_list(persons) self.assertEqual(persons[0] in self.list, True) new_person = Person('Nando', 32) self.assertEqual(new_person in self.list, False) def testSelect(self): first = persons[0] self.list.add_list(persons) self.list.select(first) self.assertEqual(self.list.get_selected(), first) self.list.remove(first) self.assertRaises(ValueError, self.list.select, first)
class ListContainer(gtk.HBox): """A ListContainer is an L{ObjectList} with buttons to be able to modify the content of the list. Depending on the list_mode, @see L{set_list_mode} you will have add, remove and edit buttons. Signals ======= - B{add-item} (returns item): - emitted when the add button is clicked, you're expected to return an object here - B{remove-item} (item, returns bool): - emitted when removing an item, you can block the removal from the list by returning False - B{edit-item} (item): - emitted when editing an item you can block the update afterwards by returning False @ivar add_button: add button @type add_button: L{gtk.Button} @ivar remove_button: remove button @type remove_button: L{gtk.Button} @ivar edit_button: edit button @type edit_button: L{gtk.Button} """ gsignal('add-item', retval=object) gsignal('remove-item', object, retval=bool) gsignal('edit-item', object, retval=bool) gsignal('selection-changed', object) def __init__(self, columns, orientation=gtk.ORIENTATION_VERTICAL): """ Create a new ListContainer object. @param columns: columns for the L{kiwi.ui.objectlist.ObjectList} @type columns: a list of L{kiwi.ui.objectlist.Columns} @param orientation: the position where the buttons will be placed: at the right (vertically) or at the bottom (horizontally) of the list. Defaults to the right of the list. @type: gtk.ORIENTATION_HORIZONTAL or gtk.ORIENTATION_VERTICAL """ self._list_type = None gtk.HBox.__init__(self) self._orientation = orientation self._create_ui(columns) self.set_list_type(ListType.NORMAL) # Private API def _create_ui(self, columns): self.list = ObjectList(columns) self.list.connect('selection-changed', self._on_list__selection_changed) self.list.connect('row-activated', self._on_list__row_activated) self.add_button = gtk.Button(stock=gtk.STOCK_ADD) self.add_button.connect('clicked', self._on_add_button__clicked) self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE) self.remove_button.set_sensitive(False) self.remove_button.connect('clicked', self._on_remove_button__clicked) self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT) self.edit_button.set_sensitive(False) self.edit_button.connect('clicked', self._on_edit_button__clicked) self._vbox = gtk.VBox(spacing=6) if self._orientation == gtk.ORIENTATION_VERTICAL: self.pack_start(self.list) self.list.show() self._add_buttons_to_box(self._vbox) self._pack_vbox() elif self._orientation == gtk.ORIENTATION_HORIZONTAL: self._vbox.pack_start(self.list) self.list.show() hbox = gtk.HBox(spacing=6) self._add_buttons_to_box(hbox) self._vbox.pack_start(hbox, expand=False) hbox.show() self._pack_vbox() else: raise TypeError( "buttons_orientation must be gtk.ORIENTATION_VERTICAL " " or gtk.ORIENTATION_HORIZONTAL") def _add_buttons_to_box(self, box): box.pack_start(self.add_button, expand=False) box.pack_start(self.remove_button, expand=False) box.pack_start(self.edit_button, expand=False) def _pack_vbox(self): self.pack_start(self._vbox, expand=False, padding=6) self._vbox.show() def _set_child_packing(self, padding): expand = self._orientation == gtk.ORIENTATION_HORIZONTAL self.set_child_packing(self._vbox, expand, True, padding, gtk.PACK_START) def _add_item(self): retval = self.emit('add-item') if retval is None: return elif isinstance(retval, NotImplementedError): raise retval self.list.append(retval) def _remove_item(self, item): retval = self.emit('remove-item', item) if retval: self.list.remove(item) def _edit_item(self, item): retval = self.emit('edit-item', item) if retval: self.list.update(item) # Public API def add_item(self, item): """Appends an item to the list @param item: item to append """ self.list.append(item) def add_items(self, items): """Appends a list of items to the list @param items: items to add @type items: a sequence of items """ self.list.extend(items) def remove_item(self, item): """Removes an item from the list @param item: item to remove """ self.list.remove(item) def update_item(self, item): """Updates an item in the list. You should call this if you change the object @param item: item to update """ self.list.update(item) def default_remove(self, item): """Asks the user confirmation for removal of an item. @param item: a description of the item that will be removed @returns: True if the user confirm the removal, False otherwise """ response = yesno(_('Do you want to remove %s ?') % (quote(str(item)), ), parent=None, default=gtk.RESPONSE_OK, buttons=((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL), (gtk.STOCK_REMOVE, gtk.RESPONSE_OK))) return response == gtk.RESPONSE_OK def set_list_type(self, list_type): """Sets the kind of list type. @param list_type: """ if not isinstance(list_type, ListType): raise TypeError("list_type must be a ListType enum") self.add_button.set_property('visible', (list_type != ListType.READONLY and list_type != ListType.REMOVEONLY and list_type != ListType.UNADDABLE)) self.remove_button.set_property( 'visible', (list_type != ListType.READONLY and list_type != ListType.UNREMOVABLE)) self.edit_button.set_property('visible', (list_type != ListType.READONLY and list_type != ListType.UNEDITABLE and list_type != ListType.REMOVEONLY)) if list_type in [ListType.READONLY, ListType.REMOVEONLY]: padding = 0 else: padding = 6 self._set_child_packing(padding) self._list_type = list_type def clear(self): """Removes all the items in the list""" self.list.clear() # Callbacks def _on_list__selection_changed(self, list, selection): object_selected = selection is not None self.remove_button.set_sensitive(object_selected) self.edit_button.set_sensitive(object_selected) self.emit('selection-changed', selection) def _on_list__row_activated(self, list, item): if (self._list_type != ListType.READONLY and self._list_type != ListType.UNEDITABLE): self._edit_item(item) def _on_add_button__clicked(self, button): self._add_item() def _on_remove_button__clicked(self, button): self._remove_item(self.list.get_selected()) def _on_edit_button__clicked(self, button): self._edit_item(self.list.get_selected())
class ListContainer(gtk.HBox): """A ListContainer is an L{ObjectList} with buttons to be able to modify the content of the list. Depending on the list_mode, @see L{set_list_mode} you will have add, remove and edit buttons. Signals ======= - B{add-item} (returns item): - emitted when the add button is clicked, you're expected to return an object here - B{remove-item} (item, returns bool): - emitted when removing an item, you can block the removal from the list by returning False - B{edit-item} (item): - emitted when editing an item you can block the update afterwards by returning False @ivar add_button: add button @type add_button: L{gtk.Button} @ivar remove_button: remove button @type remove_button: L{gtk.Button} @ivar edit_button: edit button @type edit_button: L{gtk.Button} """ gsignal('add-item', retval=object) gsignal('remove-item', object, retval=bool) gsignal('edit-item', object, retval=bool) gsignal('selection-changed', object) def __init__(self, columns, orientation=gtk.ORIENTATION_VERTICAL): """ Create a new ListContainer object. @param columns: columns for the L{kiwi.ui.objectlist.ObjectList} @type columns: a list of L{kiwi.ui.objectlist.Columns} @param orientation: the position where the buttons will be placed: at the right (vertically) or at the bottom (horizontally) of the list. Defaults to the right of the list. @type: gtk.ORIENTATION_HORIZONTAL or gtk.ORIENTATION_VERTICAL """ self._list_type = None gtk.HBox.__init__(self) self._orientation = orientation self._create_ui(columns) self.set_list_type(ListType.NORMAL) # Private API def _create_ui(self, columns): self.list = ObjectList(columns) self.list.connect('selection-changed', self._on_list__selection_changed) self.list.connect('row-activated', self._on_list__row_activated) self.add_button = gtk.Button(stock=gtk.STOCK_ADD) self.add_button.connect('clicked', self._on_add_button__clicked) self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE) self.remove_button.set_sensitive(False) self.remove_button.connect('clicked', self._on_remove_button__clicked) self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT) self.edit_button.set_sensitive(False) self.edit_button.connect('clicked', self._on_edit_button__clicked) self._vbox = gtk.VBox(spacing=6) if self._orientation == gtk.ORIENTATION_VERTICAL: self.pack_start(self.list) self.list.show() self._add_buttons_to_box(self._vbox) self._pack_vbox() elif self._orientation == gtk.ORIENTATION_HORIZONTAL: self._vbox.pack_start(self.list) self.list.show() hbox = gtk.HBox(spacing=6) self._add_buttons_to_box(hbox) self._vbox.pack_start(hbox, expand=False) hbox.show() self._pack_vbox() else: raise TypeError( "buttons_orientation must be gtk.ORIENTATION_VERTICAL " " or gtk.ORIENTATION_HORIZONTAL") def _add_buttons_to_box(self, box): box.pack_start(self.add_button, expand=False) box.pack_start(self.remove_button, expand=False) box.pack_start(self.edit_button, expand=False) def _pack_vbox(self): self.pack_start(self._vbox, expand=False, padding=6) self._vbox.show() def _set_child_packing(self, padding): expand = self._orientation == gtk.ORIENTATION_HORIZONTAL self.set_child_packing(self._vbox, expand, True, padding, gtk.PACK_START) def _add_item(self): retval = self.emit('add-item') if retval is None: return elif isinstance(retval, NotImplementedError): raise retval self.list.append(retval) def _remove_item(self, item): retval = self.emit('remove-item', item) if retval: self.list.remove(item) def _edit_item(self, item): retval = self.emit('edit-item', item) if retval: self.list.update(item) # Public API def add_item(self, item): """Appends an item to the list @param item: item to append """ self.list.append(item) def add_items(self, items): """Appends a list of items to the list @param items: items to add @type items: a sequence of items """ self.list.extend(items) def remove_item(self, item): """Removes an item from the list @param item: item to remove """ self.list.remove(item) def update_item(self, item): """Updates an item in the list. You should call this if you change the object @param item: item to update """ self.list.update(item) def default_remove(self, item): """Asks the user confirmation for removal of an item. @param item: a description of the item that will be removed @returns: True if the user confirm the removal, False otherwise """ response = yesno(_('Do you want to remove %s ?') % (quote(str(item)),), parent=None, default=gtk.RESPONSE_OK, buttons=((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL), (gtk.STOCK_REMOVE, gtk.RESPONSE_OK))) return response == gtk.RESPONSE_OK def set_list_type(self, list_type): """Sets the kind of list type. @param list_type: """ if not isinstance(list_type, ListType): raise TypeError("list_type must be a ListType enum") self.add_button.set_property( 'visible', (list_type != ListType.READONLY and list_type != ListType.REMOVEONLY and list_type != ListType.UNADDABLE)) self.remove_button.set_property( 'visible', (list_type != ListType.READONLY and list_type != ListType.UNREMOVABLE)) self.edit_button.set_property( 'visible', (list_type != ListType.READONLY and list_type != ListType.UNEDITABLE and list_type != ListType.REMOVEONLY)) if list_type in [ListType.READONLY, ListType.REMOVEONLY]: padding = 0 else: padding = 6 self._set_child_packing(padding) self._list_type = list_type def clear(self): """Removes all the items in the list""" self.list.clear() # Callbacks def _on_list__selection_changed(self, list, selection): object_selected = selection is not None self.remove_button.set_sensitive(object_selected) self.edit_button.set_sensitive(object_selected) self.emit('selection-changed', selection) def _on_list__row_activated(self, list, item): if (self._list_type != ListType.READONLY and self._list_type != ListType.UNEDITABLE): self._edit_item(item) def _on_add_button__clicked(self, button): self._add_item() def _on_remove_button__clicked(self, button): self._remove_item(self.list.get_selected()) def _on_edit_button__clicked(self, button): self._edit_item(self.list.get_selected())
class ChecklistView(PidaView): key = 'checklist.view' icon_name = 'gtk-todo' label_text = _('Check list') def create_ui(self): self._vbox = gtk.VBox(spacing=3) self._vbox.set_border_width(3) self.create_toolbar() self.create_newitem() self.create_list() self.add_main_widget(self._vbox) self._vbox.show_all() def create_tab_label(self, icon_name, text): if None in [icon_name, text]: return None label = gtk.Label(text) b_factory = gtk.HBox b = b_factory(spacing=2) icon = gtk.image_new_from_stock(icon_name, gtk.ICON_SIZE_MENU) b.pack_start(icon) b.pack_start(label) b.show_all() return b def create_list(self): self._list = ObjectList([ Column('done', title=_('Done'), data_type=bool, editable=True), Column('title', title=_('Title'), data_type=str, editable=True, expand=True), Column('priority', title=_('Priority'), data_type=ChecklistStatus, editable=True) ]) self._list.connect('cell-edited', self._on_item_edit) self._list.connect('selection-changed', self._on_item_selected) self._list.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self._vbox.add(self._list) self._sort_combo = AttrSortCombo(self._list, [ ('done', _('Done')), ('title', _('Title')), ('priority', _('Priority')), ], 'title') self._vbox.pack_start(self._sort_combo, expand=False) self._list.show_all() self._sort_combo.show_all() def create_newitem(self): self._hbox = gtk.HBox(spacing=3) self._newitem_title = gtk.Entry() self._newitem_title.connect('changed', self._on_newitem_changed) self._newitem_ok = gtk.Button(stock=gtk.STOCK_ADD) self._newitem_ok.connect('clicked', self._on_item_add) self._newitem_ok.set_sensitive(False) self._hbox.pack_start(self._newitem_title, expand=True) self._hbox.pack_start(self._newitem_ok, expand=False) self._vbox.pack_start(self._hbox, expand=False) self._hbox.show_all() def create_toolbar(self): self._uim = gtk.UIManager() self._uim.insert_action_group(self.svc.get_action_group(), 0) uim_data = pkgutil.get_data(__name__, 'uidef/checklist-toolbar.xml') self._uim.add_ui_from_string(uim_data) self._uim.ensure_update() self._toolbar = self._uim.get_toplevels('toolbar')[0] self._toolbar.set_style(gtk.TOOLBAR_ICONS) self._toolbar.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR) self._vbox.pack_start(self._toolbar, expand=False) self.svc.get_action('checklist_del').set_sensitive(False) self._toolbar.show_all() def add_item(self, item): self._list.append(item, select=True) self.svc.save() def update_item(self, item): self._list.update(item) self.svc.save() def remove_item(self, item): self._list.remove(item) self.svc.save() def clear(self): self._list.clear() def _on_item_selected(self, olist, item): self.svc.get_action('checklist_del').set_sensitive(item is not None) self.svc.set_current(item) def _on_item_edit(self, olist, item, value): self.svc.save() def _on_item_add(self, w): title = self._newitem_title.get_text() self.svc.add_item(ChecklistItem(title=title)) self._newitem_title.set_text('') def _on_newitem_changed(self, w): self._newitem_ok.set_sensitive(self._newitem_title.get_text() != '') def can_be_closed(self): self.svc.get_action('show_checklist').set_active(False)
class DataTests(unittest.TestCase): """In all this tests we use the same configuration for a list""" def setUp(self): self.win = Gtk.Window() self.win.set_default_size(400, 400) self.list = ObjectList([Column('name'), Column('age')]) self.win.add(self.list) refresh_gui() def tearDown(self): self.win.destroy() del self.win def testAddingOneInstance(self): # we should have two columns now self.assertEqual(2, len(self.list.get_columns())) person = Person('henrique', 21) self.list.append(person) refresh_gui() # usually you don't use the model directly, but tests are all about # breaking APIs, right? self.assertEqual(self.list[0], person) self.assertEqual(self.list[0].name, 'henrique') self.assertEqual(self.list[0].age, 21) # we still have to columns, right? self.assertEqual(2, len(self.list.get_columns())) def testAddingAObjectList(self): global persons self.list.add_list(persons) refresh_gui() self.assertEqual(len(self.list), len(persons)) def testAddingABunchOfInstances(self): global persons for person in persons: self.list.append(person) refresh_gui() self.assertEqual(len(self.list), len(persons)) def testRemovingOneInstance(self): global persons self.list.add_list(persons) refresh_gui() # we are going to remove Kiko person = persons[2] self.list.remove(person) self.assertEqual(len(self.list), len(persons) - 1) # now let's remove something that is not on the list #new_person = Person('Evandro', 24) #self.assertRaises(ValueError, self.list.remove, new_person) # note that even a new person with the same values as a person # in the list is not considered to be in the list #existing_person = Person('Gustavo', 25) #self.assertRaises(ValueError, self.list.remove, # existing_person) def testClearObjectList(self): global persons self.list.add_list(persons) refresh_gui() self.list.clear() self.assertEqual(len(self.list), 0) def testUpdatingOneInstance(self): global persons self.list.add_list(persons) refresh_gui() persons[0].age = 29 self.list.update(persons[0]) refresh_gui() # Do we have the same number of instances that we had before ? self.assertEqual(len(self.list), len(persons)) # Trying to find our updated instance in the list self.assertEqual(self.list[0].age, 29) # let's be evil new_person = Person('Nando', 32) self.assertRaises(ValueError, self.list.update, new_person) def testContains(self): global persons self.list.add_list(persons) self.assertEqual(persons[0] in self.list, True) new_person = Person('Nando', 32) self.assertEqual(new_person in self.list, False) def testSelect(self): first = persons[0] self.list.add_list(persons) self.list.select(first) self.assertEqual(self.list.get_selected(), first) self.list.remove(first) self.assertRaises(ValueError, self.list.select, first)
class ListContainer(Gtk.Box): """A ListContainer is an :class:`ObjectList` with buttons to be able to modify the content of the list. Depending on the list_mode, @see :class:`set_list_mode` you will have add, remove and edit buttons. Signals ======= - B{add-item} (returns item): - emitted when the add button is clicked, you're expected to return an object here - B{remove-item} (item, returns bool): - emitted when removing an item, you can block the removal from the list by returning False - B{edit-item} (item): - emitted when editing an item you can block the update afterwards by returning False :ivar add_button: add button :type add_button: :class:`Gtk.Button` :ivar remove_button: remove button :type remove_button: :class:`Gtk.Button` :ivar edit_button: edit button :type edit_button: :class:`Gtk.Button` """ __gtype_name__ = 'ListContainer' gsignal('add-item', retval=object) gsignal('remove-item', object, retval=bool) gsignal('edit-item', object, retval=bool) gsignal('selection-changed', object) def __init__(self, columns, orientation=Gtk.Orientation.VERTICAL): """ Create a new ListContainer object. :param columns: columns for the :class:`kiwi.ui.objectlist.ObjectList` :type columns: a list of :class:`kiwi.ui.objectlist.Columns` :param orientation: the position where the buttons will be placed: at the right (vertically) or at the bottom (horizontally) of the list. Defaults to the right of the list. :type: Gtk.Orientation.HORIZONTAL or Gtk.Orientation.VERTICAL """ self._list_type = None super(ListContainer, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self._orientation = orientation self._create_ui(columns) self.set_list_type(ListType.NORMAL) # Private API def _create_ui(self, columns): self.list = ObjectList(columns) self.list.connect('selection-changed', self._on_list__selection_changed) self.list.connect('row-activated', self._on_list__row_activated) self.add_button = Gtk.Button(stock=Gtk.STOCK_ADD) self.add_button.connect('clicked', self._on_add_button__clicked) self.remove_button = Gtk.Button(stock=Gtk.STOCK_REMOVE) self.remove_button.set_sensitive(False) self.remove_button.connect('clicked', self._on_remove_button__clicked) self.edit_button = Gtk.Button(stock=Gtk.STOCK_EDIT) self.edit_button.set_sensitive(False) self.edit_button.connect('clicked', self._on_edit_button__clicked) self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) if self._orientation == Gtk.Orientation.VERTICAL: self.pack_start(self.list, True, True, 0) self.list.show() self._add_buttons_to_box(self._vbox) self._pack_vbox() elif self._orientation == Gtk.Orientation.HORIZONTAL: self._vbox.pack_start(self.list, True, True, 0) self.list.show() hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) self._add_buttons_to_box(hbox) self._vbox.pack_start(hbox, False, True, 0) hbox.show() self._pack_vbox() else: raise TypeError( "buttons_orientation must be Gtk.Orientation.VERTICAL " " or Gtk.Orientation.HORIZONTAL") def _add_buttons_to_box(self, box): box.pack_start(self.add_button, False, True, 0) box.pack_start(self.remove_button, False, True, 0) box.pack_start(self.edit_button, False, True, 0) def _pack_vbox(self): self.pack_start(self._vbox, False, True, 6) self._vbox.show() def _set_child_packing(self, padding): expand = self._orientation == Gtk.Orientation.HORIZONTAL self.set_child_packing(self._vbox, expand, True, padding, Gtk.PackType.START) def _add_item(self): retval = self.emit('add-item') if retval is None: return elif isinstance(retval, NotImplementedError): raise retval self.list.append(retval) self.list.refresh() def _remove_item(self, item): retval = self.emit('remove-item', item) if retval: self.list.remove(item) def _edit_item(self, item): retval = self.emit('edit-item', item) if retval: self.list.update(item) # Public API def add_item(self, item): """Appends an item to the list :param item: item to append """ self.list.append(item) def add_items(self, items): """Appends a list of items to the list :param items: items to add :type items: a sequence of items """ self.list.extend(items) def remove_item(self, item): """Removes an item from the list :param item: item to remove """ self.list.remove(item) def update_item(self, item): """Updates an item in the list. You should call this if you change the object :param item: item to update """ self.list.update(item) def default_remove(self, item): """Asks the user confirmation for removal of an item. :param item: a description of the item that will be removed :returns: True if the user confirm the removal, False otherwise """ response = yesno(_('Do you want to remove %s ?') % (GLib.markup_escape_text(str(item)),), parent=None, default=Gtk.ResponseType.OK, buttons=((Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL), (Gtk.STOCK_REMOVE, Gtk.ResponseType.OK))) return response == Gtk.ResponseType.OK def set_list_type(self, list_type): """Sets the kind of list type. :param list_type: """ if not isinstance(list_type, ListType): raise TypeError("list_type must be a ListType enum") self.add_button.set_property( 'visible', list_type not in [ListType.READONLY, ListType.REMOVEONLY, ListType.UNADDABLE]) self.remove_button.set_property( 'visible', list_type not in [ListType.READONLY, ListType.ADDONLY, ListType.UNREMOVABLE]) self.edit_button.set_property( 'visible', list_type not in [ListType.READONLY, ListType.ADDONLY, ListType.UNEDITABLE, ListType.REMOVEONLY]) if list_type in [ListType.READONLY, ListType.REMOVEONLY]: padding = 0 else: padding = 6 self._set_child_packing(padding) self._list_type = list_type def clear(self): """Removes all the items in the list""" self.list.clear() # Callbacks def _on_list__selection_changed(self, list, selection): object_selected = selection is not None self.remove_button.set_sensitive(object_selected) self.edit_button.set_sensitive(object_selected) self.emit('selection-changed', selection) def _on_list__row_activated(self, list, item): if self._list_type not in [ListType.READONLY, ListType.ADDONLY, ListType.UNEDITABLE]: self._edit_item(item) def _on_add_button__clicked(self, button): self._add_item() def _on_remove_button__clicked(self, button): self._remove_item(self.list.get_selected()) def _on_edit_button__clicked(self, button): self._edit_item(self.list.get_selected())
class ChecklistView(PidaView): key = 'checklist.view' icon_name = 'gtk-todo' label_text = _('Check list') def create_ui(self): self._vbox = gtk.VBox(spacing=3) self._vbox.set_border_width(3) self.create_toolbar() self.create_newitem() self.create_list() self.add_main_widget(self._vbox) self._vbox.show_all() def create_tab_label(self, icon_name, text): if None in [icon_name, text]: return None label = gtk.Label(text) b_factory = gtk.HBox b = b_factory(spacing=2) icon = gtk.image_new_from_stock(icon_name, gtk.ICON_SIZE_MENU) b.pack_start(icon) b.pack_start(label) b.show_all() return b def create_list(self): self._list = ObjectList([ Column('done', title=_('Done'), data_type=bool, editable=True), Column('title', title=_('Title'), data_type=str, editable=True, expand=True), Column('priority', title=_('Priority'), data_type=ChecklistStatus, editable=True) ]) self._list.connect('cell-edited', self._on_item_edit) self._list.connect('selection-changed', self._on_item_selected) self._list.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self._vbox.add(self._list) self._sort_combo = AttrSortCombo(self._list, [ ('done', _('Done')), ('title', _('Title')), ('priority', _('Priority')), ], 'title') self._vbox.pack_start(self._sort_combo, expand=False) self._list.show_all() self._sort_combo.show_all() def create_newitem(self): self._hbox = gtk.HBox(spacing=3) self._newitem_title = gtk.Entry() self._newitem_title.connect('changed', self._on_newitem_changed) self._newitem_ok = gtk.Button(stock=gtk.STOCK_ADD) self._newitem_ok.connect('clicked', self._on_item_add) self._newitem_ok.set_sensitive(False) self._hbox.pack_start(self._newitem_title, expand=True) self._hbox.pack_start(self._newitem_ok, expand=False) self._vbox.pack_start(self._hbox, expand=False) self._hbox.show_all() def create_toolbar(self): self._uim = gtk.UIManager() self._uim.insert_action_group(self.svc.get_action_group(), 0) uim_data = pkgutil.get_data(__name__, 'uidef/checklist-toolbar.xml') self._uim.add_ui_from_string(uim_data) self._uim.ensure_update() self._toolbar = self._uim.get_toplevels('toolbar')[0] self._toolbar.set_style(gtk.TOOLBAR_ICONS) self._toolbar.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR) self._vbox.pack_start(self._toolbar, expand=False) self.svc.get_action('checklist_del').set_sensitive(False) self._toolbar.show_all() def add_item(self, item): self._list.append(item, select=True) self.svc.save() def update_item(self, item): self._list.update(item) self.svc.save() def remove_item(self, item): self._list.remove(item) self.svc.save() def clear(self): self._list.clear() def _on_item_selected(self, olist, item): self.svc.get_action('checklist_del').set_sensitive(item is not None) self.svc.set_current(item) def _on_item_edit(self, olist, item, value): self.svc.save() def _on_item_add(self, w): title = self._newitem_title.get_text() self.svc.add_item(ChecklistItem(title=title)) self._newitem_title.set_text('') def _on_newitem_changed(self, w): self._newitem_ok.set_sensitive(self._newitem_title.get_text() != '') def can_be_closed(self): self.svc.get_action('show_checklist').set_active(False)
class ListContainer(Gtk.Box): """A ListContainer is an :class:`ObjectList` with buttons to be able to modify the content of the list. Depending on the list_mode, @see :class:`set_list_mode` you will have add, remove and edit buttons. Signals ======= - B{add-item} (returns item): - emitted when the add button is clicked, you're expected to return an object here - B{remove-item} (item, returns bool): - emitted when removing an item, you can block the removal from the list by returning False - B{edit-item} (item): - emitted when editing an item you can block the update afterwards by returning False :ivar add_button: add button :type add_button: :class:`Gtk.Button` :ivar remove_button: remove button :type remove_button: :class:`Gtk.Button` :ivar edit_button: edit button :type edit_button: :class:`Gtk.Button` """ __gtype_name__ = 'ListContainer' gsignal('add-item', retval=object) gsignal('remove-item', object, retval=bool) gsignal('edit-item', object, retval=bool) gsignal('selection-changed', object) def __init__(self, columns, orientation=Gtk.Orientation.VERTICAL): """ Create a new ListContainer object. :param columns: columns for the :class:`kiwi.ui.objectlist.ObjectList` :type columns: a list of :class:`kiwi.ui.objectlist.Columns` :param orientation: the position where the buttons will be placed: at the right (vertically) or at the bottom (horizontally) of the list. Defaults to the right of the list. :type: Gtk.Orientation.HORIZONTAL or Gtk.Orientation.VERTICAL """ self._list_type = None super(ListContainer, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self._orientation = orientation self._create_ui(columns) self.set_list_type(ListType.NORMAL) # Private API def _create_ui(self, columns): self.list = ObjectList(columns) self.list.connect('selection-changed', self._on_list__selection_changed) self.list.connect('row-activated', self._on_list__row_activated) self.add_button = Gtk.Button(stock=Gtk.STOCK_ADD) self.add_button.connect('clicked', self._on_add_button__clicked) self.remove_button = Gtk.Button(stock=Gtk.STOCK_REMOVE) self.remove_button.set_sensitive(False) self.remove_button.connect('clicked', self._on_remove_button__clicked) self.edit_button = Gtk.Button(stock=Gtk.STOCK_EDIT) self.edit_button.set_sensitive(False) self.edit_button.connect('clicked', self._on_edit_button__clicked) self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) if self._orientation == Gtk.Orientation.VERTICAL: self.pack_start(self.list, True, True, 0) self.list.show() self._add_buttons_to_box(self._vbox) self._pack_vbox() elif self._orientation == Gtk.Orientation.HORIZONTAL: self._vbox.pack_start(self.list, True, True, 0) self.list.show() hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) self._add_buttons_to_box(hbox) self._vbox.pack_start(hbox, False, True, 0) hbox.show() self._pack_vbox() else: raise TypeError( "buttons_orientation must be Gtk.Orientation.VERTICAL " " or Gtk.Orientation.HORIZONTAL") def _add_buttons_to_box(self, box): box.pack_start(self.add_button, False, True, 0) box.pack_start(self.remove_button, False, True, 0) box.pack_start(self.edit_button, False, True, 0) def _pack_vbox(self): self.pack_start(self._vbox, False, True, 6) self._vbox.show() def _set_child_packing(self, padding): expand = self._orientation == Gtk.Orientation.HORIZONTAL self.set_child_packing(self._vbox, expand, True, padding, Gtk.PackType.START) def _add_item(self): retval = self.emit('add-item') if retval is None: return elif isinstance(retval, NotImplementedError): raise retval self.list.append(retval) self.list.refresh() def _remove_item(self, item): retval = self.emit('remove-item', item) if retval: self.list.remove(item) def _edit_item(self, item): retval = self.emit('edit-item', item) if retval: self.list.update(item) # Public API def add_item(self, item): """Appends an item to the list :param item: item to append """ self.list.append(item) def add_items(self, items): """Appends a list of items to the list :param items: items to add :type items: a sequence of items """ self.list.extend(items) def remove_item(self, item): """Removes an item from the list :param item: item to remove """ self.list.remove(item) def update_item(self, item): """Updates an item in the list. You should call this if you change the object :param item: item to update """ self.list.update(item) def default_remove(self, item): """Asks the user confirmation for removal of an item. :param item: a description of the item that will be removed :returns: True if the user confirm the removal, False otherwise """ response = yesno(_('Do you want to remove %s ?') % (GLib.markup_escape_text(str(item)), ), parent=None, default=Gtk.ResponseType.OK, buttons=((Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL), (Gtk.STOCK_REMOVE, Gtk.ResponseType.OK))) return response == Gtk.ResponseType.OK def set_list_type(self, list_type): """Sets the kind of list type. :param list_type: """ if not isinstance(list_type, ListType): raise TypeError("list_type must be a ListType enum") self.add_button.set_property( 'visible', list_type not in [ ListType.READONLY, ListType.REMOVEONLY, ListType.UNADDABLE ]) self.remove_button.set_property( 'visible', list_type not in [ListType.READONLY, ListType.ADDONLY, ListType.UNREMOVABLE]) self.edit_button.set_property( 'visible', list_type not in [ ListType.READONLY, ListType.ADDONLY, ListType.UNEDITABLE, ListType.REMOVEONLY ]) if list_type in [ListType.READONLY, ListType.REMOVEONLY]: padding = 0 else: padding = 6 self._set_child_packing(padding) self._list_type = list_type def clear(self): """Removes all the items in the list""" self.list.clear() # Callbacks def _on_list__selection_changed(self, list, selection): object_selected = selection is not None self.remove_button.set_sensitive(object_selected) self.edit_button.set_sensitive(object_selected) self.emit('selection-changed', selection) def _on_list__row_activated(self, list, item): if self._list_type not in [ ListType.READONLY, ListType.ADDONLY, ListType.UNEDITABLE ]: self._edit_item(item) def _on_add_button__clicked(self, button): self._add_item() def _on_remove_button__clicked(self, button): self._remove_item(self.list.get_selected()) def _on_edit_button__clicked(self, button): self._edit_item(self.list.get_selected())