urwid:使光标不可见
Urwid: make cursor invisible
我正在使用 urwid,它 Python "framework" 用于在 ncurses 中设计终端用户界面。不过有一件事我不能在 urwid 中做,而这在 curses 中很容易——使光标不可见。就像现在一样,选择按钮时光标是可见的,而且看起来很丑陋。有没有办法禁用它?
urwid uses the curs_set 函数,但不会在任何地方将其作为 class 方法公开。有人可以修改 urwid 以允许使用此方法;否则没有可靠的方法可以做到这一点。
您可以将其报告为 issue。
我同意 urwid.Button
上闪烁的光标看起来有点蹩脚,所以我想出了一个隐藏它的解决方案。在 urwid 中,Button
class 只是 WidgetWrap
的子 class,包含一个 SelectableIcon
和两个文本小部件(封闭的“<”和“>” ).默认情况下,SelectableIcon
class 将光标位置设置为标签的第一个字符。通过 subclassing SelectableIcon
,修改光标位置,然后将其包装到 urwid.WidgetWrap
subclass 中,您可以创建自己的自定义按钮,它可以完成内置的所有技巧-in Button
,甚至更多。
这是我项目中的样子。
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)
接下来,您可以将 ButtonLabel
对象与任何其他对象一起包装到 WidgetWrap
子class 中,这将是您的自定义按钮 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
在这段代码中,FixedButton
WidgetWrap
子class中实际上没有太多的widgets组合,但是你可以添加一个“[
”和“ ]
" 到按钮的边缘,将其包装成 LineBox
,等等。如果所有这些都是多余的,您可以将事件处理函数移动到 ButtonLabel
class,并让它在被点击时发出信号。
要使按钮在用户移动时反转,将其包装到 AttrMap
并将 focus_map
设置为某个调色板条目(在我的例子中是“button_reversed
”) .
按照 Drunken Master 的回答,但 "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)
在这里,我们只修改了按钮的外观,其他方面的行为保持不变。
根据 Drunken Master 的回答,我已经尽可能地清理了解决方案。
urwid.SelectableIcon
基本上是一个带有丑陋的闪烁光标的 urwid.Text
字段。
因此,与其覆盖 urwid.SelectableIcon
并将其打包到 urwid.WidgetWrap
,不如直接使用 urwid.Text
并使其可选择并对 button/mouse 激活做出反应(灵感来自 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()
很有魅力:
我正在使用 urwid,它 Python "framework" 用于在 ncurses 中设计终端用户界面。不过有一件事我不能在 urwid 中做,而这在 curses 中很容易——使光标不可见。就像现在一样,选择按钮时光标是可见的,而且看起来很丑陋。有没有办法禁用它?
urwid uses the curs_set 函数,但不会在任何地方将其作为 class 方法公开。有人可以修改 urwid 以允许使用此方法;否则没有可靠的方法可以做到这一点。
您可以将其报告为 issue。
我同意 urwid.Button
上闪烁的光标看起来有点蹩脚,所以我想出了一个隐藏它的解决方案。在 urwid 中,Button
class 只是 WidgetWrap
的子 class,包含一个 SelectableIcon
和两个文本小部件(封闭的“<”和“>” ).默认情况下,SelectableIcon
class 将光标位置设置为标签的第一个字符。通过 subclassing SelectableIcon
,修改光标位置,然后将其包装到 urwid.WidgetWrap
subclass 中,您可以创建自己的自定义按钮,它可以完成内置的所有技巧-in Button
,甚至更多。
这是我项目中的样子。
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)
接下来,您可以将 ButtonLabel
对象与任何其他对象一起包装到 WidgetWrap
子class 中,这将是您的自定义按钮 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
在这段代码中,FixedButton
WidgetWrap
子class中实际上没有太多的widgets组合,但是你可以添加一个“[
”和“ ]
" 到按钮的边缘,将其包装成 LineBox
,等等。如果所有这些都是多余的,您可以将事件处理函数移动到 ButtonLabel
class,并让它在被点击时发出信号。
要使按钮在用户移动时反转,将其包装到 AttrMap
并将 focus_map
设置为某个调色板条目(在我的例子中是“button_reversed
”) .
按照 Drunken Master 的回答,但 "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)
在这里,我们只修改了按钮的外观,其他方面的行为保持不变。
根据 Drunken Master 的回答,我已经尽可能地清理了解决方案。
urwid.SelectableIcon
基本上是一个带有丑陋的闪烁光标的 urwid.Text
字段。
因此,与其覆盖 urwid.SelectableIcon
并将其打包到 urwid.WidgetWrap
,不如直接使用 urwid.Text
并使其可选择并对 button/mouse 激活做出反应(灵感来自 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()
很有魅力: