我正在用python和urwid编写一个应用程序。
我需要一个带有自动完成功能的Edit
小部件。在documentation中还没有看到一个,所以我尝试在pop_up example的基础上实现它
然而,我面对的事实是弹出窗口小部件有焦点,这是一个问题,因为:
如何设置PopUpLauncher以获得焦点
从那以后this answer可能会有所帮助
我的自定义小部件类的一个非常简化的版本:
#!/usr/bin/env python
import urwid
class AutoCompleteEdit(urwid.PopUpLauncher):
CMD_INSERT_SELECTED = "complete"
command_map = urwid.CommandMap()
command_map['tab'] = CMD_INSERT_SELECTED
def __init__(self, get_completions):
self.edit_widget = urwid.Edit()
self.__super.__init__(self.edit_widget)
self.get_completions = get_completions
# ------- user input ------
def keypress(self, size, key):
cmd = self.command_map[key]
if cmd is None:
out = self.__super.keypress(size, key)
self.update_completions()
return out
return self.__super.keypress(size, key)
def forwarded_keypress(self, key):
if self.edit_widget.valid_char(key):
#if (isinstance(key, text_type) and not isinstance(self._caption, text_type)):
# # screen is sending us unicode input, must be using utf-8
# # encoding because that's all we support, so convert it
# # to bytes to match our caption's type
# key = key.encode('utf-8')
self.edit_widget.insert_text(key)
self.update_completions()
return
cmd = self.command_map[key]
if cmd == self.CMD_INSERT_SELECTED:
self.insert_selected()
return
elif cmd == urwid.CURSOR_LEFT:
p = self.edit_widget.edit_pos
if p == 0:
return key
p = urwid.move_prev_char(self.edit_widget.edit_text, 0, p)
self.edit_widget.set_edit_pos(p)
elif cmd == urwid.CURSOR_RIGHT:
p = self.edit_widget.edit_pos
if p >= len(self.edit_widget.edit_text):
return key
p = urwid.move_next_char(self.edit_widget.edit_text, p, len(self.edit_widget.edit_text))
self.edit_widget.set_edit_pos(p)
elif key == "backspace":
self.edit_widget.pref_col_maxcol = None, None
if not self.edit_widget._delete_highlighted():
p = self.edit_widget.edit_pos
if p == 0:
return key
p = urwid.move_prev_char(self.edit_widget.edit_text,0,p)
self.edit_widget.set_edit_text(self.edit_widget.edit_text[:p] + self.edit_widget.edit_text[self.edit_widget.edit_pos:])
self.edit_widget.set_edit_pos(p)
elif key == "delete":
self.edit_widget.pref_col_maxcol = None, None
if not self.edit_widget._delete_highlighted():
p = self.edit_widget.edit_pos
if p >= len(self.edit_widget.edit_text):
return key
p = urwid.move_next_char(self.edit_widget.edit_text,p,len(self.edit_widget.edit_text))
self.edit_widget.set_edit_text(self.edit_widget.edit_text[:self.edit_widget.edit_pos] + self.edit_widget.edit_text[p:])
else:
return key
self.update_completions()
return key
def update_completions(self):
i = self.edit_widget.edit_pos
text = self.edit_widget.edit_text[:i]
prefix, completions = self.get_completions(text)
self.prefix = prefix
self.completions = completions
if not self.completions:
if self.is_open():
self.close_pop_up()
return
if not self.is_open():
self.open_pop_up()
self._pop_up_widget.update_completions(completions)
def insert_selected(self):
text = self._pop_up_widget.get_selected()
i = self.edit_widget.edit_pos - len(self.prefix)
assert i >= 0
text = text[i:]
self.edit_widget.insert_text(text)
self.close_pop_up()
# ------- internal ------
def is_open(self):
return self._pop_up_widget
# ------- implementation of abstract methods ------
def create_pop_up(self):
return PopUpList(self.forwarded_keypress)
def get_pop_up_parameters(self):
height = len(self.completions)
width = max(len(x) for x in self.completions)
return {'left':len(self.prefix), 'top':1, 'overlay_width':width, 'overlay_height':height}
class PopUpList(urwid.WidgetWrap):
ATTR = 'popup-button'
ATTR_FOCUS = 'popup-button-focus'
def __init__(self, keypress_callback):
self.body = urwid.SimpleListWalker([urwid.Text("")])
widget = urwid.ListBox(self.body)
widget = urwid.AttrMap(widget, self.ATTR)
self.__super.__init__(widget)
self.keypress_callback = keypress_callback
def update_completions(self, completions):
self.body.clear()
for x in completions:
widget = ListEntry(x)
widget = urwid.AttrMap(widget, self.ATTR, self.ATTR_FOCUS)
self.body.append(widget)
def get_selected(self):
focus_widget, focus_pos = self.body.get_focus()
return self.body[focus_pos].original_widget.text
def keypress(self, size, key):
key = self.keypress_callback(key)
if key:
return super().keypress(size, key)
class ListEntry(urwid.Text):
#https://stackoverflow.com/a/56759094
_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
if __name__ == '__main__':
palette = [
('popup-button', 'white', 'dark blue'),
('popup-button-focus', 'white,standout', 'dark blue'),
('error', 'dark red', 'default'),
]
completions = ["hello", "hi", "world", "earth", "universe"]
def get_completions(start):
i = start.rfind(" ")
if i == -1:
prefix = ""
else:
i += 1
prefix = start[:i]
start = start[i:]
return prefix, [word for word in completions if word.startswith(start)]
widget = AutoCompleteEdit(get_completions)
widget = urwid.Filler(widget)
#WARNING: note the pop_ups=True
urwid.MainLoop(widget, palette, pop_ups=True).run()
我至少为问题的第一部分(光标的可见性)找到了解决方案:重写
AutoCompleteEdit
类中的render
方法:第二个问题,代码重复,仍然存在。 实际上,这不仅仅是代码复制,因为某些功能(将光标移动到最开始或最末尾)正在使用keypress方法的size参数。我不知道尺寸,所以我不能把它复制出来。 所以如果有人知道更好的方法我会很感激的
相关问题 更多 >
编程相关推荐