问题
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?
回答1:
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.
回答2:
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).
回答3:
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.
回答4:
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:
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
Work like a charm:
来源:https://stackoverflow.com/questions/34633447/urwid-make-cursor-invisible