class StockIconDialog(BaseDialog): def __init__(self, parent=None): BaseDialog.__init__(self, parent, title=_('Stock Icons'), buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) self.set_size_request(260, 330) self._stockicons = ObjectList([Column('stock_id', use_stock=True), Column('name')]) self._stockicons.set_headers_visible(False) self._stockicons.connect('row-activated', self._on_stockicons__row_activated) self._icons = {} for stock_label, stock_id in get_stock_icons(): icon = Settable(stock_id=stock_id, name=stock_label) self._stockicons.append(icon) self._icons[stock_id] = icon self.vbox.pack_start(self._stockicons) self._stockicons.show() def select(self, value): icon = self._icons.get(value) if icon: self._stockicons.select(icon) def get_selected(self): icon = self._stockicons.get_selected() if icon: return icon.stock_id def _on_stockicons__row_activated(self, objectlist, icon): self.emit('response', gtk.RESPONSE_OK)
class Diary(ProxyDelegate): def __init__(self): self.entries = ObjectList([Column("title", width=120), Column("period", width=80), Column("text", expand=True)]) ProxyDelegate.__init__(self, DiaryEntry(), ['title', 'period', 'text'], gladefile="diary", delete_handler=self.quit_if_last) self.hbox.pack_start(self.entries) self.entries.show() self.entries.grab_focus() def on_add__clicked(self, button): entry = DiaryEntry() entry.title = 'New title' self.set_model(entry) self.entries.append(entry) self.title.grab_focus() def on_remove__clicked(self, button): entry = self.entries.get_selected() if entry: self.entries.remove(entry) def on_entries__selection_changed(self, entries, instance): if instance: self.set_model(instance)
class Diary(ProxyDelegate): def __init__(self): self.entries = ObjectList([ Column("title", width=120), Column("period", width=80), Column("text", expand=True) ]) ProxyDelegate.__init__(self, DiaryEntry(), ['title', 'period', 'text'], gladefile="diary.ui", delete_handler=self.quit_if_last) self.hbox.pack_start(self.entries, True, True, 0) self.entries.show() self.entries.grab_focus() def on_add__clicked(self, button): entry = DiaryEntry() entry.title = 'New title' self.set_model(entry) self.entries.append(entry) self.title.grab_focus() def on_remove__clicked(self, button): entry = self.entries.get_selected() if entry: self.entries.remove(entry) def on_entries__selection_changed(self, entries, instance): if instance: self.set_model(instance)
class SimpleListDialog(BasicDialog): size = (500, 400) def __init__(self, columns, objects, hide_cancel_btn=True, title='', multiple=True, header_text=""): """ Create a new SimpleListDialog. :param columns: :param objects: :param hide_cancel_btn: :param title: :param multiple: if we're allowed to select multiple items :type multiple: boolean """ BasicDialog.__init__(self, size=self.size, title=title, header_text=header_text) if hide_cancel_btn: self.cancel_button.hide() if multiple: selection_mode = gtk.SELECTION_MULTIPLE else: selection_mode = gtk.SELECTION_BROWSE self.setup_slave(columns, objects, selection_mode) def setup_slave(self, columns, objects, selection_mode): self.main.remove(self.main_label) self._klist = ObjectList(columns, objects, selection_mode) self.main.add(self._klist) self._klist.show() def get_selection(self): mode = self._klist.get_selection_mode() if mode == gtk.SELECTION_MULTIPLE: return self._klist.get_selected_rows() selection = self._klist.get_selected() if not selection: return [] return [selection] # # BasicDialog # def confirm(self): super(SimpleListDialog, self).confirm() self.retval = self.retval and self.get_selection()
class SimpleListDialog(BasicDialog): size = (500, 400) def __init__(self, columns, objects, hide_cancel_btn=True, title='', multiple=True, header_text=""): """ Create a new SimpleListDialog. :param columns: :param objects: :param hide_cancel_btn: :param title: :param multiple: if we're allowed to select multiple items :type multiple: boolean """ BasicDialog.__init__(self, size=self.size, title=title, header_text=header_text) if hide_cancel_btn: self.cancel_button.hide() if multiple: selection_mode = gtk.SELECTION_MULTIPLE else: selection_mode = gtk.SELECTION_BROWSE self.setup_slave(columns, objects, selection_mode) def setup_slave(self, columns, objects, selection_mode): self.main.remove(self.main_label) self._klist = ObjectList(columns, objects, selection_mode) self.main.add(self._klist) self._klist.show() def get_selection(self): mode = self._klist.get_selection_mode() if mode == gtk.SELECTION_MULTIPLE: return self._klist.get_selected_rows() selection = self._klist.get_selected() if not selection: return [] return [selection] # # BasicDialog # def confirm(self): super(SimpleListDialog, self).confirm() self.retval = self.retval and self.get_selection()
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 DetailsTab(gtk.VBox): details_dialog_class = None def __init__(self, model, parent): super(DetailsTab, self).__init__() self.model = model self._parent = parent self.set_spacing(6) self.set_border_width(6) self.klist = ObjectList(self.get_columns()) self.klist.add_list(self.populate()) self.pack_start(self.klist) self.klist.show() if len(self.klist) and self.get_details_dialog_class(): self.button_box = gtk.HButtonBox() self.button_box.set_layout(gtk.BUTTONBOX_START) details_button = gtk.Button(self.details_lbl) self.button_box.pack_start(details_button) details_button.set_sensitive(bool(self.klist.get_selected())) details_button.show() self.pack_end(self.button_box, False, False) self.button_box.show() self.button_box.details_button = details_button details_button.connect('clicked', self._on_details_button__clicked) self.klist.connect('row-activated', self._on_klist__row_activated) self.klist.connect('selection-changed', self._on_klist__selection_changed) self.setup_widgets() def refresh(self): """Refreshes the list of respective tab.""" self.klist.clear() self.klist.add_list(self.populate()) def get_columns(self): """Returns a list of columns this tab should show.""" raise NotImplementedError def show_details(self): """Called when the details button is clicked. Displays the details of the selected object in the list.""" model = self.get_details_model(self.klist.get_selected()) run_dialog(self.get_details_dialog_class(), parent=self._parent, store=self._parent.store, model=model, visual_mode=True) def get_label(self): """Returns the name of the tab.""" label = gtk.Label(self.labels[1]) return label def get_details_model(self, model): """Subclassses can overwrite this method if the details dialog class needs a model different than the one on the list.""" return model def get_details_dialog_class(self): """Subclasses must return the dialog that should be displayed for more information about the item on the list""" return self.details_dialog_class def setup_widgets(self): """Override this if tab needs to do some custom widget setup.""" # # Callbacks # def _on_details_button__clicked(self, button): self.show_details() def _on_klist__row_activated(self, klist, item): self.show_details() def _on_klist__selection_changed(self, klist, data): self.button_box.details_button.set_sensitive(bool(data))
class DetailsTab(gtk.VBox): details_dialog_class = None def __init__(self, model, parent): super(DetailsTab, self).__init__() self.model = model self._parent = parent self.set_spacing(6) self.set_border_width(6) self.klist = ObjectList(self.get_columns()) self.klist.add_list(self.populate()) self.pack_start(self.klist) self.klist.show() if len(self.klist) and self.get_details_dialog_class(): self.button_box = gtk.HButtonBox() self.button_box.set_layout(gtk.BUTTONBOX_START) details_button = gtk.Button(self.details_lbl) self.button_box.pack_start(details_button) details_button.set_sensitive(bool(self.klist.get_selected())) details_button.show() self.pack_end(self.button_box, False, False) self.button_box.show() self.button_box.details_button = details_button details_button.connect('clicked', self._on_details_button__clicked) self.klist.connect('row-activated', self._on_klist__row_activated) self.klist.connect('selection-changed', self._on_klist__selection_changed) self.setup_widgets() def refresh(self): """Refreshes the list of respective tab.""" self.klist.clear() self.klist.add_list(self.populate()) def get_columns(self): """Returns a list of columns this tab should show.""" raise NotImplementedError def show_details(self): """Called when the details button is clicked. Displays the details of the selected object in the list.""" model = self.get_details_model(self.klist.get_selected()) run_dialog(self.get_details_dialog_class(), parent=self._parent, store=self._parent.store, model=model, visual_mode=True) def get_label(self): """Returns the name of the tab.""" label = gtk.Label(self.labels[1]) return label def get_details_model(self, model): """Subclassses can overwrite this method if the details dialog class needs a model different than the one on the list.""" return model def get_details_dialog_class(self): """Subclasses must return the dialog that should be displayed for more information about the item on the list""" return self.details_dialog_class def setup_widgets(self): """Override this if tab needs to do some custom widget setup.""" # # Callbacks # def _on_details_button__clicked(self, button): self.show_details() def _on_klist__row_activated(self, klist, item): self.show_details() def _on_klist__selection_changed(self, klist, data): self.button_box.details_button.set_sensitive(bool(data))
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 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 MethodTest(unittest.TestCase): def setUp(self): self.klist = ObjectList([Column('name', sorted=True)], [Settable(name='first')]) def testNonZero(self): self.assertEqual(self.klist.__nonzero__(), True) self.klist.remove(self.klist[0]) self.assertEqual(self.klist.__nonzero__(), True) if not self.klist: raise AssertionError def testIter(self): for item1 in self.klist: pass for item2 in iter(self.klist): self.assertEqual(item1, item2) def testGetItem(self): self.klist.append(Settable(name='second')) model = self.klist.get_model() item1 = model[0][0] item2 = model[1][0] self.assertEqual(self.klist[0], item1) self.assertEqual(self.klist[:1], [item1]) self.assertEqual(self.klist[-1:], [item2]) self.assertRaises(TypeError, self.klist.__getitem__, None) def testSetItem(self): self.klist[0] = Settable(name='second') self.assertRaises(NotImplementedError, self.klist.__setitem__, slice(0), None) self.assertRaises(TypeError, self.klist.__setitem__, None, None) def testIndex(self): self.assertRaises(NotImplementedError, self.klist.index, 0, start=0) self.assertRaises(NotImplementedError, self.klist.index, 0, stop=0) self.assertEqual(self.klist.index(self.klist[0]), 0) self.assertRaises(ValueError, self.klist.index, None) def testCount(self): item = self.klist[0] self.assertEqual(self.klist.count(item), 1) self.klist.append(item) self.assertEqual(self.klist.count(item), 2) self.klist.clear() self.assertEqual(self.klist.count(item), 0) def testPop(self): self.assertRaises(NotImplementedError, self.klist.pop, None) def testReverse(self): self.assertRaises(NotImplementedError, self.klist.reverse, 1, 2) def testSort(self): self.assertRaises(NotImplementedError, self.klist.sort, 1, 2) def testSelectPath(self): self.klist.get_treeview().get_selection().set_mode( Gtk.SelectionMode.NONE) self.assertRaises(TypeError, self.klist.select_paths, (0, )) self.klist.get_treeview().get_selection().set_mode( Gtk.SelectionMode.SINGLE) self.klist.select_paths((0, )) def testSelect(self): self.klist.get_treeview().get_selection().set_mode( Gtk.SelectionMode.NONE) self.assertRaises(TypeError, self.klist.select, None) self.klist.get_treeview().get_selection().set_mode( Gtk.SelectionMode.SINGLE) def testGetSelected(self): item = self.klist[0] self.klist.select(item) self.klist.get_treeview().get_selection().set_mode( Gtk.SelectionMode.SINGLE) self.assertEqual(self.klist.get_selected(), item) def testGetSelectedRows(self): self.klist.get_treeview().get_selection().set_mode( Gtk.SelectionMode.MULTIPLE) item = self.klist[0] self.klist.select(item) self.assertEqual(self.klist.get_selected_rows(), [item]) def testGetNextAndPrevious(self): self.klist.append(Settable(name='second')) self.klist.append(Settable(name='third')) item1, item2, item3 = self.klist self.assertEqual(self.klist.get_next(item1), item2) self.assertEqual(self.klist.get_next(item2), item3) self.assertEqual(self.klist.get_next(item3), item1) self.assertRaises(ValueError, self.klist.get_next, None) self.assertEqual(self.klist.get_previous(item1), item3) self.assertEqual(self.klist.get_previous(item2), item1) self.assertEqual(self.klist.get_previous(item3), item2) self.assertRaises(ValueError, self.klist.get_previous, None) def testInsert(self): self.klist = ObjectList([Column('name')]) self.assertEqual(list(self.klist), []) self.klist.insert(0, Settable(name='one')) self.assertEqual(self.klist[0].name, 'one') self.klist.insert(0, Settable(name='two')) self.assertEqual(self.klist[0].name, 'two') self.assertEqual(self.klist[1].name, 'one') self.klist.insert(1, Settable(name='three')) self.assertEqual(self.klist[0].name, 'two') self.assertEqual(self.klist[1].name, 'three') self.assertEqual(self.klist[2].name, 'one')
class PluginManagerDialog(BasicDialog): size = (500, 350) title = _(u'Plugin Manager') help_section = 'plugin' def __init__(self, store): header = _(u'Select the plugin you want to activate and click in ' 'the apply button.') BasicDialog.__init__(self, hide_footer=False, size=PluginManagerDialog.size, title=PluginManagerDialog.title, header_text=header) self.store = store self._manager = get_plugin_manager() self._setup_widgets() def _update_widgets(self): selected = self.klist.get_selected() assert selected self.ok_button.set_sensitive(selected.can_activate()) def _setup_widgets(self): self.set_ok_label(_(u'Activate'), gtk.STOCK_APPLY) self.ok_button.set_sensitive(False) plugins = [] for name in sorted(self._manager.available_plugins_names): # FIXME: Remove when magento plugin is functional for end users if not is_developer_mode() and name == 'magento': continue if platform.system() == 'Windows': if name in ['ecf', 'tef']: continue desc = self._manager.get_description_by_name(name) plugins.append( _PluginModel(name, name in self._manager.installed_plugins_names, desc)) self.klist = ObjectList(self._get_columns(), plugins, gtk.SELECTION_BROWSE) self.klist.set_headers_visible(False) self.klist.connect("selection-changed", self._on_klist__selection_changed) self.main.remove(self.main.get_child()) self.main.add(self.klist) self.klist.show() def _get_columns(self): return [ Column('is_active', title=_('Active'), width=20, data_type=bool), Column('icon', data_type=str, width=24, use_stock=True, icon_size=gtk.ICON_SIZE_BUTTON), Column('description', data_type=str, expand=True, use_markup=True) ] def _enable_plugin(self, plugin_model): plugin_name = plugin_model.name # This should not really be necessary, but there may be deadlocks when # activating the plugin. See bug 5272 default_store = get_default_store() default_store.commit() self._manager.install_plugin(plugin_name) self._manager.activate_plugin(plugin_name) # # BasicDialog # def confirm(self): msg = _("Are you sure you want activate this plugin?\n" "Please note that, once activated you will not " "be able to disable it.") response = yesno(msg, gtk.RESPONSE_NO, _("Activate plugin"), _("Not now")) if response: self._enable_plugin(self.klist.get_selected()) self.close() # # Callbacks # def _on_klist__selection_changed(self, list, data): self._update_widgets()
class PluginManagerDialog(BasicDialog): size = (500, 350) title = _(u'Plugin Manager') help_section = 'plugin' def __init__(self, store): header = _(u'Select the plugin you want to activate and click in ' 'the apply button.') BasicDialog.__init__(self, hide_footer=False, size=PluginManagerDialog.size, title=PluginManagerDialog.title, header_text=header) self.store = store self._manager = get_plugin_manager() self._setup_widgets() def _update_widgets(self): selected = self.klist.get_selected() assert selected self.ok_button.set_sensitive(selected.can_activate()) def _setup_widgets(self): self.set_ok_label(_(u'Activate'), gtk.STOCK_APPLY) self.ok_button.set_sensitive(False) plugins = [] for name in sorted(self._manager.available_plugins_names): # FIXME: Remove when magento plugin is functional for end users if not is_developer_mode() and name == 'magento': continue if platform.system() == 'Windows': if name in ['ecf', 'tef']: continue desc = self._manager.get_description_by_name(name) plugins.append(_PluginModel(name, name in self._manager.installed_plugins_names, desc)) self.klist = ObjectList(self._get_columns(), plugins, gtk.SELECTION_BROWSE) self.klist.set_headers_visible(False) self.klist.connect("selection-changed", self._on_klist__selection_changed) self.main.remove(self.main.get_child()) self.main.add(self.klist) self.klist.show() def _get_columns(self): return [Column('is_active', title=_('Active'), width=20, data_type=bool), Column('icon', data_type=str, width=24, use_stock=True, icon_size=gtk.ICON_SIZE_BUTTON), Column('description', data_type=str, expand=True, use_markup=True)] def _enable_plugin(self, plugin_model): plugin_name = plugin_model.name # This should not really be necessary, but there may be deadlocks when # activating the plugin. See bug 5272 default_store = get_default_store() default_store.commit() self._manager.install_plugin(plugin_name) self._manager.activate_plugin(plugin_name) # # BasicDialog # def confirm(self): msg = _("Are you sure you want activate this plugin?\n" "Please note that, once activated you will not " "be able to disable it.") response = yesno(msg, gtk.RESPONSE_NO, _("Activate plugin"), _("Not now")) if response: self._enable_plugin(self.klist.get_selected()) self.close() # # Callbacks # def _on_klist__selection_changed(self, list, data): self._update_widgets()
class ShortcutsEditor(BasicDialog): size = (700, 400) title = _("Keyboard shortcuts") def __init__(self): BasicDialog.__init__(self, size=ShortcutsEditor.size, title=ShortcutsEditor.title) self._create_ui() def _create_ui(self): self.cancel_button.hide() hbox = gtk.HBox(spacing=6) self.main.remove(self.main.get_child()) self.main.add(hbox) hbox.show() self.categories = ObjectList( [Column('label', sorted=True, expand=True)], get_binding_categories(), gtk.SELECTION_BROWSE) self.categories.connect('selection-changed', self._on_categories__selection_changed) self.categories.set_headers_visible(False) self.categories.set_size_request(200, -1) hbox.pack_start(self.categories, False, False) self.categories.show() box = gtk.VBox(spacing=6) hbox.pack_start(box) box.show() self.shortcuts = ObjectList(self._get_columns(), [], gtk.SELECTION_BROWSE) box.pack_start(self.shortcuts) self.shortcuts.show() self._label = gtk.Label( _("You need to restart Stoq for the changes to take effect")) box.pack_start(self._label, False, False, 6) box.show() defaults_button = gtk.Button(_("Reset defaults")) defaults_button.connect('clicked', self._on_defaults_button__clicked) self.action_area.pack_start(defaults_button, False, False, 6) self.action_area.reorder_child(defaults_button, 0) defaults_button.show() def _on_categories__selection_changed(self, categories, category): if not category: return self.shortcuts.add_list(get_bindings(category.name), clear=True) def _on_defaults_button__clicked(self, button): old = self.categories.get_selected() api.user_settings.remove('shortcuts') remove_user_bindings() self._label.show() self.categories.refresh() self.categories.select(old) def _get_columns(self): return [Column('description', _("Description"), data_type=str, expand=True, sorted=True), ShortcutColumn('shortcut', _("Shortcut"), self)] def set_binding(self, binding): set_user_binding(binding.name, binding.shortcut) d = api.user_settings.get('shortcuts', {}) d[binding.name] = binding.shortcut self._label.show() def remove_binding(self, binding): remove_user_binding(binding.name) d = api.user_settings.get('shortcuts', {}) try: del d[binding.name] except KeyError: pass self._label.show()
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 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 PluginManagerDialog(BasicDialog): size = (500, 350) title = _(u'Plugin Manager') help_section = 'plugin' def __init__(self, store): header = _(u'Select the plugin you want to activate and click in ' 'the apply button.') BasicDialog.__init__(self, hide_footer=False, size=PluginManagerDialog.size, title=PluginManagerDialog.title, header_text=header) self.store = store self._manager = get_plugin_manager() self._setup_widgets() def _update_widgets(self): selected = self.klist.get_selected() assert selected self.ok_button.set_sensitive(selected.can_activate()) def _setup_widgets(self): self.set_ok_label(_(u'Activate'), Gtk.STOCK_APPLY) self.ok_button.set_sensitive(False) plugins = [] for name in sorted(self._manager.available_plugins_names): desc = self._manager.get_description_by_name(name) plugins.append(_PluginModel(name, name in self._manager.installed_plugins_names, desc)) self.klist = ObjectList(self._get_columns(), plugins, Gtk.SelectionMode.BROWSE) self.klist.set_headers_visible(False) self.klist.connect("selection-changed", self._on_klist__selection_changed) self.main.remove(self.main.get_child()) self.main.add(self.klist) self.klist.show() def _get_columns(self): return [Column('is_active', title=_('Active'), width=20, data_type=bool), Column('icon', data_type=str, width=24, use_stock=True, icon_size=Gtk.IconSize.BUTTON), Column('description', data_type=str, expand=True, use_markup=True)] def _enable_plugin(self, plugin_model): plugin_name = plugin_model.name # This should not really be necessary, but there may be deadlocks when # activating the plugin. See bug 5272 default_store = get_default_store() default_store.commit() with new_store() as store: self._manager.install_plugin(store, plugin_name) self._manager.activate_plugin(plugin_name) info(_("The plugin %s was successfully activated. Please, restart all " "Stoq instances connected to this installation.") % (plugin_name, )) # # BasicDialog # def confirm(self): msg = _("Are you sure you want activate this plugin?\n" "Please note that, once activated you will not " "be able to disable it.") response = yesno(msg, Gtk.ResponseType.NO, _("Activate plugin"), _("Not now")) if response: self._enable_plugin(self.klist.get_selected()) self.close() # # Callbacks # def _on_klist__selection_changed(self, list, data): self._update_widgets()
class PaymentMethodsDialog(BasicDialog): # TODO Bug 2406 will avoid duplicating code here size = (400, 400) title = _("Payment Method Settings") # TODO: implement editor for 'multiple' payment method. METHOD_EDITORS = {u'card': CardPaymentMethodEditor, u'money': PaymentMethodEditor, u'check': PaymentMethodEditor, u'credit': PaymentMethodEditor, u'bill': PaymentMethodEditor, u'deposit': PaymentMethodEditor, u'store_credit': PaymentMethodEditor} def __init__(self, store): BasicDialog.__init__(self, hide_footer=True, size=PaymentMethodsDialog.size, title=PaymentMethodsDialog.title) self._can_edit = False self.store = store self._setup_list() self._setup_slaves() def _setup_slaves(self): self._toolbar_slave = SearchEditorToolBar() self._toolbar_slave.connect("edit", self._on_edit_button__clicked) self._toolbar_slave.new_button.hide() self._toolbar_slave.edit_button.set_sensitive(False) self.attach_slave("extra_holder", self._toolbar_slave) def _setup_list(self): methods = PaymentMethod.get_editable_methods(self.store) self.klist = ObjectList(self._get_columns(), methods, Gtk.SelectionMode.BROWSE) self.klist.connect("selection-changed", self._on_klist__selection_changed) self.klist.connect("row-activated", self._on_klist__row_activated) self.klist.connect("cell-edited", self.on_cell_edited) self.main.remove(self.main.get_child()) self.main.add(self.klist) self.klist.show() def _get_columns(self): return [Column('description', title=_('Payment Method'), data_type=str, expand=True), Column('is_active', title=_('Active'), data_type=bool, editable=True)] def _edit_item(self, item): editor = self.METHOD_EDITORS.get(item.method_name, None) if not editor: raise TypeError('Invalid payment method adapter: %s' % item.method_name) store = api.new_store() item = store.fetch(item) retval = run_dialog(editor, self, store, item) store.confirm(retval) store.close() # # Callbacks # def on_cell_edited(self, klist, obj, attr): # All the payment methods could be (de)activate, except the 'money' # payment method. if obj.method_name != u'money': store = obj.store store.commit() else: obj.is_active = True def _on_klist__selection_changed(self, list, data): self._can_edit = (data and data.method_name in self.METHOD_EDITORS.keys()) self._toolbar_slave.edit_button.set_sensitive(self._can_edit) def _on_edit_button__clicked(self, toolbar_slave): assert self._can_edit self._edit_item(self.klist.get_selected()) def _on_klist__row_activated(self, list, data): if not self._can_edit: return self._edit_item(data)
class ShortcutsEditor(BasicDialog): size = (700, 400) title = _("Keyboard shortcuts") def __init__(self): BasicDialog.__init__(self, size=ShortcutsEditor.size, title=ShortcutsEditor.title) self._create_ui() def _create_ui(self): self.cancel_button.hide() hbox = gtk.HBox(spacing=6) self.main.remove(self.main.get_child()) self.main.add(hbox) hbox.show() self.categories = ObjectList( [Column('label', sorted=True, expand=True)], get_binding_categories(), gtk.SELECTION_BROWSE) self.categories.connect('selection-changed', self._on_categories__selection_changed) self.categories.set_headers_visible(False) self.categories.set_size_request(200, -1) hbox.pack_start(self.categories, False, False) self.categories.show() box = gtk.VBox(spacing=6) hbox.pack_start(box) box.show() self.shortcuts = ObjectList(self._get_columns(), [], gtk.SELECTION_BROWSE) box.pack_start(self.shortcuts) self.shortcuts.show() self._label = gtk.Label( _("You need to restart Stoq for the changes to take effect")) box.pack_start(self._label, False, False, 6) box.show() defaults_button = gtk.Button(_("Reset defaults")) defaults_button.connect('clicked', self._on_defaults_button__clicked) self.action_area.pack_start(defaults_button, False, False, 6) self.action_area.reorder_child(defaults_button, 0) defaults_button.show() def _on_categories__selection_changed(self, categories, category): if not category: return self.shortcuts.add_list(get_bindings(category.name), clear=True) def _on_defaults_button__clicked(self, button): old = self.categories.get_selected() api.user_settings.remove('shortcuts') remove_user_bindings() self._label.show() self.categories.refresh() self.categories.select(old) def _get_columns(self): return [ Column('description', _("Description"), data_type=str, expand=True, sorted=True), ShortcutColumn('shortcut', _("Shortcut"), self) ] def set_binding(self, binding): set_user_binding(binding.name, binding.shortcut) d = api.user_settings.get('shortcuts', {}) d[binding.name] = binding.shortcut self._label.show() def remove_binding(self, binding): remove_user_binding(binding.name) d = api.user_settings.get('shortcuts', {}) try: del d[binding.name] except KeyError: pass self._label.show()
class PluginManagerDialog(BasicDialog): size = (500, 350) title = _(u'Plugin Manager') help_section = 'plugin' def __init__(self, store): header = _(u'Select the plugin you want to activate and click in ' 'the apply button.') BasicDialog.__init__(self, hide_footer=False, size=PluginManagerDialog.size, title=PluginManagerDialog.title, header_text=header) self.store = store self._manager = get_plugin_manager() self._setup_widgets() def _update_widgets(self): selected = self.klist.get_selected() self.ok_button.set_sensitive(selected and selected.can_activate()) def _setup_widgets(self): self.set_ok_label(_(u'Activate'), Gtk.STOCK_APPLY) self.ok_button.set_sensitive(False) plugins = [] for name in sorted(self._manager.available_plugins_names): desc = self._manager.get_description_by_name(name) plugins.append( _PluginModel(name, name in self._manager.installed_plugins_names, desc)) self.klist = ObjectList(self._get_columns(), plugins, Gtk.SelectionMode.BROWSE) self.klist.set_headers_visible(False) self.klist.connect("selection-changed", self._on_klist__selection_changed) self.main.remove(self.main.get_child()) self.main.add(self.klist) self.klist.show() def _get_columns(self): return [ Column('is_active', title=_('Active'), width=20, data_type=bool), Column('icon', data_type=str, width=24, use_stock=True, icon_size=Gtk.IconSize.BUTTON), Column('description', data_type=str, expand=True, use_markup=True) ] def _enable_plugin(self, plugin_model): plugin_name = plugin_model.name # This should not really be necessary, but there may be deadlocks when # activating the plugin. See bug 5272 default_store = get_default_store() default_store.commit() with new_store() as store: self._manager.install_plugin(store, plugin_name) self._manager.activate_plugin(plugin_name) info( _("The plugin %s was successfully activated. Please, restart all " "Stoq instances connected to this installation.") % (plugin_name, )) # # BasicDialog # def confirm(self): msg = _("Are you sure you want activate this plugin?\n" "Please note that, once activated you will not " "be able to disable it.") response = yesno(msg, Gtk.ResponseType.NO, _("Activate plugin"), _("Not now")) if response: self._enable_plugin(self.klist.get_selected()) self.close() # # Callbacks # def _on_klist__selection_changed(self, list, data): self._update_widgets()
class search_results_window: """A results window for a searching for a recipe""" def __init__ (self, searchline="", mode=gtk.SELECTION_SINGLE): """Search for the name like searchstring""" self.window=gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.connect("destroy", self.exit) self.window.set_size_request(600, 250) self.vbox=gtk.VBox(homogeneous=False,spacing=0) self.window.add(self.vbox) self.cur=con.cursor() if searchline.count(':') !=0: search_options=dict(self.parse_search_string(searchline)) print search_options results=[[5, 'Foo2', 'yummy food'], [6, 'Foobar', 'best foobar evar!']] else: searchline="%" + searchline + "%" self.cur.execute("""SELECT recipe_id,name,description FROM recipes WHERE name LIKE %s""", (searchline,)) results=self.cur.fetchall() my_columns = [ Column("name", title="Name", sorted=True), Column("desc", title="Description") ] self.objectlist = ObjectList(my_columns, mode=mode) self.objectlist.set_size_request(600,225) recipes=[RecipeInfo(x) for x in results] self.objectlist.add_list(recipes) self.b=gtk.Button("Show Selected") self.b.connect("clicked", self.show_recipe2) self.vbox.add(self.objectlist) self.vbox.add(self.b) self.window.show_all() def parse_search_string(self,s): foo,indexes=findall(':',s) p=0 l=[] s_len=len(s) for i in indexes: for j in range(i+1,s_len+1): #print (j,s_len) if j >= s_len: l.append(s[p:j].strip()) break else: if s[j] == " ": t=j if s[j] == ':': l.append(s[p:t].strip()) p=t break return [[y.lower() for y in x.split(':')] for x in l] def show_recipe2(self,widget,*data): recipe=self.objectlist.get_selected() self.window.hide() current_recipe(recipe_id=recipe.r_id) def show_recipe(self,widget,data): self.window.hide() recipe=current_recipe(recipe_id=data) def exit(self, widget): main_window.window.show()
class MethodTest(unittest.TestCase): def setUp(self): self.klist = ObjectList([Column('name', sorted=True)], [Settable(name='first')]) def testNonZero(self): self.assertEqual(self.klist.__nonzero__(), True) self.klist.remove(self.klist[0]) self.assertEqual(self.klist.__nonzero__(), True) if not self.klist: raise AssertionError def testIter(self): for item1 in self.klist: pass for item2 in iter(self.klist): self.assertEqual(item1, item2) def testGetItem(self): self.klist.append(Settable(name='second')) model = self.klist.get_model() item1 = model[0][0] item2 = model[1][0] self.assertEqual(self.klist[0], item1) self.assertEqual(self.klist[:1], [item1]) self.assertEqual(self.klist[-1:], [item2]) self.assertRaises(TypeError, self.klist.__getitem__, None) def testSetItem(self): self.klist[0] = Settable(name='second') self.assertRaises(NotImplementedError, self.klist.__setitem__, slice(0), None) self.assertRaises(TypeError, self.klist.__setitem__, None, None) def testIndex(self): self.assertRaises(NotImplementedError, self.klist.index, 0, start=0) self.assertRaises(NotImplementedError, self.klist.index, 0, stop=0) self.assertEqual(self.klist.index(self.klist[0]), 0) self.assertRaises(ValueError, self.klist.index, None) def testCount(self): item = self.klist[0] self.assertEqual(self.klist.count(item), 1) self.klist.append(item) self.assertEqual(self.klist.count(item), 2) self.klist.clear() self.assertEqual(self.klist.count(item), 0) def testPop(self): self.assertRaises(NotImplementedError, self.klist.pop, None) def testReverse(self): self.assertRaises(NotImplementedError, self.klist.reverse, 1, 2) def testSort(self): self.assertRaises(NotImplementedError, self.klist.sort, 1, 2) def testSelectPath(self): self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_NONE) self.assertRaises(TypeError, self.klist.select_paths, (0,)) self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_SINGLE) self.klist.select_paths((0,)) def testSelect(self): self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_NONE) self.assertRaises(TypeError, self.klist.select, None) self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_SINGLE) def testGetSelected(self): item = self.klist[0] self.klist.select(item) self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_SINGLE) self.assertEqual(self.klist.get_selected(), item) def testGetSelectedRows(self): self.klist.get_treeview().get_selection().set_mode(gtk.SELECTION_MULTIPLE) item = self.klist[0] self.klist.select(item) self.assertEqual(self.klist.get_selected_rows(), [item]) def testGetNextAndPrevious(self): self.klist.append(Settable(name='second')) self.klist.append(Settable(name='third')) item1, item2, item3 = self.klist self.assertEqual(self.klist.get_next(item1), item2) self.assertEqual(self.klist.get_next(item2), item3) self.assertEqual(self.klist.get_next(item3), item1) self.assertRaises(ValueError, self.klist.get_next, None) self.assertEqual(self.klist.get_previous(item1), item3) self.assertEqual(self.klist.get_previous(item2), item1) self.assertEqual(self.klist.get_previous(item3), item2) self.assertRaises(ValueError, self.klist.get_previous, None) def testInsert(self): self.klist = ObjectList([Column('name')]) self.assertEqual(list(self.klist), []) self.klist.insert(0, Settable(name='one')) self.assertEqual(self.klist[0].name, 'one') self.klist.insert(0, Settable(name='two')) self.assertEqual(self.klist[0].name, 'two') self.assertEqual(self.klist[1].name, 'one') self.klist.insert(1, Settable(name='three')) self.assertEqual(self.klist[0].name, 'two') self.assertEqual(self.klist[1].name, 'three') self.assertEqual(self.klist[2].name, 'one')
class Connections(GladeWidget): gladeFile = "connections.glade" gsignal("have-connection", bool) gsignal("connection-activated", object) gsignal("connections-cleared") def __init__(self): GladeWidget.__init__(self) columns = [ Column("host", title=_("Hostname"), searchable=True), Column( "timestamp", title=_("Last used"), sorted=True, order=gtk.SORT_DESCENDING, format_func=format_timestamp ), ] self._connections = ObjectList(columns, objects=getRecentConnections(), mode=gtk.SELECTION_SINGLE) self._connections.connect("row-activated", self._on_objectlist_row_activated) self._connections.connect("selection-changed", self._on_objectlist_selection_changed) self._connections.set_size_request(-1, 160) self.page.pack_start(self._connections) self.page.reorder_child(self._connections, 0) self._connections.get_treeview().set_search_equal_func(self._searchEqual) self._connections.show() self._updateButtons() def _updateButtons(self): canClear = hasRecentConnections() self.button_clear.set_sensitive(canClear) self.button_clear_all.set_sensitive(canClear) if not canClear: self.emit("connections-cleared") def _searchEqual(self, model, column, key, iter): connection = model.get(iter, column)[0] if key in connection.name: return False # True means doesn't match return True def _clear_all(self): for conn in self._connections: os.unlink(conn.filename) self._connections.clear() def _clear(self, conn): self._connections.remove(conn) os.unlink(conn.filename) # Public API def grab_focus(self): if len(self._connections): self._connections.select(self._connections[0]) self._connections.grab_focus() def get_selected(self): return self._connections.get_selected() def update(self, connection): os.utime(connection.filename, None) # Callbacks def on_button_clear_clicked(self, button): conn = self._connections.get_selected() if conn: self._clear(conn) self._updateButtons() def on_button_clear_all_clicked(self, button): self._clear_all() self._updateButtons() def _on_objectlist_row_activated(self, connections, connection): self.emit("connection-activated", connection) def _on_objectlist_selection_changed(self, connections, connection): self.emit("have-connection", bool(connection))
class PaymentMethodsDialog(BasicDialog): # TODO Bug 2406 will avoid duplicating code here size = (400, 400) title = _("Payment Method Settings") # TODO: implement editor for 'multiple' payment method. METHOD_EDITORS = { u'card': CardPaymentMethodEditor, u'money': PaymentMethodEditor, u'check': PaymentMethodEditor, u'credit': PaymentMethodEditor, u'bill': PaymentMethodEditor, u'deposit': PaymentMethodEditor, u'store_credit': PaymentMethodEditor } def __init__(self, store): BasicDialog.__init__(self, hide_footer=True, size=PaymentMethodsDialog.size, title=PaymentMethodsDialog.title) self._can_edit = False self.store = store self._setup_list() self._setup_slaves() def _setup_slaves(self): self._toolbar_slave = SearchEditorToolBar() self._toolbar_slave.connect("edit", self._on_edit_button__clicked) self._toolbar_slave.new_button.hide() self._toolbar_slave.edit_button.set_sensitive(False) self.attach_slave("extra_holder", self._toolbar_slave) def _setup_list(self): methods = PaymentMethod.get_editable_methods(self.store) self.klist = ObjectList(self._get_columns(), methods, Gtk.SelectionMode.BROWSE) self.klist.connect("selection-changed", self._on_klist__selection_changed) self.klist.connect("row-activated", self._on_klist__row_activated) self.klist.connect("cell-edited", self.on_cell_edited) self.main.remove(self.main.get_child()) self.main.add(self.klist) self.klist.show() def _get_columns(self): return [ Column('description', title=_('Payment Method'), data_type=str, expand=True), Column('is_active', title=_('Active'), data_type=bool, editable=True) ] def _edit_item(self, item): editor = self.METHOD_EDITORS.get(item.method_name, None) if not editor: raise TypeError('Invalid payment method adapter: %s' % item.method_name) store = api.new_store() item = store.fetch(item) retval = run_dialog(editor, self, store, item) store.confirm(retval) store.close() # # Callbacks # def on_cell_edited(self, klist, obj, attr): # All the payment methods could be (de)activate, except the 'money' # payment method. if obj.method_name != u'money': store = obj.store store.commit() else: obj.is_active = True def _on_klist__selection_changed(self, list, data): self._can_edit = (data and data.method_name in self.METHOD_EDITORS.keys()) self._toolbar_slave.edit_button.set_sensitive(self._can_edit) def _on_edit_button__clicked(self, toolbar_slave): assert self._can_edit self._edit_item(self.klist.get_selected()) def _on_klist__row_activated(self, list, data): if not self._can_edit: return self._edit_item(data)
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())