I'm using urwid, which is a Python "framework" for designing terminal user interfaces in ncurses. There's one thing though that I'm not able to do in urwid that was easy in curses - make the cursor invisible. As it is now, the cursor is visible when selecting buttons, and it just looks plain ugly. Is there a way to disable it?
4 Answers
I agree that the flashing cursor on an urwid.Button
looks a bit lame, so I've come up with a solution to hide it. In urwid, the Button
class is just a subclass of WidgetWrap
containing a SelectableIcon
and two Text widgets (the enclosing "<" and ">"). It's the SelectableIcon
class that sets the cursor position to the first character of the label, by default. By subclassing SelectableIcon
, modifying the cursor position and then wrapping it into an urwid.WidgetWrap
subclass you can create your own custom button that can do all the tricks a built-in Button
, or even more.
Here' what it looks like in my project.
import urwid
class ButtonLabel(urwid.SelectableIcon):
def __init__(self, text):
"""
Here's the trick:
we move the cursor out to the right of the label/text, so it doesn't show
"""
curs_pos = len(text) + 1
urwid.SelectableIcon.__init__(self, text, cursor_position=curs_pos)
Next, you can wrap a ButtonLabel
object along with any other objects into a WidgetWrap
subclass that will be your custom button class.
class FixedButton(urwid.WidgetWrap):
_selectable = True
signals = ["click"]
def __init__(self, label):
self.label = ButtonLabel(label)
# you could combine the ButtonLabel object with other widgets here
display_widget = self.label
urwid.WidgetWrap.__init__(self, urwid.AttrMap(display_widget, None, focus_map="button_reversed"))
def keypress(self, size, key):
"""
catch all the keys you want to handle here
and emit the click signal along with any data
"""
pass
def set_label(self, new_label):
# we can set the label at run time, if necessary
self.label.set_text(str(new_label))
def mouse_event(self, size, event, button, col, row, focus):
"""
handle any mouse events here
and emit the click signal along with any data
"""
pass
In this code, there is actually not much combination of widgets in the FixedButton
WidgetWrap
subclass, but you could add a "[
" and "]
" to the edges of the button, wrap it into a LineBox
, etc. If all this is superfluous, you can just move the event handling functions into the ButtonLabel
class, and make it emit a signal when it gets clicked.
To make the button reversed when the user moves on it, wrap it into AttrMap
and set the focus_map
to some palette entry ("button_reversed
", in my case).

- 2,930
- 3
- 20
- 36
-
Hi @imrek, I needed a solution to this as well. Your's is a nice one. 2 things I found: 1) you don't really need to sublclass SelectableIcon - you could do everything from the self.label instance within FixedButton - checking the length and setting the cursor position. 2) your code will not work if 'label' is markup rather than plain text. Solution would be to use the urwid util function decompose_tagmarkup(). Full code in FixedButton.__init()__ looks like: text, attrib = decompose_tagmarkup(label) cursor_pos = len(text) + 1 self._label = urwid.SelectableIcon("", cursor_pos) – scottmac991 Aug 10 '20 at 14:46
Building upon Drunken Master's answer, I've cleaned up the solution as much as possible.
The urwid.SelectableIcon
is basically an urwid.Text
field with this ugly blinking cursor.
So instead of overriding the urwid.SelectableIcon
and packing it into an urwid.WidgetWrap
, let's take an urwid.Text
directly and make it selectable and react to button/mouse activation (inspired from urwid's simple menu tutorial):
import urwid
choices = u'Chapman Cleese Gilliam Idle Jones Palin'.split()
class ListEntry(urwid.Text):
_selectable = True
signals = ["click"]
def keypress(self, size, key):
"""
Send 'click' signal on 'activate' command.
"""
if self._command_map[key] != urwid.ACTIVATE:
return key
self._emit('click')
def mouse_event(self, size, event, button, x, y, focus):
"""
Send 'click' signal on button 1 press.
"""
if button != 1 or not urwid.util.is_mouse_press(event):
return False
self._emit('click')
return True
def menu(title, choices):
body = [urwid.Text(title), urwid.Divider()]
for c in choices:
button = ListEntry(c)
urwid.connect_signal(button, 'click', item_chosen, c)
body.append(urwid.AttrMap(button, None, focus_map='reversed'))
return urwid.ListBox(urwid.SimpleFocusListWalker(body))
def item_chosen(button, choice):
response = urwid.Text([u'You chose ', choice, u'\n'])
done = ListEntry(u'Ok')
urwid.connect_signal(done, 'click', exit_program)
main.original_widget = urwid.Filler(urwid.Pile([response,
urwid.AttrMap(done, None, focus_map='reversed')]))
def exit_program(button):
raise urwid.ExitMainLoop()
main = urwid.Padding(menu(u'Pythons', choices), left=2, right=2)
top = urwid.Overlay(main, urwid.SolidFill(u'\N{MEDIUM SHADE}'),
align='center', width=('relative', 60),
valign='middle', height=('relative', 60),
min_width=20, min_height=9)
urwid.MainLoop(top, palette=[('reversed', 'standout', '')]).run()
Works like a charm:

- 1,210
- 2
- 13
- 18
-
Do you have a full working example? I'm not sure how to use your code as-is. If you know of a better solution 18 months on, even better. This Question is the top result when looking to hide the cursor (and have the pretty inversed colours for showing focus), so would benefit from a full example. – Etzeitet Feb 22 '21 at 16:32
-
1Hi @Etzeitet, have a look into my edited answer. You can use the `ListEntry` class simply as replacement of `urwid.Button` class. – orzechow Feb 24 '21 at 10:33
-
fantastic! I can confirm the example works. I'm slowly getting the hang of urwid, and this certain helps. Thanks for the update! – Etzeitet Feb 24 '21 at 15:07
urwid uses the curs_set function, but does not expose it as a class method anywhere. Someone could modify urwid to allow using this method; otherwise there's no reliable method of doing this.
You might report it as an issue.

- 51,086
- 7
- 70
- 105
-
1I see. I posted an issue there, hopefully urwid devs will consider this. – makos Jan 09 '16 at 02:36
-
Exposing the func is insufficient as urwid will just turn the cursor back on. makos reported this upstream: https://github.com/urwid/urwid/issues/170 – Mike Frysinger Dec 19 '21 at 01:06
Along the lines of Drunken Master's answer, but with "minimally invasive surgery":
class ButtonLabel(urwid.SelectableIcon):
'''
use Drunken Master's trick to move the cursor out of view
'''
def set_text(self, label):
'''
set_text is invoked by Button.set_label
'''
self.__super.set_text(label)
self._cursor_position = len(label) + 1
class MyButton(urwid.Button):
'''
- override __init__ to use our ButtonLabel instead of urwid.SelectableIcon
- make button_left and button_right plain strings and variable width -
any string, including an empty string, can be set and displayed
- otherwise, we leave Button behaviour unchanged
'''
button_left = "["
button_right = "]"
def __init__(self, label, on_press=None, user_data=None):
self._label = ButtonLabel("")
cols = urwid.Columns([
('fixed', len(self.button_left), urwid.Text(self.button_left)),
self._label,
('fixed', len(self.button_right), urwid.Text(self.button_right))],
dividechars=1)
super(urwid.Button, self).__init__(cols)
if on_press:
urwid.connect_signal(self, 'click', on_press, user_data)
self.set_label(label)
Here, we only modify the button's appearance but otherwise leave its behaviour unchanged.

- 123
- 5