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 PropertiesAdminGtkNode(BaseAdminGtkNode): gladeFile = os.path.join('flumotion', 'component', 'base', 'properties.glade') def __init__(self, state, admin): BaseAdminGtkNode.__init__(self, state, admin, title=_("Properties")) def haveWidgetTree(self): self.widget = gtk.VBox() self.widget.set_border_width(6) self.properties = ObjectList( [Column('name'), Column('value')]) self.properties.set_size_request(-1, 200) self.widget.pack_start(self.properties, False, False) self.properties.show() properties = self.state.get('config')['properties'] propertyNames = properties.keys()[:] propertyNames.sort() for name in propertyNames: self.properties.append( Settable(name=name, value=properties[name])) return self.widget
def add_tab(self, name): box = gtk.HBox() box.set_border_width(6) box.show() olist = ObjectList() box.pack_start(olist) olist.show() self.history_notebook.append_page(box, gtk.Label(name)) return olist
def add_tab(self, name): box = gtk.HBox() box.set_border_width(6) box.show() olist = ObjectList() box.pack_start(olist) olist.show() self.history_notebook.append_page(box, gtk.Label(name)) return olist
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()
def _create_field_list(self): items = ObjectList([Column('description', width=200), Column('len', data_type=int, editable=True)]) items.enable_dnd() items.set_size_request(200, -1) descriptions = {} invoice_fields = get_invoice_fields() for invoice_field in sorted(invoice_fields, key=operator.attrgetter('name')): items.append( Settable(description=invoice_field.get_description(), name=invoice_field.name, len=invoice_field.length)) descriptions[invoice_field.name] = invoice_field.description self._field_descriptions = descriptions self.left_vbox.pack_end(items, True, True) items.show()
class DeviceConstantsDialog(BasicDialog): size = (500, 300) def __init__(self, store, printer): self._constant_slave = None self.store = store self.printer = printer BasicDialog.__init__(self, hide_footer=False, title='edit', size=self.size) self.main.set_border_width(6) self._create_ui() def _create_ui(self): hbox = Gtk.HBox() self.klist = ObjectList([Column('name')]) self.klist.set_size_request(150, -1) self.klist.get_treeview().set_headers_visible(False) self.klist.connect('selection-changed', self._on_klist__selection_changed) hbox.pack_start(self.klist, True, True, 0) hbox.show() for name, ctype in [(_(u'Units'), DeviceConstant.TYPE_UNIT), (_(u'Tax'), DeviceConstant.TYPE_TAX), (_(u'Payments'), DeviceConstant.TYPE_PAYMENT)]: self.klist.append(Settable(name=name, type=ctype)) self.klist.show() self._constant_slave = _DeviceConstantsList(self.store, self.printer) self._constant_slave.switch(DeviceConstant.TYPE_UNIT) hbox.pack_start(self._constant_slave.get_toplevel(), True, True, 0) # FIXME: redesign BasicDialog self.main.remove(self.main_label) self.main.add(hbox) hbox.show_all() def _on_klist__selection_changed(self, klist, selected): self._constant_slave.switch(selected.type)
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()
def _create_field_list(self): items = ObjectList([ Column('description', width=200), Column('len', data_type=int, editable=True) ]) items.enable_dnd() items.set_size_request(200, -1) descriptions = {} invoice_fields = get_invoice_fields() for invoice_field in sorted(invoice_fields, key=operator.attrgetter('name')): items.append( Settable(description=invoice_field.get_description(), name=invoice_field.name, len=invoice_field.length)) descriptions[invoice_field.name] = invoice_field.description self._field_descriptions = descriptions self.left_vbox.pack_end(items, True, True) items.show()
class DeviceConstantsDialog(BasicDialog): size = (500, 300) def __init__(self, store, printer): self._constant_slave = None self.store = store self.printer = printer BasicDialog.__init__(self, hide_footer=False, title='edit', size=self.size) self.main.set_border_width(6) self._create_ui() def _create_ui(self): hbox = gtk.HBox() self.klist = ObjectList([Column('name')]) self.klist.set_size_request(150, -1) self.klist.get_treeview().set_headers_visible(False) self.klist.connect('selection-changed', self._on_klist__selection_changed) hbox.pack_start(self.klist) hbox.show() for name, ctype in [(_(u'Units'), DeviceConstant.TYPE_UNIT), (_(u'Tax'), DeviceConstant.TYPE_TAX), (_(u'Payments'), DeviceConstant.TYPE_PAYMENT)]: self.klist.append(Settable(name=name, type=ctype)) self.klist.show() self._constant_slave = _DeviceConstantsList(self.store, self.printer) self._constant_slave.switch(DeviceConstant.TYPE_UNIT) hbox.pack_start(self._constant_slave.get_toplevel()) # FIXME: redesign BasicDialog self.main.remove(self.main_label) self.main.add(hbox) hbox.show_all() def _on_klist__selection_changed(self, klist, selected): self._constant_slave.switch(selected.type)
class PropertiesAdminGtkNode(BaseAdminGtkNode): gladeFile = os.path.join('flumotion', 'component', 'base', 'properties.glade') uiStateHandlers = None _properties = {} def __init__(self, state, admin): BaseAdminGtkNode.__init__(self, state, admin, title=_("Properties")) def haveWidgetTree(self): self.widget = gtk.VBox() self.widget.set_border_width(6) self.properties = ObjectList( [Column('name'), Column('value')]) self.properties.set_size_request(-1, 200) self.widget.pack_start(self.properties, False, False) self.properties.show() self._reloadProperties(self.state.get('config')['properties']) return self.widget # IStateListener Interface def stateSet(self, object, key, value): if key == 'properties': self._reloadProperties(value) ### Private methods def _reloadProperties(self, properties): if properties is None: return self.properties.clear() propertyNames = properties.keys()[:] propertyNames.sort() for name in propertyNames: self.properties.append( Settable(name=name, value=properties[name]))
class DeliveryEditor(BaseEditor): """An editor for :class:`stoqlib.domain.sale.Delivery`""" title = _("Delivery editor") gladefile = 'DeliveryEditor' size = (700, 500) model_type = Delivery model_name = _('Delivery') form_holder_name = 'forms' form_columns = 2 @cached_property() def fields(self): # Only users with admin or purchase permission can modify transporters user = api.get_current_user(self.store) can_modify_transporter = any(( user.profile.check_app_permission(u'admin'), user.profile.check_app_permission(u'purchase'), )) freight_types = [(v, k) for k, v in Delivery.freights.items()] states = [(v, v) for v in api.get_l10n_field('state').state_list] return collections.OrderedDict( recipient_str=TextField(_("Recipient"), proxy=True, editable=False), transporter_id=PersonField(_("Transporter"), proxy=True, person_type=Transporter, can_add=can_modify_transporter, can_edit=can_modify_transporter), address=AddressField(_("Address"), proxy=True, mandatory=True), is_sent_check=BoolField(_("Was sent to deliver?")), send_date=DateField(_("Send date"), mandatory=True, proxy=True), tracking_code=TextField(_("Tracking code"), proxy=True), freight_type=ChoiceField(_("Freight type"), proxy=True, values=freight_types), volumes_kind=TextField(_("Volumes kind"), proxy=True), volumes_quantity=IntegerField(_("Volumes quantity"), proxy=True), volumes_net_weight=NumericField(_("Volumes net weight"), proxy=True, digits=3), volumes_gross_weight=NumericField(_("Volumes gross weight"), proxy=True, digits=3), vehicle_license_plate=TextField(_("Vehicle license plate"), proxy=True), vehicle_state=ChoiceField(_("Vehicle state"), proxy=True, use_entry=True, values=states), vehicle_registration=TextField(_("Vehicle registration"), proxy=True), is_received_check=BoolField(_("Was received by recipient?")), receive_date=DateField(_("Receive date"), mandatory=True, proxy=True), empty=EmptyField(), ) def __init__(self, store, *args, **kwargs): self._configuring_proxies = False super(DeliveryEditor, self).__init__(store, *args, **kwargs) # # BaseEditor Hooks # def setup_proxies(self): self._configuring_proxies = True self._setup_widgets() self._update_status_widgets() self._configuring_proxies = False def setup_slaves(self): self.delivery_items = ObjectList( columns=self._get_delivery_items_columns(), objects=self.model.delivery_items, ) self.delivery_items_holder.add(self.delivery_items) self.delivery_items.show() # # Private # def _setup_widgets(self): for widget in (self.receive_date, self.send_date, self.tracking_code): widget.set_sensitive(False) def _update_status_widgets(self): if self.model.status == Delivery.STATUS_INITIAL: for widget in [self.is_sent_check, self.is_received_check]: widget.set_active(False) elif self.model.status == Delivery.STATUS_SENT: self.is_sent_check.set_active(True) self.is_received_check.set_active(False) elif self.model.status == Delivery.STATUS_RECEIVED: for widget in [self.is_sent_check, self.is_received_check]: widget.set_active(True) else: raise ValueError(_("Invalid status for %s") % ( self.model.__class__.__name__)) def _get_delivery_items_columns(self): return [ Column('sellable.description', title=_('Products to deliver'), data_type=str, expand=True, sorted=True), Column('quantity', title=_('Quantity'), data_type=decimal.Decimal, format_func=format_quantity), ] # # Callbacks # def on_is_sent_check__toggled(self, button): active = button.get_active() # When delivered, don't let user change transporter or address self.transporter_id.set_sensitive(not active) self.address.set_sensitive(not active) for widget in [self.send_date, self.tracking_code]: widget.set_sensitive(active) if not self.model.send_date: self.send_date.update(localtoday().date()) if self._configuring_proxies: # Do not change status above return if active: self.model.send(api.get_current_user(self.store)) else: self.model.set_initial() def on_is_received_check__toggled(self, button): active = button.get_active() self.receive_date.set_sensitive(active) # If it was received, don't let the user unmark is_sent_check self.is_sent_check.set_sensitive(not active) if not self.is_sent_check.get_active(): self.is_sent_check.set_active(True) if not self.model.receive_date: self.receive_date.update(localtoday().date()) if self._configuring_proxies: # Do not change status above return if active: self.model.receive() else: # We have to change this status manually because set_sent won't # allow us to change from RECEIVED to it. Also, it we would call # set_sent it would overwrite the sent_date that we set previously self.model.status = self.model.STATUS_SENT
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 FormFieldEditor(BasicDialog): size = (700, 400) title = _("Form fields") def __init__(self, store): self.store = store BasicDialog.__init__(self, size=FormFieldEditor.size, title=FormFieldEditor.title) self._create_ui() def _create_ui(self): hbox = gtk.HBox() self.main.remove(self.main.get_child()) self.main.add(hbox) hbox.show() self.forms = ObjectList( [Column('description', title=_('Description'), sorted=True, expand=True, format_func=stoqlib_gettext)], self.store.find(UIForm), gtk.SELECTION_BROWSE) self.forms.connect('selection-changed', self._on_forms__selection_changed) self.forms.set_headers_visible(False) self.forms.set_size_request(200, -1) hbox.pack_start(self.forms, False, False) self.forms.show() box = gtk.VBox() hbox.pack_start(box) box.show() self.fields = ObjectList(self._get_columns(), [], gtk.SELECTION_BROWSE) box.pack_start(self.fields) self.fields.show() box.show() def _on_forms__selection_changed(self, forms, form): if not form: return self.fields.add_list(self.store.find(UIField, ui_form=form), clear=True) def _get_columns(self): return [Column('description', title=_('Description'), data_type=str, expand=True, sorted=True, format_func=stoqlib_gettext), Column('visible', title=_('Visible'), data_type=bool, width=120, editable=True), Column('mandatory', title=_('Mandatory'), data_type=bool, width=120, editable=True)] def confirm(self, *args): self.store.confirm(True) BasicDialog.confirm(self, *args) info(_("Changes will be applied after all instances of Stoq are restarted.")) def cancel(self, *args): self.store.rollback(close=False) BasicDialog.confirm(self, *args)
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() 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 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 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 FormFieldEditor(BasicDialog): size = (700, 400) title = _("Form fields") def __init__(self, store): self.store = store BasicDialog.__init__(self, size=FormFieldEditor.size, title=FormFieldEditor.title) self._create_ui() def _create_ui(self): hbox = gtk.HBox() self.main.remove(self.main.get_child()) self.main.add(hbox) hbox.show() self.forms = ObjectList( [Column('description', title=_('Description'), sorted=True, expand=True, format_func=stoqlib_gettext)], self.store.find(UIForm), gtk.SELECTION_BROWSE) self.forms.connect('selection-changed', self._on_forms__selection_changed) self.forms.set_headers_visible(False) self.forms.set_size_request(200, -1) hbox.pack_start(self.forms, False, False) self.forms.show() box = gtk.VBox() hbox.pack_start(box) box.show() self.fields = ObjectList(self._get_columns(), [], gtk.SELECTION_BROWSE) box.pack_start(self.fields) self.fields.show() box.show() def _on_forms__selection_changed(self, forms, form): if not form: return self.fields.add_list(self.store.find(UIField, ui_form=form), clear=True) def _get_columns(self): return [Column('description', title=_('Description'), data_type=str, expand=True, sorted=True, format_func=stoqlib_gettext), Column('visible', title=_('Visible'), data_type=bool, width=120, editable=True), Column('mandatory', title=_('Mandatory'), data_type=bool, width=120, editable=True)] def confirm(self, *args): self.store.confirm(True) BasicDialog.confirm(self, *args) def cancel(self, *args): self.store.rollback(close=False) BasicDialog.confirm(self, *args)
class CreateDeliveryEditor(BaseEditor): """A fake delivery editor implementation. This is used to get get information for creating a delivery, without really creating a it. """ model_name = _('Delivery') model_type = CreateDeliveryModel form_holder_name = 'forms' gladefile = 'CreateDeliveryEditor' title = _('New Delivery') form_columns = 2 size = (750, 550) @cached_property() def fields(self): # Only users with admin or purchase permission can modify transporters user = api.get_current_user(self.store) can_modify_transporter = any(( user.profile.check_app_permission(u'admin'), user.profile.check_app_permission(u'purchase'), )) return collections.OrderedDict( client=PersonQueryField(_("Client"), proxy=True, mandatory=True, person_type=Client), transporter_id=PersonField(_("Transporter"), proxy=True, person_type=Transporter, can_add=can_modify_transporter, can_edit=can_modify_transporter), address=AddressField(_("Address"), proxy=True, mandatory=True), price=PriceField(_("Delivery cost"), proxy=True), estimated_fix_date=DateField(_("Estimated delivery date"), proxy=True), ) def __init__(self, store, model=None, sale_items=None): self.sale_items = sale_items self._deliver_items = [] if not model: for sale_item in sale_items: sale_item.deliver = True else: model.create_savepoint() # Store this information for later rollback. for sale_item in sale_items: self._deliver_items.append(sale_item.deliver) BaseEditor.__init__(self, store, model) self._setup_widgets() def _setup_widgets(self): self.additional_info_label.set_size('small') self.additional_info_label.set_color('red') self.register_validate_function(self._validate_widgets) self.set_description(self.model_name) self._update_widgets() def _validate_widgets(self, validation_value): if validation_value: validation_value = any(item.deliver for item in self.items) self.refresh_ok(validation_value) def _update_widgets(self): if self.model.notes: self.additional_info_label.show() else: self.additional_info_label.hide() def _get_sale_items_columns(self): return [Column('code', title=_('Code'), data_type=str), Column('description', title=_('Description'), data_type=str, expand=True), Column('quantity', title=_('Quantity'), data_type=decimal.Decimal, format_func=format_quantity), Column('deliver', title=_('Deliver'), data_type=bool, editable=True)] # # Callbacks # def on_additional_info_button__clicked(self, button): if run_dialog(NoteEditor, self, self.store, self.model, 'notes', title=_('Delivery Instructions')): self._update_widgets() def on_estimated_fix_date__validate(self, widget, date): if date < localtoday().date(): return ValidationError(_("Expected delivery date must " "be set to a future date")) def on_price__validate(self, widget, price): if price < 0: return ValidationError( _("The Delivery cost must be a positive value.")) def on_client__content_changed(self, entry): client = entry.read() if client is None: return self.fields['address'].set_from_client(client) def on_transporter_id__validate(self, widget, transporter_id): transporter = self.store.get(Transporter, transporter_id) return StockOperationPersonValidationEvent.emit(transporter.person, type(transporter)) def on_client__validate(self, widget, client): return StockOperationPersonValidationEvent.emit(client.person, type(client)) def _on_items__cell_edited(self, items, item, attribute): self.force_validation() # # BaseEditor hooks # def create_model(self, store): price = sysparam.get_object(store, 'DELIVERY_SERVICE').sellable.price return CreateDeliveryModel(price=price) def setup_slaves(self): self.items = ObjectList(columns=self._get_sale_items_columns(), objects=self.sale_items) self.items.connect('cell-edited', self._on_items__cell_edited) self.addition_list_holder.add(self.items) self.items.show() def on_cancel(self): # FIXME: When Kiwi allows choosing proxies to save upon confirm, apply # that here instead of making this rollback by hand. Bug 5415. self.model.rollback_to_savepoint() if self._deliver_items: for sale_item, deliver in zip(self.sale_items, self._deliver_items): sale_item.deliver = deliver def on_confirm(self): estimated_fix_date = self.estimated_fix_date.read() for sale_item in self.sale_items: sale_item.estimated_fix_date = estimated_fix_date
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 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 DokuwikiView(GladeDelegate): """ A dokuwiki editor window """ def __init__(self): GladeDelegate.__init__(self, gladefile="pydoku", delete_handler=self.quit_if_last) self.setup_wikitree() self.setup_attachments() self.setup_side() self.setup_sourceview() self.setup_htmlview() self.page_edit = self.view.notebook1.get_nth_page(0) self.page_view = self.view.notebook1.get_nth_page(1) self.page_attach = self.view.notebook1.get_nth_page(2) self.show_all() def quit_if_last(self, *args): self.htmlview.destroy() # for some reason has to be deleted explicitly GladeDelegate.quit_if_last(self) # general interface functions def post(self, text): id = self.view.statusbar.get_context_id("zap") self.view.statusbar.push(id, text) # setup functions def setup_side(self): columns = ['user', 'sum', 'type', 'version', 'ip'] columns = [Column(s) for s in columns] self.versionlist = ObjectList(columns) self.view.side_vbox.pack_start(gtk.Label('Version Log:'), False, False) self.view.side_vbox.add(self.versionlist) self.view.side_vbox.pack_start(gtk.Label('BackLinks:'), False, False) self.backlinks = ObjectList([Column('name')]) self.view.side_vbox.add(self.backlinks) def setup_attachments(self): columns = ['id', 'size', 'lastModified', 'writable', 'isimg', 'perms'] columns = [Column(s) for s in columns] self.attachmentlist = ObjectList(columns) self.view.attachments_vbox.add(self.attachmentlist) def setup_wikitree(self): columns = ['name', 'id', 'lastModified', 'perms', 'size'] columns = [Column(s) for s in columns] self.objectlist = ObjectTree(columns) self.objectlist.connect("selection-changed", self.selected) self.view.vbox2.add(self.objectlist) def setup_htmlview(self): self.htmlview = gtkmozembed.MozEmbed() self.view.html_scrolledwindow.add(self.htmlview) self.htmlview.realize() self.htmlview.show() def setup_sourceview(self): self.buffer = DokuwikiBuffer(table) self.editor = gtksourceview.SourceView(self.buffer) accel_group = gtk.AccelGroup() self.get_toplevel().add_accel_group(accel_group) self.editor.add_accelerator("paste-clipboard", accel_group, ord('v'), gtk.gdk.CONTROL_MASK, 0) self.editor.add_accelerator("copy-clipboard", accel_group, ord('c'), gtk.gdk.CONTROL_MASK, 0) self.editor.add_accelerator("cut-clipboard", accel_group, ord('x'), gtk.gdk.CONTROL_MASK, 0) #self.editor = gtk.TextView(self.buffer) self.editor.set_left_margin(5) self.editor.set_right_margin(5) self.editor.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.view.scrolledwindow1.add(self.editor) # dokuwiki operations def get_version(self): version = self._rpc.dokuwiki.getVersion() self.view.version.set_text(version) def get_pagelist(self): pages = self._rpc.wiki.getAllPages() self._sections = {} self.objectlist.clear() for page in pages: self.add_page(page) self.view.new_page.set_sensitive(True) self.view.delete_page.set_sensitive(True) def get_attachments(self, ns): attachments = self._rpc.wiki.getAttachments(ns, {}) attachments = [DictWrapper(s) for s in attachments] self.attachmentlist.add_list(attachments) def get_backlinks(self, pagename): backlinks = self._rpc.wiki.getBackLinks(pagename) backlinks = [Section(s) for s in backlinks] self.backlinks.add_list(backlinks) def get_versions(self, pagename): versionlist = self._rpc.wiki.getPageVersions(pagename, 0) versionlist = [DictWrapper(s) for s in versionlist] self.versionlist.add_list(versionlist) def get_htmlview(self, pagename): text = self._rpc.wiki.getPageHTML(pagename) self.htmlview.render_data(text, len(text), self.url.get_text(), 'text/html') # XXX following is for gtkhtml (not used) #self.document.clear() #self.document.open_stream('text/html') #self.document.write_stream(text) #self.document.close_stream() def put_page(self, text, summary, minor): pars = {} if summary: pars['sum'] = summary if minor: pars['minor'] = minor self._rpc.wiki.putPage(self.current, text, pars) if not self.current in self._sections: self.add_page({"id":self.current}) # put a page into the page tree def add_page(self, page): name = page["id"] path = name.split(":") prev = None for i,pathm in enumerate(path): if i == len(path)-1: # a page new = DictWrapper(page, pathm) self._sections[name] = new self.objectlist.append(prev, new, False) else: # a namespace part_path = ":".join(path[:i+1]) if not part_path in self._sections: new = Section(pathm, part_path) self._sections[part_path] = new self.objectlist.append(prev, new, False) else: new = self._sections[part_path] prev = new # page selected callback def selected(self, widget, object): if not object: return if isinstance(object, Section): self.get_attachments(object.id) if not isinstance(object, DictWrapper): return text = self._rpc.wiki.getPage(object.id) self.current = object.id self.buffer.add_text(text) self.get_htmlview(self.current) self.get_backlinks(object.id) self.get_versions(object.id) # kiwi interface callbacks def on_view_edit__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_edit, gtk.Label('edit'), 0) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_edit)) def on_view_view__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_view, gtk.Label('view'), 1) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_view)) def on_view_attachments__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_attach, gtk.Label('attach')) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_attach)) def on_view_extra__toggled(self, widget): if widget.get_active(): self.backlinks.show() self.versionlist.show() self.view.hpaned2.set_position(self._prevpos) else: self.backlinks.hide() self.versionlist.hide() self._prevpos = self.view.hpaned2.get_position() self.view.hpaned2.set_position(self.view.hpaned2.allocation.width) def on_button_list__clicked(self, *args): self.post("Connecting...") dialog = ModalDialog("User Details") # prepare widgets = {} items = ["user", "password"] for i,item in enumerate(items): widgets[item] = gtk.Entry() if i == 1: widgets[item].set_visibility(False) hbox = gtk.HBox() hbox.pack_start(gtk.Label(item+': ')) hbox.add(widgets[item]) dialog.vbox.add(hbox) dialog.show_all() # run response = dialog.run() user = widgets['user'].get_text() password = widgets['password'].get_text() dialog.destroy() if not response == gtk.RESPONSE_ACCEPT: return # following commented line is for gtkhtml (not used) #simplebrowser.currentUrl = self.view.url.get_text() # handle response params = urlencode({'u':user,'p':password}) fullurl = self.view.url.get_text() + "/lib/exe/xmlrpc.php?"+ params self._rpc = ServerProxy(fullurl) try: self.get_version() except: self.post("Failure to connect") self.get_pagelist() self.post("Connected") def on_delete_page__clicked(self, *args): dialog = ModalDialog("Are you sure?") response = dialog.run() if response == gtk.RESPONSE_ACCEPT: value = self._sections[self.current] sel = self.objectlist.remove(value) self._rpc.wiki.putPage(self.current, "", {}) self.current = None dialog.destroy() def on_new_page__clicked(self, *args): dialog = ModalDialog("Name for the new page") text_w = gtk.Entry() text_w.show() response = [] dialog.vbox.add(text_w) response = dialog.run() if response == gtk.RESPONSE_ACCEPT: text = text_w.get_text() if text: self.current = text dialog.destroy() def on_button_h1__clicked(self, *args): self.buffer.set_style('h1') def on_button_h2__clicked(self, *args): self.buffer.set_style('h2') def on_button_h3__clicked(self, *args): self.buffer.set_style('h3') def on_button_h4__clicked(self, *args): self.buffer.set_style('h4') def on_button_h5__clicked(self, *args): self.buffer.set_style('h5') def on_button_h6__clicked(self, *args): self.buffer.set_style('h6') def on_button_bold__clicked(self, *args): self.buffer.set_style('bold') def on_button_italic__clicked(self, *args): self.buffer.set_style('italic') def on_button_clear_style__clicked(self, *args): self.buffer.clear_style() def on_button_save__clicked(self, *args): self.post("Saving...") dialog = ModalDialog("Commit message") entry = gtk.Entry() minor = gtk.CheckButton("Minor") dialog.vbox.add(gtk.Label("Your attention to detail\nIs greatly appreciated")) dialog.vbox.add(entry) dialog.vbox.add(minor) dialog.show_all() response = dialog.run() if response == gtk.RESPONSE_ACCEPT: text = self.buffer.process_text() self.put_page(text, entry.get_text(), minor.get_active()) self.get_htmlview(self.current) self.get_versions(self.current) self.post("Saved") dialog.destroy() # unused stuff def request_url(self, document, url, stream): f = simplebrowser.open_url(url) stream.write(f.read()) def setup_htmlview_gtkhtml(self): # XXX not used now self.document = gtkhtml2.Document() self.document.connect('request_url', self.request_url) self.htmlview = gtkhtml2.View() self.htmlview.set_document(self.document) def setup_sourceview_gtksourceview(self): # XXX not used now self.buffer = gtksourceview.Buffer(table) self.editor = gtksourceview.View(self.buffer) if True: self.editor.set_show_line_numbers(True) lm = gtksourceview.LanguageManager() self.editor.set_indent_on_tab(True) self.editor.set_indent_width(4) self.editor.set_property("auto-indent", True) self.editor.set_property("highlight-current-line", True) self.editor.set_insert_spaces_instead_of_tabs(True) lang = lm.get_language("python") self.buffer.set_language(lang) self.buffer.set_highlight_syntax(True)
class DokuwikiView(GladeDelegate): """ A dokuwiki editor window """ def __init__(self): GladeDelegate.__init__(self, gladefile="pydoku", delete_handler=self.quit_if_last) self._icons = {} self.throbber_icon = Throbber(self.view.throbber) self.setup_wikitree() self.setup_wikislist() self.setup_attachments() self.setup_lastchanges() self.setup_side() self.setup_sourceview() self.setup_htmlview() self.page_edit = self.view.notebook1.get_nth_page(0) self.page_view = self.view.notebook1.get_nth_page(1) self.page_attach = self.view.notebook1.get_nth_page(2) self.show_all() if len(cfg.getChildren()): wiki = cfg.getChildren()[0] self.connect(wiki.url, wiki.user, wiki.password) self.wiki = wiki if wiki.current: self.load_page(wiki.current) # quit override to work with twisted def quit_if_last(self, *args): self.htmlview.destroy() # for some reason has to be deleted explicitly windows = [toplevel for toplevel in gtk.window_list_toplevels() if toplevel.get_property('type') == gtk.WINDOW_TOPLEVEL] if len(windows) == 1: reactor.stop() # general interface functions def post(self, text): id = self.view.statusbar.get_context_id("zap") self.view.statusbar.push(id, text) # setup functions def setup_wikislist(self): columns = [Column('url',format_func=self.get_favicon,data_type=gtk.gdk.Pixbuf,icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)] self.wikislist = ObjectList(columns) columns.append(Column('url', title='Wiki', column='url')) self.wikislist.set_columns(columns) self.view.vbox2.pack_start(self.wikislist) self.view.vbox2.reorder_child(self.wikislist, 0) self.wikislist.add_list(cfg.getChildren()) self.wikislist.connect("selection-changed", self.wiki_selected) threads.deferToThread(self.download_favicons, cfg) def download_favicons(self, cfg): for wiki in cfg.getChildren(): self.download_favicon(wiki) def download_favicon(self, wiki): import urllib icon_url = wiki.url+"/lib/tpl/sidebar/images/favicon.ico" filename, headers = urllib.urlretrieve(icon_url, "/tmp/ico.ico") if headers["Content-Type"] == 'image/x-icon': self.add_favicon(wiki, filename) def get_favicon(self, wiki_url): return self._icons.get(wiki_url, page_icon) def add_favicon(self, wiki, filename): pixbuf = gtk.gdk.pixbuf_new_from_file(filename) pixbuf = pixbuf.scale_simple(16,16,gtk.gdk.INTERP_BILINEAR) self._icons[wiki.url] = pixbuf self.objectlist.refresh() def setup_side(self): columns = ['sum', 'user', 'type', 'version', 'ip'] columns = [Column(s) for s in columns] self.versionlist = ObjectList(columns) self.view.side_vbox.pack_start(gtk.Label('Version Log:'), False, False) self.view.side_vbox.add(self.versionlist) self.versionlist.connect("selection-changed", self.version_selected) self.view.side_vbox.pack_start(gtk.Label('BackLinks:'), False, False) self.backlinks = ObjectList([Column('name')]) self.backlinks.connect("selection-changed", self.change_selected) self.view.side_vbox.add(self.backlinks) def setup_attachments(self): columns = ['id', 'size', 'lastModified', 'writable', 'isimg', 'perms'] columns = [Column(s) for s in columns] self.attachmentlist = ObjectList(columns) self.view.attachments_vbox.add(self.attachmentlist) def setup_lastchanges(self): columns = ['name', 'author', 'lastModified', 'perms', 'version', 'size'] columns = [Column(s) for s in columns] columns.append(Column('lastModified', sorted=True, order=gtk.SORT_DESCENDING)) self.lastchangeslist = ObjectList(columns) self.lastchangeslist.connect("selection-changed", self.change_selected) self.view.side_vbox.add(self.lastchangeslist) def setup_wikitree(self): columns = ['id', 'lastModified', 'perms', 'size'] columns = [Column(s) for s in columns] columns.insert(0, Column('icon', title='name', data_type=gtk.gdk.Pixbuf)) self.objectlist = ObjectTree(columns) columns.insert(1, Column('name', column='icon')) self.objectlist.set_columns(columns) self.objectlist.connect("selection-changed", self.selected) self.view.vbox2.add(self.objectlist) def html_realized(self, widget): if self.wiki and self.wiki.current: self.get_htmlview(self.wiki.current) def setup_htmlview(self): self.htmlview = gtkmozembed.MozEmbed() self.view.html_scrolledwindow.add_with_viewport(self.htmlview) self.htmlview.connect('realize', self.html_realized) #self.htmlview.set_size_request(800,600) #self.htmlview.realize() #self.view.html_scrolledwindow.show_all() #self.htmlview.show() def setup_sourceview(self): self.buffer = DokuwikiBuffer(table) self.editor = gtksourceview.SourceView(self.buffer) #self.editor.set_show_line_numbers(True) accel_group = gtk.AccelGroup() self.get_toplevel().add_accel_group(accel_group) self.editor.add_accelerator("paste-clipboard", accel_group, ord('v'), gtk.gdk.CONTROL_MASK, 0) self.editor.add_accelerator("copy-clipboard", accel_group, ord('c'), gtk.gdk.CONTROL_MASK, 0) self.editor.add_accelerator("cut-clipboard", accel_group, ord('x'), gtk.gdk.CONTROL_MASK, 0) #self.editor = gtk.TextView(self.buffer) self.editor.set_left_margin(5) self.editor.set_right_margin(5) self.editor.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.view.scrolledwindow1.add(self.editor) lm = gtksourceview.SourceLanguagesManager() langs = lm.get_available_languages() lang_diffs = filter(lambda s: s.get_name() == 'Diff', langs) if lang_diffs: self.buffer.set_language(lang_diffs[0]) # dokuwiki operations def _getVersion(self): return self._rpc.dokuwiki.getVersion() def get_version(self): return threads.deferToThread(self._getVersion) def get_pagelist(self): print "getpagelist1" pages = self._rpc.wiki.getAllPages() self._sections = {} self.objectlist.clear() print "getpagelist1.5" print "PAGES",pages for page in pages: self.add_page(page) print "getpagelist2" self.view.new_page.set_sensitive(True) self.view.delete_page.set_sensitive(True) if self.wiki.current: self.set_selection(self.wiki.current) print "getpagelist3" # XXX self.get_recent_changes() print "getpagelist2" def _getRecentChanges(self): return self._rpc.wiki.getRecentChanges(int(time.time()-(60*60*24*7*12))) def _gotRecentChanges(self, changes): changes = [DictWrapper(s) for s in changes] self.lastchangeslist.add_list(changes) def get_recent_changes(self): self.callDeferred(self._getRecentChanges, self._gotRecentChanges) def get_attachments(self, ns): attachments = self._rpc.wiki.getAttachments(ns, {}) attachments = [DictWrapper(s) for s in attachments] self.attachmentlist.add_list(attachments) def _getBackLinks(self, pagename): return self._rpc.wiki.getBackLinks(pagename) def _gotBackLinks(self, backlinks): backlinks = [Section(s) for s in backlinks] self.backlinks.add_list(backlinks) def get_backlinks(self, pagename): self.callDeferred(self._getBackLinks, self._gotBackLinks, pagename) def _getVersions(self, pagename): return self._rpc.wiki.getPageVersions(pagename, 0) def _gotVersions(self, versionlist): versionlist = [DictWrapper(s) for s in versionlist] self.versionlist.add_list(versionlist) def get_versions(self, pagename): self.callDeferred(self._getVersions, self._gotVersions, pagename) def _getHtmlData(self, pagename): text = self._rpc.wiki.getPageHTML(pagename) return text def _gotHtmlData(self, text): self.throbber_icon.stop() if not self.htmlview.window: return text = """<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head><body>"""+text+"</body>" self.htmlview.render_data(text, len(text), self.wiki.url, 'text/html') self.htmlview.realize() self.htmlview.show() def get_htmlview(self, pagename): self.throbber_icon.start() self.callDeferred(self._getHtmlData, self._gotHtmlData, pagename) #d.addErrback(self.someError) # XXX following is for gtkhtml (not used) #self.document.clear() #self.document.open_stream('text/html') #self.document.write_stream(text) #self.document.close_stream() def callDeferred(self, get_func, got_func, *args): d = threads.deferToThread(get_func, *args) d.addCallback(got_func) def _getEditText(self, pagename): return self._rpc.wiki.getPage(pagename) def _gotEditText(self, text): self.throbber_icon.stop() self.buffer.set_highlight(False) self.editor.set_editable(True) self.buffer.add_text(text) def _getDiffText(self, pagename, version, idx): return self._rpc.wiki.getPageVersion(pagename, version), idx def _gotDiffText(self, data): import difflib import StringIO text, idx = data self.textstack[idx] = text if not None in self.textstack: fromlines = self.textstack[0].split("\n") tolines = self.textstack[1].split("\n") diff_text = difflib.unified_diff(fromlines, tolines, "a", "b", self.versions[0], self.versions[1]) self.throbber_icon.stop() self.buffer.clear() str_buffer = StringIO.StringIO() for line in diff_text: str_buffer.write(line+'\n') str_buffer.seek(0) self.buffer.add_text(str_buffer.read()) self.buffer.set_highlight(True) self.editor.set_editable(False) def get_difftext(self, pagename, version, prev_version): self.throbber_icon.start() self.textstack = [None, None] self.versions = [version, prev_version] self.callDeferred(self._getDiffText, self._gotDiffText, pagename, prev_version, 0) self.callDeferred(self._getDiffText, self._gotDiffText, pagename, version, 1) def get_edittext(self, pagename): self.throbber_icon.start() self.callDeferred(self._getEditText, self._gotEditText, pagename) def put_page(self, text, summary, minor): pars = {} if summary: pars['sum'] = summary if minor: pars['minor'] = minor d = threads.deferToThread(self._rpc.wiki.putPage, self.wiki.current, text, pars) return d # put a page into the page tree def add_page(self, page): print page try: name = page["id"] path = name.split(":") prev = None for i, pathm in enumerate(path): if i == len(path)-1: # a page new = DictWrapper(page, pathm) self._sections[name] = new self.objectlist.append(prev, new, False) else: # a namespace part_path = ":".join(path[:i+1]) if not part_path in self._sections: new = Section(pathm, part_path) self._sections[part_path] = new self.objectlist.append(prev, new, False) else: new = self._sections[part_path] prev = new except: traceback.print_exc() def expand_to(self, pagename): path = pagename.split(":") for i, pathm in enumerate(path): if not i == len(path)-1: section = self._sections[":".join(path[:i+1])] self.view.objectlist.expand(section) def set_selection(self, pagename): obj = self._sections[pagename] self.expand_to(pagename) self.view.objectlist.select(obj, True) #self.selected(widget, obj) # page selected callback def wiki_selected(self, widget, wiki): self.connect(wiki.url, wiki.user, wiki.password) self.objectlist.clear() self.versionlist.clear() self.lastchangeslist.clear() self.backlinks.clear() self._sections = {} self.wiki = wiki self.buffer.clear() if wiki.current: self.load_page(wiki.current) def version_selected(self, widget, object): # yes, the previous item is the next in the widget if object == None: return previous = widget.get_next(object) if not previous: return prev_version = previous.version self.get_difftext(self.wiki.current, int(object.version), int(prev_version)) def change_selected(self, widget, object): if not object: return self.set_selection(object.name) def selected(self, widget, object): if not object: return if isinstance(object, Section): self.get_attachments(object.id) if not isinstance(object, DictWrapper): return self.wiki.current = object.id cfg.save() self.load_page(object.id) def load_page(self, pagename): self.get_edittext(pagename) self.get_htmlview(pagename) self.get_backlinks(pagename) self.get_versions(pagename) # kiwi interface callbacks def on_view_edit__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_edit, gtk.Label('edit'), 0) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_edit)) def on_view_view__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_view, gtk.Label('view'), 1) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_view)) def on_view_attachments__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_attach, gtk.Label('attach')) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_attach)) def on_view_extra__toggled(self, widget): if widget.get_active(): self.backlinks.show() self.versionlist.show() self.view.hpaned2.set_position(self._prevpos) else: self.backlinks.hide() self.versionlist.hide() self._prevpos = self.view.hpaned2.get_position() self.view.hpaned2.set_position(self.view.hpaned2.allocation.width) def on_button_add__clicked(self, *args): dialog = ModalDialog("User Details") # prepare widgets = {} items = ["url","user", "password"] for i, item in enumerate(items): widgets[item] = gtk.Entry() if i == 2: widgets[item].set_visibility(False) hbox = gtk.HBox() hbox.pack_start(gtk.Label(item+': ')) hbox.add(widgets[item]) dialog.vbox.add(hbox) dialog.show_all() # run response = dialog.run() user = widgets['user'].get_text() password = widgets['password'].get_text() url = widgets['url'].get_text() dialog.destroy() if not response == gtk.RESPONSE_ACCEPT: return self.wiki = cfg.new(Dokuwiki, url=url, user=user, password=password) cfg.addChild(self.wiki) cfg.save() self.connect(url, user, password) def get_full_url(self, url, user, password): try: if user and password: split_url = url.split('://') proto = split_url[0] base_url = split_url[1] return proto + '://' + user + ':' + password + '@' + base_url return url except: traceback.print_exc() def connect(self, url, user, password): # following commented line is for gtkhtml (not used) #simplebrowser.currentUrl = self.view.url.get_text() # handle response self.post("Connecting to " + url) params = urlencode({'u':user, 'p':password}) print self.get_full_url(url, user, password) fullurl = self.get_full_url(url, user, password) + "/lib/exe/xmlrpc.php?"+ params print "serverproxy1" self._rpc = ServerProxy(fullurl) print "serverproxy1" d = self.get_version() d.addCallback(self.connected) d.addErrback(self.error_connecting) def error_connecting(self, failure): self.post("Error connecting to " + self.wiki.url) print failure.getErrorMessage() def connected(self, version): print "connected1" self.view.version.set_text(version) print "connected1.5" self.get_pagelist() print "connected2" self.post("Connected") def on_delete_page__clicked(self, *args): dialog = ModalDialog("Are you sure?") response = dialog.run() if response == gtk.RESPONSE_ACCEPT: value = self._sections[self.wiki.current] sel = self.objectlist.remove(value) self._rpc.wiki.putPage(self.wiki.current, "", {}) self.wiki.current = '' cfg.save() dialog.destroy() def on_new_page__clicked(self, *args): dialog = ModalDialog("Name for the new page") text_w = gtk.Entry() text_w.show() response = [] dialog.vbox.add(text_w) response = dialog.run() if response == gtk.RESPONSE_ACCEPT: text = text_w.get_text() if text: self.wiki.current = text cfg.save() self.buffer.clear() dialog.destroy() def on_button_h1__clicked(self, *args): self.buffer.set_style('h1') def on_button_h2__clicked(self, *args): self.buffer.set_style('h2') def on_button_h3__clicked(self, *args): self.buffer.set_style('h3') def on_button_h4__clicked(self, *args): self.buffer.set_style('h4') def on_button_h5__clicked(self, *args): self.buffer.set_style('h5') def on_button_h6__clicked(self, *args): self.buffer.set_style('h6') def on_button_bold__clicked(self, *args): self.buffer.set_style('bold') def on_button_italic__clicked(self, *args): self.buffer.set_style('italic') def on_button_clear_style__clicked(self, *args): self.buffer.clear_style() def _pagePut(self, *args): if not self.wiki.current in self._sections: self.add_page({"id":self.wiki.current}) self.get_htmlview(self.wiki.current) self.get_versions(self.wiki.current) self.post("Saved") def on_button_save__clicked(self, *args): """ Save button callback """ dialog = ModalDialog("Commit message") entry = gtk.Entry() minor = gtk.CheckButton("Minor") dialog.vbox.add(gtk.Label("Your attention to detail\nis greatly appreciated")) dialog.vbox.add(entry) dialog.vbox.add(minor) dialog.show_all() response = dialog.run() if response == gtk.RESPONSE_ACCEPT: self.post("Saving...") text = self.buffer.process_text() self.throbber_icon.start() d = self.put_page(text, entry.get_text(), minor.get_active()) d.addCallback(self._pagePut) dialog.destroy() # unused stuff def request_url(self, document, url, stream): f = simplebrowser.open_url(url) stream.write(f.read()) def setup_htmlview_gtkhtml(self): # XXX not used now self.document = gtkhtml2.Document() self.document.connect('request_url', self.request_url) self.htmlview = gtkhtml2.View() self.htmlview.set_document(self.document)
class FormFieldEditor(BasicDialog): size = (700, 400) title = _("Form fields") def __init__(self, store): self.store = store BasicDialog.__init__(self, size=FormFieldEditor.size, title=FormFieldEditor.title) self._create_ui() def _create_ui(self): hbox = Gtk.HBox() self.main.remove(self.main.get_child()) self.main.add(hbox) hbox.show() self.forms = ObjectList( [Column('description', title=_('Description'), sorted=True, expand=True, format_func=stoqlib_gettext)], self.store.find(UIForm), Gtk.SelectionMode.BROWSE) self.forms.connect('selection-changed', self._on_forms__selection_changed) self.forms.set_headers_visible(False) self.forms.set_size_request(200, -1) hbox.pack_start(self.forms, False, False, 0) self.forms.show() box = Gtk.VBox() hbox.pack_start(box, True, True, 0) box.show() self.fields = ObjectList(self._get_columns(), [], Gtk.SelectionMode.BROWSE) box.pack_start(self.fields, True, True, 0) self.fields.show() box.show() def _on_forms__selection_changed(self, forms, form): if not form: return self.fields.add_list(self.store.find(UIField, ui_form=form), clear=True) self.fields.set_cell_data_func(self._uifield__cell_data_func) def _uifield__cell_data_func(self, column, renderer, obj, text): if isinstance(renderer, Gtk.CellRendererText): return text manager = get_plugin_manager() if manager.is_any_active(['nfe', 'nfce']): is_editable = obj.field_name not in [u'street', u'district', u'city', u'state', u'country', u'street_number'] renderer.set_property('sensitive', is_editable) renderer.set_property('activatable', is_editable) return text def _get_columns(self): return [Column('description', title=_('Description'), data_type=str, expand=True, sorted=True, format_func=stoqlib_gettext), Column('visible', title=_('Visible'), data_type=bool, width=120, editable=True), Column('mandatory', title=_('Mandatory'), data_type=bool, width=120, editable=True)] def confirm(self, *args): self.store.confirm(True) BasicDialog.confirm(self, *args) info(_("Changes will be applied after all instances of Stoq are restarted.")) def cancel(self, *args): self.store.rollback(close=False) BasicDialog.confirm(self, *args)
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 ChartDialog(gtk.Window): def __init__(self): self._js_data = None self._js_options = None self._current = None gtk.Window.__init__(self) self.set_size_request(800, 480) self.vbox = gtk.VBox() self.add(self.vbox) self.vbox.show() hbox = gtk.HBox() self.vbox.pack_start(hbox, False, False, 6) hbox.show() label = gtk.Label('Period:') hbox.pack_start(label, False, False, 6) label.show() self.chart_type = ProxyComboBox() self.chart_type.connect( 'content-changed', self._on_chart_type__content_changed) hbox.pack_start(self.chart_type, False, False, 6) self.chart_type.show() self.period_values = ProxyComboBox() self.period_values.connect( 'content-changed', self._on_period_values__content_changed) hbox.pack_start(self.period_values, False, False, 6) self.period_values.show() self._view = WebView() self._view.get_view().connect( 'load-finished', self._on_view__document_load_finished) self.vbox.pack_start(self._view, True, True) self.results = ObjectList() self.results.connect( 'row-activated', self._on_results__row_activated) self.vbox.pack_start(self.results, True, True) self._setup_daemon() @api.async def _setup_daemon(self): daemon = yield start_daemon() self._daemon_uri = daemon.base_uri proxy = daemon.get_client() yield proxy.callRemote('start_webservice') self.chart_type.prefill([ ('Year', 'YearlyPayments'), ('Month', 'MonthlyPayments'), ('Day', 'DailyPayments'), ]) @api.async def _invoke_chart(self, chart_type_name, **report_kwargs): def _get_chart_url(**kwargs): params = [] for key, value in kwargs.items(): params.append(key + '=' + str(value)) return '%s/web/chart.json?%s' % ( self._daemon_uri, '&'.join(params)) url = _get_chart_url(type=chart_type_name, **report_kwargs) page = yield getPage(url) data = json.loads(page) api.asyncReturn(data) def _render_chart(self, chart_class, response): self._render_javascript(chart_class, response) self._render_objectlist(chart_class, response) def _render_javascript(self, chart_class, response): ticks = [item['short_title'] for item in response['items']] self._js_data = response['data'] options = {} options['description'] = response['description'] options['series'] = [dict(label=c['title']) for c in chart_class.columns][1:] options['xaxis_ticks'] = ticks self._js_options = options self._view.load_uri('%s/web/static/chart.html' % ( self._daemon_uri,)) def _render_objectlist(self, chart_class, response): columns = [] for kwargs in chart_class.columns: kwargs = kwargs.copy() name = kwargs.pop('name') columns.append(Column(name, **kwargs)) self.results.set_columns(columns) items = [] for item in response['items']: settable = Settable(**item) settable.chart_class = chart_class items.append(settable) self.results.add_list(items, clear=True) self.results.show() def _load_finished(self): self._view.js_function_call( "plot", self._js_data, self._js_options) @api.async def _show_one(self, chart_type_name, start, end): chart_class = get_chart_class(chart_type_name) report_kwargs = dict(start=start.strftime('%Y-%m-%d'), end=end.strftime('%Y-%m-%d')) # Get chart datab response = yield self._invoke_chart(chart_type_name, **report_kwargs) self._render_chart(chart_class, response) def _update_period_values(self): chart_type_name = self.chart_type.get_selected() chart_class = get_chart_class(chart_type_name) values = chart_class.get_combo_labels() self.period_values.prefill(values) # # Callbacks # def _on_view__document_load_finished(self, view, frame): self._load_finished() def _on_chart_type__content_changed(self, combo): self._update_period_values() def _on_period_values__content_changed(self, combo): kind = self.chart_type.get_selected() value = self.period_values.get_selected() if not value: return start, end = value if self._current == (kind, start, end): return self._show_one(kind, start, end) self._current = kind, start, end def _on_results__row_activated(self, results, item): chart_type_name = item.chart_class.__name__ if chart_type_name == 'YearlyPayments': start = localdate(item.year, 1, 1).date() end = localdate(item.year, 12, 31).date() chart_type_name = 'MonthlyPayments' elif chart_type_name == 'MonthlyPayments': start = localdate(item.year, item.month, 1).date() end = start + relativedelta(days=31) chart_type_name = 'DailyPayments' else: return self._show_one(chart_type_name, start, end)
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 DeliveryEditor(BaseEditor): """An editor for :class:`stoqlib.domain.sale.Delivery`""" title = _("Delivery editor") gladefile = 'DeliveryEditor' size = (700, 500) model_type = Delivery model_name = _('Delivery') form_holder_name = 'forms' form_columns = 2 @cached_property() def fields(self): # Only users with admin or purchase permission can modify transporters user = api.get_current_user(self.store) can_modify_transporter = any(( user.profile.check_app_permission(u'admin'), user.profile.check_app_permission(u'purchase'), )) freight_types = [(v, k) for k, v in Delivery.freights.items()] states = [(v, v) for v in api.get_l10n_field('state').state_list] return collections.OrderedDict( recipient_str=TextField(_("Recipient"), proxy=True, editable=False), transporter_id=PersonField(_("Transporter"), proxy=True, person_type=Transporter, can_add=can_modify_transporter, can_edit=can_modify_transporter), address=AddressField(_("Address"), proxy=True, mandatory=True), is_sent_check=BoolField(_("Was sent to deliver?")), send_date=DateField(_("Send date"), mandatory=True, proxy=True), tracking_code=TextField(_("Tracking code"), proxy=True), freight_type=ChoiceField(_("Freight type"), proxy=True, values=freight_types), volumes_kind=TextField(_("Volumes kind"), proxy=True), volumes_quantity=IntegerField(_("Volumes quantity"), proxy=True), volumes_net_weight=NumericField(_("Volumes net weight"), proxy=True, digits=3), volumes_gross_weight=NumericField(_("Volumes gross weight"), proxy=True, digits=3), vehicle_license_plate=TextField(_("Vehicle license plate"), proxy=True), vehicle_state=ChoiceField(_("Vehicle state"), proxy=True, use_entry=True, values=states), vehicle_registration=TextField(_("Vehicle registration"), proxy=True), is_received_check=BoolField(_("Was received by recipient?")), receive_date=DateField(_("Receive date"), mandatory=True, proxy=True), empty=EmptyField(), ) def __init__(self, store, *args, **kwargs): self._configuring_proxies = False super(DeliveryEditor, self).__init__(store, *args, **kwargs) # # BaseEditor Hooks # def setup_proxies(self): self._configuring_proxies = True self._setup_widgets() self._update_status_widgets() self._configuring_proxies = False def setup_slaves(self): self.delivery_items = ObjectList( columns=self._get_delivery_items_columns(), objects=self.model.delivery_items, ) self.delivery_items_holder.add(self.delivery_items) self.delivery_items.show() # # Private # def _setup_widgets(self): for widget in (self.receive_date, self.send_date, self.tracking_code): widget.set_sensitive(False) def _update_status_widgets(self): if self.model.status == Delivery.STATUS_INITIAL: for widget in [self.is_sent_check, self.is_received_check]: widget.set_active(False) elif self.model.status == Delivery.STATUS_SENT: self.is_sent_check.set_active(True) self.is_received_check.set_active(False) elif self.model.status == Delivery.STATUS_RECEIVED: for widget in [self.is_sent_check, self.is_received_check]: widget.set_active(True) else: raise ValueError( _("Invalid status for %s") % (self.model.__class__.__name__)) def _get_delivery_items_columns(self): return [ Column('sellable.description', title=_('Products to deliver'), data_type=str, expand=True, sorted=True), Column('quantity', title=_('Quantity'), data_type=decimal.Decimal, format_func=format_quantity), ] # # Callbacks # def on_is_sent_check__toggled(self, button): active = button.get_active() # When delivered, don't let user change transporter or address self.transporter_id.set_sensitive(not active) self.address.set_sensitive(not active) for widget in [self.send_date, self.tracking_code]: widget.set_sensitive(active) if not self.model.send_date: self.send_date.update(localtoday().date()) if self._configuring_proxies: # Do not change status above return if active: self.model.send(api.get_current_user(self.store)) else: self.model.set_initial() def on_is_received_check__toggled(self, button): active = button.get_active() self.receive_date.set_sensitive(active) # If it was received, don't let the user unmark is_sent_check self.is_sent_check.set_sensitive(not active) if not self.is_sent_check.get_active(): self.is_sent_check.set_active(True) if not self.model.receive_date: self.receive_date.update(localtoday().date()) if self._configuring_proxies: # Do not change status above return if active: self.model.receive() else: # We have to change this status manually because set_sent won't # allow us to change from RECEIVED to it. Also, it we would call # set_sent it would overwrite the sent_date that we set previously self.model.status = self.model.STATUS_SENT
class VirtLabView(BaseView): ''' MVC View ''' def __init__(self, model): self.__model = model #self.__model.set_view(self) BaseView.__init__(self, gladefile="virtlab", delete_handler=self.quit_if_last) # self.__col_pixbuf = gtk.TreeViewColumn("Image") # cellrenderer_pixbuf = gtk.CellRendererPixbuf() # cellrenderer_pixbuf.set_properties("pixbuf", ) # self.__col_pixbuf.pack_start(cellrenderer_pixbuf, False) # self.__col_pixbuf.add_attribute(cellrenderer_pixbuf, "pixbuf", 1) tableColumns = [ Column("image", title=" ", width=30, data_type=gtk.gdk.Pixbuf, sorted=False), Column("name", title='VM Name', width=130, sorted=True), Column("state", title='State', width=70), Column("order", title='Order (Delay/min)', width=145), Column("ordinal", visible=False), Column("delay", visible=False), Column("desc", title='Description', width=200) ] self.vmlist_widget = ObjectList(tableColumns) self.vmlist_widget.set_size_request(300, 400) self.vmlist_widget.set_selection_mode(gtk.SELECTION_SINGLE) self.hbox4.pack_start(self.vmlist_widget) store = gtk.ListStore(gobject.TYPE_STRING) self.vmlist_widget.show() self.__dialog = None self.__status_text = gtk.TextBuffer() try: self.populate_vmlist() self.populate_order_dropdown(store, len(self.__model.get_vms())) except VMLabException as exception: if exception.vme_id is c.EXCEPTION_LIBVIRT_001: error("Initialization error", "No connection to Libvirtd.\n Exiting.") exit(1) self.ordercombo.set_model(store) cell = gtk.CellRendererText() self.ordercombo.pack_start(cell, True) self.ordercombo.add_attribute(cell, 'text', 0) self.virtlab.set_size_request(800, 460) self.change_title("") self.__statusbar_ctx = self.statusbar.get_context_id("virtlab") self.virtlab.set_icon(gtk.gdk.pixbuf_new_from_file("pixmaps/about-logo.png")) def __delaystring(self, delay): if delay > 0: return " (" + str(delay) + ")" else: return "" def change_title(self, value): if value == "" or None: self.virtlab.set_title(c.WINDOW_TITLE) else: self.virtlab.set_title(c.WINDOW_TITLE + "(" + value + ")") def dialog_filechooser_open(self): chooser = gtk.FileChooserDialog(title="Open VMLab Project", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_OK) filter = gtk.FileFilter() filter.add_pattern("*"+c.SAVE_FILE_SUFFIX) chooser.set_filter(filter) file_name = "" response = chooser.run() if response == gtk.RESPONSE_OK: file_name = chooser.get_filename() chooser.destroy() return file_name elif response == gtk.RESPONSE_CANCEL: chooser.destroy() return def dialog_filechooser_save(self): chooser = gtk.FileChooserDialog(title="Save VMLab Project", action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_CANCEL) filter = gtk.FileFilter() filter.add_pattern("*"+c.SAVE_FILE_SUFFIX) file_name = "" response = chooser.run() if response == gtk.RESPONSE_OK: file_name = chooser.get_filename() chooser.destroy() return file_name elif response == gtk.RESPONSE_CANCEL: chooser.destroy() return elif response == gtk.RESPONSE_CLOSE: chooser.destroy() return def populate_vmlist(self): ''' Populates view with current status ''' self.vmlist_widget.clear() for vm_instance in self.__model.get_vms(): self.vmlist_widget.append(Settable(image=populate_image(vm_instance.get_state()), name=vm_instance.get_name(), state=vm_instance.get_state().get_state_str(), order=self.get_display_order(vm_instance.get_order()) + self.__delaystring(vm_instance.get_delay()), ordinal=vm_instance.get_order(), delay=vm_instance.get_delay(), desc=vm_instance.get_desc())) def populate_order_dropdown(self, list_store, vm_count): list_store.clear() list_store.append([""]) if vm_count > 0: for num in range(1, vm_count + 1): list_store.append([self.get_display_order(num)]) def add_status_dialogbox(self, text): field_content = self.__status_text.get_text(self.__status_text.get_start_iter(), self.__status_text.get_end_iter(), False) field_content += strftime("%d %b %Y %H:%M:%S", localtime()) + " " + text + "\n" self.__status_text.set_text(field_content) def clear_dialog_log(self): self.__status_text.set_text("") def show_dialog(self): self.__dialog = gtk.Dialog(title="Launch status", parent=self.virtlab, flags=gtk.DIALOG_MODAL, buttons=None) self.__dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.__dialog.set_transient_for(self.virtlab) close_button = gtk.Button("Close window") terminate_button = gtk.Button("Terminate") clear_button = gtk.Button("Clear log") start_button = gtk.Button("Launch scheduled") start_once_button = gtk.Button("Launch all once") close_button.connect("clicked", lambda d, r: r.destroy(), self.__dialog) clear_button.connect("clicked", lambda d, r: r.clear_dialog_log(), self) terminate_button.connect("clicked", self.controller.hook_dialog_terminate_clicked, None) start_button.connect("clicked", self.controller.hook_dialog_start_clicked, None) start_once_button.connect("clicked", self.controller.hook_dialog_all_start_clicked, None) scrolled_window = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) text_view = gtk.TextView() scrolled_window.add_with_viewport(text_view) scrolled_window.show() text_view.set_buffer(self.__status_text) # pylint: disable=E1101 self.__dialog.action_area.pack_start(start_button, True, True, 0) self.__dialog.action_area.pack_start(start_once_button, True, True, 0) self.__dialog.action_area.pack_start(clear_button, True, True, 0) self.__dialog.action_area.pack_start(terminate_button, True, True, 0) self.__dialog.action_area.pack_start(close_button, True, True, 0) self.__dialog.vbox.pack_start(scrolled_window, True, True, 0) scrolled_window.set_size_request(500, 200) start_button.show() start_once_button.show() terminate_button.show() close_button.show() clear_button.show() text_view.show() self.__dialog.show() text_view.set_editable(False) text_view.set_cursor_visible(False) def show_about(self): about = gtk.AboutDialog() about.set_program_name("Virtual Lab Manager") about.set_version("1.0 RC") import os path = os.path.dirname(virtlab.__file__) f = open(path + '/LICENCE', 'r') about.set_license(f.read()) about.set_copyright("GPLv3, (c) Authors") about.set_logo(gtk.gdk.pixbuf_new_from_file("pixmaps/about-logo.png")) about.set_authors(["Harri Savolainen", "Esa Elo"]) about.set_comments("Virtual Machine lab tool") about.set_website("https://github.com/hsavolai/vmlab") about.run() about.hide() def dialog_overwrite(self): response = messagedialog(gtk.MESSAGE_QUESTION, "File exists, overwrite?", None, self.toplevel, gtk.BUTTONS_OK_CANCEL ) if response == gtk.RESPONSE_OK: return True return False def dialog_file_error(self, error_msg): messagedialog(gtk.MESSAGE_ERROR, "File operation failed!", error_msg, self.toplevel, gtk.BUTTONS_OK ) def set_statusbar(self, value): #self.statusbar.remove_all(self.__statusbar_ctx) self.statusbar.pop(self.__statusbar_ctx) self.statusbar.push(self.__statusbar_ctx, value) @staticmethod def get_display_order(number): if number is 0: return "" upper_suffixes = { "11": "th", "12": "th", "13": "th" } lower_suffixes = { "1": "st", "2": "nd", "3": "rd", } digits = str(number) if digits[-2:] in upper_suffixes: return digits + upper_suffixes[digits[-2:]] if digits[-1:] in lower_suffixes: return digits + lower_suffixes[digits[-1:]] else: return digits + "th"
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 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 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 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 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 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 DeliveryEditor(BaseEditor): """An editor for :class:`stoqlib.domain.sale.Delivery`""" title = _("Delivery editor") gladefile = 'DeliveryEditor' size = (-1, 400) model_type = Delivery model_name = _('Delivery') form_holder_name = 'forms' form_columns = 2 @cached_property() def fields(self): # Only users with admin or purchase permission can modify transporters user = api.get_current_user(self.store) can_modify_transporter = any(( user.profile.check_app_permission(u'admin'), user.profile.check_app_permission(u'purchase'), )) return collections.OrderedDict( client_str=TextField(_("Client"), proxy=True, editable=False, colspan=2), transporter_id=PersonField(_("Transporter"), proxy=True, person_type=Transporter, colspan=2, can_add=can_modify_transporter, can_edit=can_modify_transporter), address=AddressField(_("Address"), proxy=True, mandatory=True, colspan=2), was_delivered_check=BoolField(_("Was sent to deliver?")), deliver_date=DateField(_("Delivery date"), mandatory=True, proxy=True), tracking_code=TextField(_("Tracking code"), proxy=True), was_received_check=BoolField(_("Was received by client?")), receive_date=DateField(_("Receive date"), mandatory=True, proxy=True), empty=EmptyField(), ) def __init__(self, store, *args, **kwargs): self._configuring_proxies = False super(DeliveryEditor, self).__init__(store, *args, **kwargs) # # BaseEditor Hooks # def setup_proxies(self): self._configuring_proxies = True self._setup_widgets() self._update_status_widgets() self._configuring_proxies = False def setup_slaves(self): self.delivery_items = ObjectList( columns=self._get_delivery_items_columns(), objects=self.model.delivery_items, ) self.delivery_items_holder.add(self.delivery_items) self.delivery_items.show() # # Private # def _setup_widgets(self): for widget in (self.receive_date, self.deliver_date, self.tracking_code): widget.set_sensitive(False) def _update_status_widgets(self): if self.model.status == Delivery.STATUS_INITIAL: for widget in (self.was_delivered_check, self.was_received_check): widget.set_active(False) elif self.model.status == Delivery.STATUS_SENT: self.was_delivered_check.set_active(True) self.was_received_check.set_active(False) elif self.model.status == Delivery.STATUS_RECEIVED: for widget in (self.was_delivered_check, self.was_received_check): widget.set_active(True) else: raise ValueError(_("Invalid status for %s") % ( self.model.__class__.__name__)) def _get_delivery_items_columns(self): return [ Column('sellable.description', title=_('Products to deliver'), data_type=str, expand=True, sorted=True), Column('quantity', title=_('Quantity'), data_type=decimal.Decimal, format_func=format_quantity), ] # # Callbacks # def on_was_delivered_check__toggled(self, button): active = button.get_active() # When delivered, don't let user change transporter or address self.transporter_id.set_sensitive(not active) self.address.set_sensitive(not active) for widget in (self.deliver_date, self.tracking_code): widget.set_sensitive(active) if not self.model.deliver_date: self.deliver_date.update(localtoday().date()) if self._configuring_proxies: # Do not change status above return if active: self.model.set_sent() else: self.model.set_initial() def on_was_received_check__toggled(self, button): active = button.get_active() self.receive_date.set_sensitive(active) # If it was received, don't let the user unmark was_delivered_check self.was_delivered_check.set_sensitive(not active) if not self.was_delivered_check.get_active(): self.was_delivered_check.set_active(True) if not self.model.receive_date: self.receive_date.update(localtoday().date()) if self._configuring_proxies: # Do not change status above return if active: self.model.set_received() else: self.model.set_sent()
class ChartDialog(gtk.Window): def __init__(self): self._js_data = None self._js_options = None self._current = None gtk.Window.__init__(self) self.set_size_request(800, 480) self.vbox = gtk.VBox() self.add(self.vbox) self.vbox.show() hbox = gtk.HBox() self.vbox.pack_start(hbox, False, False, 6) hbox.show() label = gtk.Label('Period:') hbox.pack_start(label, False, False, 6) label.show() self.chart_type = ProxyComboBox() self.chart_type.connect('content-changed', self._on_chart_type__content_changed) hbox.pack_start(self.chart_type, False, False, 6) self.chart_type.show() self.period_values = ProxyComboBox() self.period_values.connect('content-changed', self._on_period_values__content_changed) hbox.pack_start(self.period_values, False, False, 6) self.period_values.show() self._view = WebView() self._view.get_view().connect('load-finished', self._on_view__document_load_finished) self.vbox.pack_start(self._view, True, True) self.results = ObjectList() self.results.connect('row-activated', self._on_results__row_activated) self.vbox.pack_start(self.results, True, True) self._setup_daemon() @api. async def _setup_daemon(self): daemon = yield start_daemon() self._daemon_uri = daemon.base_uri proxy = daemon.get_client() yield proxy.callRemote('start_webservice') self.chart_type.prefill([ ('Year', 'YearlyPayments'), ('Month', 'MonthlyPayments'), ('Day', 'DailyPayments'), ]) @api. async def _invoke_chart(self, chart_type_name, **report_kwargs): def _get_chart_url(**kwargs): params = [] for key, value in kwargs.items(): params.append(key + '=' + str(value)) return '%s/web/chart.json?%s' % (self._daemon_uri, '&'.join(params)) url = _get_chart_url(type=chart_type_name, **report_kwargs) page = yield getPage(url) data = json.loads(page) api.asyncReturn(data) def _render_chart(self, chart_class, response): self._render_javascript(chart_class, response) self._render_objectlist(chart_class, response) def _render_javascript(self, chart_class, response): ticks = [item['short_title'] for item in response['items']] self._js_data = response['data'] options = {} options['description'] = response['description'] options['series'] = [ dict(label=c['title']) for c in chart_class.columns ][1:] options['xaxis_ticks'] = ticks self._js_options = options self._view.load_uri('%s/web/static/chart.html' % (self._daemon_uri, )) def _render_objectlist(self, chart_class, response): columns = [] for kwargs in chart_class.columns: kwargs = kwargs.copy() name = kwargs.pop('name') columns.append(Column(name, **kwargs)) self.results.set_columns(columns) items = [] for item in response['items']: settable = Settable(**item) settable.chart_class = chart_class items.append(settable) self.results.add_list(items, clear=True) self.results.show() def _load_finished(self): self._view.js_function_call("plot", self._js_data, self._js_options) @api. async def _show_one(self, chart_type_name, start, end): chart_class = get_chart_class(chart_type_name) report_kwargs = dict(start=start.strftime('%Y-%m-%d'), end=end.strftime('%Y-%m-%d')) # Get chart datab response = yield self._invoke_chart(chart_type_name, **report_kwargs) self._render_chart(chart_class, response) def _update_period_values(self): chart_type_name = self.chart_type.get_selected() chart_class = get_chart_class(chart_type_name) values = chart_class.get_combo_labels() self.period_values.prefill(values) # # Callbacks # def _on_view__document_load_finished(self, view, frame): self._load_finished() def _on_chart_type__content_changed(self, combo): self._update_period_values() def _on_period_values__content_changed(self, combo): kind = self.chart_type.get_selected() value = self.period_values.get_selected() if not value: return start, end = value if self._current == (kind, start, end): return self._show_one(kind, start, end) self._current = kind, start, end def _on_results__row_activated(self, results, item): chart_type_name = item.chart_class.__name__ if chart_type_name == 'YearlyPayments': start = localdate(item.year, 1, 1).date() end = localdate(item.year, 12, 31).date() chart_type_name = 'MonthlyPayments' elif chart_type_name == 'MonthlyPayments': start = localdate(item.year, item.month, 1).date() end = start + relativedelta(days=31) chart_type_name = 'DailyPayments' else: return self._show_one(chart_type_name, start, end)
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())