简体   繁体   中英

Set cursor position into TextInput

I am using Python 2.7 and kivy.
When i click outside of Text into TextInput then i cannot type. Can someone help me that when I click into textInput then how to set or shift cursor end of text?
i am using instance.cursor = (len(instance.text), 0) in def on_focus() for set position of cursor but its not working.

test.py

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.uix.textinput import TextInput
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, NumericProperty, DictProperty, StringProperty
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (400, 50)

class RightFloatInput(TextInput):
    decimal = NumericProperty(0)
    negative = False

    def __init__(self, **kwargs):
        super(RightFloatInput, self).__init__(**kwargs)
        self.multiline = False

    def right_adjust(self, text):
        max_width = self.width - self.padding[0] - self.padding[2]

        new_text = text.strip()
        text_width = self._get_text_width(new_text, self.tab_width, self._label_cached)

        while text_width < max_width:
            new_text = ' ' + new_text
            text_width = self._get_text_width(new_text, self.tab_width, self._label_cached)

        while text_width >= max_width:
            if new_text != ' ':
                break
            else:
                new_text = new_text[1:]
                text_width = self._get_text_width(new_text, self.tab_width, self._label_cached)

        return new_text.rstrip()

    def on_size(self, instance, value):
        super(RightFloatInput, self).on_size(instance, value)
        if len(self._lines) == 0:
            return True
        cc, cr = self.cursor
        cur_text = self._lines[cr]
        initial_len = len(cur_text)
        super(RightFloatInput, self)._refresh_text(self.right_adjust(cur_text))
        final_len = len(self._lines[cr])
        self.cursor = self.get_cursor_from_index(final_len - (initial_len - cc))

        return

    def delete_selection(self, from_undo=False):
        # print('delete_selection')
        if not self._selection:
            return
        cr = self.cursor[1]
        initial_len = len(self._lines[cr])
        a, b = self._selection_from, self._selection_to
        if a > b:
            a, b = b, a
        super(RightFloatInput, self).delete_selection(from_undo=from_undo)
        cur_text = self._lines[cr]
        super(RightFloatInput, self)._refresh_text(self.right_adjust(cur_text))
        final_len = len(self._lines[cr])
        self.cursor = self.get_cursor_from_index(final_len - (initial_len - b))

    def do_backspace(self, from_undo=False, mode='bkspc'):
        # print('do_backspace')
        cc, cr = self.cursor
        initial_len = len(self._lines[cr])
        super(RightFloatInput, self).do_backspace(from_undo=from_undo, mode=mode)
        cc, cr = self.cursor
        cur_text = self._lines[cr]
        super(RightFloatInput, self)._refresh_text(self.right_adjust(cur_text))
        final_len = len(self._lines[cr])
        self.cursor = self.get_cursor_from_index(final_len - (initial_len - cc) + 1)

    def insert_text(self, the_text, from_undo=False):
        if self.readonly == True:
            return

        cc, cr = self.cursor
        cur_text = self._lines[cr]
        initial_len = len(cur_text)
        new_text = self.right_adjust(cur_text[:cc] + the_text + cur_text[cc:])

        try:
            if self.negative == True:
                if str(the_text) != "-":
                    num = float(new_text)  # throw exception if new_text is invalid float
            else:
                num = float(new_text)
        except ValueError:
            return

        if str(the_text) != "-":
            if num > 10000000:
                return

        self._lines[cr] = ''
        self._refresh_text(self.right_adjust(new_text))
        final_len = len(self._lines[cr])
        self.cursor = self.get_cursor_from_index(final_len - (initial_len - cc))

    def set_right_adj_text(self, text):
        self._refresh_text(self.right_adjust(text))

    def on_focus(self, instance, isFocused):
        if isFocused:
            final_len = len(instance.text)
            instance.cursor = self.get_cursor_from_index(final_len)
            Clock.schedule_once(lambda dt: self.selected_text())

        if instance.focus:
            self.cursor = (final_len, 0)
            print("TextInput is focused [focus={}]".format(instance.focus))
            instance.cursor = (len(instance.text), 0)
            Clock.schedule_once(lambda dt: instance.select_all())
            return
        else:
            try:
                num = float(self.text.strip())  # throw exception if new_text is invalid float
            except ValueError:
                return

            new_text = self.text.strip()
            new_text = self.right_adjust(new_text)
            self._refresh_text(new_text)
            final_len = len(new_text)
            self.cursor = self.get_cursor_from_index(final_len)

    def on_touch_down_2(self, instanse):
        final_len = len(self.text)
        self.cursor = self.get_cursor_from_index(final_len)
        return False

    def selected_text(self):
        ci = self.cursor_index()
        cc = self.cursor_col
        line = self._lines[self.cursor_row]
        len_line = len(line)
        start = max(0, len(line[:cc]) - line[:cc].rfind(u' ') - 1)
        end = line[cc:].find(u' ')
        end = end if end > - 1 else (len_line - cc)
        Clock.schedule_once(lambda dt: self.select_text(ci - start, ci + end))

    def on_text(self, instance, text):
        if text.count('-') > 1:
            self.text = "-"
            return

        new_text = self.right_adjust(text)

        self._refresh_text(new_text)

        final_len = len(new_text)
        self.cursor = self.get_cursor_from_index(final_len)

    def on_text_validate_old(self):
        try:
            num = float(self.text.strip())  # throw exception if new_text is invalid float
        except ValueError:
            return

        if self.decimal == 1 and self.text.strip() != "":
            self.text = str("{0:.1f}".format(float(self.text.strip())))

        if self.decimal == 2 and self.text.strip() != "":
            self.text = str("{0:.2f}".format(float(self.text.strip())))

        if self.decimal == 3 and self.text.strip() != "":
            self.text = str("{0:.3f}".format(float(self.text.strip())))

        new_text = self.right_adjust(self.text)
        self._refresh_text(new_text)
        final_len = len(new_text)
        self.cursor = self.get_cursor_from_index(final_len)


class abc(BoxLayout):
    pass

class Test(App):
    def build(self):
        return abc()


if __name__ == '__main__':
    Test().run()

test.kv

<abc>:
    BoxLayout:
        orientation: "vertical"
        size_hint_y: .5

        BoxLayout:
            orientation: "horizontal"
            spacing: 10, 10
            size_hint_x: .6

            Label:
                text: "TEXT"
                text_size: self.size
                valign: 'middle'
                size_hint_x: .2

            RightFloatInput:
                size_hint_x: .4
                text : "100"

TextInput - RightAlign

Add the following:

  1. Import statement, from functools import partial
  2. New a method set_cursor()
  3. Clock.schedule_once(partial(self.set_cursor, instance), 1)

Snippet

from functools import partial
...
    def set_cursor(self, instance, dt):
        instance.cursor = (len(instance.text), 0)

    def on_focus(self, instance, isFocused):

        if instance.focus:
            final_len = len(instance.text)
            self.cursor = (final_len, 0)
            print("TextInput is focused [focus={}]".format(instance.focus))
            Clock.schedule_once(lambda dt: instance.select_all())
            Clock.schedule_once(partial(self.set_cursor, instance), 1)
            return
        else:
            try:
                num = float(self.text.strip())  # throw exception if new_text is invalid float
            except ValueError:
                return
            new_text = self.text.strip()
            new_text = self.right_adjust(new_text)
            self._refresh_text(new_text)
            final_len = len(new_text)
            self.cursor = self.get_cursor_from_index(final_len)

TextInput - LeftAlign

To set cursor at end of text, use instance.cursor = (len(instance.text), 0)

Snippet

def on_focus(self, instance):
    if instance.focus:
        print("TextInput is focused [focus={}]".format(instance.focus))
        instance.cursor = (len(instance.text), 0)
        Clock.schedule_once(lambda dt: instance.select_all())
    else:
        print("TextInput is defocused [focus={}]".format(instance.focus))

Note

There is a typo in the Kivy documentation. The cursor is tuple of (col, row) and not (row, col)

TextInput » cursor

 cursor 

Tuple of (row, col) values indicating the current cursor position. You can set a new (row, col) if you want to move the cursor. The scrolling area will be automatically updated to ensure that the cursor is visible inside the viewport.

cursor is an AliasProperty.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM