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 ShowLastPackets(InformationWindow): ## @var win # window ## @var visualizer # visualizer ## @var viz_node # visualizer node ## @var node # the node ## @var tx_list # packet transmit list ## @var rx_list # packet receive list ## @var drop_list # packet drop list ## @var packet_capture_options # packet capture options ## @var packet_filter_widget # packet filter widget ## @var packet_filter_list # list of TypeIdConfig instances ## @var op_AND_button # AND button ## @var op_OR_button # OR button class PacketList(gtk.ScrolledWindow): """ PacketList class """ ## @var table_model # table model ( COLUMN_TIME, COLUMN_INTERFACE, COLUMN_SIZE, COLUMN_CONTENTS, ) = range(4) def __init__(self): """ Initializer @param self this object """ super(ShowLastPackets.PacketList, self).__init__() self.set_properties(hscrollbar_policy=gtk.POLICY_AUTOMATIC, vscrollbar_policy=gtk.POLICY_AUTOMATIC) self.table_model = gtk.ListStore(*([str] * 4)) treeview = gtk.TreeView(self.table_model) treeview.show() self.add(treeview) def add_column(descr, colid): column = gtk.TreeViewColumn(descr, gtk.CellRendererText(), text=colid) treeview.append_column(column) add_column("Time", self.COLUMN_TIME) add_column("Interface", self.COLUMN_INTERFACE) add_column("Size", self.COLUMN_SIZE) add_column("Contents", self.COLUMN_CONTENTS) def update(self, node, packet_list): """! Update function @param self this object @param node the node @param packet_list packet list @return none """ self.table_model.clear() for sample in packet_list: tree_iter = self.table_model.append() if sample.device is None: interface_name = "(unknown)" else: interface_name = ns.core.Names.FindName(sample.device) if not interface_name: interface_name = "(interface %i)" % sample.device.GetIfIndex( ) self.table_model.set(tree_iter, self.COLUMN_TIME, str(sample.time.GetSeconds()), self.COLUMN_INTERFACE, interface_name, self.COLUMN_SIZE, str(sample.packet.GetSize()), self.COLUMN_CONTENTS, str(sample.packet)) def __init__(self, visualizer, node_index): """ Initializer @param self this object @param visualizer the visualizer object @param node_index the node index """ InformationWindow.__init__(self) self.win = gtk.Dialog(parent=visualizer.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) self.win.connect("response", self._response_cb) self.win.set_title("Last packets for node %i" % node_index) self.visualizer = visualizer self.viz_node = visualizer.get_node(node_index) self.node = ns.network.NodeList.GetNode(node_index) def smart_expand(expander, vbox): if expander.get_expanded(): vbox.set_child_packing(expander, expand=True, fill=True, padding=0, pack_type=gtk.PACK_START) else: vbox.set_child_packing(expander, expand=False, fill=False, padding=0, pack_type=gtk.PACK_START) main_hbox = gtk.HBox(False, 4) main_hbox.show() main_vbox = gtk.VBox(False, 4) main_vbox.show() self.win.vbox.add(main_hbox) main_hbox.add(main_vbox) self.tx_list = self.PacketList() self.tx_list.show() group = gtk.Expander("Last transmitted packets") group.show() group.add(self.tx_list) main_vbox.pack_start(group, expand=False, fill=False) group.connect_after("activate", smart_expand, main_vbox) self.rx_list = self.PacketList() self.rx_list.show() group = gtk.Expander("Last received packets") group.show() group.add(self.rx_list) main_vbox.pack_start(group, expand=False, fill=False) group.connect_after("activate", smart_expand, main_vbox) self.drop_list = self.PacketList() self.drop_list.show() group = gtk.Expander("Last dropped packets") group.show() group.add(self.drop_list) main_vbox.pack_start(group, expand=False, fill=False) group.connect_after("activate", smart_expand, main_vbox) # Packet Filter # - options self.packet_capture_options = ns.visualizer.PyViz.PacketCaptureOptions( ) self.packet_capture_options.numLastPackets = 100 packet_filter_vbox = gtk.VBox(False, 4) packet_filter_vbox.show() main_hbox.add(packet_filter_vbox) sel_buttons_box = gtk.HButtonBox() sel_buttons_box.show() packet_filter_vbox.pack_start(sel_buttons_box, False, False, 4) select_all_button = gobject.new(gtk.Button, label="Sel. All", visible=True) select_none_button = gobject.new(gtk.Button, label="Sel. None", visible=True) sel_buttons_box.add(select_all_button) sel_buttons_box.add(select_none_button) self.packet_filter_widget = ObjectList([ Column('selected', title="Sel.", data_type=bool, editable=True), Column('name', title="Header"), ], sortable=True) self.packet_filter_widget.show() packet_filter_vbox.pack_start(self.packet_filter_widget, True, True, 4) class TypeIdConfig(object): __slots__ = ['name', 'selected', 'typeid'] self.packet_filter_list = [] # list of TypeIdConfig instances Header = ns.core.TypeId.LookupByName("ns3::Header") Trailer = ns.core.TypeId.LookupByName("ns3::Trailer") for typeid_i in range(ns.core.TypeId.GetRegisteredN()): typeid = ns.core.TypeId.GetRegistered(typeid_i) # check if this is a header or trailer subtype typeid_tmp = typeid type_is_good = False while 1: if typeid_tmp == Header or typeid_tmp == Trailer: type_is_good = True break if typeid_tmp.HasParent(): typeid_tmp = typeid_tmp.GetParent() else: break if not type_is_good: continue if typeid in [Header, Trailer]: continue c = TypeIdConfig() c.selected = True c.name = typeid.GetName() c.typeid = typeid self.packet_filter_list.append(c) self.packet_filter_widget.add_list(self.packet_filter_list) def update_capture_options(): if self.op_AND_button.props.active: self.packet_capture_options.mode = ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_AND else: self.packet_capture_options.mode = ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_OR self.packet_capture_options.numLastPackets = 100 self.packet_capture_options.headers = [ c.typeid for c in self.packet_filter_list if c.selected ] self.visualizer.simulation.lock.acquire() try: self.visualizer.simulation.sim_helper.SetPacketCaptureOptions( self.node.GetId(), self.packet_capture_options) finally: self.visualizer.simulation.lock.release() def sel_all_cb(bt): for c in self.packet_filter_list: c.selected = True self.packet_filter_widget.refresh() update_capture_options() def sel_none_cb(bt): for c in self.packet_filter_list: c.selected = False self.packet_filter_widget.refresh() update_capture_options() select_all_button.connect("clicked", sel_all_cb) select_none_button.connect("clicked", sel_none_cb) op_buttons_box = gtk.HButtonBox() op_buttons_box.show() packet_filter_vbox.pack_start(op_buttons_box, False, False, 4) self.op_AND_button = gobject.new(gtk.RadioButton, label="AND", visible=True) self.op_OR_button = gobject.new(gtk.RadioButton, label="OR", visible=True, group=self.op_AND_button) op_buttons_box.add(self.op_AND_button) op_buttons_box.add(self.op_OR_button) self.op_OR_button.props.active = True self.op_AND_button.connect("toggled", lambda b: update_capture_options()) def cell_edited(l, obj, attribute): update_capture_options() self.packet_filter_widget.connect("cell-edited", cell_edited) update_capture_options() self.visualizer.add_information_window(self) self.win.set_default_size(600, 300) self.win.show() def _response_cb(self, win, response): """! Response callback function @param self this object @param win the window @param response the response @return none """ self.win.destroy() self.visualizer.remove_information_window(self) def update(self): """! Update function @param self this object @return none """ last_packets = self.visualizer.simulation.sim_helper.GetLastPackets( self.node.GetId()) self.tx_list.update(self.node, last_packets.lastTransmittedPackets) self.rx_list.update(self.node, last_packets.lastReceivedPackets) self.drop_list.update(self.node, last_packets.lastDroppedPackets)
class ListContainer(gtk.HBox): """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` """ 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 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) 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.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 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 ShowLastPackets(InformationWindow): ## @var win # window ## @var visualizer # visualizer ## @var viz_node # visualizer node ## @var node # the node ## @var tx_list # packet transmit list ## @var rx_list # packet receive list ## @var drop_list # packet drop list ## @var packet_capture_options # packet capture options ## @var packet_filter_widget # packet filter widget ## @var packet_filter_list # list of TypeIdConfig instances ## @var op_AND_button # AND button ## @var op_OR_button # OR button class PacketList(Gtk.ScrolledWindow): """ PacketList class """ ## @var table_model # table model ( COLUMN_TIME, COLUMN_INTERFACE, COLUMN_SIZE, COLUMN_CONTENTS, ) = range(4) def __init__(self): """ Initializer @param self this object """ super(ShowLastPackets.PacketList, self).__init__() self.set_properties(hscrollbar_policy=Gtk.PolicyType.AUTOMATIC, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC) self.table_model = Gtk.ListStore(*([str]*4)) treeview = Gtk.TreeView(self.table_model) treeview.show() self.add(treeview) def add_column(descr, colid): column = Gtk.TreeViewColumn(descr, Gtk.CellRendererText(), text=colid) treeview.append_column(column) add_column("Time", self.COLUMN_TIME) add_column("Interface", self.COLUMN_INTERFACE) add_column("Size", self.COLUMN_SIZE) add_column("Contents", self.COLUMN_CONTENTS) def update(self, node, packet_list): """! Update function @param self this object @param node the node @param packet_list packet list @return none """ self.table_model.clear() for sample in packet_list: tree_iter = self.table_model.append() if sample.device is None: interface_name = "(unknown)" else: interface_name = ns.core.Names.FindName(sample.device) if not interface_name: interface_name = "(interface %i)" % sample.device.GetIfIndex() self.table_model.set(tree_iter, self.COLUMN_TIME, str(sample.time.GetSeconds()), self.COLUMN_INTERFACE, interface_name, self.COLUMN_SIZE, str(sample.packet.GetSize ()), self.COLUMN_CONTENTS, str(sample.packet) ) def __init__(self, visualizer, node_index): """ Initializer @param self this object @param visualizer the visualizer object @param node_index the node index """ InformationWindow.__init__(self) self.win = Gtk.Dialog(parent=visualizer.window, flags=Gtk.DialogFlags.DESTROY_WITH_PARENT|Gtk.DialogFlags.NO_SEPARATOR, buttons=(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)) self.win.connect("response", self._response_cb) self.win.set_title("Last packets for node %i" % node_index) self.visualizer = visualizer self.viz_node = visualizer.get_node(node_index) self.node = ns.network.NodeList.GetNode(node_index) def smart_expand(expander, vbox): if expander.get_expanded(): vbox.set_child_packing(expander, expand=True, fill=True, padding=0, pack_type=Gtk.PACK_START) else: vbox.set_child_packing(expander, expand=False, fill=False, padding=0, pack_type=Gtk.PACK_START) main_hbox = Gtk.HBox(False, 4) main_hbox.show() main_vbox = Gtk.VBox(False, 4) main_vbox.show() self.win.vbox.add(main_hbox) main_hbox.add(main_vbox) self.tx_list = self.PacketList() self.tx_list.show() group = Gtk.Expander("Last transmitted packets") group.show() group.add(self.tx_list) main_vbox.pack_start(group, expand=False, fill=False) group.connect_after("activate", smart_expand, main_vbox) self.rx_list = self.PacketList() self.rx_list.show() group = Gtk.Expander("Last received packets") group.show() group.add(self.rx_list) main_vbox.pack_start(group, expand=False, fill=False) group.connect_after("activate", smart_expand, main_vbox) self.drop_list = self.PacketList() self.drop_list.show() group = Gtk.Expander("Last dropped packets") group.show() group.add(self.drop_list) main_vbox.pack_start(group, expand=False, fill=False) group.connect_after("activate", smart_expand, main_vbox) # Packet Filter # - options self.packet_capture_options = ns.visualizer.PyViz.PacketCaptureOptions() self.packet_capture_options.numLastPackets = 100 packet_filter_vbox = Gtk.VBox(False, 4) packet_filter_vbox.show() main_hbox.add(packet_filter_vbox) sel_buttons_box = Gtk.HButtonBox() sel_buttons_box.show() packet_filter_vbox.pack_start(sel_buttons_box, False, False, 4) select_all_button = GObject.new(Gtk.Button, label="Sel. All", visible=True) select_none_button = GObject.new(Gtk.Button, label="Sel. None", visible=True) sel_buttons_box.add(select_all_button) sel_buttons_box.add(select_none_button) self.packet_filter_widget = ObjectList([ Column('selected', title="Sel.", data_type=bool, editable=True), Column('name', title="Header"), ], sortable=True) self.packet_filter_widget.show() packet_filter_vbox.pack_start(self.packet_filter_widget, True, True, 4) class TypeIdConfig(object): __slots__ = ['name', 'selected', 'typeid'] self.packet_filter_list = [] # list of TypeIdConfig instances Header = ns.core.TypeId.LookupByName("ns3::Header") Trailer = ns.core.TypeId.LookupByName("ns3::Trailer") for typeid_i in range(ns.core.TypeId.GetRegisteredN()): typeid = ns.core.TypeId.GetRegistered(typeid_i) # check if this is a header or trailer subtype typeid_tmp = typeid type_is_good = False while 1: if typeid_tmp == Header or typeid_tmp == Trailer: type_is_good = True break if typeid_tmp.HasParent(): typeid_tmp = typeid_tmp.GetParent() else: break if not type_is_good: continue if typeid in [Header, Trailer]: continue c = TypeIdConfig() c.selected = True c.name = typeid.GetName() c.typeid = typeid self.packet_filter_list.append(c) self.packet_filter_widget.add_list(self.packet_filter_list) def update_capture_options(): if self.op_AND_button.props.active: self.packet_capture_options.mode = ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_AND else: self.packet_capture_options.mode = ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_OR self.packet_capture_options.numLastPackets = 100 self.packet_capture_options.headers = [c.typeid for c in self.packet_filter_list if c.selected] self.visualizer.simulation.lock.acquire() try: self.visualizer.simulation.sim_helper.SetPacketCaptureOptions( self.node.GetId(), self.packet_capture_options) finally: self.visualizer.simulation.lock.release() def sel_all_cb(bt): for c in self.packet_filter_list: c.selected = True self.packet_filter_widget.refresh() update_capture_options() def sel_none_cb(bt): for c in self.packet_filter_list: c.selected = False self.packet_filter_widget.refresh() update_capture_options() select_all_button.connect("clicked", sel_all_cb) select_none_button.connect("clicked", sel_none_cb) op_buttons_box = Gtk.HButtonBox() op_buttons_box.show() packet_filter_vbox.pack_start(op_buttons_box, False, False, 4) self.op_AND_button = GObject.new(Gtk.RadioButton, label="AND", visible=True) self.op_OR_button = GObject.new(Gtk.RadioButton, label="OR", visible=True, group=self.op_AND_button) op_buttons_box.add(self.op_AND_button) op_buttons_box.add(self.op_OR_button) self.op_OR_button.props.active = True self.op_AND_button.connect("toggled", lambda b: update_capture_options()) def cell_edited(l, obj, attribute): update_capture_options() self.packet_filter_widget.connect("cell-edited", cell_edited) update_capture_options() self.visualizer.add_information_window(self) self.win.set_default_size(600, 300) self.win.show() def _response_cb(self, win, response): """! Response callback function @param self this object @param win the window @param response the response @return none """ self.win.destroy() self.visualizer.remove_information_window(self) def update(self): """! Update function @param self this object @return none """ last_packets = self.visualizer.simulation.sim_helper.GetLastPackets(self.node.GetId()) self.tx_list.update(self.node, last_packets.lastTransmittedPackets) self.rx_list.update(self.node, last_packets.lastReceivedPackets) self.drop_list.update(self.node, last_packets.lastDroppedPackets)
class ListDialog: """ Diálogo de edição de uma tabela com botões padrão e uma lista de campos. """ def __init__(self, controle, tabela, titulo): """ controle é um objeto da classe Controle. tabela é o nome de uma instancia de Classe no Controle que referencia um tabela no Modelo. titulo é nome de leitura da tabela sendo editada (ex. 'Categorias') """ self.controle = controle self.tabela = tabela self.data = [] self.buttons = [] self.new_method = self.create_new_record self.save_method = self.salvar self.populate_method = self.populate self.titulo = titulo or self.tabela.nome_tabela self.edit_mode = False self.editing_new = False self.editing = False self.selection = None self.do_nothing = False def make_widget(self, fields, custom_buttons = []): """ Cria e retorna o widget que contém o toolbar, a lista e os campos para edição. fields é uma lista de FieldType. custom_buttons é uma lista de ListToolButton que substitui os botões padrão. """ #-------Campos self.fields = [] for field in fields: if field.show_field: field.label = gtk.Label(field.titulo + ":") if field.tabelacombo: field.entry = ComboEntry() tabelacombo = getattr(self.controle, field.tabelacombo) itens = tabelacombo.combo() field.entry.prefill(itens) else: field.entry = ProxyEntry(field.tipo) field.entry.set_mask(field.mask) self.fields.append(field) vbox_main = gtk.VBox() hbox_topo = gtk.HBox() self.widget = gtk.EventBox() #-------Toolbar toolbar = gtk.Toolbar() toolbar.set_orientation(gtk.ORIENTATION_VERTICAL) toolbar.set_style(gtk.TOOLBAR_BOTH) if not custom_buttons: self.tb_novo = ListToolButton(self.default_new, gtk.STOCK_NEW) self.tb_edit = ListToolButton(self.default_edit, gtk.STOCK_EDIT) self.custom_buttons = [self.tb_novo, self.tb_edit] else: self.custom_buttons = custom_buttons for tool_button in self.custom_buttons: toolbar.insert(tool_button.button, -1) #-------Lista vbox_lista = gtk.VBox() hbox_entry = gtk.HBox() self.entry_localizar = gtk.Entry() self.entry_localizar.connect('activate', self.localizar) label = gtk.Label('Localizar') frame_lista = gtk.Frame(self.titulo) self.listview = self.create_list() self.listview.connect("row_activated", self.on_row_activated) self.listview.connect('selection-changed',self.on_selection_changed) #-------Frame frame_dados = gtk.Frame("Informações") hbox_dados = gtk.HBox(False, 6) vbox_label = gtk.VBox(True, 4) vbox_entry = gtk.VBox(True, 4) for field in self.fields: if field.show_field: vbox_label.pack_start(field.label, False, True, 8) vbox_entry.pack_start(field.entry, False, True, 8) #Botões self.button_save = gtk.Button(stock=gtk.STOCK_SAVE) self.button_save.connect("clicked", self.save_method) self.button_cancel = gtk.Button(stock=gtk.STOCK_CANCEL) self.button_cancel.connect("clicked", self.cancel) vbox_dados_buttons = gtk.VButtonBox() vbox_dados_buttons.set_layout(gtk.BUTTONBOX_SPREAD) vbox_dados_buttons.add(self.button_save) vbox_dados_buttons.add(self.button_cancel) #-------Notify self.notify = self.controle.notify() self.notify_box = self.notify.get_widget() self.notify.show_notify('info','Clique em NOVO para adicionar um novo item') self.tabela.set_notify(self.notify) #-------Posicionar todos frame_lista.add(self.listview) vbox_lista.pack_start(hbox_entry, False, False, 2) vbox_lista.pack_start(frame_lista, True, True, 2) hbox_entry.pack_end(self.entry_localizar, False, False, 2) hbox_entry.pack_end(label, False, False, 2) hbox_topo.pack_start(toolbar, False, False, 5) hbox_topo.pack_start(vbox_lista, True, True, 2) hbox_dados.pack_start(vbox_label, False, False, 2) hbox_dados.pack_start(vbox_entry, True, True, 2) hbox_dados.pack_start(vbox_dados_buttons, False, False, 2) frame_dados.add(hbox_dados) vbox_main.pack_start(hbox_topo, True, True, 2) vbox_main.pack_start(frame_dados, False, False, 2) vbox_main.pack_start(self.notify_box, False, True, 2) self.widget.add(vbox_main) self.set_edit_mode(False) return self.widget def create_list(self): columns = [] for field in self.fields: if field.show_in_list: columns.append(Column(field.field_name, data_type = field.tipo, title = field.titulo)) self.lista = ObjectList(columns) self.data = self.populate_method() self.lista.extend(self.data) return self.lista def localizar(self, entry): text = self.entry_localizar.get_text().lower() searches = text.split(" ") if not searches: #lista vazia lista = self.data else: lista = [] for item in self.data: itemtext = "" #reúne o texto inteiro do item (separado por espaço): for field in self.fields: if field.searchable: itemtext += str(getattr(item, field.field_name)).lower() + " " contain_all = True for searchtext in searches: if searchtext not in itemtext: contain_all = False if contain_all: lista.append(item) self.lista.add_list(lista) def set_edit_mode(self, edit_mode): """ Coloca ou tira os campos em modo de edição. """ for field in self.fields: if field.entry: field.entry.set_sensitive(edit_mode) for tool_button in self.custom_buttons: tool_button.button.set_sensitive(not edit_mode) self.entry_localizar.set_sensitive(not edit_mode) self.button_save.set_sensitive(edit_mode) self.button_cancel.set_sensitive(edit_mode) self.editing = edit_mode def cancel(self, widget): """Cancela a edição de um item""" self.set_edit_mode(False) self.clear_entry() self.lista.refresh() if self.editing_new: self.lista.remove(self.newobj) self.editing_new = False self.notify.show_notify('info','Clique em NOVO para adicionar um novo item') def create_new_record(self): """Adiciona um item padrão a lista para depois ser editado""" self.notify.hide() obj = ListItem() for field in self.fields: if field.tipo == str or field.tabelacombo: setattr(obj, field.field_name, '') else: setattr(obj, field.field_name, converter.from_string(field.tipo, '0')) return obj def populate(self): """Popula a lista com os itens""" itens = self.tabela.listar() objetos = [] for item in itens: obj = ListItem() for field in self.fields: if not field.tabelacombo: setattr(obj, field.field_name, item[field.field_name]) else: tabelacombo = getattr(self.controle, field.tabelacombo) descricao = tabelacombo.item_descricao(item[field.field_name]) try: setattr(obj, field.field_name, descricao[0][0]) except: setattr(obj, field.field_name, descricao) objetos.append(obj) return objetos def default_new(self, sender): self.newobj = self.new_method() #Chama new_method para criar um novo item self.data.append(self.newobj) self.listview.append(self.newobj) self.listview.refresh() self.listview.select(self.newobj) self.set_edit_mode(True) self.editing_new = True self.item = self.selection def default_edit(self, sender): if self.selection: self.populate_entry(self.selection) self.set_edit_mode(True) self.item = self.selection def on_row_activated(self, list, item): """Preenche os campos com os valores da coluna clicada e coloca em modo de edição""" self.item = item self.populate_entry(item) def populate_entry(self, item): """Preenche os campos com os itens""" self.notify.hide() for field in self.fields: if field.entry: if field.tabelacombo: field.entry.select_item_by_label(str(getattr(item, field.field_name))) else: field.entry.set_text(str(getattr(item, field.field_name))) self.set_edit_mode(True) def clear_entry(self): """Limpa os campos apos edicao ou cancelar""" for field in self.fields: if field.entry: field.entry.set_text('') self.set_edit_mode(False) def on_selection_changed(self, list, selection): """Verifica se o usuario editou os campos e não salvou""" if self.do_nothing: self.do_nothing = False return self.selection = selection response = False if self.editing == True: for field in self.fields: if field.entry: if not field.identificador: user_edited = field.entry.get_text() #Se for um novo item if self.editing_new == True: if user_edited: response = self.ask_to_save(field) if response == True: self.salvar(None) else: self.cancel(None) else: if response: self.do_nothing = True self.listview.select(self.item) else: self.cancel(None) #Se editando item da lista elif self.editing == True: field_in_list = (str(getattr(self.item, field.field_name))) if user_edited != field_in_list: response = self.ask_to_save(field) if response == True: self.salvar(None) else: self.cancel(None) else: if response: self.do_nothing = True self.listview.select(self.item) def ask_to_save(self, field): """Pergunta ao usuario sobre a edicao de campos""" #Criando um novo if self.editing_new : question =str('Deseja salvar %s ?') for field in self.fields: if field.searchable: registro = quote(str(field.entry.get_text())) #Editando else: question =str('Deseja salvar alterações em %s ?') registro = quote(str(getattr(self.item, field.field_name))) response = yesno((question) % (registro,), parent=None, default=gtk.RESPONSE_OK, buttons=((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL), (gtk.STOCK_SAVE, gtk.RESPONSE_OK))) return response == gtk.RESPONSE_OK def salvar(self, widget): """Insere ou edita um registro na tabela""" record = {} #Insere novo if self.editing_new : record = self.tabela.insert(self.fields) if record : for field in self.fields: if field.identificador: setattr(self.newobj, field.field_name, record['last_id']) elif field.tabelacombo: tabelacombo = getattr(self.controle, field.tabelacombo) descricao = tabelacombo.item_descricao(record[field.field_name]) setattr(self.newobj, field.field_name, descricao[0][0]) else: setattr(self.newobj, field.field_name, record[field.field_name]) self.editing_new = False self.lista.refresh() self.clear_entry() self.hide_notify() #Edita else: record = self.tabela.update(self.fields, self.item) if record: for field in self.fields: if field.identificador: pass elif field.tabelacombo: tabelacombo = getattr(self.controle, field.tabelacombo) descricao = tabelacombo.item_descricao(record[field.field_name]) try: setattr(self.item, field.field_name, descricao[0][0]) except: setattr(self.item, field.field_name, descricao) else: setattr(self.item, field.field_name, record[field.field_name]) self.lista.refresh() self.clear_entry() self.hide_notify() def hide_notify(self): self.notify.hide()
class ListContainer(gtk.HBox): """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` """ 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 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) 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.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 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 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()