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.