Kivy TextInput IndexError: list index out of range
使用场景
在一个触摸屏POS中使用一个Kivy 的TextInput
控件做商品扫码输入, 要求把输入框隐藏了
环境
Python: 3.6.2
Kivy: 1.11.1
OS:Windows10
代码
<FunButtonScreen>:
BoxLayout:
size_hint:1,1
orientation:'vertical'
TextInput:
id: id_scan_plu
size_hint: None, None
height: 0
width: 0
cursor_width: 0
# multiline: False
FunButtonBox:
size_hint: 1, 1
id: button_box
以上代码通过height
和width
把id
为id_scan_plu的TextInput
控件隐藏了, 效果图如下红框处实际上有一个TextInput
输入框
问题
POS是使用的触摸屏大部分是正常的,偶尔已报IndexError: list index out of range
异常, 而KeyEvent只有扫码枪, 为什么会报key_down异常呢?
2020-02-17 19:14:09,114 MainThread INFO : Base: Leaving application in progress...
2020-02-17 19:14:09,115 MainThread WARNING : stderr: Traceback (most recent call last):
2020-02-17 19:14:09,115 MainThread WARNING : stderr: File "pos_main.py", line 90, in <module>
2020-02-17 19:14:09,116 MainThread WARNING : stderr: main()
2020-02-17 19:14:09,117 MainThread WARNING : stderr: File "pos_main.py", line 77, in main
2020-02-17 19:14:09,117 MainThread WARNING : stderr: MainWindow().run()
2020-02-17 19:14:09,118 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\app.py", line 828, in run
2020-02-17 19:14:09,121 MainThread WARNING : stderr: runTouchApp()
2020-02-17 19:14:09,122 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\base.py", line 504, in runTouchApp
2020-02-17 19:14:09,124 MainThread WARNING : stderr: EventLoop.window.mainloop()
2020-02-17 19:14:09,124 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\core\window\window_sdl2.py", line 663, in mainloop
2020-02-17 19:14:09,126 MainThread WARNING : stderr: self._mainloop()
2020-02-17 19:14:09,127 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\core\window\window_sdl2.py", line 602, in _mainloop
2020-02-17 19:14:09,128 MainThread WARNING : stderr: self.modifiers):
2020-02-17 19:14:09,129 MainThread WARNING : stderr: File "kivy\_event.pyx", line 714, in kivy._event.EventDispatcher.dispatch (kivy\_event.c:8146)
2020-02-17 19:14:09,130 MainThread WARNING : stderr: File "kivy\_event.pyx", line 1225, in kivy._event.EventObservers.dispatch (kivy\_event.c:14035)
2020-02-17 19:14:09,132 MainThread WARNING : stderr: File "kivy\_event.pyx", line 1149, in kivy._event.EventObservers._dispatch (kivy\_event.c:13564)
2020-02-17 19:14:09,133 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\core\window\__init__.py", line 159, in _on_window_key_down
2020-02-17 19:14:09,135 MainThread WARNING : stderr: return self.dispatch('on_key_down', keycode, text, modifiers)
2020-02-17 19:14:09,136 MainThread WARNING : stderr: File "kivy\_event.pyx", line 714, in kivy._event.EventDispatcher.dispatch (kivy\_event.c:8146)
2020-02-17 19:14:09,137 MainThread WARNING : stderr: File "kivy\_event.pyx", line 1225, in kivy._event.EventObservers.dispatch (kivy\_event.c:14035)
2020-02-17 19:14:09,139 MainThread WARNING : stderr: File "kivy\_event.pyx", line 1149, in kivy._event.EventObservers._dispatch (kivy\_event.c:13564)
2020-02-17 19:14:09,140 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\uix\textinput.py", line 2438, in keyboard_on_key_down
2020-02-17 19:14:09,145 MainThread WARNING : stderr: self._key_down(key)
2020-02-17 19:14:09,145 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\uix\textinput.py", line 2279, in _key_down
2020-02-17 19:14:09,149 MainThread WARNING : stderr: self._alt_l or self._alt_r)
2020-02-17 19:14:09,149 MainThread WARNING : stderr: File "d:\Python35\lib\site-packages\kivy\uix\textinput.py", line 1162, in do_cursor_movement
2020-02-17 19:14:09,151 MainThread WARNING : stderr: col = min(len(self._lines[row]), col)
2020-02-17 19:14:09,152 MainThread WARNING : stderr: IndexError: list index out of range
这个问题在百度、bing、github、SO都没有搜到解决办法,只好阅读源码Python35\lib\site-packages\kivy\uix\textinput.py
:
def do_cursor_movement(self, action, control=False, alt=False):
'''Move the cursor relative to its current position.
Action can be one of :
- cursor_left: move the cursor to the left
- cursor_right: move the cursor to the right
- cursor_up: move the cursor on the previous line
- cursor_down: move the cursor on the next line
- cursor_home: move the cursor at the start of the current line
- cursor_end: move the cursor at the end of current line
- cursor_pgup: move one "page" before
- cursor_pgdown: move one "page" after
In addition, the behavior of certain actions can be modified:
- control + cursor_left: move the cursor one word to the left
- control + cursor_right: move the cursor one word to the right
- control + cursor_up: scroll up one line
- control + cursor_down: scroll down one line
- control + cursor_home: go to beginning of text
- control + cursor_end: go to end of text
- alt + cursor_up: shift line(s) up
- alt + cursor_down: shift line(s) down
.. versionchanged:: 1.9.1
'''
if not self._lines:
return
pgmove_speed = int(self.height /
(self.line_height + self.line_spacing) - 1)
col, row = self.cursor
if action == 'cursor_up':
if self.multiline and control:
self.scroll_y = max(0, self.scroll_y - self.line_height)
elif not self.readonly and self.multiline and alt:
self._shift_lines(-1)
return
else:
row = max(row - 1, 0)
col = min(len(self._lines[row]), col)
elif action == 'cursor_down':
if self.multiline and control:
maxy = self.minimum_height - self.height
self.scroll_y = max(0, min(maxy,
self.scroll_y + self.line_height))
elif not self.readonly and self.multiline and alt:
self._shift_lines(1)
return
else:
row = min(row + 1, len(self._lines) - 1)
col = min(len(self._lines[row]), col)
elif action == 'cursor_home':
col = 0
if control:
row = 0
elif action == 'cursor_end':
if control:
row = len(self._lines) - 1
col = len(self._lines[row])
elif action == 'cursor_pgup':
row = max(0, row - pgmove_speed)
col = min(len(self._lines[row]), col)
elif action == 'cursor_pgdown':
row = min(row + pgmove_speed, len(self._lines) - 1)
col = min(len(self._lines[row]), col)
elif (self._selection and self._selection_finished and
self._selection_from < self._selection_to and
action == 'cursor_left'):
current_selection_to = self._selection_to
while self._selection_from != current_selection_to:
current_selection_to -= 1
if col:
col -= 1
else:
row -= 1
col = len(self._lines[row])
elif (self._selection and self._selection_finished and
self._selection_from > self._selection_to and
action == 'cursor_right'):
current_selection_to = self._selection_to
while self._selection_from != current_selection_to:
current_selection_to += 1
if len(self._lines[row]) > col:
col += 1
else:
row += 1
col = 0
elif action == 'cursor_left':
if not self.password and control:
col, row = self._move_cursor_word_left()
else:
if col == 0:
if row:
row -= 1
col = len(self._lines[row])
else:
col, row = col - 1, row
elif action == 'cursor_right':
if not self.password and control:
col, row = self._move_cursor_word_right()
else:
if col == len(self._lines[row]):
if row < len(self._lines) - 1:
col = 0
row += 1
else:
col, row = col + 1, row
dont_move_cursor = control and action in ['cursor_up', 'cursor_down']
if dont_move_cursor:
self._trigger_update_graphics()
else:
self.cursor = (col, row)
原来店里配置了外置键盘,当输入Pgup、PgDn键是会引发异常, 由于通过height和width 都是0所以没法上下滚动而异常
解决办法
通过透明属性隐藏控件, 即把颜色的rgba都是设置成0, 背景设置为空
<FunButtonScreen>:
BoxLayout:
size_hint:1,1
orientation:'vertical'
TextInput:
id: id_scan_plu
size_hint: None, None
height: 40
width: 120
cursor_width: 0
multiline: False
color: 0, 0, 0, 0
background_normal: ""
background_active: ""
cursor_color: 0, 0, 0, 0
background_color: 0, 0, 0, 0
foreground_color: 0, 0, 0, 0
FunButtonBox:
size_hint: 1, 1
id: button_box