Je suis d'accord que le curseur clignotant sur un urwid.Button
semble un peu boiteux, alors j'ai trouvé une solution pour le cacher. En urwid, le Button
la classe est juste une sous-classe de WidgetWrap
contenant un SelectableIcon
et deux widgets Texte (les "<" et ">" englobants). C'est le SelectableIcon
classe qui définit la position du curseur sur le premier caractère de l'étiquette, par défaut. En sous-classant SelectableIcon
, en modifiant la position du curseur puis en l'enveloppant dans un urwid.WidgetWrap
sous-classe vous pouvez créer votre propre bouton personnalisé qui peut faire toutes les astuces d'un Button
intégré , ou même plus.
Voici à quoi cela ressemble dans mon projet.
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)
Ensuite, vous pouvez envelopper un ButtonLabel
objet avec tous les autres objets dans un WidgetWrap
sous-classe qui sera votre classe de boutons personnalisés.
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
Dans ce code, il n'y a en fait pas beaucoup de combinaisons de widgets dans le FixedButton
WidgetWrap
sous-classe, mais vous pouvez ajouter un "[
" et "]
" sur les bords du bouton, enveloppez-le dans un LineBox
, etc. Si tout cela est superflu, vous pouvez simplement déplacer les fonctions de gestion des événements dans le ButtonLabel
class, et lui faire émettre un signal lorsqu'il est cliqué.
Pour inverser le bouton lorsque l'utilisateur se déplace dessus, enroulez-le dans AttrMap
et définissez le focus_map
à une entrée de palette ("button_reversed
", dans mon cas).
urwid utilise la fonction curs_set, mais ne l'expose nulle part en tant que méthode de classe. Quelqu'un pourrait modifier urwid pour permettre l'utilisation de cette méthode; sinon, il n'y a pas de méthode fiable pour le faire.
Vous pouvez le signaler comme un problème.
En m'appuyant sur la réponse de Drunken Master, j'ai nettoyé la solution autant que possible.
Le urwid.SelectableIcon
est fondamentalement un urwid.Text
champ avec ce vilain curseur clignotant. Ainsi, au lieu de remplacer le urwid.SelectableIcon
et de l'emballer dans un urwid.WidgetWrap
, prenons un urwid.Text
directement et rendez-le sélectionnable et réagissez à l'activation du bouton/de la souris (inspiré du didacticiel de menu simple d'urwid):
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()
Fonctionne comme un charme :