Posts: 8
Threads: 3
Joined: Apr 2022
Apr-03-2022, 07:43 AM
(This post was last modified: Apr-04-2022, 05:16 AM by Yoriz.
Edit Reason: Formatting
)
Hello, so I want to make all four abilities: "Strength", "Speed", "Stealth", "Cunning" to be placed in one window -- since right now with the enterbox, I have to press "OK" to move onto the next ability, after I've inputted the value for it, four times until it is added to the nested dictionary (card_catalogue). I have attempted to manipulate the code from enterbox to multenterbox for a few hours, just cannot get it to work. This is probably very simple to do, if anybody could help that would be sincerely appreciated.
Here is the original code.
def add_items(combo):
'''Allows the user to add items to a specified combo.'''
msg = "Enter item/s for combo, separated by a comma."
title = "Enter card items"
items = easygui.enterbox(msg, title)
items_list = items.split(",")
for i in items_list:
# Input handling: To handle a situation where the user enters
# a space after the comma when entering items.
item = i.strip()
msg = item + ": Enter Strength/Speed/Stealth/Cunning Value:"
title = item + " Monster Card Value:"
card_catalogue[combo][item] = easygui.enterbox(msg, title)
# Calls function to display combo.
show_menu(combo) Finally, here is the attempt I have made to convert it to a multenterbox, which does not add to the dictionary like this - "Strength: 5", and the other 3 abilities, instead it displays "['1', '2', '3', '4']" (For the input values of the abilities (strength, speed, stealth, cunning):
def add_items(combo):
'''Allows the user to add items to a specified combo.'''
msg = "Enter item/s for combo, separated by a comma."
title = "Enter card items"
items = easygui.enterbox(msg, title)
items_list = items.split(",")
for i in items_list:
# Input handling: To handle a situation where the user enters
# a space after the comma when entering items.
item = i.strip()
msg = item + ": Enter Value"
title = item + " Monster Card Value:"
values = "Strength","Speed","Stealth","Cunning"
card_catalogue[combo][item] = easygui.multenterbox(msg, title, values)
# Calls function to display combo.
show_menu(combo) This is what I am wanting from the input taken from the multenterbox:
Strength: 5
Speed: 15
Stealth: 4
Cunning: 21
Let me know if anything else should be provided! :-)
Posts: 6,811
Threads: 20
Joined: Feb 2020
Apr-03-2022, 02:32 PM
(This post was last modified: Apr-03-2022, 02:44 PM by deanhystad.)
I followed the example provided for multienterbox here:
https://easygui.readthedocs.io/en/latest...l#enterbox
And came up with this:
import easygui as gui
def get_fields(fields, title="Statistics", prompt="Enter Statistics"):
values = gui.multenterbox(prompt, title, fields)
# make sure that none of the fields were left blank
while True:
message = None
missing_fields = ", ".join([field for field, value in zip(fields, values) if not value.strip()])
if missing_fields:
message = f"Enter values for {missing_fields}"
else:
try:
values = list(map(int, values))
except ValueError:
message = "Entries must be integers"
if message:
values = gui.multenterbox(message, title, fields, values)
else:
return {field:value for field, value in zip(fields, values)}
print(get_fields(("Strength", "Speed", "Stealth", "Cunning"))) Provide as many fields as you like. You can also change the prompt and window tittle. Returns a dictionary.
Posts: 8
Threads: 3
Joined: Apr 2022
Apr-04-2022, 05:04 AM
(This post was last modified: Apr-04-2022, 05:18 AM by Yoriz.
Edit Reason: removed unnecessary quote of previous post
)
Thank you again for your help, I have tried to use your method, it does return a dictionary although I am not sure how to add the ability/values to my dictionary, for it to be displayed -- here is my original code that does not use a multenterbox, if you try "Add Card" in the gui, it takes input and outputs not like {'Strength': 1, 'Speed': 2, 'Stealth': 3, 'Cunning': 4}, instead like
"STONELING MONSTER CARD
Strength: 7
Speed: 1
Stealth: 25
Cunning: 15
In the terminal and the gui. I think the problem is that this is not linked to my nested dictionary, which is why it is not adding it, and this window pops up before the initla displayed options of what the user wants to choose to do.
Here is my full code:
import easygui
card_catalogue = {
"Stoneling" : {
"Strength" : 7,
"Speed" : 1,
"Stealth" : 25,
"Cunning" : 15
},
"Vexscream" : {
"Strength" : 1,
"Speed" : 6,
"Stealth" : 21,
"Cunning" : 19
},
"Dawnmirage" : {
"Strength" : 5,
"Speed" : 15,
"Stealth" : 18,
"Cunning" : 22
},
"Blazegolem" : {
"Strength" : 15,
"Speed" : 20,
"Stealth" : 23,
"Cunning" : 6
},
"Websnake" : {
"Strength" : 7,
"Speed" : 15,
"Stealth" : 10,
"Cunning" : 5
},
"Moldvine" : {
"Strength" : 21,
"Speed" : 18,
"Stealth" : 14,
"Cunning" : 5
},
"Vortexwing" : {
"Strength" : 19,
"Speed" : 13,
"Stealth" : 19,
"Cunning" : 2
},
"Rotthing" : {
"Strength" : 16,
"Speed" : 7,
"Stealth" : 4,
"Cunning" : 12
},
"Froststep" : {
"Strength" : 14,
"Speed" : 14,
"Stealth" : 17,
"Cunning" : 4
},
"Wispghoul" : {
"Strength" : 17,
"Speed" : 19,
"Stealth" : 3,
"Cunning" : 2
}
}
VALUE_MIN = 1
VALUE_MAX = 25
def display_options():
'''Displays options and gets user to indicate what they want to do'''
options = {
"Add card": add_combo,
"Find card": search,
"Delete card": delete,
"Output all cards": output_menu,
"Exit": leave,
}
get_input = "Y"
# Loop below continues until user selects "Exit" from options and
# get_input is set as "N"
while get_input != "N":
msg = "What would you like to do?"
title = "MENU MAKER OPTIONS"
choices = []
for i in options:
choices.append(i)
selection = easygui.buttonbox(msg, title, choices)
# Calls a specific function, based on the user's selection from
# the menu.
# For example, if user clicks on "Delete" button, it will be
# get_input = options[leave()]
get_input = options[selection]()
def add_combo():
'''Allows user to add a new combo to the menu, as well as the items
in the combo and their prices.
'''
add_more = "Yes"
while add_more == "Yes":
combo_name = easygui.enterbox("Enter Monster Card name: ",
title = "Monster Card Name")
name_check = check_exists(combo_name)
if name_check == combo_name:
easygui.msgbox("That card name has already been used.")
break
else:
combo_name = combo_name.capitalize()
card_catalogue[combo_name] = {}
add_items(combo_name)
# Checks if user would like to add another combo.
msg = "Do you want to add another monster card?"
title = "Add card?"
choices = ["Yes", "No"]
add_more = easygui.buttonbox(msg, title, choices)
def search():
'''Searches for specified combo in the menu.
If the combo is in the menu, the combo details are displayed on
screen. Otherwise, the user is told that there is no such combo
and given the option to add the combo.
'''
msg = "Enter card name:"
title = "Card to search for"
search = easygui.enterbox(msg, title)
# Calls function to check whether the combo already exists
combo = check_exists(search)
if combo:
# Calls function to display combo details on screen.
show_menu(combo)
else:
# If the combo does not exist, the user is told this and
# given the option to add the combo.
msg = "There is no such combo on the menu.\nWould you like to add the combo?"
title = "Card does not exist"
choices = ["Yes", "No"]
add = easygui.buttonbox(msg, title, choices)
if add == "Yes":
add_combo() # Calls function to add a combo.
def delete():
'''Allows user to delete a combo.'''
del_combo = ""
while del_combo == "":
msg = "Enter name of the card you want to delete."
title = "Card to delete"
del_combo = easygui.enterbox(msg, title)
# Calls function to check if the combo entered by user exists.
del_combo = check_exists(del_combo)
# If the combo exists, the user is asked to confirm whether
# they want to delete the combo. Otherwise, they are shown an
# error message.
if del_combo:
msg = "Are you sure you want to delete the " + del_combo + " Card?"
title = "Confirm delete"
choices = ["Yes","No"]
sure = easygui.buttonbox(msg, title, choices)
if sure == "Yes":
card_catalogue.pop(del_combo)
else:
msg = "That combo is not on the menu."
title = "ERROR: Combo not on menu"
easygui.msgbox(msg, title)
del_combo = ""
def output_menu():
'''Outputs the full menu to Python Shell (for printing)'''
output = ""
for combo_name, combo_info in card_catalogue.items():
print("\n\n" + combo_name.upper() + " MONSTER CARD")
output += f"{combo_name.capitalize()}:\n"
for key in combo_info:
price = format(combo_info[key])
print(key + ": " + price)
output += f"\t{key}: {price}\n"
print("_" * 26)
easygui.textbox("These are the contents of the card catalogue",
"Display All Combos", output)
def validate_value(value, item):
"""Input validation: Checks that the user has input a valid value.
"""
# If the user presses the Cancel button, function is called to
# confirm that the user wants to exit.
if value == None:
value = query_cancel()
# If the user presses the OK button without entering a value,
# the no_value() function is called to display an error message
# and get input.
while value == "":
value = no_value(item)
# The while loop checks that input is valid (from VALUE_MIN to
# VALUE_MAX), and if not an error message is displayed, and the
# user is prompted to re-enter the value.
while float(value) < VALUE_MIN or float(value) > VALUE_MAX:
msg = "Please enter a valid value for " + item + " (from " + str(VALUE_MIN) + " to " + str(VALUE_MAX) + ")."
title = "ERROR"
value = easygui.enterbox(msg, title)
# If the user pressed OK without entering a value, the function
# is called to display and error message and get input.
while value == "":
value = no_value(item)
# If the user presses the Cancel button, function is called to
# confirm that the user wants to exit.
if value == None:
value = query_cancel()
return (value)
def no_value(item):
"""Input validation: If the user presses the OK button without
entering a value, they are shown an error message, and prompted
again to enter a value.
"""
msg = "You must enter a value for " + item + " (from $" + str(VALUE_MIN) + " to $" + str(VALUE_MAX) + ")."
title = "ERROR"
value = easygui.enterbox(msg, title)
# If the user presses the Cancel button, function is called to
# confirm that the user wants to exit.
if value == None:
value = query_cancel()
return value
def query_cancel():
"""Input validation: If the user presses the Cancel button, they are
asked if they are sure they want to exit the current menu item.
"""
leave = easygui.buttonbox("Do you want to exit?", choices=["Yes", "No"])
# If the user confirms they want to exit, they are shown the main
# list of options.
if leave == "Yes":
# Takes back to the main menu or you could simply have exit()
display_options()
else:
# Returns nothing so that the program continues
return 0
def check_exists(search):
'''Checks whether a combo exists (ie is on the menu).
'''
for combo_name, combo_info in card_catalogue.items():
if search.capitalize() == combo_name:
combo = combo_name
return(combo)
break
def show_menu(name):
'''Displays the details for the combo that has been added.'''
output = ["\n\n***" + name + " Combo details***\n"]
for value in card_catalogue[name]:
price = format(card_catalogue[name][(value)])
info = value + ": " + price
easygui.msgbox(output.append(info))
easygui.msgbox("\n".join(output))
def add_items(combo):
'''Allows the user to add items to a specified combo.'''
msg = "Enter item/s for combo, separated by a comma."
title = "Enter card items"
items = easygui.enterbox(msg, title)
items_list = items.split(",")
for i in items_list:
# Input handling: To handle a situation where the user enters
# a space after the comma when entering items.
item = i.strip()
msg = item + ": Enter Strength/Speed/Stealth/Cunning Value:"
title = item + " Monster Card Value:"
card_catalogue[combo][item] = easygui.enterbox(msg, title)
# Calls function to display combo.
show_menu(combo)
def leave():
'''Farewells user if they have chosen to exit the program.'''
easygui.msgbox("Thanks for using the menu builder.")
return "N"
# The main program starts below.
display_options() If you run this program and select add card, you will see what I mean ~ all I am really trying to figure out is how I can do the same thing the enterbox is doing, but instead with a multenterbox that is all in one window, thanks!
Posts: 6,811
Threads: 20
Joined: Feb 2020
Apr-04-2022, 02:51 PM
(This post was last modified: Apr-04-2022, 05:49 PM by deanhystad.)
It would get called by add_combo() like this:
def get_fields(msg, title, fields):
"""Get values for multiple fields. Returns values in dictionry"""
values = easygui.multenterbox(msg, title, fields)
# make sure that none of the fields were left blank
while True:
missing_fields = ", ".join([field for field, value in zip(fields, values) if not value.strip()])
if missing_fields:
values = easygui.multenterbox(f"Enter values for {missing_fields}", title, fields, values)
else:
return {field:value for field, value in zip(fields, values)}
def add_combo():
'''Add new combo to menu'''
while True:
combo_name = easygui.enterbox(
"Enter Monster Card name: ",
title = "Monster Card Name").capitalize()
if check_exists(combo_name):
easygui.msgbox("That card name has already been used.")
else:
card_catalogue[combo_name] = get_fields(
msg="Enter Values",
title="Monster Card Value",
fields=("Strength", "Speed", "Stealth", "Cunning"))
if not confirm("Do you want to add another monster card?"):
break I notice you use confirm (Yes/No) dialogs in several places in your code. It might make sense to write a function that makes this easier.
def confirm(msg, title="Confirm):
"""Return True if user selects Yes, else return False"""
choices = ["Yes", "No"]
return easygui.buttonbox(msg, title, choices) == choices[0] The check_exists function only returns a value if the name is found, otherwise the default value None is returned. This is a bad practice. If a function ever returns a value it should always return a value.
def check_exists(name):
'''Checks whether a combo exists. Return combo name if found, else None'''
name = name.capitalize()
if name in card_catalogue:
return name
return None There is an easier way to build a list of dictionary keys.
def display_options():
'''Displays options and gets user to indicate what they want to do'''
options = {
"Add card": add_combo,
"Find card": search,
"Delete card": delete,
"Output all cards": output_menu,
"Exit": leave}
# Loop until user selects "Exit" options
msg = "What would you like to do?",
title = "MENU MAKER OPTIONS",
choices = list(options.keys())
while True:
if options[easygui.buttonbox(msg, title, choices)]() == "No":
break
Posts: 8
Threads: 3
Joined: Apr 2022
I sincerely appreciate your efforts in voluntarily giving me a hand on this, deanhystad, you are truly amazing. I spent countless hours trying to figure how to convert the enterbox to multenterbox, but I just couldn't figure out a solution, many thanks again, wish I could give you +999999 reputation
Posts: 6,811
Threads: 20
Joined: Feb 2020
Read all the recipes in the easygui documentation. They do a pretty good job explaining how everything works and provide examples for common design patterns. Look at the examples, find the one most similar to what you are trying to do. Copy the pattern.
|