0

I'm trying to make a InputBox that starts a date picker and when you select a date it will appear in the box. So I tyied like this:

from kivymd.app import MDApp
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.menu import MDDropdownMenu
from kivy.metrics import dp
from kivymd.uix.snackbar import Snackbar
from kivymd.uix.dialog import MDDialog
from kivymd.uix.picker import MDDatePicker


class MainScreen(Screen):
    pass

class Predef(Screen):
    pass

class WindowManager(ScreenManager):
    pass



class MyApp(MDApp):
    def build(self):
        self.birthday = ''
        
        kv = Builder.load_file('file.kv')
        return kv
    

    def on_save(self, instance, value, date_range):
    
        print(value)
        self.birthday = value
        self.root.ids.input_box_date.text = self.birthday


    def on_cancel(self, instance, value):
        print("cancel")
    def date(self):
            date_dialog = MDDatePicker(
                title='Birthday',
                title_input= 'Birthday'
                )
            date_dialog.bind(on_save=self.on_save, on_cancel=self.on_cancel)
            date_dialog.open()
        
    
if __name__ == '__main__':
    MyApp().run()

And this is the code in the kv file:

WindowManager:
    MainScreen:
    Predef:
<MainScreen>:
    name: "main"
    MDBoxLayout:
        orientation: 'vertical'
        size: root.width, root.height
        MDToolbar:
            title: "App"
            left_action_items: [["menu", lambda x: side_bar.set_state("open")]]
        Label:
            text: 'Image'
            color: (0, 0, 0, 1)

        MDBottomAppBar:
            MDToolbar:
                icon: "botão.png"
                type: "bottom"
    MDNavigationDrawer:
        id: side_bar
        BoxLayout:
            orientation: 'vertical'
            padding: '16dp'
            Label:
                color: (0, 0, 0, 1)
                font_size: 35
                text: 'Settings      '
                size_hint_y: None
                height: self.texture_size[1]
                pos_hint: {'y': 0, 'left': 1}
            ScrollView:
                MDList:
                    OneLineListItem:
                        text: 'Predefitions'
                        on_release:
                            side_bar.set_state("close")
                            app.root.current = 'Predef'
                            root.manager.transition.direction = "up"
<Predef>:
    name: 'Predef'
    MDBoxLayout:
        orientation: 'vertical'
        size: root.height, root.width
        MDToolbar:
            title: "Predefinitions"
            left_action_items: [["menu", lambda x: bar.set_state("open")]]
            pos_hint: {'x': 0, 'top': 1}
        Label:
            color: (0, 0, 0, 1)
            text: 'Birthday'
        MDTextFieldRound:
            id: input_box_date    
            hint_text: "Birthday"
            text: app.birthday
            size_hint_x : 0.5
            pos_hint: {'center_x' : 0.5}
            on_focus: app.date()
        Label:
            text: ''
    MDNavigationDrawer:
        id: bar
        BoxLayout:
            orientation: 'vertical'
            padding: '16dp'
            Label:
                color: (0, 0, 0, 1)
                font_size: 35
                text: 'Settings      '
                size_hint_y: None
                height: self.texture_size[1]
                pos_hint: {'y': 0, 'left': 1}
            ScrollView:
                MDList:
                    OneLineListItem:
                        text: 'Main Screen'
                        on_release:
                            bar.set_state("close")
                            app.root.current = 'main'
                            root.manager.transition.direction = "up"     

When it starts everything is okay. The InputBox starts empty as I expected, but when I click OK button I get this error:

'''
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "kivy\properties.pyx", line 861, in kivy.properties.ObservableDict.__getattr__
 KeyError: 'input_box_date'
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\program.py", line 112, in <module>
     MyApp().run()
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\app.py", line 950, in run
     runTouchApp()
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 582, in runTouchApp
     EventLoop.mainloop()
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 347, in mainloop
     self.idle()
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 391, in idle
     self.dispatch_input()
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 342, in dispatch_input
     post_dispatch_input(*pop(0))
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 308, in post_dispatch_input
     wid.dispatch('on_touch_up', me)
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivymd\uix\behaviors\ripple_behavior.py", line 296, in on_touch_up
     return super().on_touch_up(touch)
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivymd\uix\button.py", line 981, in on_touch_up
     return super().on_touch_up(touch)
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\behaviors\button.py", line 179, in on_touch_up
     self.dispatch('on_release')
   File "kivy\_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
   File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1132, in kivy._event.EventObservers._dispatch
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 57, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "<string>", line 213, in <module>
   File "kivy\_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
   File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1172, in kivy._event.EventObservers._dispatch
   File "C:\Users\MYUSERNAME\AppData\Local\Programs\Python\Python39\program.py", line 77, in on_save
     self.root.ids.input_box_date.text = self.birthday
   File "kivy\properties.pyx", line 864, in kivy.properties.ObservableDict.__getattr__
 AttributeError: 'super' object has no attribute '__getattr__'
'''

I have no idea why I'm getting this error. I tryied replacing the self.root.ids.input_box_date.text = self.birthday with self.root.ids.input_box_date.text = "string", but nothing changes. What sould I do?

Hue
  • 13
  • 2

2 Answers2

1

TL;DR: A screen is between root and ids and the date-picker returns datetime.date which needs to be converted to string for text-field: self.root.get_screen('Predef').ids.input_box_datex.text = str(value).

Root and children

In your KV file you have defined two screens inside the root WindowManager:

WindowManager:
    MainScreen:
    Predef:

In your python script access this root using self.root. So print(self.root) will give output similar to:

<main.WindowManager object at 0x7fa580c48f28>

Inside this root, the screens can be found as indexed list using self.root.screens. A print will output:

[<Screen name='main'>, <Screen name='Predef'>]

To access a screen by index, e.g. the first use screens[0] or for a specific screen by given id use get_screen(id).

As all Kivy widgets the screen has children: All of these child widgets have ids that can be listed as dict using self.root.get_screen('Predef').ids :

{'input_box_date': <WeakProxy to <kivymd.uix.textfield.MDTextFieldRound object at 0x7fc1aa8c9c88>>, 'bar': <WeakProxy to <kivymd.uix.navigationdrawer.MDNavigationDrawer object at 0x7fc1a9c9dc88>>}

and accessed by either ids['input_box_date'] or ids.input_box_date.

See also:

Set date on text-field

Since the text-field is defined inside screen with id 'Predef' use either of these:

  • self.root.get_screen('Predef').ids['input_box_date']
  • self.root.get_screen('Predef').ids.input_box_date

However the date-picker returns a value of type datetime so you need to convert it to string using str():

def on_save(self, instance, value, date_range):
    print("value/type:", value, type(value))
    self.birthday = value  # the instance variable becomes of type date
    self.root.get_screen('Predef').ids.input_box_date.text = str(date)  # convert to string

See also:

Accessing the relevant elements in your tree

print(self.root)
print(self.root.screens)
print(self.root.screens[1].ids)
print(self.root.screens[1].ids.input_box_date)
print(self.root.get_screen('Predef').children)
print(self.root.get_screen('Predef').ids['input_box_date'])
print(self.root.get_screen('Predef').ids.input_box_date)

See:

hc_dev
  • 8,389
  • 1
  • 26
  • 38
0

The error message says KeyError: 'input_box_date'. That means that there is no input_box_date key in the ids that you used. That key is defined in the <Predef>: rule, so that id will be in the ids of the Predef instance. The fix is to access that id using the Predef instance. Try replacing:

self.root.ids.input_box_date.text = self.birthday

with:

self.root.get_screen('Predef').ids.input_box_date.text = self.birthday
John Anderson
  • 35,991
  • 4
  • 13
  • 36