def __init__(self): gtk.HBox.__init__(self) self._popping_down = False self._old_date = None # bootstrap problems, kiwi.ui.widgets.entry imports dateentry # we need to use a proxy entry because we want the mask from kiwi.ui.widgets.entry import ProxyEntry self.entry = ProxyEntry() self.entry.connect('changed', self._on_entry__changed) self.entry.connect('activate', self._on_entry__activate) self.entry.set_property('data-type', datetime.date) mask = self.entry.get_mask() if mask: self.entry.set_width_chars(len(mask)) self.pack_start(self.entry, False, False) self.entry.show() self._button = gtk.ToggleButton() self._button.connect('scroll-event', self._on_entry__scroll_event) self._button.connect('toggled', self._on_button__toggled) self._button.set_focus_on_click(False) self.pack_start(self._button, False, False) self._button.show() arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE) self._button.add(arrow) arrow.show() self._popup = _DateEntryPopup(self) self._popup.connect('date-selected', self._on_popup__date_selected) self._popup.connect('hide', self._on_popup__hide) self._popup.set_size_request(-1, 24)
def test_attach(self): entry = ProxyEntry() entry.data_type = currency self.assertEqual(entry.get_property('secondary-icon-pixbuf'), None) calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) pixbuf_pixels = calc.render_icon(STOQ_CALC, gtk.ICON_SIZE_MENU).get_pixels() self.assertEqual( entry.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels) entry.set_sensitive(False) self.assertEqual(entry.get_property('secondary-icon-pixbuf'), None) entry.set_sensitive(True) self.assertEqual( entry.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels) spinbutton = ProxySpinButton() spinbutton.data_type = currency self.assertEqual(spinbutton.get_property('secondary-icon-pixbuf'), None) calc = CalculatorPopup(spinbutton, CalculatorPopup.MODE_SUB) pixbuf_pixels = calc.render_icon(STOQ_CALC, gtk.ICON_SIZE_MENU).get_pixels() self.assertEqual( spinbutton.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels) spinbutton.set_sensitive(False) self.assertEqual(spinbutton.get_property('secondary-icon-pixbuf'), None) spinbutton.set_sensitive(True) self.assertEqual( spinbutton.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels)
def __init__(self): entry = ProxyEntry() ComboEntry.__init__(self, entry=entry) ValidatableProxyWidgetMixin.__init__(self) entry.connect('content-changed', self._on_entry__content_changed) entry.connect('validation-changed', self._on_entry__validation_changed)
def testDigitMask(self): e = ProxyEntry() e.set_mask('000.000') self.assertEqual(e.get_text(), ' . ') e.set_text('123.456') self.assertEqual(e.get_text(), '123.456') e.delete_text(0, 2) self.assertEqual(e.get_text(), '345.6 ')
def testRead(self): entry = ProxyEntry() entry.set_text('1') entry.set_property("data-type", "int") self.assertEqual(entry.read(), 1) entry.set_text('') self.assertEqual(entry.read(), ValueUnset)
def _create_interface(self, run_editor=None): self.sale = self.create_sale() self.window = Gtk.Window() self.entry = ProxyEntry() self.window.add(self.entry) self.client_gadget = SearchEntryGadget( self.entry, self.store, model=self.sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=self.window, run_editor=run_editor) self.client_gadget.get_model_obj = lambda obj: obj and obj.client
def testAsciiMask(self): e = ProxyEntry() e.set_mask('LLLL-L') self.assertEqual(e.get_text(), ' - ') self.assertEqual(e.get_fields(), ['', '']) e.set_text('abcd-e') self.assertEqual(e.get_text(), 'abcd-e') self.assertEqual(e.get_fields(), ['abcd', 'e'])
def testMaskSmallFields(self): e = ProxyEntry() e.set_mask('0.0.0') self.assertEqual(e.get_text(), ' . . ') self.assertEqual(e.get_fields(), ['', '', '']) e.set_text('1.2.3') self.assertEqual(e.get_text(), '1.2.3') self.assertEqual(e.get_fields(), ['1', '2', '3'])
def testAlphaNumericMask(self): e = ProxyEntry() e.set_mask('&&&-aaa') self.assertEqual(e.get_text(), ' - ') self.assertEqual(e.get_fields(), ['', '']) e.set_text('aáé-á1e') self.assertEqual(e.get_text(), 'aáé-á1e') self.assertEqual(e.get_fields(), ['aáé', 'á1e'])
def get_main_widget(self): # This is done on entry to check where to put the validation/mandatory # icons. We should put the calculator on the other side. # Note that spinbuttons are always right aligned and thus # xalign will always be 1.0 if self.attached_widget.get_alignment() > 0.5: self._icon_pos = 'secondary-icon' else: self._icon_pos = 'primary-icon' self.attached_widget.set_property(self._icon_pos + '-activatable', True) self.attached_widget.set_property( self._icon_pos + '-tooltip-text', _("Do calculations on top of this value")) self.attached_widget.connect('notify::sensitive', self._on_entry_sensitive__notify) self.attached_widget.connect('icon-press', self._on_entry__icon_press) self._toggle_calculator_icon() vbox = Gtk.VBox(spacing=6) vbox.show() self._main_label = Gtk.Label() self._main_label.set_ellipsize(Pango.EllipsizeMode.END) vbox.pack_start(self._main_label, True, True, 0) self._main_label.show() self._entry = ProxyEntry() # FIXME: We need a model_attribute here or else the entry.is_valid() # will always return None. Check proxywidget.py's FIXME to see why self._entry.model_attribute = 'not_used' self._entry.connect('validate', self._on_entry__validate) self._entry.connect_after('changed', self._after_entry__changed) self._entry.set_alignment(1.0) vbox.pack_start(self._entry, True, True, 0) self._entry.show() hbox = Gtk.HBox(spacing=6) vbox.pack_start(hbox, True, True, 0) hbox.show() self._label = Gtk.Label() self._label.set_property('xalign', 1.0) self._label.set_use_markup(True) hbox.pack_start(self._label, True, True, 0) self._label.show() self._warning = Gtk.Image() hbox.pack_start(self._warning, False, False, 0) return vbox
def _create_interface(self): self.sale = self.create_sale() self.window = gtk.Window() self.entry = ProxyEntry() self.window.add(self.entry) self.client_gadget = SearchEntryGadget(self.entry, self.store, model=self.sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=self.window)
def test_apply(self): entry = ProxyEntry() entry.data_type = currency entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) # calc.popup will not work here, so call _update_ui directly calc._update_ui() calc._entry.set_text('10%') event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) event.keyval = gtk.keysyms.Return event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) calc.emit('key-press-event', event) self.assertEqual(entry.read(), 135)
def __init__(self): super(DateEntry, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED) self._popping_down = False self._old_date = None self._block_changed = False # This will force both the entry and the button have the same height self._sizegroup = Gtk.SizeGroup.new(Gtk.SizeGroupMode.VERTICAL) # bootstrap problems, kiwi.ui.widgets.entry imports dateentry # we need to use a proxy entry because we want the mask from kiwi.ui.widgets.entry import ProxyEntry self.entry = ProxyEntry() # Set datatype before connecting to change event, to not get when the # mask is set self.entry.set_property('data-type', datetime.date) self.entry.connect('changed', self._on_entry__changed) self.entry.connect('activate', self._on_entry__activate) mask = self.entry.get_mask() if mask: self.entry.set_width_chars(len(mask)) self.pack_start(self.entry, True, True, 0) self.entry.set_valign(Gtk.Align.CENTER) self._sizegroup.add_widget(self.entry) self.entry.show() self._button = Gtk.ToggleButton() self._button.set_valign(Gtk.Align.CENTER) self._button.connect('scroll-event', self._on_entry__scroll_event) self._button.connect('toggled', self._on_button__toggled) self._button.set_focus_on_click(False) self.pack_start(self._button, False, False, 0) self._sizegroup.add_widget(self._button) self._button.show() arrow = Gtk.Arrow(arrow_type=Gtk.ArrowType.DOWN, shadow_type=Gtk.ShadowType.NONE) self._button.add(arrow) arrow.show() self._popup = _DateEntryPopup(self) self._popup.connect('date-selected', self._on_popup__date_selected) self._popup.connect('hide', self._on_popup__hide) self._popup.set_size_request(-1, 24) self.set_valign(Gtk.Align.CENTER)
def build_widget(self): if self.editable: widget = ProxyEntry() if self.max_length != 0: widget.set_width_chars(self.max_length) if self.input_mask: widget.set_mask(self.input_mask) else: widget = ProxyLabel() # This label should be left aligned. widget.set_alignment(0, 0.5) return widget
def _setup_entry_slave(self, box=None): widget = ProxyEntry() widget.props.sensitive = self.sensitive widget.data_type = unicode widget.model_attribute = "field_value" self.proxy.add_widget("field_value", widget) if box is None: self.container.add(widget) else: box.pack_start(widget) widget.show() widget.connect('validate', self._on_entry__validate) widget.connect('validation-changed', self._on_entry__validation_changed) self._entry = widget
def _setup_widgets(self): # Hide total and subtotal self.table1.hide() self.hbox4.hide() # Hide invoice number details self.invoice_number_label.hide() self.invoice_number.hide() # Hide cost center combobox self.cost_center_lbl.hide() self.cost_center.hide() # Responsible combo self.salesperson_lbl.set_text(_(u'Responsible:')) self.salesperson.model_attribute = 'responsible' users = self.store.find(LoginUser, is_active=True) self.salesperson.prefill(api.for_person_combo(users)) self.salesperson.set_sensitive(False) self._fill_clients_combo() self._fill_clients_category_combo() self.expire_date.mandatory = True # CFOP combo self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() # Transporter/RemovedBy Combo self.transporter_lbl.set_text(_(u'Removed By:')) self.create_transporter.hide() # removed_by widget self.removed_by = ProxyEntry(unicode) self.removed_by.model_attribute = 'removed_by' if 'removed_by' not in self.proxy_widgets: self.proxy_widgets.append('removed_by') self.removed_by.show() self._replace_widget(self.transporter, self.removed_by) # Operation Nature widget self.operation_nature.hide() self.nature_lbl.hide()
def test_popup(self): entry = ProxyEntry() entry.data_type = currency entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS) event.window = Gdk.get_default_root_window() with mock.patch.object(calc, 'popup') as popup: entry.emit('icon-press', Gtk.EntryIconPosition.PRIMARY, event) self.assertEqual(popup.call_count, 0) entry.emit('icon-press', Gtk.EntryIconPosition.SECONDARY, event) self.assertEqual(popup.call_count, 1)
def add_entry(self, data_type): """Add a entry with the specified data_type The user will be able to provide any information in the entry that should be used by this operation (for instance, a number do multiply a value for or a string to replace a value for) """ entry = ProxyEntry(data_type=data_type) self.pack_start(entry, False, False, 0) return entry
def _create_interface(self): self.sale = self.create_sale() self.window = gtk.Window() self.entry = ProxyEntry() self.window.add(self.entry) self.client_gadget = SearchEntryGadget( self.entry, self.store, model=self.sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=self.window)
def test_popdown(self): entry = ProxyEntry() entry.data_type = currency entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) with contextlib.nested( mock.patch.object(calc, '_maybe_apply_new_value'), mock.patch.object(calc, 'popdown')) as (manv, popdown): # Those keys should try to apply the value for keyval in [gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab]: event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) event.keyval = keyval event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) self.assertEqual(manv.call_count, 1) self.assertEqual(popdown.call_count, 0) manv.reset_mock() popdown.reset_mock() event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # Escape should popdown the popup event.keyval = gtk.keysyms.Escape event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) self.assertEqual(popdown.call_count, 1) self.assertEqual(manv.call_count, 0) manv.reset_mock() popdown.reset_mock() event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # Any other should not do anything event.keyval = gtk.keysyms.A event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) self.assertEqual(manv.call_count, 0) self.assertEqual(popdown.call_count, 0)
def test_popdown(self): entry = ProxyEntry() entry.data_type = currency entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) with contextlib.nested( mock.patch.object(calc, '_maybe_apply_new_value'), mock.patch.object(calc, 'popdown')) as (manv, popdown): # Those keys should try to apply the value for keyval in [ gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab ]: event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) event.keyval = keyval event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) self.assertEqual(manv.call_count, 1) self.assertEqual(popdown.call_count, 0) manv.reset_mock() popdown.reset_mock() event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # Escape should popdown the popup event.keyval = gtk.keysyms.Escape event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) self.assertEqual(popdown.call_count, 1) self.assertEqual(manv.call_count, 0) manv.reset_mock() popdown.reset_mock() event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # Any other should not do anything event.keyval = gtk.keysyms.A event.window = gtk.gdk.get_default_root_window() calc.emit('key-press-event', event) self.assertEqual(manv.call_count, 0) self.assertEqual(popdown.call_count, 0)
def _setup_entry_slave(self, box=None): widget = ProxyEntry() widget.props.sensitive = self.sensitive widget.data_type = unicode widget.model_attribute = "field_value" self.proxy.add_widget("field_value", widget) if box is None: self.container.add(widget) else: box.pack_start(widget) widget.show() widget.connect("validate", self._on_entry__validate) widget.connect("validation-changed", self._on_entry__validation_changed) self._entry = widget
def _create_interface(self, run_editor=None): self.sale = self.create_sale() self.window = gtk.Window() self.entry = ProxyEntry() self.window.add(self.entry) self.client_gadget = SearchEntryGadget( self.entry, self.store, model=self.sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=self.window, run_editor=run_editor) self.client_gadget.get_model_obj = lambda obj: obj and obj.client
def test_popup(self): entry = ProxyEntry() entry.data_type = currency entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) event = gtk.gdk.Event(gtk.gdk.BUTTON_PRESS) event.window = gtk.gdk.get_default_root_window() with mock.patch.object(calc, 'popup') as popup: entry.emit('icon-press', gtk.ENTRY_ICON_PRIMARY, event) self.assertEqual(popup.call_count, 0) entry.emit('icon-press', gtk.ENTRY_ICON_SECONDARY, event) popup.assert_called_once()
def test_create(self): window = Gtk.Window() box = Gtk.VBox() window.add(box) entry = ProxyEntry() box.pack_start(entry, True, True, 0) self.check_dialog(window, 'search-entry-before-replace') sale = self.create_sale() SearchEntryGadget(entry, self.store, model=sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=window) self.check_dialog(window, 'search-entry-after-replace')
def get_main_widget(self): # This is done on entry to check where to put the validation/mandatory # icons. We should put the calculator on the other side. # Note that spinbuttons are always right aligned and thus # xalign will always be 1.0 if self.attached_widget.get_alignment() > 0.5: self._icon_pos = 'secondary-icon' else: self._icon_pos = 'primary-icon' self.attached_widget.set_property(self._icon_pos + '-activatable', True) self.attached_widget.set_property( self._icon_pos + '-tooltip-text', _("Do calculations on top of this value")) self.attached_widget.connect( 'notify::sensitive', self._on_entry_sensitive__notify) self.attached_widget.connect('icon-press', self._on_entry__icon_press) self._toggle_calculator_icon() vbox = Gtk.VBox(spacing=6) vbox.show() self._main_label = Gtk.Label() self._main_label.set_ellipsize(Pango.EllipsizeMode.END) vbox.pack_start(self._main_label, True, True, 0) self._main_label.show() self._entry = ProxyEntry() # FIXME: We need a model_attribute here or else the entry.is_valid() # will always return None. Check proxywidget.py's FIXME to see why self._entry.model_attribute = 'not_used' self._entry.connect('validate', self._on_entry__validate) self._entry.connect_after('changed', self._after_entry__changed) self._entry.set_alignment(1.0) vbox.pack_start(self._entry, True, True, 0) self._entry.show() hbox = Gtk.HBox(spacing=6) vbox.pack_start(hbox, True, True, 0) hbox.show() self._label = Gtk.Label() self._label.set_property('xalign', 1.0) self._label.set_use_markup(True) hbox.pack_start(self._label, True, True, 0) self._label.show() self._warning = Gtk.Image() hbox.pack_start(self._warning, False, False, 0) return vbox
def _setup_widgets(self): # Hide total and subtotal self.summary_table.hide() self.total_box.hide() # Hide cost center combobox self.cost_center_lbl.hide() self.cost_center.hide() # Responsible combo self.salesperson_lbl.set_text(_(u'Responsible:')) self.salesperson.model_attribute = 'responsible' users = self.store.find(LoginUser, is_active=True) self.salesperson.prefill(stoq_api.for_person_combo(users)) self.salesperson.set_sensitive(False) self._setup_clients_widget() self._fill_clients_category_combo() self.expire_date.mandatory = True # CFOP combo self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() # Transporter/RemovedBy Combo self.transporter_lbl.set_text(_(u'Removed By:')) self.create_transporter.hide() # removed_by widget self.removed_by = ProxyEntry(str) self.removed_by.model_attribute = 'removed_by' if 'removed_by' not in self.proxy_widgets: self.proxy_widgets.append('removed_by') self.removed_by.show() self._replace_widget(self.transporter, self.removed_by)
class DateTimeEditUI(GladeSlaveDelegate): def __init__(self, dt = None): if not dt: self.dt = datetime.datetime.now() else: self.dt = dt # Set up the user interface GladeSlaveDelegate.__init__(self, gladefile="mo_datetime_edit", toplevel_name="window_main") self.entry_date = DateEntry() self.entry_time = ProxyEntry(data_type=datetime.time) self.set_datetime(self.dt) self.entry_date.connect('changed', self.entry_date__changed) self.entry_time.connect('changed', self.entry_time__changed) self.hbox.pack_start(self.entry_date, expand=False, fill=False) self.hbox.pack_start(self.entry_time, expand=False, fill=False) self.show() def entry_date__changed(self, *args): dt = self.entry_date.get_date() if dt != None: self.dt = self.dt.replace(dt.year, dt.month, dt.day) def entry_time__changed(self, *args): dt = None try: dt = self.entry_time.read() except ValidationError, e: # Ignore invalid times because the user might still be filling it # out. pass if dt != None: self.dt = self.dt.replace(self.dt.year, self.dt.month, self.dt.day, dt.hour, dt.minute)
def build_widget(self): if self.editable: widget = ProxyEntry() if self.max_length != 0: widget.set_width_chars(self.max_length) if self.input_mask: widget.set_mask(self.input_mask) else: widget = ProxyLabel() return widget
def test_with_cfop(self, run_dialog, new_store): new_store.return_value = self.store window = Gtk.Window() entry = ProxyEntry() window.add(entry) sale = self.create_sale() gadget = SearchEntryGadget( entry, self.store, model=sale, model_property='cfop', search_columns=['name'], search_class=CfopSearch, parent=window) with mock.patch.object(self.store, 'commit'): with mock.patch.object(self.store, 'close'): run_dialog.return_value = None self.click(gadget.edit_button) run_dialog.assert_called_once_with( CfopEditor, window, self.store, sale.cfop)
def __init__(self, dt = None): if not dt: self.dt = datetime.datetime.now() else: self.dt = dt # Set up the user interface GladeSlaveDelegate.__init__(self, gladefile="mo_datetime_edit", toplevel_name="window_main") self.entry_date = DateEntry() self.entry_time = ProxyEntry(data_type=datetime.time) self.set_datetime(self.dt) self.entry_date.connect('changed', self.entry_date__changed) self.entry_time.connect('changed', self.entry_time__changed) self.hbox.pack_start(self.entry_date, expand=False, fill=False) self.hbox.pack_start(self.entry_time, expand=False, fill=False) self.show()
def test_apply(self): entry = ProxyEntry() entry.data_type = currency entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) # calc.popup will not work here, so call validate_popup directly calc.validate_popup() calc._entry.set_text('10%') event = Gdk.Event.new(Gdk.EventType.KEY_PRESS) event.keyval = Gdk.KEY_Return event.window = Gdk.get_default_root_window() calc.emit('key-press-event', event) calc.emit('key-press-event', event) self.assertEqual(entry.read(), 135)
def get_widget_for_type(rtype): if rtype is types.boolean: return ProxyCheckButton() elif rtype is types.file: w = ProxyFileChooserButton('Select File') #w.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) return w elif rtype is types.readonlyfile: w = ProxyFileChooserButton('Select File') w.set_sensitive(False) #w.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) return w elif rtype in [types.directory]: w = ProxyFileChooserButton(title='Select Directory') w.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) return w elif rtype is types.font: return ProxyFontButton() elif rtype is types.color: return CleverProxyColorButton() elif rtype is types.integer: w = ProxySpinButton() return w elif rtype.__name__ is 'intrange': adjvals = rtype.lower, rtype.upper, rtype.step adj = gtk.Adjustment(0, *adjvals) w = ProxySpinButton() w.set_adjustment(adj) return w elif rtype is types.readonly: return FormattedLabel(VC_NAME_MU) elif rtype.__name__ is 'stringlist': w = ProxyComboBox() w.set_property('data-type', str) w.prefill(rtype.choices) return w else: w = ProxyEntry(data_type=str) w.set_width_chars(18) return w
def test_validate(self): def validate_entry(entry, value): if value == 100: return ValidationError() # FIXME: For some reason, entry is not emitting 'changed' event # on set_text, not even if we call entry.emit('changed') by hand. # That only happens here on the test. Figure out why def update_entry(entry, value): entry.set_text(value) entry.validate(force=True) entry = ProxyEntry() entry.data_type = currency entry.connect('validate', validate_entry) entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) # calc.popup will not work here, so call validate_popup directly calc.validate_popup() self.assertValid(calc, ['_entry']) self.assertNotVisible(calc, ['_warning']) for value in ['abc', '+10%', '-10%', '+10', '-10']: update_entry(calc._entry, value) self.assertInvalid(calc, ['_entry']) self.assertNotVisible(calc, ['_warning']) update_entry(calc._entry, '40') self.assertValid(calc, ['_entry']) self.assertNotVisible(calc, ['_warning']) # 50 of discount will make the value invalid on entry # (see validate_entry above) update_entry(calc._entry, '50') self.assertValid(calc, ['_entry']) self.assertVisible(calc, ['_warning'])
def test_validate(self): def validate_entry(entry, value): if value == 100: return ValidationError() # FIXME: For some reason, entry is not emitting 'changed' event # on set_text, not even if we call entry.emit('changed') by hand. # That only happens here on the test. Figure out why def update_entry(entry, value): entry.set_text(value) entry.validate(force=True) entry = ProxyEntry() entry.data_type = currency entry.connect('validate', validate_entry) entry.set_text('150') calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB) # calc.popup will not work here, so call _update_ui directly calc._update_ui() self.assertValid(calc, ['_entry']) self.assertNotVisible(calc, ['_warning']) for value in ['abc', '+10%', '-10%', '+10', '-10']: update_entry(calc._entry, value) self.assertInvalid(calc, ['_entry']) self.assertNotVisible(calc, ['_warning']) update_entry(calc._entry, '40') self.assertValid(calc, ['_entry']) self.assertNotVisible(calc, ['_warning']) # 50 of discount will make the value invalid on entry # (see validate_entry above) update_entry(calc._entry, '50') self.assertValid(calc, ['_entry']) self.assertVisible(calc, ['_warning'])
def _setup_entry_slave(self, box=None): widget = ProxyEntry() # Try to simulate insensitive appearance for non-editable entries # while keeping them selectable widget.set_editable(self.sensitive) if not self.sensitive: sc = widget.get_style_context() sc.add_class('visualmode') widget.data_type = str widget.model_attribute = "field_value" self.proxy.add_widget("field_value", widget) if box is None: self.container.add(widget) else: box.pack_start(widget, True, True, 0) widget.show() widget.connect('validate', self._on_entry__validate) widget.connect('validation-changed', self._on_entry__validation_changed) self._entry = widget
class TestSearchEntryGadget(GUITest): def _create_interface(self, run_editor=None): self.sale = self.create_sale() self.window = gtk.Window() self.entry = ProxyEntry() self.window.add(self.entry) self.client_gadget = SearchEntryGadget(self.entry, self.store, model=self.sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=self.window, run_editor=run_editor) self.client_gadget.get_model_obj = lambda obj: obj and obj.client def test_create(self): window = gtk.Window() box = gtk.VBox() window.add(box) entry = ProxyEntry() box.pack_start(entry) self.check_dialog(window, 'search-entry-before-replace') sale = self.create_sale() SearchEntryGadget(entry, self.store, model=sale, model_property='client', search_columns=['name'], search_class=ClientSearch, parent=window) self.check_dialog(window, 'search-entry-after-replace') @mock.patch('stoqlib.gui.widgets.searchentry.run_dialog') def test_run_search(self, run_dialog): self._create_interface() run_dialog.return_value = None self.click(self.client_gadget.find_button) run_dialog.assert_called_once_with(ClientSearch, self.window, self.store, initial_string='', double_click_confirm=True) @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store') @mock.patch('stoqlib.gui.widgets.searchentry.run_person_role_dialog') def test_run_editor(self, run_dialog, new_store): new_store.return_value = self.store self._create_interface() client = self.create_client(name=u'Fulano de Tal') run_dialog.return_value = self.store.find( ClientView, ClientView.id == client.id).one() with mock.patch.object(self.store, 'commit'): with mock.patch.object(self.store, 'close'): self.click(self.client_gadget.edit_button) run_dialog.assert_called_once_with(ClientEditor, self.window, self.store, None) self.assertEquals(self.entry.read(), client) self.assertEquals(self.entry.get_text(), u'Fulano de Tal') @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store') def test_run_editor_override(self, new_store): new_store.return_value = self.store run_editor = mock.MagicMock() run_editor.return_value = None self.assertEquals(run_editor.call_count, 0) self._create_interface(run_editor=run_editor) with mock.patch.object(self.store, 'commit'): with mock.patch.object(self.store, 'close'): self.click(self.client_gadget.edit_button) self.assertEquals(run_editor.call_count, 1) @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store') @mock.patch('stoqlib.gui.widgets.searchentry.run_dialog') def test_entry_activate(self, run_dialog, new_store): new_store.return_value = self.store self._create_interface() fulano = self.create_client(u'Fulano de Tal') ciclano = self.create_client(u'Cicrano de Tal') # There should be only one match for Fulano, then the entry should be # updated with this only match self.entry.set_text('Fulano') self.entry.activate() self.assertEquals(self.entry.get_text(), 'Fulano de Tal') self.assertEquals(self.entry.read(), fulano) # Now when we use 'de tal', there are two clients that match. The # search should be displayed run_dialog.return_value = self.store.find( ClientView, ClientView.id == ciclano.id).one() self.entry.set_text('de tal') self.entry.activate() run_dialog.assert_called_once_with(ClientSearch, self.window, self.store, initial_string='de tal', double_click_confirm=True) self.assertEquals(self.entry.get_text(), 'Cicrano de Tal') self.assertEquals(self.entry.read(), ciclano) @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store') @mock.patch('stoqlib.gui.widgets.searchentry.run_dialog') def test_with_cfop(self, run_dialog, new_store): new_store.return_value = self.store window = gtk.Window() entry = ProxyEntry() window.add(entry) sale = self.create_sale() gadget = SearchEntryGadget(entry, self.store, model=sale, model_property='cfop', search_columns=['name'], search_class=CfopSearch, parent=window) with mock.patch.object(self.store, 'commit'): with mock.patch.object(self.store, 'close'): run_dialog.return_value = None self.click(gadget.edit_button) run_dialog.assert_called_once_with(CfopEditor, window, self.store, sale.cfop)
def _setup_entry_slave(self, box=None): widget = ProxyEntry() # Try to simulate insensitive appearance for non-editable entries # while keeping them selectable widget.set_editable(self.sensitive) if not self.sensitive: style = widget.get_style() widget.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_INSENSITIVE]) widget.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_INSENSITIVE]) widget.data_type = unicode widget.model_attribute = "field_value" self.proxy.add_widget("field_value", widget) if box is None: self.container.add(widget) else: box.pack_start(widget) widget.show() widget.connect('validate', self._on_entry__validate) widget.connect('validation-changed', self._on_entry__validation_changed) self._entry = widget
class CalculatorPopup(PopupWindow): """A popup calculator for entries Right now it supports both :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` and :class:`kiwi.ui.widgets.entry.ProxyEntry`, as long as their data types are numeric (e.g. int, currency, Decimal, etc) """ #: The add mode. Any value typed on the entry will be added to the #: original value. e.g. 10% means +10% MODE_ADD = 0 #: The sub mode. Any value typed on the entry will be subtracted from the #: original value. e.g. 10% means -10% MODE_SUB = 1 _mode = None _data_type_mapper = { 'currency': currency, 'Decimal': decimal.Decimal, } def __init__(self, entry, mode): """ :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB` """ self._mode = mode self._new_value = None self._data_type = self._data_type_mapper[entry.data_type] self._converter = converter.get_converter(self._data_type) super(CalculatorPopup, self).__init__(entry) # # Public API # def get_main_widget(self): # This is done on entry to check where to put the validation/mandatory # icons. We should put the calculator on the other side. # Note that spinbuttons are always right aligned and thus # xalign will always be 1.0 if self.attached_widget.get_alignment() > 0.5: self._icon_pos = 'secondary-icon' else: self._icon_pos = 'primary-icon' self.attached_widget.set_property(self._icon_pos + '-activatable', True) self.attached_widget.set_property( self._icon_pos + '-tooltip-text', _("Do calculations on top of this value")) self.attached_widget.connect( 'notify::sensitive', self._on_entry_sensitive__notify) self.attached_widget.connect('icon-press', self._on_entry__icon_press) self._toggle_calculator_icon() vbox = Gtk.VBox(spacing=6) vbox.show() self._main_label = Gtk.Label() self._main_label.set_ellipsize(Pango.EllipsizeMode.END) vbox.pack_start(self._main_label, True, True, 0) self._main_label.show() self._entry = ProxyEntry() # FIXME: We need a model_attribute here or else the entry.is_valid() # will always return None. Check proxywidget.py's FIXME to see why self._entry.model_attribute = 'not_used' self._entry.connect('validate', self._on_entry__validate) self._entry.connect_after('changed', self._after_entry__changed) self._entry.set_alignment(1.0) vbox.pack_start(self._entry, True, True, 0) self._entry.show() hbox = Gtk.HBox(spacing=6) vbox.pack_start(hbox, True, True, 0) hbox.show() self._label = Gtk.Label() self._label.set_property('xalign', 1.0) self._label.set_use_markup(True) hbox.pack_start(self._label, True, True, 0) self._label.show() self._warning = Gtk.Image() hbox.pack_start(self._warning, False, False, 0) return vbox def validate_popup(self): try: self._new_value = self._data_type(self.attached_widget.get_text()) except decimal.InvalidOperation: return False self._entry.set_text('') self._entry.set_tooltip_text(_("Use absolute or percentage (%) value")) self._preview_new_value() self._main_label.set_text(self._get_main_label()) return True def confirm(self): self._maybe_apply_new_value() # # Private # def _get_main_label(self): if self._mode == self.MODE_ADD: return (_("Surcharge") if self._data_type == currency else _("Addition")) elif self._mode == self.MODE_SUB: return (_("Discount") if self._data_type == currency else _("Subtraction")) else: raise AssertionError def _set_warning(self, warning): if warning is None: self._warning.hide() else: self._warning.set_from_stock(Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.MENU) self._warning.set_tooltip_text(warning) self._warning.show() def _get_new_value(self): operation = self._entry.get_text().strip() operation = operation.replace(',', '.') if operation.endswith('%'): op_value = operation[:-1] percentage = True else: op_value = operation percentage = False if not operation: return if operation[0] in ['+', '-']: raise ValueError(_("Operator signals are not supported...")) if self._mode == self.MODE_SUB: op = operator.sub elif self._mode == self.MODE_ADD: op = operator.add try: op_value = decimal.Decimal(op_value) except decimal.InvalidOperation: raise ValueError( _("'%s' is not a valid operation...") % (operation,)) if percentage: value = op(self._new_value, self._new_value * (op_value / 100)) else: value = op(self._new_value, op_value) return value def _update_new_value(self): if not self._entry.is_valid(): return self._new_value = self._get_new_value() self._entry.set_text('') self._preview_new_value() def _preview_new_value(self): self._label.set_markup('<b>%s</b>' % ( self._converter.as_string(self._new_value), )) def _maybe_apply_new_value(self): if self._entry.get_text(): self._update_new_value() return self.attached_widget.update(self._new_value) self.popdown() def _toggle_calculator_icon(self): if self.attached_widget.get_sensitive(): icon = STOQ_CALC else: icon = None self.attached_widget.set_property(self._icon_pos + '-name', icon) # # Callbacks # def _on_entry__validate(self, entry, value): try: value = self._get_new_value() except ValueError as err: return ValidationError('%s\n%s' % (err, _("Use absolute or percentage (%) value"))) if value: warning = self.attached_widget.emit('validate', value) warning = warning and str(warning) else: warning = None self._set_warning(warning) def _after_entry__changed(self, entry): entry.validate(force=True) def _on_entry_sensitive__notify(self, obj, pspec): self._toggle_calculator_icon() def _on_entry__icon_press(self, entry, icon_pos, event): if icon_pos != Gtk.EntryIconPosition.SECONDARY: return self.popup()
def _create_entry(self, mandatory=False): entry = ProxyEntry() entry.data_type = str # Set as empty or kiwi will return ValueUnset on entry.read() # and we would have to take that in consideration everywhere here entry.update(u'') entry.mandatory = mandatory self.setup_entry(entry) entry.connect_after('content-changed', self._after_entry__content_changed) entry.connect_after('changed', self._after_entry__changed) entry.connect('validate', self._on_entry__validate) entry.connect('activate', self._on_entry__activate) return entry
class EditorPropertyName(EditorProperty): duplicate_name_msg = _("There is already a widget with this name") no_name_msg = _("The widget must have a name") def __init__(self, property_class, app): EditorProperty.__init__(self, property_class, app) self.input = ProxyEntry() self.input.set_property('data-type', str) self.input.connect('changed', self._changed_text) def load(self, widget): self._start_load(widget) self._internal_load(widget) self._finish_load() def _internal_load(self, widget): value = self.property.value pos = self.input.get_position() self.input.delete_text(0, -1) if value: self.input.insert_text(value) self.input.set_position(pos) # validate that the name exists and is unique if not value: self.input.set_invalid(self.no_name_msg) elif self._project.get_gadget_by_name(value) != widget: self.input.set_invalid(self.duplicate_name_msg) else: self.input.set_valid() def _changed_text(self, entry): """ Update the name property of the widget if the name is valid. """ if self.property_loading: return name = entry.get_chars(0, -1) if not name: self.input.set_invalid(self.no_name_msg) elif self._project.get_gadget_by_name(name): self.input.set_invalid(self.duplicate_name_msg) else: self.input.set_valid() self.set(name)
def build_widget(self): widget = ProxyEntry() return widget
def _create_entry(self, mandatory=False): entry = ProxyEntry() entry.data_type = unicode # Set as empty or kiwi will return ValueUnset on entry.read() # and we would have to take that in consideration everywhere here entry.update(u'') entry.mandatory = mandatory self.setup_entry(entry) entry.connect_after('content-changed', self._after_entry__content_changed) entry.connect_after('changed', self._after_entry__changed) entry.connect('validate', self._on_entry__validate) entry.connect('activate', self._on_entry__activate) return entry
class AccountEditor(BaseEditor): """ Account Editor """ gladefile = "AccountEditor" proxy_widgets = ['description', 'code'] model_type = Account model_name = _('Account') def __init__(self, store, model=None, parent_account=None): self._last_account_type = None self._bank_number = -1 self._bank_widgets = [] self._bank_option_widgets = [] self._option_fields = {} self._test_button = None self.existing = model is not None self.parent_account = parent_account self.bank_model = _TemporaryBankAccount() BaseEditor.__init__(self, store, model) action_area = self.main_dialog.action_area action_area.set_layout(Gtk.ButtonBoxStyle.END) action_area.pack_start(self._test_button, False, False, 0) action_area.set_child_secondary(self._test_button, True) self._test_button.show() # # BaseEditor hooks # def create_model(self, store): return Account(description=u"", account_type=Account.TYPE_CASH, store=store) def _setup_widgets(self): self._test_button = Gtk.Button(_("Print a test bill")) self._test_button.connect('clicked', self._on_test_button__clicked) self.parent_accounts = AccountTree(with_code=False, create_mode=True) self.parent_accounts.connect( 'selection-changed', self._on_parent_accounts__selection_changed) self.tree_box.pack_start(self.parent_accounts, True, True, 0) self.tree_box.reorder_child(self.parent_accounts, 0) if sysparam.compare_object('IMBALANCE_ACCOUNT', self.model): self.account_type.set_sensitive(False) self.account_type.prefill(Account.account_type_descriptions) account_type = self.model.account_type self.parent_accounts.insert_initial(self.store, edited_account=self.model) if self.parent_account: account = self.parent_accounts.get_account_by_id( self.parent_account.id) self.parent_accounts.select(account) if not self.existing: account_type = account.account_type self.account_type.select(account_type) self.parent_accounts.show() def setup_proxies(self): self._setup_widgets() self.add_proxy(self.model, AccountEditor.proxy_widgets) def validate_confirm(self): if not self.model.description: return False account = self.parent_accounts.get_selected() if not account: return True return account.selectable def on_confirm(self): new_parent = self.parent_accounts.get_selected() if new_parent: new_parent = new_parent.account if new_parent != self.model: self.model.parent = new_parent self.model.account_type = self.account_type.get_selected() self._save_bank() def refresh_ok(self, value): BaseEditor.refresh_ok(self, value) account_type = self.account_type.get_selected() if account_type != Account.TYPE_BANK: value = False self._test_button.set_sensitive(value) # Private def _save_bank(self): bank_account = self.model.bank if not bank_account: bank_account = BankAccount(account=self.model, store=self.store) # FIXME: Who sets this to a str? bank_account.bank_account = unicode(self.bank_model.bank_account) bank_account.bank_branch = unicode(self.bank_model.bank_branch) if self._bank_number is not None: bank_account.bank_number = self.bank_model.bank_number self._save_bank_bill_options(bank_account) def _save_bank_bill_options(self, bank_account): for option, entry in self._option_fields.items(): value = unicode(entry.get_text()) bill_option = self.store.find(BillOption, bank_account=bank_account, option=option).one() if bill_option is None: bill_option = BillOption(store=self.store, bank_account=bank_account, option=option, value=value) bill_option.value = value def _add_widget(self, label, widget, options=False): n_rows = self.table.props.n_rows l = Gtk.Label() l.set_markup(label) l.props.xalign = 1.0 self.table.resize(n_rows + 1, 2) self.table.attach(l, 0, 1, n_rows, n_rows + 1, Gtk.AttachOptions.FILL, 0, 0, 0) self.table.attach(widget, 1, 2, n_rows, n_rows + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0) if options: self._bank_option_widgets.extend([l, widget]) else: self._bank_widgets.extend([l, widget]) l.show() return l def _update_bank_type(self): self._remove_bank_option_widgets() bank_number = self.bank_type.get_selected() bank_info = None if bank_number: bank_info = get_bank_info_by_number(bank_number) self.bank_number = ProxyEntry() self.bank_number.props.data_type = int self.bank_number.set_sensitive(False) bank_number_lbl = self._add_widget(api.escape(_("Number:")), self.bank_number, options=True) self.bank_branch = ProxyEntry() self.bank_branch.connect('validate', self._on_bank_branch__validate, bank_info) self.bank_branch.props.data_type = 'str' self.bank_branch.props.mandatory = True self.bank_branch.model_attribute = "bank_branch" bank_branch_lbl = self._add_widget(api.escape(_("Agency:")), self.bank_branch, options=True) if bank_number is not None: bank_branch_lbl.show() self.bank_branch.show() else: bank_branch_lbl.hide() self.bank_account = ProxyEntry() self.bank_account.connect('validate', self._on_bank_account__validate, bank_info) self._add_widget(api.escape(_("Account:")), self.bank_account, options=True) self.bank_account.model_attribute = "bank_account" self.bank_account.props.data_type = 'str' if bank_number is not None: self.bank_account.props.mandatory = True self.bank_account.show() attributes = ['bank_account', 'bank_branch', 'bank_number'] if bank_number is not None: bank_number_lbl.show() self.bank_number.show() self.bank_model.bank_number = bank_number for i, option in enumerate(bank_info.get_extra_options()): name = 'option' + str(i) entry = ProxyEntry() entry.model_attribute = name setattr(self, name, entry) # Set the model attr too so it can be validated setattr(self.bank_model, name, u'') entry.props.data_type = 'str' entry.connect('validate', self._on_bank_option__validate, bank_info, option) name = option.replace('_', ' ').capitalize() self._add_widget("<i>%s</i>:" % api.escape(name), entry, options=True) entry.show() self._option_fields[option] = entry attributes.append(entry.model_attribute) else: bank_number_lbl.hide() self.bank_proxy = self.add_proxy(self.bank_model, attributes) self._fill_bank_account() def _fill_bank_account(self): if not self.model.bank: return self.bank_model.bank_branch = self.model.bank.bank_branch.encode( 'utf-8') self.bank_model.bank_account = self.model.bank.bank_account.encode( 'utf-8') self.bank_proxy.update('bank_branch') self.bank_proxy.update('bank_account') bill_options = list( self.store.find(BillOption, bank_account=self.model.bank)) for bill_option in bill_options: if bill_option.option is None: continue field_entry = self._option_fields.get(bill_option.option) if field_entry: field_entry.set_text(bill_option.value) def _update_account_type(self, account_type): if not self.account_type.get_sensitive(): return if account_type != Account.TYPE_BANK: self._remove_bank_widgets() self._remove_bank_option_widgets() self.code.set_sensitive(True) return self.code.set_sensitive(False) self.bank_type = ProxyComboBox() self._add_widget(api.escape(_("Bank:")), self.bank_type) self.bank_type.connect('content-changed', self._on_bank_type__content_changed) self.bank_type.show() banks = [(_('Generic'), None)] banks.extend([(b.description, b.bank_number) for b in get_all_banks()]) self.bank_type.prefill(banks) if self.model.bank: try: self.bank_type.select(self.model.bank.bank_number) except KeyError: self.bank_type.select(None) self._update_bank_type() def _remove_bank_widgets(self): for widget in self._bank_widgets: widget.get_parent().remove(widget) widget.destroy() self.table.resize(5, 2) self._bank_widgets = [] def _remove_bank_option_widgets(self): for widget in self._bank_option_widgets: widget.get_parent().remove(widget) widget.destroy() self.table.resize(5 + len(self._bank_widgets) / 2, 2) self._bank_option_widgets = [] self._option_fields = {} def _print_test_bill(self): try: bank_info = get_bank_info_by_number(self.bank_model.bank_number) except NotImplementedError: info(_("This bank does not support printing of bills")) return kwargs = dict( valor_documento=12345.67, data_vencimento=datetime.date.today(), data_documento=datetime.date.today(), data_processamento=datetime.date.today(), nosso_numero=u'24533', numero_documento=u'1138', sacado=[_(u"Drawee"), _(u"Address"), _(u"Details")], cedente=[_(u"Supplier"), _(u"Address"), _(u"Details"), ("CNPJ")], demonstrativo=[_(u"Demonstration")], instrucoes=[_(u"Instructions")], agencia=self.bank_model.bank_branch, conta=self.bank_model.bank_account, ) for opt in self.bank_model.options: kwargs[opt.option] = opt.value data = bank_info(**kwargs) print_report(BillTestReport, data) # Callbacks def _on_parent_accounts__selection_changed(self, objectlist, account): self.force_validation() def on_description__activate(self, entry): if self.validate_confirm(): self.confirm() def on_description__validate(self, entry, text): if not text: return ValidationError(_("Account description cannot be empty")) def on_account_type__content_changed(self, account_type): account_type = account_type.get_selected() if self._last_account_type == account_type: return self._update_account_type(account_type) self._last_account_type = account_type def _on_bank_type__content_changed(self, bank_type): bank_number = bank_type.get_selected() if self._bank_number == bank_number: return self._update_bank_type() self._bank_number = bank_number def _on_bank_branch__validate(self, entry, value, bank_info): if bank_info: try: bank_info.validate_field(value) except BoletoException as e: return ValidationError(str(e)) def _on_bank_account__validate(self, entry, value, bank_info): if bank_info: try: bank_info.validate_field(value) except BoletoException as e: return ValidationError(str(e)) def _on_bank_option__validate(self, entry, value, bank_info, option): try: bank_info.validate_option(option, value) except BoletoException as e: return ValidationError(str(e)) self.bank_model.set_option(option, value) def _on_test_button__clicked(self, button): self._print_test_bill()
def build_widget(self): entry = ProxyEntry() return entry
def _update_bank_type(self): self._remove_bank_option_widgets() bank_number = self.bank_type.get_selected() bank_info = None if bank_number: bank_info = get_bank_info_by_number(bank_number) self.bank_number = ProxyEntry() self.bank_number.props.data_type = int self.bank_number.set_sensitive(False) bank_number_lbl = self._add_widget(api.escape(_("Number:")), self.bank_number, options=True) self.bank_branch = ProxyEntry() self.bank_branch.connect('validate', self._on_bank_branch__validate, bank_info) self.bank_branch.props.data_type = 'str' self.bank_branch.props.mandatory = True self.bank_branch.model_attribute = "bank_branch" bank_branch_lbl = self._add_widget(api.escape(_("Agency:")), self.bank_branch, options=True) if bank_number is not None: bank_branch_lbl.show() self.bank_branch.show() else: bank_branch_lbl.hide() self.bank_account = ProxyEntry() self.bank_account.connect('validate', self._on_bank_account__validate, bank_info) self._add_widget(api.escape(_("Account:")), self.bank_account, options=True) self.bank_account.model_attribute = "bank_account" self.bank_account.props.data_type = 'str' if bank_number is not None: self.bank_account.props.mandatory = True self.bank_account.show() attributes = ['bank_account', 'bank_branch', 'bank_number'] if bank_number is not None: bank_number_lbl.show() self.bank_number.show() self.bank_model.bank_number = bank_number for i, option in enumerate(bank_info.get_extra_options()): name = 'option' + str(i) entry = ProxyEntry() entry.model_attribute = name setattr(self, name, entry) # Set the model attr too so it can be validated setattr(self.bank_model, name, u'') entry.props.data_type = 'str' entry.connect('validate', self._on_bank_option__validate, bank_info, option) name = option.replace('_', ' ').capitalize() self._add_widget("<i>%s</i>:" % api.escape(name), entry, options=True) entry.show() self._option_fields[option] = entry attributes.append(entry.model_attribute) else: bank_number_lbl.hide() self.bank_proxy = self.add_proxy(self.bank_model, attributes) self._fill_bank_account()
class StartNewLoanStep(WizardEditorStep): gladefile = 'SalesPersonStep' model_type = Loan proxy_widgets = ['client', 'salesperson', 'expire_date', 'client_category'] def _setup_widgets(self): # Hide total and subtotal self.summary_table.hide() self.total_box.hide() # Hide invoice number details self.invoice_number_label.hide() self.invoice_number.hide() # Hide cost center combobox self.cost_center_lbl.hide() self.cost_center.hide() # Responsible combo self.salesperson_lbl.set_text(_(u'Responsible:')) self.salesperson.model_attribute = 'responsible' users = self.store.find(LoginUser, is_active=True) self.salesperson.prefill(api.for_person_combo(users)) self.salesperson.set_sensitive(False) self._setup_clients_widget() self._fill_clients_category_combo() self.expire_date.mandatory = True # CFOP combo self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() # Transporter/RemovedBy Combo self.transporter_lbl.set_text(_(u'Removed By:')) self.create_transporter.hide() # removed_by widget self.removed_by = ProxyEntry(unicode) self.removed_by.model_attribute = 'removed_by' if 'removed_by' not in self.proxy_widgets: self.proxy_widgets.append('removed_by') self.removed_by.show() self._replace_widget(self.transporter, self.removed_by) def _setup_clients_widget(self): self.client.mandatory = True self.client_gadget = ClientSearchEntryGadget(entry=self.client, store=self.store, model=self.model, parent=self.wizard) def _fill_clients_category_combo(self): categories = self.store.find(ClientCategory) self.client_category.prefill(api.for_combo(categories, empty='')) def _replace_widget(self, old_widget, new_widget): # retrieve the position, since we will replace two widgets later. parent = old_widget.get_parent() top = parent.child_get_property(old_widget, 'top-attach') bottom = parent.child_get_property(old_widget, 'bottom-attach') left = parent.child_get_property(old_widget, 'left-attach') right = parent.child_get_property(old_widget, 'right-attach') parent.remove(old_widget) parent.attach(new_widget, left, right, top, bottom) def _get_client(self): client_id = self.client.read() return self.store.get(Client, client_id) # # WizardStep hooks # def post_init(self): self.register_validate_function(self.wizard.refresh_next) self.force_validation() def next_step(self): return LoanItemStep(self.wizard, self, self.store, self.model) def has_previous_step(self): return False def setup_proxies(self): self._setup_widgets() self.proxy = self.add_proxy(self.model, StartNewLoanStep.proxy_widgets) # # Callbacks # def on_client__changed(self, widget): client = self._get_client() if not client: return self.client_category.select(client.category) def on_expire_date__validate(self, widget, value): if value < localtoday().date(): msg = _(u"The expire date must be set to today or a future date.") return ValidationError(msg) def on_observations_button__clicked(self, *args): run_dialog(NoteEditor, self.wizard, self.store, self.model, 'notes', title=_("Additional Information"))
class StartNewLoanStep(WizardEditorStep): gladefile = 'SalesPersonStep' model_type = Loan proxy_widgets = ['client', 'salesperson', 'expire_date', 'client_category'] def _setup_widgets(self): # Hide total and subtotal self.table1.hide() self.hbox4.hide() # Hide invoice number details self.invoice_number_label.hide() self.invoice_number.hide() # Hide cost center combobox self.cost_center_lbl.hide() self.cost_center.hide() # Responsible combo self.salesperson_lbl.set_text(_(u'Responsible:')) self.salesperson.model_attribute = 'responsible' users = self.store.find(LoginUser, is_active=True) self.salesperson.prefill(api.for_person_combo(users)) self.salesperson.set_sensitive(False) self._fill_clients_combo() self._fill_clients_category_combo() self.expire_date.mandatory = True # CFOP combo self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() # Transporter/RemovedBy Combo self.transporter_lbl.set_text(_(u'Removed By:')) self.create_transporter.hide() # removed_by widget self.removed_by = ProxyEntry(unicode) self.removed_by.model_attribute = 'removed_by' if 'removed_by' not in self.proxy_widgets: self.proxy_widgets.append('removed_by') self.removed_by.show() self._replace_widget(self.transporter, self.removed_by) # Operation Nature widget self.operation_nature.hide() self.nature_lbl.hide() def _fill_clients_combo(self): # FIXME: This should not be using a normal ProxyComboEntry, # we need a specialized widget that does the searching # on demand. items = Client.get_active_items(self.store) self.client.prefill(items) self.client.mandatory = True def _fill_clients_category_combo(self): categories = self.store.find(ClientCategory) self.client_category.prefill(api.for_combo(categories, empty='')) def _replace_widget(self, old_widget, new_widget): # retrieve the position, since we will replace two widgets later. parent = old_widget.get_parent() top = parent.child_get_property(old_widget, 'top-attach') bottom = parent.child_get_property(old_widget, 'bottom-attach') left = parent.child_get_property(old_widget, 'left-attach') right = parent.child_get_property(old_widget, 'right-attach') parent.remove(old_widget) parent.attach(new_widget, left, right, top, bottom) parent.child_set_property(new_widget, 'y-padding', 3) parent.child_set_property(new_widget, 'x-padding', 3) # # WizardStep hooks # def post_init(self): self.toogle_client_details() self.register_validate_function(self.wizard.refresh_next) self.force_validation() def next_step(self): return LoanItemStep(self.wizard, self, self.store, self.model) def has_previous_step(self): return False def setup_proxies(self): self._setup_widgets() self.proxy = self.add_proxy(self.model, StartNewLoanStep.proxy_widgets) def toogle_client_details(self): client = self.client.read() self.client_details.set_sensitive(bool(client)) # # Callbacks # def on_create_client__clicked(self, button): store = api.new_store() client = run_person_role_dialog(ClientEditor, self.wizard, store, None) retval = store.confirm(client) client = self.store.fetch(client) store.close() if not retval: return self._fill_clients_combo() self.client.select(client) def on_client__changed(self, widget): self.toogle_client_details() client = self.client.get_selected() if not client: return self.client_category.select(client.category) def on_expire_date__validate(self, widget, value): if value < localtoday().date(): msg = _(u"The expire date must be set to today or a future date.") return ValidationError(msg) def on_observations_button__clicked(self, *args): run_dialog(NoteEditor, self.wizard, self.store, self.model, 'notes', title=_("Additional Information")) def on_client_details__clicked(self, button): client = self.model.client run_dialog(ClientDetailsDialog, self.wizard, self.store, client)
class CalculatorPopup(gtk.Window): """A popup calculator for entries Right now it supports both :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` and :class:`kiwi.ui.widgets.entry.ProxyEntry`, as long as their data types are numeric (e.g. int, currency, Decimal, etc) """ #: The add mode. Any value typed on the entry will be added to the #: original value. e.g. 10% means +10% MODE_ADD = 0 #: The sub mode. Any value typed on the entry will be subtracted from the #: original value. e.g. 10% means -10% MODE_SUB = 1 _mode = None _data_type_mapper = { 'currency': currency, 'Decimal': decimal.Decimal, } def __init__(self, entry, mode): """ :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB` """ self._mode = mode self._new_value = None self._popped_entry = entry self._data_type = self._data_type_mapper[entry.data_type] self._converter = converter.get_converter(self._data_type) gtk.Window.__init__(self, gtk.WINDOW_POPUP) self._create_ui() # # Public API # def popup(self): if not self._popped_entry.get_realized(): return if not self._update_ui(): return toplevel = self._popped_entry.get_toplevel().get_toplevel() if (isinstance(toplevel, (gtk.Window, gtk.Dialog)) and toplevel.get_group()): toplevel.get_group().add_window(self) # width is meant for the popup window x, y, width, height = self._get_position() self.set_size_request(width, -1) self.move(x, y) self.show_all() if not self._popup_grab_window(): self.hide() return self.grab_add() def popdown(self): if not self._popped_entry.get_realized(): return self.grab_remove() self.hide() self._popped_entry.grab_focus() # # Private # def _create_ui(self): self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK) self.connect('key-press-event', self._on__key_press_event) self.connect('button-press-event', self._on__button_press_event) # This is done on entry to check where to put the validation/mandatory # icons. We should put the calculator on the other side. # Note that spinbuttons are always right aligned and thus # xalign will always be 1.0 if self._popped_entry.get_property('xalign') > 0.5: self._icon_pos = 'secondary-icon' else: self._icon_pos = 'primary-icon' self._popped_entry.set_property(self._icon_pos + '-activatable', True) self._popped_entry.set_property(self._icon_pos + '-tooltip-text', _("Do calculations on top of this value")) self._popped_entry.connect('notify::sensitive', self._on_popped_entry_sensitive__notify) self._popped_entry.connect('icon-press', self._on_popped_entry__icon_press) self._toggle_calculator_icon() frame = gtk.Frame() frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT) self.add(frame) frame.show() alignment = gtk.Alignment(0.5, 0.5, 1.0, 1.0) alignment.set_padding(6, 6, 2, 2) frame.add(alignment) alignment.show() vbox = gtk.VBox(spacing=6) alignment.add(vbox) vbox.show() self._main_label = gtk.Label() self._main_label.set_ellipsize(pango.ELLIPSIZE_END) vbox.pack_start(self._main_label, True, True) self._main_label.show() self._entry = ProxyEntry() # FIXME: We need a model_attribute here or else the entry.is_valid() # will always return None. Check proxywidget.py's FIXME to see why self._entry.model_attribute = 'not_used' self._entry.connect('validate', self._on_entry__validate) self._entry.connect_after('changed', self._after_entry__changed) self._entry.set_alignment(1.0) vbox.pack_start(self._entry, True, True) self._entry.show() hbox = gtk.HBox(spacing=6) vbox.pack_start(hbox, True, True) hbox.show() self._label = gtk.Label() self._label.set_property('xalign', 1.0) self._label.set_use_markup(True) hbox.pack_start(self._label, True, True) self._label.show() self._warning = gtk.Image() hbox.pack_start(self._warning, False, False) self.set_resizable(False) self.set_screen(self._popped_entry.get_screen()) def _update_ui(self): try: self._new_value = self._data_type(self._popped_entry.get_text()) except decimal.InvalidOperation: return False self._entry.set_text('') self._entry.set_tooltip_text(_("Use absolute or percentage (%) value")) self._preview_new_value() self._main_label.set_text(self._get_main_label()) return True def _get_main_label(self): if self._mode == self.MODE_ADD: return (_("Surcharge") if self._data_type == currency else _("Addition")) elif self._mode == self.MODE_SUB: return (_("Discount") if self._data_type == currency else _("Subtraction")) else: raise AssertionError def _set_warning(self, warning): if warning is None: self._warning.hide() else: self._warning.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_MENU) self._warning.set_tooltip_text(warning) self._warning.show() def _popup_grab_window(self): activate_time = 0L window = self.get_window() grab_status = gtk.gdk.pointer_grab(window, True, (gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK), None, None, activate_time) if grab_status == gtk.gdk.GRAB_SUCCESS: if gtk.gdk.keyboard_grab(window, True, activate_time) == 0: return True else: window.get_display().pointer_ungrab(activate_time) return False return False def _get_position(self): widget = self._popped_entry allocation = widget.get_allocation() window = widget.get_window() if hasattr(window, 'get_root_coords'): x = 0 y = 0 if not widget.get_has_window(): x += allocation.x y += allocation.y x, y = window.get_root_coords(x, y) else: # PyGTK lacks gdk_window_get_root_coords(), # but we can use get_origin() instead, which is the # same thing in our case. x, y = widget.window.get_origin() return x, y + allocation.height, allocation.width, allocation.height def _get_new_value(self): operation = self._entry.get_text().strip() operation = operation.replace(',', '.') if operation.endswith('%'): op_value = operation[:-1] percentage = True else: op_value = operation percentage = False if not operation: return if operation[0] in ['+', '-']: raise ValueError(_("Operator signals are not supported...")) if self._mode == self.MODE_SUB: op = operator.sub elif self._mode == self.MODE_ADD: op = operator.add try: op_value = decimal.Decimal(op_value) except decimal.InvalidOperation: raise ValueError( _("'%s' is not a valid operation...") % (operation,)) if percentage: value = op(self._new_value, self._new_value * (op_value / 100)) else: value = op(self._new_value, op_value) return value def _update_new_value(self): if not self._entry.is_valid(): return self._new_value = self._get_new_value() self._entry.set_text('') self._preview_new_value() def _preview_new_value(self): self._label.set_markup('<b>%s</b>' % ( self._converter.as_string(self._new_value), )) def _maybe_apply_new_value(self): if self._entry.get_text(): self._update_new_value() return self._popped_entry.update(self._new_value) self.popdown() def _toggle_calculator_icon(self): if self._popped_entry.get_sensitive(): pixbuf = self.render_icon(STOQ_CALC, gtk.ICON_SIZE_MENU) else: pixbuf = None self._popped_entry.set_property(self._icon_pos + '-pixbuf', pixbuf) # # Callbacks # def _on__key_press_event(self, window, event): keyval = event.keyval if keyval == gtk.keysyms.Escape: self.popdown() return True elif keyval in [gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab]: self._maybe_apply_new_value() return True return False def _on__button_press_event(self, window, event): # If we're clicking outside of the window # close the popup if (event.window != self.get_window() or (tuple(self.allocation.intersect( gtk.gdk.Rectangle(x=int(event.x), y=int(event.y), width=1, height=1)))) == (0, 0, 0, 0)): self.popdown() def _on_entry__validate(self, entry, value): try: value = self._get_new_value() except ValueError as err: return ValidationError('%s\n%s' % (err, _("Use absolute or percentage (%) value"))) if value: warning = self._popped_entry.emit('validate', value) warning = warning and str(warning) else: warning = None self._set_warning(warning) def _after_entry__changed(self, entry): entry.validate(force=True) def _on_popped_entry_sensitive__notify(self, obj, pspec): self._toggle_calculator_icon() def _on_popped_entry__icon_press(self, entry, icon_pos, event): if icon_pos != gtk.ENTRY_ICON_SECONDARY: return self.popup()
import gtk from kiwi.ui.widgets.entry import ProxyEntry def on_entry_activate(entry): print 'You selected:', entry.read() gtk.main_quit() win = gtk.Window() win.connect('delete-event', gtk.main_quit) vbox = gtk.VBox() win.add(vbox) # Normal entry entry = ProxyEntry() entry.data_type = str entry.connect('activate', on_entry_activate) entry.prefill(['Belo Horizonte', u'São Carlos', u'São Paulo', u'Båstad', u'Örnsköldsvik', 'sanca', 'sampa']) vbox.pack_start(entry) entry = ProxyEntry() entry.data_type = int entry.connect('activate', on_entry_activate) entry.prefill([('Brazil', 186), ('Sweden', 9), ('China', 1306)]) vbox.pack_start(entry)
def _create_ui(self): self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK) self.connect('key-press-event', self._on__key_press_event) self.connect('button-press-event', self._on__button_press_event) # This is done on entry to check where to put the validation/mandatory # icons. We should put the calculator on the other side. # Note that spinbuttons are always right aligned and thus # xalign will always be 1.0 if self._popped_entry.get_property('xalign') > 0.5: self._icon_pos = 'secondary-icon' else: self._icon_pos = 'primary-icon' self._popped_entry.set_property(self._icon_pos + '-activatable', True) self._popped_entry.set_property(self._icon_pos + '-tooltip-text', _("Do calculations on top of this value")) self._popped_entry.connect('notify::sensitive', self._on_popped_entry_sensitive__notify) self._popped_entry.connect('icon-press', self._on_popped_entry__icon_press) self._toggle_calculator_icon() frame = gtk.Frame() frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT) self.add(frame) frame.show() alignment = gtk.Alignment(0.5, 0.5, 1.0, 1.0) alignment.set_padding(6, 6, 2, 2) frame.add(alignment) alignment.show() vbox = gtk.VBox(spacing=6) alignment.add(vbox) vbox.show() self._main_label = gtk.Label() self._main_label.set_ellipsize(pango.ELLIPSIZE_END) vbox.pack_start(self._main_label, True, True) self._main_label.show() self._entry = ProxyEntry() # FIXME: We need a model_attribute here or else the entry.is_valid() # will always return None. Check proxywidget.py's FIXME to see why self._entry.model_attribute = 'not_used' self._entry.connect('validate', self._on_entry__validate) self._entry.connect_after('changed', self._after_entry__changed) self._entry.set_alignment(1.0) vbox.pack_start(self._entry, True, True) self._entry.show() hbox = gtk.HBox(spacing=6) vbox.pack_start(hbox, True, True) hbox.show() self._label = gtk.Label() self._label.set_property('xalign', 1.0) self._label.set_use_markup(True) hbox.pack_start(self._label, True, True) self._label.show() self._warning = gtk.Image() hbox.pack_start(self._warning, False, False) self.set_resizable(False) self.set_screen(self._popped_entry.get_screen())
class CalculatorPopup(PopupWindow): """A popup calculator for entries Right now it supports both :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` and :class:`kiwi.ui.widgets.entry.ProxyEntry`, as long as their data types are numeric (e.g. int, currency, Decimal, etc) """ #: The add mode. Any value typed on the entry will be added to the #: original value. e.g. 10% means +10% MODE_ADD = 0 #: The sub mode. Any value typed on the entry will be subtracted from the #: original value. e.g. 10% means -10% MODE_SUB = 1 _mode = None _data_type_mapper = { 'currency': currency, 'Decimal': decimal.Decimal, } def __init__(self, entry, mode): """ :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB` """ self._mode = mode self._new_value = None self._data_type = self._data_type_mapper[entry.data_type] self._converter = converter.get_converter(self._data_type) super(CalculatorPopup, self).__init__(entry) # # Public API # def get_main_widget(self): # This is done on entry to check where to put the validation/mandatory # icons. We should put the calculator on the other side. # Note that spinbuttons are always right aligned and thus # xalign will always be 1.0 if self.attached_widget.get_alignment() > 0.5: self._icon_pos = 'secondary-icon' else: self._icon_pos = 'primary-icon' self.attached_widget.set_property(self._icon_pos + '-activatable', True) self.attached_widget.set_property( self._icon_pos + '-tooltip-text', _("Do calculations on top of this value")) self.attached_widget.connect('notify::sensitive', self._on_entry_sensitive__notify) self.attached_widget.connect('icon-press', self._on_entry__icon_press) self._toggle_calculator_icon() vbox = Gtk.VBox(spacing=6) vbox.show() self._main_label = Gtk.Label() self._main_label.set_ellipsize(Pango.EllipsizeMode.END) vbox.pack_start(self._main_label, True, True, 0) self._main_label.show() self._entry = ProxyEntry() # FIXME: We need a model_attribute here or else the entry.is_valid() # will always return None. Check proxywidget.py's FIXME to see why self._entry.model_attribute = 'not_used' self._entry.connect('validate', self._on_entry__validate) self._entry.connect_after('changed', self._after_entry__changed) self._entry.set_alignment(1.0) vbox.pack_start(self._entry, True, True, 0) self._entry.show() hbox = Gtk.HBox(spacing=6) vbox.pack_start(hbox, True, True, 0) hbox.show() self._label = Gtk.Label() self._label.set_property('xalign', 1.0) self._label.set_use_markup(True) hbox.pack_start(self._label, True, True, 0) self._label.show() self._warning = Gtk.Image() hbox.pack_start(self._warning, False, False, 0) return vbox def validate_popup(self): try: self._new_value = self._data_type(self.attached_widget.get_text()) except decimal.InvalidOperation: return False self._entry.set_text('') self._entry.set_tooltip_text(_("Use absolute or percentage (%) value")) self._preview_new_value() self._main_label.set_text(self._get_main_label()) return True def confirm(self): self._maybe_apply_new_value() # # Private # def _get_main_label(self): if self._mode == self.MODE_ADD: return (_("Surcharge") if self._data_type == currency else _("Addition")) elif self._mode == self.MODE_SUB: return (_("Discount") if self._data_type == currency else _("Subtraction")) else: raise AssertionError def _set_warning(self, warning): if warning is None: self._warning.hide() else: self._warning.set_from_stock(Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.MENU) self._warning.set_tooltip_text(warning) self._warning.show() def _get_new_value(self): operation = self._entry.get_text().strip() operation = operation.replace(',', '.') if operation.endswith('%'): op_value = operation[:-1] percentage = True else: op_value = operation percentage = False if not operation: return if operation[0] in ['+', '-']: raise ValueError(_("Operator signals are not supported...")) if self._mode == self.MODE_SUB: op = operator.sub elif self._mode == self.MODE_ADD: op = operator.add try: op_value = decimal.Decimal(op_value) except decimal.InvalidOperation: raise ValueError( _("'%s' is not a valid operation...") % (operation, )) if percentage: value = op(self._new_value, self._new_value * (op_value / 100)) else: value = op(self._new_value, op_value) return value def _update_new_value(self): if not self._entry.is_valid(): return self._new_value = self._get_new_value() self._entry.set_text('') self._preview_new_value() def _preview_new_value(self): self._label.set_markup('<b>%s</b>' % (self._converter.as_string(self._new_value), )) def _maybe_apply_new_value(self): if self._entry.get_text(): self._update_new_value() return self.attached_widget.update(self._new_value) self.popdown() def _toggle_calculator_icon(self): if self.attached_widget.get_sensitive(): pixbuf = self.render_icon(STOQ_CALC, Gtk.IconSize.MENU) else: pixbuf = None self.attached_widget.set_property(self._icon_pos + '-pixbuf', pixbuf) # # Callbacks # def _on_entry__validate(self, entry, value): try: value = self._get_new_value() except ValueError as err: return ValidationError( '%s\n%s' % (err, _("Use absolute or percentage (%) value"))) if value: warning = self.attached_widget.emit('validate', value) warning = warning and str(warning) else: warning = None self._set_warning(warning) def _after_entry__changed(self, entry): entry.validate(force=True) def _on_entry_sensitive__notify(self, obj, pspec): self._toggle_calculator_icon() def _on_entry__icon_press(self, entry, icon_pos, event): if icon_pos != Gtk.EntryIconPosition.SECONDARY: return self.popup()
def _setup_entry_slave(self, box=None): widget = ProxyEntry() # Try to simulate insensitive appearance for non-editable entries # while keeping them selectable widget.set_editable(self.sensitive) if not self.sensitive: style = widget.get_style() widget.modify_text( gtk.STATE_NORMAL, style.text[gtk.STATE_INSENSITIVE]) widget.modify_base( gtk.STATE_NORMAL, style.base[gtk.STATE_INSENSITIVE]) widget.data_type = unicode widget.model_attribute = "field_value" self.proxy.add_widget("field_value", widget) if box is None: self.container.add(widget) else: box.pack_start(widget) widget.show() widget.connect('validate', self._on_entry__validate) widget.connect('validation-changed', self._on_entry__validation_changed) self._entry = widget
def __init__(self, property_class, app): EditorProperty.__init__(self, property_class, app) self.input = ProxyEntry() self.input.set_property('data-type', str) self.input.connect('changed', self._changed_text)