I want to change an urwid.Edit's text from within its "change" signal handler. However, it doesn't do anything. Minimal working example:
import urwid
input_line = urwid.Edit(multiline=True)
def input_change(widget, text):
if text.endswith("\n"):
input_line.set_edit_text('')
urwid.connect_signal(input_line, 'change', input_change)
urwid.MainLoop(urwid.Filler(input_line)).run()
If you press enter, it will actually call .set_edit_text(), but the text remains the same. How do I achieve what I want?
As you can see in the source, the set_edit_text
method emits your "change"
event, and then immediately afterward, it sets the _edit_text
to the actual value.*
You can also verify this by, e.g., logging input_line.edit_text
immediately after your set_edit_text
to see that you did successfully change it.
What you need to do here is subclass the Edit
widget, and override set_edit_text
,** not handle the "change"
signal. Then it's easy.
For example:
def set_edit_text(self, text):
if text.endswith('\n'):
super().set_edit_text('')
else:
super().set_edit_text(text)
As mentioned above, there's a very good reason for a GUI framework to have events that fire before the change is applied: that gives your event handler a way to see both the current value and the new value.
Of course there's also a very good reason for a GUI framework to have events that fire after the change is applied.
Some frameworks provide both. For example, in Cocoa, you usually get a fooWillChange:
message before the change, and a fooDidChange:
message afterward. Also, in some frameworks, the "before" event gives you a way to influence how the event gets handled (replace one of its values, swallow the event so it doesn't get passed up the chain, etc.). And then there's Tkinter, which provides some way to do all of these different things, but they're all completely different from each other, and different from widget to widget…
Is it a bug for a framework to not have all of the possible options? Well, there's a downside to a framework being too big and too general. It's harder to develop and maintain, and, worse, harder to learn. I think urwid made a reasonable choice here. Especially since it's written in relatively simple pure Python with a class class hierarchy that makes it easy to override any behavior you don't like.
However, you could maybe call it a documentation bug that Urwid doesn't tell you which kind of signal logic is uses (immutable "before" events), and offers very little guidance on what to override to customize behavior.
* It's also worth noting that your change
handler is getting called in the middle of set_edit_text
. In urwid, calling set_edit_text
from this handler isn't a problem, but in many other UI libraries it could lead to infinite recursion or bizarre behavior.
** You could of course monkeypatch Edit
instead of subclassing, unless you have a particular reason to do that, I wouldn't.
Here is another way to do it by overriding "keypress" and defining your own "done" signal that is emitted when you press enter:
class CustomEdit(urwid.Edit):
_metaclass_ = urwid.signals.MetaSignals
signals = ['done']
def keypress(self, size, key):
if key == 'enter':
urwid.emit_signal(self, 'done', self, self.get_edit_text()) #if you dont need a reference to the CustomEdit instance you can drop the 3rd argument
super(CustomEdit, self).set_edit_text('')
return
elif key == 'esc':
super(CustomEdit, self).set_edit_text('')
return
urwid.Edit.keypress(self, size, key)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With