pythonpython-3.xkivyjsonstore

Using jsonstore in Kivy


I created a GUI based off this question while trying to teach myself how to use jsonstore. I don't have the reputation points to add a comment so I'm asking my question here. I think I have the basic idea down but for some reason I can't save the data to a json file. I get the following error:

AttributeError: 'NoneType' object has no attribute 'text'

I've tried following the documentation but I can't see anywhere where it would explain what I'm doing wrong.

main.py

from kivy.storage.jsonstore import JsonStore
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty


Window.clearcolor = (0.02745098, 0.074509804, 0.121568627, 1)
Window.size = (2000, 900)

class TitleScreen(Screen):
    pass

class MainScreen(Screen):
    pass

class CreateProfile(Screen):
    First = ObjectProperty()
    Middle = ObjectProperty()
    Last = ObjectProperty()

    def __init__(self, **kwargs):
        super(CreateProfile, self).__init__(**kwargs)
        self.store = JsonStore("bco.json")
        self.load()

    def save(self):
        self.store.put('profile', first = self.label.text)
        self.store.put('profile', middle = self.label.text)
        self.store.put('profile', last = self.label.text)

    def load(self):
        try:
            self.Last.text = self.store.get('profile')['score']
        except KeyError:
            pass


class CreatePacket(Screen):
    pass

class ScreenManagement(ScreenManager):
    pass

presentation = Builder.load_file("customwidget.kv")

class CustomWidgetApp(App):
    def build(self):
        return presentation

if __name__ == "__main__":
    CustomWidgetApp().run()

customwidget.kv

#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import hex kivy.utils.get_color_from_hex
#: import FocusBehaviors kivy.uix.behaviors.focus

ScreenManagement:
    transition: FadeTransition()
    TitleScreen:
    MainScreen:
    CreateProfile:
    CreatePacket:

<MainLabel@Label>:
    font_size:50
    bold: True
    size_hint_x: 1
    size_hint_y: 1.85
    color: 0.396078431, 0.803921569, 0.807843137, 1
    font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'

<SubLabel@Label>:
    font_size: 35
    bold: True
    halign: "center"
    size_hint_x: 1
    size_hint_y: 1.5
    color: 0.396078431, 0.803921569, 0.807843137, 1
    font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'

<OtherLabel@Label>:
    font_size: 12
    bold: True
    color: 0.396078431, 0.803921569, 0.807843137, 1
    font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
    text_size: self.size

<Button@Button>:
    font_size: 20
    bold: True
    color: 0.396078431, 0.803921569, 0.807843137, 1
    font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
    background_color: 0.02745098, 0.074509804, 0.121568627, .01
    canvas.before:
        Color:
            rgba: 0.396078431, 0.803921569, 0.807843137, 1
        Line:
            width: 2
            rectangle: self.x, self.y, self.width, self.height
    on_press: self.background_color = (0.396078431, 0.803921569, 0.807843137, 1)
    on_release: self.background_color = (0.02745098, 0.074509804, 0.121568627, .01)

# TODO: Create a focus behavior to "Tab" between widgets
<TextInput@TextInput>:
    font_size: 12
    color: 0.396078431, 0.803921569, 0.807843137, 1
    font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
    padding_x: 10
    padding_y: 10
    focus_next: None
    focus_previous: None
    unfocus_on_touch: True
    background_color: 0.02745098, 0.074509804, 0.121568627, .01
    canvas.before:
        Color:
            rgba: 0.396078431, 0.803921569, 0.807843137, 1
        Line:
            width: 1
            rectangle: self.x, self.y, self.width, self.height

<TitleScreen>:
    id: "title"
    FloatLayout:
        MainLabel:
            text: "Easy Button"
            size_hint_x: 1
            size_hint_y: 1.25

        SubLabel:
            text: 'Test'
            size_hint_x: 1
            size_hint_y: 1
        Button:
            text: "Click Here To Continue"
            on_release: app.root.current = "main"
            size_hint: (.75, .15)
            pos_hint: {'x': .12, 'y': .2}

<MainScreen>:
    name: "main"
    MainLabel:
        text: "Home Page"
    BoxLayout:
        Button:
            on_release: app.root.current = "create_profile"
            text: "Create Profile"
            size_hint: (.5, .15)
        Button:
            on_release: app.root.current = "create_packet"
            text: "Create Packet"
            size_hint: (.5, .15)

<CreateProfile>:
    name: "create_profile"

    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'top'
        MainLabel:
            text: "Create Profile"
            size_hint: (1, .15)

    BoxLayout:
        size_hint: (.95, .2)
        pos_hint: {'x': 0, 'y': .85}
        spacing: 10
        padding: 10
        halign: "left"

        OtherLabel:
            text: "First"

        OtherLabel:
            text: "Middle"

        OtherLabel:
            text: "Last"

    BoxLayout:
        size_hint: (.95, .07)
        pos_hint: {'x': 0, 'y': .8}
        spacing: 20
        padding: 10
        halign: "right"
        text_size: self.size

        TextInput:
            id: 'First'


        TextInput:
            id: "Middle"


        TextInput:
            id: 'Last'    

    BoxLayout:

        Button:
            on_release: app.root.current = "main"
            text: "back Home"
            size_hint: (.5, .15)
        Button:
            on_release: root.save()
            text: "Save Profile"
            size_hint: (.5, .15)

<CreatePacket>:
    name: "create_packet"
    MainLabel:
        text: "Select Packet"
    FloatLayout:
        Button:
            on_release: app.root.current = "main"
            text: "back Home"
            size_hint: (1, .15)

Solution

  • Your code has several problems but the main one is that you do not understand how to expose any .kv widget to .py, one of the simplest ways is to use an ObjectProperty as you try to do but that property is not linked to the widget, I prefer to do the creation in the .kv for its simplicity.

    On the other hand I recommend you avoid the abuse of try-except since it hides errors, the best thing is to verify.

    Another error is that you are overwriting the value of profile in the .json, the idea is to save everything in one.

    Considering the above, the solution is:

    *.py

    from kivy.app import App
    from kivy.storage.jsonstore import JsonStore
    from kivy.lang import Builder
    from kivy.uix.screenmanager import Screen, ScreenManager
    from kivy.core.window import Window
    from kivy.clock import Clock
    
    
    Window.clearcolor = (0.02745098, 0.074509804, 0.121568627, 1)
    Window.size = (2000, 900)
    
    class TitleScreen(Screen):
        pass
    
    class MainScreen(Screen):
        pass
    
    class CreateProfile(Screen):
        def __init__(self, **kwargs):
            super(CreateProfile, self).__init__(**kwargs)
            self.store = JsonStore("bco.json")
            Clock.schedule_once(lambda *args: self.load())
    
        def save(self):
            self.store.put('profile', 
                first = self.first.text,
                middle = self.middle.text,
                last = self.last.text)
    
        def load(self):
            if self.store.exists('profile'):
                profile = self.store.get('profile')
                v = [("first", self.first), ("middle", self.middle), ("last", self.last)]
                for key, ti in v:
                    val = profile.get(key)
                    if val:
                        ti.text = val
    
    class CreatePacket(Screen):
        pass
    
    class ScreenManagement(ScreenManager):
        pass
    
    presentation = Builder.load_file("customwidget.kv")
    
    class CustomWidgetApp(App):
        def build(self):
            return presentation
    
    if __name__ == "__main__":
        CustomWidgetApp().run()
    

    *.kv

    #: import FadeTransition kivy.uix.screenmanager.FadeTransition
    #: import hex kivy.utils.get_color_from_hex
    #: import FocusBehaviors kivy.uix.behaviors.focus
    
    ScreenManagement:
        transition: FadeTransition()
        TitleScreen:
        MainScreen:
        CreateProfile:
        CreatePacket:
    
    <MainLabel@Label>:
        font_size:50
        bold: True
        size_hint_x: 1
        size_hint_y: 1.85
        color: 0.396078431, 0.803921569, 0.807843137, 1
        font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
    
    <SubLabel@Label>:
        font_size: 35
        bold: True
        halign: "center"
        size_hint_x: 1
        size_hint_y: 1.5
        color: 0.396078431, 0.803921569, 0.807843137, 1
        font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
    
    <OtherLabel@Label>:
        font_size: 12
        bold: True
        color: 0.396078431, 0.803921569, 0.807843137, 1
        font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
        text_size: self.size
    
    <Button@Button>:
        font_size: 20
        bold: True
        color: 0.396078431, 0.803921569, 0.807843137, 1
        font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
        background_color: 0.02745098, 0.074509804, 0.121568627, .01
        canvas.before:
            Color:
                rgba: 0.396078431, 0.803921569, 0.807843137, 1
            Line:
                width: 2
                rectangle: self.x, self.y, self.width, self.height
        on_press: self.background_color = (0.396078431, 0.803921569, 0.807843137, 1)
        on_release: self.background_color = (0.02745098, 0.074509804, 0.121568627, .01)
    
    # TODO: Create a focus behavior to "Tab" between widgets
    <TextInput@TextInput>:
        font_size: 12
        color: 0.396078431, 0.803921569, 0.807843137, 1
        font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
        padding_x: 10
        padding_y: 10
        focus_next: None
        focus_previous: None
        unfocus_on_touch: True
        background_color: 0.02745098, 0.074509804, 0.121568627, .01
        canvas.before:
            Color:
                rgba: 0.396078431, 0.803921569, 0.807843137, 1
            Line:
                width: 1
                rectangle: self.x, self.y, self.width, self.height
    
    <TitleScreen>:
        id: "title"
        FloatLayout:
            MainLabel:
                text: "Easy Button"
                size_hint_x: 1
                size_hint_y: 1.25
    
            SubLabel:
                text: 'Test'
                size_hint_x: 1
                size_hint_y: 1
            Button:
                text: "Click Here To Continue"
                on_release: app.root.current = "main"
                size_hint: (.75, .15)
                pos_hint: {'x': .12, 'y': .2}
    
    <MainScreen>:
        name: "main"
        MainLabel:
            text: "Home Page"
        BoxLayout:
            Button:
                on_release: app.root.current = "create_profile"
                text: "Create Profile"
                size_hint: (.5, .15)
            Button:
                on_release: app.root.current = "create_packet"
                text: "Create Packet"
                size_hint: (.5, .15)
    
    <CreateProfile>:
        name: "create_profile"
    
        first: first
        middle: middle
        last: last
    
        AnchorLayout:
            anchor_x: 'center'
            anchor_y: 'top'
            MainLabel:
                text: "Create Profile"
                size_hint: (1, .15)
    
        BoxLayout:
            size_hint: (.95, .2)
            pos_hint: {'x': 0, 'y': .85}
            spacing: 10
            padding: 10
            halign: "left"
    
            OtherLabel:
                text: "First"
    
            OtherLabel:
                text: "Middle"
    
            OtherLabel:
                text: "Last"
        BoxLayout:
            size_hint: (.95, .07)
            pos_hint: {'x': 0, 'y': .8}
            spacing: 20
            padding: 10
            halign: "right"
            text_size: self.size
            TextInput:
                id: first
            TextInput:
                id: middle
            TextInput:
                id: last    
    
        BoxLayout:
            Button:
                on_release: app.root.current = "main"
                text: "back Home"
                size_hint: (.5, .15)
            Button:
                on_release: root.save()
                text: "Save Profile"
                size_hint: (.5, .15)
    
    <CreatePacket>:
        name: "create_packet"
        MainLabel:
            text: "Select Packet"
        FloatLayout:
            Button:
                on_release: app.root.current = "main"
                text: "back Home"
                size_hint: (1, .15)