С виджетами TextInput от Kivy или KivyMD я не могу перемещать курсор с помощью стрелок на клавиатуре (с панели numeri c) - PullRequest
0 голосов
/ 07 мая 2020

Я тестирую Kivy на компьютере Windows 10. Когда я щелкаю виджет TextInput, я могу вводить и перемещать курсор с помощью стрелок «перевернутый T» на моей клавиатуре, но не с помощью стрелок на моей панели numeri c (с NumLock = Off), которые я использую все время. Есть ли решение для этого?

Ответы [ 2 ]

2 голосов
/ 07 мая 2020

Вы можете использовать класс, расширяющий TextInput и использующий числовые c стрелки на клавиатуре. Вот пример:

class MyTextInput(TextInput):
    def keyboard_on_key_down(self, window, keycode, text, modifiers):
        if 'numlock' not in modifiers:
            if keycode[0] == 264: # up arrow
                self.do_cursor_movement('cursor_up')
            elif keycode[0] == 262:  # right arrow
                self.do_cursor_movement('cursor_right')
            elif keycode[0] == 258:  # down arrow
                self.do_cursor_movement('cursor_down')
            elif keycode[0] == 260:  # left arrow
                self.do_cursor_movement('cursor_left')
        return super(MyTextInput, self).keyboard_on_key_down(window, keycode, text, modifiers)
0 голосов
/ 07 мая 2020

С помощью Джона Андерсона я смог найти полное решение, переназначив клавиши цифровой клавиатуры на обычные клавиши, когда numlock выключен:

from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivymd.app import MDApp
from kivymd.uix.textfield import MDTextField

numpad_map = dict(
        numpad1=(279, 'end'     ),      #257
        numpad2=(274, 'down'    ),      #258
        numpad3=(281, 'pagedown'),      #259
        numpad4=(276, 'left'    ),      #260
        numpad6=(275, 'right'   ),      #262
        numpad7=(278, 'home'    ),      #263
        numpad8=(273, 'up'      ),      #264
        numpad9=(280, 'pageup'  ),      #265
        numpaddecimal=(127, 'delete'),  #266
    )

def fix_numpad(keycode, modifiers):
    """This function actually remap numpad arrows to regular arrows if numlock is off"""
    keynum, keylbl = keycode
    if 'numlock' not in modifiers and keylbl in numpad_map:
        keycode = numpad_map[keylbl]
    return keycode

class TextInput2(TextInput):
    def keyboard_on_key_down(self, window, keycode, text, modifiers):
        keycode = fix_numpad(keycode, modifiers)
        return super().keyboard_on_key_down(window, keycode, text, modifiers)

class MDTextField2(MDTextField):
    def keyboard_on_key_down(self, window, keycode, text, modifiers):
        keycode = fix_numpad(keycode, modifiers)
        return super().keyboard_on_key_down(window, keycode, text, modifiers)

KV = """
MDBoxLayout:
    orientation: 'vertical'
    md_bg_color: app.theme_cls.bg_normal

    TextInput2:

    MDTextField2:

"""

class Test(MDApp):
    def build(self):
        return Builder.load_string(KV)

Test().run()

EDIT: Копаем дальше в исходном коде я понял, что MDTextField имеет множество подклассов и наследуется от FixedHintTextInput, который наследуется от TextInput. Итак, я нашел универсальный способ исправления TextInput. Я не уверен, что это самое надежное решение, но я считаю его несколько элегантным, потому что оно исправляет поведение всех виджетов без изменения имен классов в нашем layout (пока исходный код kivy не будет исправлен). Вот код:

from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivymd.app import MDApp

def wrap_class_function(class_type, fn_attr_name, fn_wrapper_maker):
    """Replaces a method at the Class-level (class function) so that it affects
    all instance methods (current and future, including subclasses)"""
    old_fn = getattr(class_type, fn_attr_name)    # Get original class function
    new_fn = fn_wrapper_maker(old_fn)             # Create new wrapper around it
    new_fn.__name__ = fn_attr_name                # Set the __name__ of wrapper (so bindings still work)
    setattr(class_type, fn_attr_name, new_fn)     # Replace old function with new one

def keyboard_on_key_down_wrapper_maker(original_fn):
    """Creates a wrapper function that applies the numpad patch, to be used
    directly on TextInput base class (to fix all subclasses)"""
    numpad_map = dict(
            numpad1=(279, 'end'     ),      #257
            numpad2=(274, 'down'    ),      #258
            numpad3=(281, 'pagedown'),      #259
            numpad4=(276, 'left'    ),      #260
            numpad6=(275, 'right'   ),      #262
            numpad7=(278, 'home'    ),      #263
            numpad8=(273, 'up'      ),      #264
            numpad9=(280, 'pageup'  ),      #265
            numpaddecimal=(127, 'delete'),  #266
        )
    def keyboard_on_key_down_wrapper(self, window, keycode, text, modifiers):
        key, key_str = keycode
        if 'numlock' not in modifiers and key_str in numpad_map:
            keycode = numpad_map[key_str]
        return original_fn(self, window, keycode, text, modifiers)
    return keyboard_on_key_down_wrapper

wrap_class_function(TextInput, 'keyboard_on_key_down', keyboard_on_key_down_wrapper_maker)

KV = """
MDBoxLayout:
    orientation: 'vertical'
    md_bg_color: app.theme_cls.bg_normal

    TextInput:

    MDTextField:  # This widget inherits TextInput patch
"""

class Test(MDApp):
    def build(self):
        return Builder.load_string(KV)

Test().run()
...