Search code examples
pythonpython-2.7kivykivy-language

Kivy : How to start text from right side in TextInput?


When I type in MyFloatInput TextInput then text start from right side of TextInput,It's working perfect
but I set value of MyFloatInput TextInput from .py then it start from left side.It doesn't show in right side.
Can someone tell me what is wrong with code?

test.py

import kivy

from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.uix.textinput import TextInput

Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (400, 100)


class MyFloatInput(TextInput):

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

    def right_adjust(self, text):
        max_width = self.width - self.padding[0] - self.padding[2]
        new_text = text
        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[0] != ' ':
                break
            else:
                new_text = new_text[1:]
                text_width = self._get_text_width(new_text, self.tab_width, self._label_cached)
        return new_text

    def delete_selection(self, from_undo=False):
        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(MyFloatInput, self).delete_selection(from_undo=from_undo)
        cur_text = self._lines[cr]
        super(MyFloatInput, 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'):
        cc, cr = self.cursor
        initial_len = len(self._lines[cr])
        super(MyFloatInput, self).do_backspace(from_undo=from_undo, mode=mode)
        cc, cr = self.cursor
        cur_text = self._lines[cr]
        super(MyFloatInput, 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):
        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:
            num = float(new_text) # throw exception if new_text is invalid float
        except ValueError:
            return
        self._lines[cr] = ''
        super(MyFloatInput, self).insert_text(new_text, from_undo=from_undo)
        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):
        num = float(text)  # throws exception if text is invalid float
        self._refresh_text(self.right_adjust(text))

    def on_text(self, instance, text):
        #num = float(text)  # throws exception if text is invalid float
        self._refresh_text(self.right_adjust(text))

class Testing(Screen):

    def __init__(self, **kwargs):
        super(Testing, self).__init__(**kwargs)
        Clock.schedule_once(lambda dt: setattr(self.test, 'text', str(100)))

class Test(App):

    def build(self):
        self.root = Builder.load_file('test.kv')
        return self.root


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

test.kv

Testing:
    test:test
    BoxLayout:
        orientation: "vertical"
        padding : 20, 20

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

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

            MyFloatInput:
                size_hint_x: .6
                id : test

Solution

  • I made significant changes to the MyFloatInput class. It now no longer needs the string import. It now handles deleting a selection, and you can now set the text from a .py file using the set_right_adj_text("some text") method. Here is the improved class:

    class MyFloatInput(TextInput):
    
        def __init__(self, **kwargs):
            super(MyFloatInput, self).__init__(**kwargs)
            self.multiline = False
    
        def right_adjust(self, text):
            max_width = self.width - self.padding[0] - self.padding[2]
            new_text = text
            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[0] != ' ':
                    break
                else:
                    new_text = new_text[1:]
                    text_width = self._get_text_width(new_text, self.tab_width, self._label_cached)
            return new_text
    
        def on_size(self, instance, value):
            super(MyFloatInput, 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(MyFloatInput, 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 True
    
        def delete_selection(self, from_undo=False):
            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(MyFloatInput, self).delete_selection(from_undo=from_undo)
            cur_text = self._lines[cr]
            super(MyFloatInput, 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'):
            cc, cr = self.cursor
            initial_len = len(self._lines[cr])
            super(MyFloatInput, self).do_backspace(from_undo=from_undo, mode=mode)
            cc, cr = self.cursor
            cur_text = self._lines[cr]
            super(MyFloatInput, 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):
            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:
                num = float(new_text) # throw exception if new_text is invalid float
            except ValueError:
                return
            self._lines[cr] = ''
            super(MyFloatInput, self).insert_text(new_text, from_undo=from_undo)
            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):
            num = float(text)  # throws exception if text is invalid float
            self._refresh_text(self.right_adjust(text))