简体   繁体   中英

Python/Kivy text input to dictionary in json file

Hi I'm trying to add to a nested dictionary that's stored in a json file. I am taking inputs from the user and trying to add those to the nested dictionary. Here's a working part of my code...

main.py:

from kivy.app import App
from kivy.config import Config
from kivy.clock import *
from functools import *
import json
Config.set('graphics', 'width', '480')
Config.set('graphics', 'height', '800')
#Config.set('kivy', 'keyboard_mode', 'dock')
from kivy.uix.screenmanager import *
from kivy.uix.button import *
from kivy.uix.label import *
from kivy.uix.textinput import *
from kivy.clock import *

class SettingsScreen(Screen):

    @mainthread
    def on_enter(self):
        # Drink Manager Tab
        with open("drink_list.json") as f:
            drink_list = json.load(f)
            for drink in drink_list:
                drink_buttons = Button(text=str(drink), size_hint=(None, None))
                self.ids.grid.add_widget(drink_buttons)
        f.close()

        # Add 'Add New Drink' button
        add_new_drink_button = Button(text='Add New Drink', pos=(20, 110), size_hint=(.9, .1))
        add_new_drink_button.bind(on_press=self.add)
        self.ids.done_button_layout.add_widget(add_new_drink_button)

        # Add 'Done!' button
        done_button = Button(text='Done!', pos=(20, 20), size_hint=(.9, .1))
        done_button.bind(on_press=self.done)
        self.ids.done_button_layout.add_widget(done_button)

        # Pump Assignment Tab
        with open("pumps.json") as f:
            test_list = json.load(f)
            for pump in test_list:
                pump_buttons = Button(text=pump + '\n' + test_list[pump]['Ingredient'] + '\n Enabled - ' + str(test_list[pump]['Enabled']), halign='center', size_hint=(None, None))
                self.ids.pump_grid.add_widget(pump_buttons)
                pump_buttons.bind(on_press = partial(self.edit_pump ,pump))
        f.close()

    def add(self, *args):
        self.manager.transition.direction = 'left'
        self.manager.current = 'add_new_drink_screen'

    def done(self, *args):
        self.manager.transition.direction = 'right'
        self.manager.current = 'home_screen'


    def edit_pump(self, pump, *args):
        Test.editing_pump = pump
        self.manager.transition.direction = 'left'
        self.manager.current = 'edit_pump_screen'


    def on_leave(self, *args):
        self.ids.grid.clear_widgets()
        self.ids.pump_grid.clear_widgets()

class AddNewDrinkScreen(Screen):

    @mainthread
    def on_enter(self):
        with open("pumps.json") as f:
            test_list = json.load(f)
            new_drink_name_label = Label(text='Drink Name:\n(Will be displayed on menu)', halign='center', size_hint=(.5, .05))
            self.new_drink_name_input = TextInput(multiline=False, size_hint=(.5, .05))
            new_drink_ingredients_label = Label(text='Ingredients & amount in ML:\nE.g... Rum: 50, Pepsi: 100\nSee ingredients available below (Case sensitive)', halign='center', size_hint=(1, .09))
            self.new_drink_ingredients_input = TextInput(multiline=False, size_hint=(1, .09))
            new_drink_enabled_label = Label(text='Enabled:\n(True or False)', halign='center', size_hint=(.5, .05))
            self.new_drink_enabled_input = TextInput(text='True', multiline=False, size_hint=(.5, .05))
            save_button = Button(text='Save', halign='center', size_hint=(1, .1))
            cancel_button = Button(text='Cancel', halign='center', size_hint=(1, .1))
            self.ids.add_new_drink_grid.add_widget(new_drink_name_label)
            self.ids.add_new_drink_grid.add_widget(self.new_drink_name_input)
            self.ids.add_new_drink_grid.add_widget(new_drink_ingredients_label)
            self.ids.add_new_drink_grid.add_widget(self.new_drink_ingredients_input)
            self.ids.add_new_drink_grid.add_widget(new_drink_enabled_label)
            self.ids.add_new_drink_grid.add_widget(self.new_drink_enabled_input)
            for pump in test_list:
                ingredients_hint = Label(text=test_list[pump]['Ingredient'], halign='center', size_hint=(.2, .05))
                self.ids.add_new_drink_grid.add_widget(ingredients_hint)
            self.ids.add_new_drink_grid.add_widget(save_button)
            self.ids.add_new_drink_grid.add_widget(cancel_button)
            save_button.bind(on_press=self.save_new_drink)
            cancel_button.bind(on_press=self.cancel)
        f.close()

    def save_new_drink(self, instance):
        new_name = self.new_drink_name_input.text
        new_ingredients = self.new_drink_ingredients_input.text
        new_enabled = bool(self.new_drink_enabled_input.text)
        print(new_name)
        print(type(new_name))
        print(new_ingredients)
        print(type(new_ingredients))
        print(new_enabled)
        print(type(new_enabled))

        with open('drink_list.json') as drink_list_file:
            new_drink = json.load(drink_list_file)

            new_drink[new_name] = {'ingredients': {new_ingredients}, 'Enabled': new_enabled}

        with open('drink_list.json', 'w') as drink_list_file:
            json.dump(new_drink, drink_list_file, indent=4)

    def cancel(self, *args):
        self.manager.transition.direction = 'right'
        self.manager.current = 'settings_screen'

    def on_leave(self, *args):
        self.ids.grid3.clear_widgets()

class Test(App):
    editing_pump = ''
    new_drink_name = ''

    pass


Test().run()

test.kv:

#:kivy 1.9.0
ScreenManager:
    SettingsScreen:
    AddNewDrinkScreen:

<SettingsScreen>:
    name: 'settings_screen'

    TabbedPanel:
        do_default_tab: False
        tab_pos: 'top_mid'
        tab_height: 70
        tab_width: self.width/3
        canvas.before:
            Color:
                rgba: 1, 1, 1, 1
            Rectangle:
                pos: self.pos
                size: self.size

        TabbedPanelItem:
            text: 'Drink Manager'

            StackLayout:
                id: grid
                orientation: 'lr-tb'
                padding: 10
                spacing: 10

        TabbedPanelItem:
            text: 'Pump Assignment'

            StackLayout:
                id: pump_grid
                orientation: 'lr-tb'
                padding: 15
                spacing: 15

    FloatLayout:
        id: done_button_layout

<AddNewDrinkScreen>:
    name: 'add_new_drink_screen'

    StackLayout:
        id: add_new_drink_grid
        orientation: 'lr-tb'
        padding: 15
        spacing: 15

pumps.json:

{
    "Pump 1": {"Pin": 11, "Ingredient": "Pepsi", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 2": {"Pin": 13, "Ingredient": "Rum", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 3": {"Pin": 15, "Ingredient": "Cherry syrup", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 4": {"Pin": 19, "Ingredient": "Lemon syrup", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 5": {"Pin": 21, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 6": {"Pin": 23, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 7": {"Pin": 29, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 8": {"Pin": 31, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 9": {"Pin": 33, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 10": {"Pin": 35, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 11": {"Pin": 37, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 12": {"Pin": 38, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 13": {"Pin": 36, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 14": {"Pin": 32, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 15": {"Pin": 26, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"},
    "Pump 16": {"Pin": 24, "Ingredient": "Unused", "Pumptime": 0.15, "Enabled": "True"}
}

drink_list.json:

{
    "Example": {
        "ingredients": {
            "Rum": 50,
            "Pepsi": 100
        },
        "Enabled": false
    }
}

The inputs in the AddNewDrinkScreen should fill in the variables in save_new_drink and then save_new_drink should add those to the dictionary in the json file. This works fine up until the new_ingredients part. I'm getting TypeError: Object of type 'set' is not JSON serializable but no matter what I try I get the same error or it adds the new drink and ingredients to the dictionary but with extra double quotations either end of the ingredients making it useless. Eg: "'Rum: 20, 'Pepsi': 100". Any ideas how I can make this work or a simpler way of doing it?

The line of code:

new_drink[new_name] = {'ingredients': {new_ingredients}, 'Enabled': new_enabled}

tries to make a new entry in the new_drink dictionary, but the building of the ingredients dictionary is incorrect. The new_ingredients variable is just a string and does not constitute a dictionary. In order to make an actual dictionary, you must parse that string. Here is a modified version of your save_new_drink() method that does that:

def save_new_drink(self, instance):
    new_name = self.new_drink_name_input.text

    # make dictionary to hold new ingredients
    new_ingredients = {}

    # parse input into the new_ingredients dict
    tokens = self.new_drink_ingredients_input.text.split(',')
    for tok in tokens:
        ingredient, amount = tok.split(':')
        new_ingredients[ingredient.strip()] = int(amount.strip())

    new_enabled = bool(self.new_drink_enabled_input.text)
    print(new_name)
    print(type(new_name))
    print(new_ingredients)
    print(type(new_ingredients))
    print(new_enabled)
    print(type(new_enabled))

    with open('drink_list.json') as drink_list_file:
        new_drink = json.load(drink_list_file)

        # use the constructed dictionary of ingredients
        new_drink[new_name] = {'ingredients': new_ingredients, 'Enabled': new_enabled}

    with open('drink_list.json', 'w') as drink_list_file:
        json.dump(new_drink, drink_list_file, indent=4)

I don't clam that the above code is a usable in production code, but it demonstrates the idea. Parsing user input is difficult. You might want to consider a different approach. Perhaps use a readonly TextInput for the recipe that is populated by clicking Buttons for ingredients and a TextInput with input_filter: 'int' for the amount. You could then use ast.literal_eval() to build the dictionary from the TextInput .

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