def setUp(self): self.view = FakeView() self.view.add('checkbutton', bool, ProxyCheckButton) self.view.add('entry', str, ProxyEntry) self.view.add('label', str, ProxyLabel) self.view.add('spinbutton', int, ProxySpinButton) self.view.add('hscale', float, ProxyHScale) self.view.add('vscale', float, ProxyVScale) self.view.add('button', str, ProxyButton) self.view.add('buttonpixbuf', gdk.Pixbuf, ProxyButton) self.view.add('textview', str, ProxyTextView) self.radio_first = self.view.add('radiobutton', str, ProxyRadioButton) self.radio_first.set_property('data_value', 'first') self.radio_second = ProxyRadioButton() self.radio_second.set_group(self.radio_first) self.radio_second.set_property('data_value', 'second') self.view.hscale.get_adjustment().upper = 200 self.view.vscale.get_adjustment().upper = 250 self.comboentry = self.view.add('comboentry', str, ProxyComboEntry) self.comboentry.prefill(['CE1', 'CE2', 'CE3']) self.comboentry.show() self.combobox = self.view.add('combobox', str, ProxyComboBox) self.combobox.prefill(['CB1', 'CB2', 'CB3']) self.combobox.show() self.model = Model() self.proxy = Proxy(self.view, self.model, self.view.widgets)
def setUp(self): self.model = Settable(attr=0) proxy = Proxy(FakeView(), self.model) self.combo = disabledeprecationcall(self.type) self.combo.data_type = int self.combo.model_attribute = 'attr' self.combo.prefill([('foo', 0), ('bar', 1)]) proxy.add_widget('attr', self.combo) self.combo.show()
def test_prefill_attr_none(self): model = Settable(attr=None) proxy = Proxy(FakeView(), model) combo = ProxyComboEntry() combo.data_type = int combo.model_attribute = 'attr' combo.prefill([('foo', 10), ('bar', 20)]) proxy.add_widget('attr', combo) self.assertEqual(model.attr, None)
def test_prefill_attr_none(self): model = Settable(attr=None) proxy = Proxy(FakeView(), model) combo = ProxyComboBox() combo.data_type = int combo.model_attribute = 'attr' combo.prefill([('foo', 10), ('bar', 20)]) proxy.add_widget('attr', combo) # Even though attr is None, the combo doesn't allow something # not prefilled in it to be selected. In this case, it will select # the first item (prefill actually does that) instead. self.assertEqual(model.attr, 10)
def add_proxy(self, model=None, widgets=None): """ Add a proxy to this view that automatically update a model when the view changes. Arguments: - model. the object we are proxing. It can be None if we don't have a model yet and we want to display the interface and set it up with future models. - widgets. the list of widgets that contains model attributes to be proxied. If it is None (or not specified) it will be the whole list of widgets this View has. This method return a Proxy object that you may want to use to force updates or setting new models. Keep a reference to it since there is no way to get that proxy later on. You have been warned (tm) """ log.debug( '%s: adding proxy for %s' % (self.__class__.__name__, model and model.__class__.__name__)) widgets = widgets or self.widgets proxy_widgets = [] for widget_name in widgets: widget = getattr(self, widget_name, None) if widget is None: continue if not IValidatableProxyWidget.providedBy(widget): continue try: widget.connect('validation-changed', self._on_child__validation_changed, widget_name) except TypeError: raise AssertionError("%r does not have a validation-changed " "signal." % widget) proxy_widgets.append((widget_name, widget)) proxy = Proxy(self, model, widgets) self._proxies.append(proxy) for widget_name, widget in proxy_widgets: # Do not store validation value for invisible/insensitive widgets. # If they turn visible/sensitive, _on_child__validation_changed # will handle that if (not widget.get_property('visible') or not widget.get_property('sensitive')): continue # Proxy.__init__ will call widget.validate(force=True), so we # can use rely on widget.is_valid() here self._validation[widget_name] = widget.is_valid() validation_log.info( "%s: %s=%r (initial)" % (self.__class__.__name__, widget_name, widget.is_valid())) return proxy
def add_proxy(self, model=None, widgets=None): """ Add a proxy to this view that automatically update a model when the view changes. Arguments: - model. the object we are proxing. It can be None if we don't have a model yet and we want to display the interface and set it up with future models. - widgets. the list of widgets that contains model attributes to be proxied. If it is None (or not specified) it will be the whole list of widgets this View has. This method return a Proxy object that you may want to use to force updates or setting new models. Keep a reference to it since there is no way to get that proxy later on. You have been warned (tm) """ log('%s: adding proxy for %s' % ( self.__class__.__name__, model and model.__class__.__name__)) widgets = widgets or self.widgets for widget_name in widgets: widget = getattr(self, widget_name, None) if widget is None: continue if not IValidatableProxyWidget.providedBy(widget): continue try: widget.connect('validation-changed', self._on_child__validation_changed, widget_name) except TypeError: raise AssertionError("%r does not have a validation-changed " "signal." % widget) proxy = Proxy(self, model, widgets) self._proxies.append(proxy) return proxy
class TestProxy(unittest.TestCase): def setUp(self): self.view = FakeView() self.view.add('checkbutton', bool, ProxyCheckButton) self.view.add('entry', str, ProxyEntry) self.view.add('label', str, ProxyLabel) self.view.add('spinbutton', int, ProxySpinButton) self.view.add('hscale', float, ProxyHScale) self.view.add('vscale', float, ProxyVScale) self.view.add('button', str, ProxyButton) self.view.add('buttonpixbuf', gdk.Pixbuf, ProxyButton) self.view.add('textview', str, ProxyTextView) self.radio_first = self.view.add('radiobutton', str, ProxyRadioButton) self.radio_first.set_property('data_value', 'first') self.radio_second = ProxyRadioButton() self.radio_second.set_group(self.radio_first) self.radio_second.set_property('data_value', 'second') self.view.hscale.get_adjustment().upper = 200 self.view.vscale.get_adjustment().upper = 250 self.comboentry = self.view.add('comboentry', str, ProxyComboEntry) self.comboentry.prefill(['CE1', 'CE2', 'CE3']) self.comboentry.show() self.combobox = self.view.add('combobox', str, ProxyComboBox) self.combobox.prefill(['CB1', 'CB2', 'CB3']) self.combobox.show() self.model = Model() self.proxy = Proxy(self.view, self.model, self.view.widgets) def testCheckButton(self): self.assertEqual(self.model.checkbutton, True) self.view.checkbutton.set_active(False) self.assertEqual(self.model.checkbutton, False) def testEntry(self): self.assertEqual(self.model.entry, 'foo') self.view.entry.set_text('bar') self.assertEqual(self.model.entry, 'bar') # If the widget is insensitive, it should not change the model self.view.entry.set_sensitive(False) self.view.entry.set_text('bin') self.assertEqual(self.model.entry, 'bar') def testLabel(self): self.assertEqual(self.model.label, 'label') self.view.label.set_text('other label') # When the proxy label is updated, the model should not change. Labels # are non interactive widget, and the user cannot edit the value. self.assertEqual(self.model.label, 'label') def testRadioButton(self): self.assertEqual(self.model.radiobutton, 'first') self.radio_second.set_active(True) self.assertEqual(self.model.radiobutton, 'second') self.radio_first.set_active(True) self.assertEqual(self.model.radiobutton, 'first') def testHScale(self): self.assertEqual(self.model.vscale, 100) self.view.vscale.set_value(220) self.assertEqual(self.model.vscale, 220) def testVScale(self): self.assertEqual(self.model.vscale, 100) self.view.vscale.set_value(200) self.assertEqual(self.model.vscale, 200) def testSpinButton(self): self.assertEqual(self.model.spinbutton, 100) self.view.spinbutton.set_text("200") self.assertEqual(self.model.spinbutton, 200) # If the widget is insensitive, it should not change the model self.view.spinbutton.set_sensitive(False) self.view.spinbutton.set_text('400') self.assertEqual(self.model.spinbutton, 200) def testTextView(self): self.assertEqual(self.model.textview, 'sliff') self.view.textview.get_buffer().set_text('sloff') self.assertEqual(self.model.textview, 'sloff') def testComboEntry(self): self.assertEqual(self.model.comboentry, 'CE1') self.view.comboentry.select('CE2') self.assertEqual(self.model.comboentry, 'CE2') self.view.comboentry.entry.set_text('CENone') self.assertEqual(self.model.comboentry, None) def testComboBox(self): self.assertEqual(self.model.combobox, 'CB1') self.view.combobox.select('CB2') self.assertEqual(self.model.combobox, 'CB2') def testButton(self): self.assertEqual(self.model.button, 'button') self.view.button.update('sliff') self.assertEqual(self.model.button, 'sliff') def testEmptyModel(self): self.radio_second.set_active(True) self.proxy.set_model(None) self.assertEqual(self.view.entry.read(), ValueUnset) self.assertEqual(self.view.checkbutton.read(), False) self.assertEqual(self.view.radiobutton.read(), 'first') self.assertEqual(self.view.label.read(), '', 'label') self.assertEqual(self.view.spinbutton.read(), ValueUnset, 'spinbutton') self.assertEqual(self.view.textview.read(), '', 'textview') self.assertEqual(self.view.comboentry.read(), None, 'comboentry') self.assertEqual(self.view.combobox.read(), 'CB1', 'combobox') def testValueUnset(self): self.view.entry.update(ValueUnset) self.assertEqual(self.view.entry.get_text(), "") self.view.spinbutton.update(ValueUnset) self.assertEqual(self.view.spinbutton.get_text(), "") def testValidationIcon(self): def validate_entry(entry, value): if value == 'error': return ValidationError() entry = self.view.entry entry.connect('validate', validate_entry) original_set_invalid = self.view.entry.set_invalid with mock.patch.object(entry, 'set_invalid') as set_invalid: # Avoid fading so we can check the icon at the same time the # widget becomes invalid set_invalid.side_effect = lambda text: original_set_invalid( text=text, fade=False) self.proxy.update('entry', 'ok') self.assertEqual(entry.get_property("secondary-icon-pixbuf"), None) self.proxy.update('entry', 'error') # Making the widget invalid should put the validation icon on it self.assertNotEqual(entry.get_property("secondary-icon-pixbuf"), None) entry.set_sensitive(False) # But if the widget becomes insensitive, the icon should be removed self.assertEqual(entry.get_property("secondary-icon-pixbuf"), None) entry.set_sensitive(True) # And re-added when the widget becomes sensitive again self.assertNotEqual(entry.get_property("secondary-icon-pixbuf"), None) def testValueChangeWhenWidgetInalid(self): def validate_entry(entry, value): if entry.make_invalid: return ValidationError("") self.view.entry.connect('validate', validate_entry) self.view.entry.make_invalid = False self.view.entry.update("Propagated immediatly") self.assertEqual(self.model.entry, "Propagated immediatly") self.view.entry.make_invalid = True self.view.entry.update("Propagated later") self.assertEqual(self.model.entry, "Propagated immediatly") self.view.entry.make_invalid = False self.view.entry.validate(force=True) self.assertEqual(self.model.entry, "Propagated later") def testUpdateValueConversion(self): # entry has data_type of str, so an int should be converted to str self.proxy.update('entry', 666) self.assertEqual(self.view.entry.read(), '666') # entry has data_type of str, so an int should be converted to str self.view.entry.set_property('data-type', unicode) with self.assertRaises(TypeError) as te: # encode to iso-8859-1 so it will produce an UnicodeDecodeError self.proxy.update('entry', 'não'.encode('iso-8859-1')) self.assertEqual( te.exception.message, ("attribute entry of model <Model button='button', " "checkbutton=True, combobox='CB1', comboentry='CE1', " "entry='666', hscale=100.0, label='label', " "radiobutton='first', spinbutton=100, textview='sliff', " "vscale=100.0> cannot be converted to unicode")) # spinbutton has data_type of float, it should not try to # do the conversion, even thought it's trivial with self.assertRaises(TypeError) as te: self.proxy.update('spinbutton', '1') self.assertEqual( te.exception.message, ("attribute spinbutton of model <Model button='button', " "checkbutton=True, combobox='CB1', comboentry='CE1', " "entry='666', hscale=100.0, label='label', " "radiobutton='first', spinbutton=100, textview='sliff', " "vscale=100.0> requires a value of type int, not str"))
class TestProxy(unittest.TestCase): def setUp(self): self.view = FakeView() self.view.add('checkbutton', bool, ProxyCheckButton) self.view.add('entry', str, ProxyEntry) self.view.add('label', str, ProxyLabel) self.view.add('spinbutton', int, ProxySpinButton) self.view.add('hscale', float, ProxyHScale) self.view.add('vscale', float, ProxyVScale) self.view.add('button', str, ProxyButton) self.view.add('buttonpixbuf', gdk.Pixbuf, ProxyButton) self.view.add('textview', str, ProxyTextView) self.radio_first = self.view.add('radiobutton', str, ProxyRadioButton) self.radio_first.set_property('data_value', 'first') self.radio_second = ProxyRadioButton() self.radio_second.set_group(self.radio_first) self.radio_second.set_property('data_value', 'second') self.view.hscale.get_adjustment().upper = 200 self.view.vscale.get_adjustment().upper = 250 self.comboentry = self.view.add('comboentry', str, ProxyComboEntry) self.comboentry.prefill(['CE1','CE2','CE3']) self.comboentry.show() self.combobox = self.view.add('combobox', str, ProxyComboBox) self.combobox.prefill(['CB1','CB2','CB3']) self.combobox.show() self.model = Model() self.proxy = Proxy(self.view, self.model, self.view.widgets) def testCheckButton(self): self.assertEqual(self.model.checkbutton, True) self.view.checkbutton.set_active(False) self.assertEqual(self.model.checkbutton, False) def testEntry(self): self.assertEqual(self.model.entry, 'foo') self.view.entry.set_text('bar') self.assertEqual(self.model.entry, 'bar') def testLabel(self): self.assertEqual(self.model.label, 'label') self.view.label.set_text('other label') self.assertEqual(self.model.label, 'other label') def testRadioButton(self): self.assertEqual(self.model.radiobutton, 'first') self.radio_second.set_active(True) self.assertEqual(self.model.radiobutton, 'second') self.radio_first.set_active(True) self.assertEqual(self.model.radiobutton, 'first') def testHScale(self): self.assertEqual(self.model.vscale, 100) self.view.vscale.set_value(220) self.assertEqual(self.model.vscale, 220) def testVScale(self): self.assertEqual(self.model.vscale, 100) self.view.vscale.set_value(200) self.assertEqual(self.model.vscale, 200) def testSpinButton(self): self.assertEqual(self.model.spinbutton, 100) self.view.spinbutton.set_text("200") self.assertEqual(self.model.spinbutton, 200) def testTextView(self): self.assertEqual(self.model.textview, 'sliff') self.view.textview.get_buffer().set_text('sloff') self.assertEqual(self.model.textview, 'sloff') def testComboEntry(self): self.assertEqual(self.model.comboentry, 'CE1') self.view.comboentry.select('CE2') self.assertEqual(self.model.comboentry, 'CE2') self.view.comboentry.entry.set_text('CENone') self.assertEqual(self.model.comboentry, None) def testComboBox(self): self.assertEqual(self.model.combobox, 'CB1') self.view.combobox.select('CB2') self.assertEqual(self.model.combobox, 'CB2') def testButton(self): self.assertEqual(self.model.button, 'button') self.view.button.update('sliff') self.assertEqual(self.model.button, 'sliff') def testButtonPixbuf(self): if sys.platform == 'win32': return conv = converter.get_converter(gdk.Pixbuf) filename = environ.find_resource('pixmap', 'validation-error-16.png') pixbuf = gdk.pixbuf_new_from_file(filename) self.assertEqual(self.view.buttonpixbuf.data_type, 'Pixbuf') self.view.buttonpixbuf.update(pixbuf) self.assertEqual(type(self.view.buttonpixbuf.read()), gdk.Pixbuf) self.assertEqual(conv.as_string(self.model.buttonpixbuf), conv.as_string(pixbuf)) self.view.buttonpixbuf.update(None) self.assertEqual(self.view.buttonpixbuf.read(), None) self.assertEqual(self.model.buttonpixbuf, None) def testEmptyModel(self): self.radio_second.set_active(True) self.proxy.set_model(None) self.assertEqual(self.view.entry.read(), ValueUnset) self.assertEqual(self.view.checkbutton.read(), False) self.assertEqual(self.view.radiobutton.read(), 'first') self.assertEqual(self.view.label.read(), '', 'label') self.assertEqual(self.view.spinbutton.read(), ValueUnset, 'spinbutton') self.assertEqual(self.view.textview.read(), ValueUnset, 'textview') self.assertEqual(self.view.comboentry.read(), None, 'comboentry') self.assertEqual(self.view.combobox.read(), 'CB1', 'combobox') def testValueUnset(self): self.view.entry.update(ValueUnset) self.assertEqual(self.view.entry.get_text(), "") self.view.spinbutton.update(ValueUnset) self.assertEqual(self.view.spinbutton.get_text(), "")
class TestProxy(unittest.TestCase): def setUp(self): self.view = FakeView() self.view.add('checkbutton', bool, ProxyCheckButton) self.view.add('entry', str, ProxyEntry) self.view.add('label', str, ProxyLabel) self.view.add('spinbutton', int, ProxySpinButton) self.view.add('hscale', float, ProxyHScale) self.view.add('vscale', float, ProxyVScale) self.view.add('button', str, ProxyButton) self.view.add('buttonpixbuf', gdk.Pixbuf, ProxyButton) self.view.add('textview', str, ProxyTextView) self.radio_first = self.view.add('radiobutton', str, ProxyRadioButton) self.radio_first.set_property('data_value', 'first') self.radio_second = ProxyRadioButton() self.radio_second.set_group(self.radio_first) self.radio_second.set_property('data_value', 'second') self.view.hscale.get_adjustment().upper = 200 self.view.vscale.get_adjustment().upper = 250 self.comboentry = self.view.add('comboentry', str, ProxyComboEntry) self.comboentry.prefill(['CE1', 'CE2', 'CE3']) self.comboentry.show() self.combobox = self.view.add('combobox', str, ProxyComboBox) self.combobox.prefill(['CB1', 'CB2', 'CB3']) self.combobox.show() self.model = Model() self.proxy = Proxy(self.view, self.model, self.view.widgets) def testCheckButton(self): self.assertEqual(self.model.checkbutton, True) self.view.checkbutton.set_active(False) self.assertEqual(self.model.checkbutton, False) def testEntry(self): self.assertEqual(self.model.entry, 'foo') self.view.entry.set_text('bar') self.assertEqual(self.model.entry, 'bar') # If the widget is insensitive, it should not change the model self.view.entry.set_sensitive(False) self.view.entry.set_text('bin') self.assertEqual(self.model.entry, 'bar') def testLabel(self): self.assertEqual(self.model.label, 'label') self.view.label.set_text('other label') # When the proxy label is updated, the model should not change. Labels # are non interactive widget, and the user cannot edit the value. self.assertEqual(self.model.label, 'label') def testRadioButton(self): self.assertEqual(self.model.radiobutton, 'first') self.radio_second.set_active(True) self.assertEqual(self.model.radiobutton, 'second') self.radio_first.set_active(True) self.assertEqual(self.model.radiobutton, 'first') def testHScale(self): self.assertEqual(self.model.vscale, 100) self.view.vscale.set_value(220) self.assertEqual(self.model.vscale, 220) def testVScale(self): self.assertEqual(self.model.vscale, 100) self.view.vscale.set_value(200) self.assertEqual(self.model.vscale, 200) def testSpinButton(self): self.assertEqual(self.model.spinbutton, 100) self.view.spinbutton.set_text("200") self.assertEqual(self.model.spinbutton, 200) # If the widget is insensitive, it should not change the model self.view.spinbutton.set_sensitive(False) self.view.spinbutton.set_text('400') self.assertEqual(self.model.spinbutton, 200) def testTextView(self): self.assertEqual(self.model.textview, 'sliff') self.view.textview.get_buffer().set_text('sloff') self.assertEqual(self.model.textview, 'sloff') def testComboEntry(self): self.assertEqual(self.model.comboentry, 'CE1') self.view.comboentry.select('CE2') self.assertEqual(self.model.comboentry, 'CE2') self.view.comboentry.entry.set_text('CENone') self.assertEqual(self.model.comboentry, None) def testComboBox(self): self.assertEqual(self.model.combobox, 'CB1') self.view.combobox.select('CB2') self.assertEqual(self.model.combobox, 'CB2') def testButton(self): self.assertEqual(self.model.button, 'button') self.view.button.update('sliff') self.assertEqual(self.model.button, 'sliff') def testEmptyModel(self): self.radio_second.set_active(True) self.proxy.set_model(None) self.assertEqual(self.view.entry.read(), ValueUnset) self.assertEqual(self.view.checkbutton.read(), False) self.assertEqual(self.view.radiobutton.read(), 'first') self.assertEqual(self.view.label.read(), '', 'label') self.assertEqual(self.view.spinbutton.read(), ValueUnset, 'spinbutton') self.assertEqual(self.view.textview.read(), ValueUnset, 'textview') self.assertEqual(self.view.comboentry.read(), None, 'comboentry') self.assertEqual(self.view.combobox.read(), 'CB1', 'combobox') def testValueUnset(self): self.view.entry.update(ValueUnset) self.assertEqual(self.view.entry.get_text(), "") self.view.spinbutton.update(ValueUnset) self.assertEqual(self.view.spinbutton.get_text(), "") def testValueChangeWhenWidgetInalid(self): def validate_entry(entry, value): if entry.make_invalid: return ValidationError("") self.view.entry.connect('validate', validate_entry) self.view.entry.make_invalid = False self.view.entry.update("Propagated immediatly") self.assertEqual(self.model.entry, "Propagated immediatly") self.view.entry.make_invalid = True self.view.entry.update("Propagated later") self.assertEqual(self.model.entry, "Propagated immediatly") self.view.entry.make_invalid = False self.view.entry.validate(force=True) self.assertEqual(self.model.entry, "Propagated later")