pythonkeyboardkivynumpadnumeric-keypad

Is there a way to show a number pad (preferably with a bubble) in a TextInput field using Kivy and Python? NEW ISSUE WITH CODE


I have an app where you input numbers (a lot) and it gets hard that the keyboard is also letters, I would like it to be just numbers (0-9), and a dot. Is there any way to do this using Kivy's .kv file and Python? I have tried input_type: 'number' in the .kv file, but I couldn't get it to work. Thanks!

Edit: I have also tried using bubbles, no luck. Although, it would be nice to have a bubble number pad if possible.

Edit 2: I have put another bounty on this question because when I put the VKeyboard on a physical device (along with a popup) it breaks. In specific, the VKeyboard wont show up (and it also crashes after going to another TextInput field) and my popup doesn't scale correctly. My logcat is shown below.

10-21 08:30:23.944 11210 11233 I python  : [INFO   ] [Logger      ] Record log in /data/user/0/org.test.bfcalc5/files/app/.kivy/logs/kivy_20-10-21_3.txt
10-21 08:30:23.944 11210 11233 I python  : [INFO   ] [Kivy        ] v1.11.1
10-21 08:30:23.945 11210 11233 I python  : [INFO   ] [Kivy        ] Installed at "/data/user/0/org.test.bfcalc5/files/app/_python_bundle/site-packages/kivy/__init__.pyc"
10-21 08:30:23.946 11210 11233 I python  : [INFO   ] [Python      ] v3.8.1 (default, Oct 18 2020, 14:08:48) 
10-21 08:30:23.946 11210 11233 I python  : [Clang 8.0.2 (https://android.googlesource.com/toolchain/clang 40173bab62ec7462
10-21 08:30:23.946 11210 11233 I python  : [INFO   ] [Python      ] Interpreter at ""
10-21 08:30:25.540 11210 11233 I python  : [INFO   ] [Factory     ] 184 symbols loaded
10-21 08:30:26.210 11210 11233 I python  : [INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_gif (img_pil, img_ffpyplayer ignored)
10-21 08:30:26.289 11210 11233 I python  : [INFO   ] [Text        ] Provider: sdl2
10-21 08:30:26.387 11210 11233 I python  : [INFO   ] [Window      ] Provider: sdl2
10-21 08:30:26.437 11210 11233 I python  : [INFO   ] [GL          ] Using the "OpenGL ES 2" graphics system
10-21 08:30:26.439 11210 11233 I python  : [INFO   ] [GL          ] Backend used <sdl2>
10-21 08:30:26.440 11210 11233 I python  : [INFO   ] [GL          ] OpenGL version <b'OpenGL ES 3.2 build 1.10@5187610'>
10-21 08:30:26.440 11210 11233 I python  : [INFO   ] [GL          ] OpenGL vendor <b'Imagination Technologies'>
10-21 08:30:26.441 11210 11233 I python  : [INFO   ] [GL          ] OpenGL renderer <b'PowerVR Rogue GE8322'>
10-21 08:30:26.442 11210 11233 I python  : [INFO   ] [GL          ] OpenGL parsed version: 3, 2
10-21 08:30:26.442 11210 11233 I python  : [INFO   ] [GL          ] Texture max size <4096>
10-21 08:30:26.443 11210 11233 I python  : [INFO   ] [GL          ] Texture max units <16>
10-21 08:30:26.482 11210 11233 I python  : [INFO   ] [Window      ] auto add sdl2 input provider
10-21 08:30:26.484 11210 11233 I python  : [INFO   ] [Window      ] virtual keyboard allowed, single mode, docked
10-21 08:30:26.756 11210 11233 I python  : [INFO   ] [GL          ] NPOT texture support is available
10-21 08:30:26.818 11210 11233 I python  : [WARNING] [Base        ] Unknown <android> provider
10-21 08:30:26.819 11210 11233 I python  : [INFO   ] [Base        ] Start application main loop
10-21 08:30:26.938 11210 11233 I python  : [WARNING] [GL          ] Unpack subimage support is not available
10-21 08:30:33.249 11210 11233 I python  : [INFO   ] [Base        ] Leaving application in progress...
10-21 08:30:33.250 11210 11233 I python  :  Traceback (most recent call last):
10-21 08:30:33.250 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/app/main.py", line 88, in <module>
10-21 08:30:33.251 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/app.py", line 855, in run
10-21 08:30:33.253 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/base.py", line 504, in runTouchApp
10-21 08:30:33.255 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/core/window/window_sdl2.py", line 747, in mainloop
10-21 08:30:33.257 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/core/window/window_sdl2.py", line 479, in _mainloop
10-21 08:30:33.258 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/base.py", line 339, in idle
10-21 08:30:33.259 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/clock.py", line 591, in tick
10-21 08:30:33.260 11210 11233 I python  :    File "kivy/_clock.pyx", line 384, in kivy._clock.CyClockBase._process_events
10-21 08:30:33.261 11210 11233 I python  :    File "kivy/_clock.pyx", line 414, in kivy._clock.CyClockBase._process_events
10-21 08:30:33.261 11210 11233 I python  :    File "kivy/_clock.pyx", line 412, in kivy._clock.CyClockBase._process_events
10-21 08:30:33.262 11210 11233 I python  :    File "kivy/_clock.pyx", line 154, in kivy._clock.ClockEvent.tick
10-21 08:30:33.263 11210 11233 I python  :    File "kivy/_clock.pyx", line 86, in kivy._clock.ClockEvent.get_callback
10-21 08:30:33.264 11210 11233 I python  :    File "/home/ubuntu/BFCalcApp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/bfcalc5/kivy/weakmethod.py", line 56, in is_dead
10-21 08:30:33.265 11210 11233 I python  :  ReferenceError: weakly-referenced object no longer exists
10-21 08:30:33.266 11210 11233 I python  : Python for android ended.

Edit 3: Thanks to Fadi Abu Raid, I was able to figure out the Pop-Up issue. Right now, I am looking to increase the size of the VKeyboard as it is very small on a phone. I found this post, but I did not find any help from it.


Solution

  • I have done it using this example:

    https://github.com/kivy/kivy/tree/master/examples/keyboard

    Your .kv file it should be like this:

    TextInput:
        input_type: 'number'
        on_focus: root.text_focused()
    

    And you should have a method to call the keyboard with numeric layout in your Python file.

    from kivy.uix.vkeyboard import VKeyboard 
    
    def text_focused(self):
        VKeyboard.layout = 'numeric.json'
        player = VKeyboard()
    

    Get the layout from the link above and save it in the same folder with your script.

    And the output should look like this:

    enter image description here

    UPDATE

    After posting this I have found a better solution here.

    However it lacks some features that I have added :

    1. Add delete button.
    2. A touch outside the bubble keypad makes it disappear.
    3. Remove some unnecessary bits of code.
    4. Allow deleting using cursor position

    You need to download arial-unicode-ms.ttf font from here and place the font in the same folder as your main.py script to be able to display the delete symbol on the keypad.

    main.py

    from kivy.app import App
    from kivy.uix.floatlayout import FloatLayout
    from kivy.uix.bubble import Bubble, BubbleButton
    from kivy.lang import Builder
    from kivy.core.window import Window
    
    
    class CustomBubbleButton(BubbleButton):
        
        def add_text(self):
            app= App.get_running_app()
            index=app.root.text_input.cursor[0]-1
            if self.text!="⌫":
                app.root.text_input.text= app.root.text_input.text[:index+1]+self.text + app.root.text_input.text[index+1:]
                app.root.text_input.cursor=(index+2,0)
            else:
                app.root.text_input.text=app.root.text_input.text[:index] + app.root.text_input.text[index+1:]
                app.root.text_input.cursor=(index,0)
        pass
    
    
    class NumericKeyboard(Bubble):
        
        def on_touch_up(self, touch):
            app= App.get_running_app()
            if  not self.collide_point(*touch.pos) and not app.root.text_input.collide_point(*touch.pos):
                app.root.remove_widget(app.root.bubb)
                app.root.text_input.focus=False
                delattr(app.root, 'bubb')     
                       
        def __init__(self, **kwargs):
            super(NumericKeyboard, self).__init__(**kwargs)
            self.create_bubble_button()
    
        def create_bubble_button(self):
            numeric_keypad = ['7', '8', '9', '4', '5', '6', '1', '2', '3', '.', '0', '⌫']
            for x in numeric_keypad:
                bubb_btn = CustomBubbleButton(text=str(x),font_name='arial-unicode-ms.ttf')
                self.layout.add_widget(bubb_btn)
    
    
    class BubbleShowcase(FloatLayout):
        
        def show_bubble(self, *l):
            if not hasattr(self, 'bubb'):
                self.bubb = NumericKeyboard()
                self.bubb.arrow_pos = "bottom_mid"
                self.add_widget(self.bubb)
    
    Builder.load_file("test.kv")
    
    class TestBubbleApp(App):
        title = "Numeric Key Pad - Using Bubble"
    
        def build(self):
            return BubbleShowcase()
    
    if __name__ == '__main__':
        Window.show_cursor = True
        TestBubbleApp().run()
    

    test.kv

    <CustomBubbleButton>:
        on_release: root.add_text()
            
    <NumericKeyboard>:
        layout: layout
        size_hint: (None, None)
        size: (160, 120)
        pos_hint: {'center_x': .5, 'y': .6}
    
        GridLayout:
            id: layout
            cols: 3
    
    <BubbleShowcase>:
        text_input: text_input
        canvas:
            Color:
                rgba: 0, 1, 1, 1
            Rectangle:
                size: self.width, self.height
        TextInput:
            id: text_input
            keyboard_mode: 'managed'
            pos_hint: {'center_x': .5, 'y': .54}
            size_hint: (0.2, 0.06)
            cursor_blink: True
            font_size: 20
            multiline: False
            on_focus: root.show_bubble()
    

    Output

    enter image description here