Store and re-use widgets in dictionary/ list - python

so i have to do a snake using tkinter and i'm asked to either store the widget in a dictionary or a list.
the code i was given to work with is :
tk_frame['score'] = Frame(fen)
tk_frame['score'].pack()
tk_frame['jeu']= Frame(fen)
tk_frame['jeu'].pack()
tk_frame['gestion']= Frame(fen)
tk_frame['gestion'].pack()
The computer asked what "tk_frame was" so i created a dictionary :
tk_frame = {'score' : Frame(fen), 'jeu' : Frame(fen), 'gestion' : Frame(fen)}
but then i have to call these keys as arguments in other functions and i don't know how i can do this.

lets say you want call 'score', do:
tk_frame['score'].pack()
assuming you want to pack these widgets.
The same principle applies for the other keys

Related

Dynamically Add Values To Tkinter Menubutton (using list) and controlling variables

Referencing to an old thread :
Dynamically Add Values To Tkinter Menubutton (using list)
when it comes to menubuton in tkinter, i'm trying to use the same code provided in the answer to dynamically add values to menu in tkinter, given the amount of items i want in my menu.
This can be done by using a list and a dictionary as follows:
menubutton = Menubutton(root, text = "Select")
menubutton.menu = Menu(menubutton)
menubutton["menu"]= menubutton.menu
# main list holding menu values
list1 = ['a', 'b', 'c']
# Creating a dictionary
dict2 = {}
# Add key-value pairs to dictionary
for i in range(0, len(list1)):
temp = {'var'+str(i): list1[i]}
dict2.update(temp)
# Finally adding values to the actual Menubutton
for i in range(0, len(list1)):
menubutton.menu.add_checkbutton(label = dict2['var'+str(i)], variable = list(dict2.keys())[i])
menubutton.pack()
My question with this now is, how do I control the variables (i.e if i want to do a .get() or .set(0) or similar), how do i reference to it considering that my variable is being generated within the for loop as it is what is adding the actual items to the menu based in the contents of the list?
and on the same note, how do I check (just for sake of simplicity, how do I print the selected variables) (and values, i dont see for example onvalue inside the checkbutton in this case, i'm assuming I'd have to pre-assign a value to each specific item of the list, and then have this value be generated based on the index of each item being created during the for loop
Thanks in advance,
If you have any sugestions on a different widget that might be more useful and convenient for this purpose (I wanted a multiple selection checkbox but thats doesnt seem to be a thing in tkinter, so menubutton is the thing I came the closest to that outcome).
Nonetheless I'd still appreciate an explanation to the above, always good to learn...

What is the best way for allowing for dynamic object creation and removal in a Plotly Dash dashboard?

Currently, I have a class which stores a dictionary of Card elements, each of which is unique. The class can also generate these cards and append them to the dictionary or remove a card from a dictionary. However, I am not sure how to best allow for this action through a callback function since the ID for a card doesn't exist until the card is made, and the functionality isn't directly within the Dash framework since a dictionary object acts as an intermediary where the objects are stored.
Basically, I am wondering what the best way to dynamically create and destroy objects with a callback is?
Thank you!
Assuming you want to avoid extra computation for building cards you wont use, I'd suggest create a which creates each card and store those functions in a dictionary. (You can also create a universal function with params that allow specificity)
my_card_functions = {
'id_1': make_id_1,
'id_2': make_id_2,
}
Creating a card could be done as such:
my_id = 'id_1'
f = my_card_functions[my_id] # will break if id isn't registered
my_card = f()
You can store the cards you want to create in a dcc.store object. Here's an example of some code you might consider:
# Pretend these are structured properly
dcc.Store(id='cards_data')
html.Div(id='my_cards',children=[])
#app.callback(
Output('my_cards','children'),
[Input('cards_data','data')],
[State('my_cards','children')]
)
def make_cards(data, children):
"""Makes cards for each id"""
if not data:
raise PreventUpdate
# The following code is not correct but serves as a demonstrative example
# Some data structure manipulation is necessary to access
# the ids from the children elements
children_ids = [x['id'] for x in children]
# Assuming your data looks something like this:
# {'add':['id_1','id_2']}
for x in data['add']:
if x not in children_ids:
f = my_card_functions[my_id]
my_card = f()
# Store values
children.append(my_card)
return children
Note, this approach does not resolve removal of cards. That could easily be done but would probably require a more dynamic use of caching.
Just on the basis of your question, I have some immediate suggestions (since there is no working code that you have posted).
1. Generate all card elements by default. They can be generated, but not 'displayed'
2. Add your callbacks to toggle the display/rendering of the cards dynamically based on the use case. That way you will have card element ids to play around with in the callbacks.
Hope this helps.

Dynamically update all instances of multiple input function

I'm creating a program with a class that has 3 input attributes. The program calls a function that creates many of these objects with their inputs being given based on some other criteria not important to this question.
As I further develop my program, I may want to add more and more attributes to the class. This means that I have to go and find all instances of the function I am using to create these objects, and change the input arguments.
For example, my program may have many of these:
create_character(blue, pizza, running)
where inputs correspond to character's favorite color, food, and activity. Later, I may want to add a fourth input, such as favorite movie, or possibly a fifth or sixth or ninety-ninth input.
Do professional programmers have any advice for structuring their code so that they don't have to go through and individually change each line that the create_character function is called so that it now has the new, correct number of inputs?
Find and replace seems fine, but this makes error possible, and also seems tedious. I'm anticipating calling this function at least 50 times.
I can think of a few options for how you could design your class to make easier to extend later new kinds of "favorite" things.
The first approach is to make most (or all) of the arguments optional. That is, you should specify a default value for each one (which might be None if there's not a real value that could apply as a default). This way, when you add an extra argument, the existing places that call the function without the new argument will still work, they'll just get the default value.
Another option would be to use a container (like a dictionary) to hold the values, rather than using a separate variable or argument for each one. For instance, in your example could represent the character's favorites using a dictionary like favorites = {'color': blue, 'food': pizza, 'activity': running} (assuming the those values are defined somewhere), and then you could pass the dictionary around instead of the separate items. If you use the get method of the dictionary, you can also make this type of design use default values (favorites.get('movie') will return None if you haven't updated the code that creates the dictionary to add a 'movie' key yet).
You can take advantage of argument/keyword argument unpacking to support dynamically-changing function parameters. And also factory function/classes that generate the function you need:
def create_character(required1, required2, *opt_args, **kwargs):
""" create_character must always be called with required1 and required2
but can receive *opt_args sequence that stores arbitrary number of
positional args. kwargs hold a dict of optional keyword args """
for i, pos_arg in enumerate(opt_args):
# pos_arg walks opt_args sequence
print "position: {}, value: {}".format(i+3, pos_arg)
for keyword, value in kwargs:
print "Keyword was: {}, Value was: {}".format(keyword, value)
pos_args = (1,2,3)
create_character('this is required','this is also required', *pos_args)
""" position: 3, value: 1
position: 4, value: 2
position: 5, value: 3 """
a_dict = {
'custom_arg1': 'custom_value1',
'custom_arg2': 'custom_value2',
'custom_arg3': 'custom_value3'
}
create_character('this is required','this is also required', **a_dict)
""" Keyword was: custom_arg2, value: custom_value2
Keyword was: custom_arg3, value: custom_value3
Keyword was: custom_arg1, value: custom_value1 """
I really like the list or dictionary input method, but it was still messy and allowed for the possibility of error. What I ended up doing was this:
I changed the class object to have no inputs. Favorites were first assigned with random, default, or unspecified options.
After the class object was created, I then edited the attributes of the object, as so:
self.favorite_movie = "unspecified"
self.favorite_activity = "unspecified"
new_character = (character())
new_character.favorite_movie = "Dr. Strangelove"
I think that the downside to this approach is that it should be slower than inputting the variables directly. The upside is that this is easy to change in the future. Perhaps when the program is finished, it will make more sense to then convert to #Blckknight 's method, and give the input as a list or dictionary.

Tkinter OptionMenu DisplayOptions and Assignment Values

In Python's Tkinter OptionMenu, is it possible to have a list of display options, but on selection, it sets a value to be some other value?
Suppose I had
variable = tk.IntVar(master)
OptionMenu(master, variable, 1, 2).pack()
options = {1:"one",2:"two"}
and wanted to display the values but assign the key to variable. Is this even possible? Or is there a way to link the OptionMenu to call a function on selection to convert it?
My real problem is more involved than the example, so the issue is just evaluating complex strings and I'd like to avoid using a StringVar.
Thanks
You already have it. Use the dictionary to map your displayed options to the actual values you want.
EG:
import Tkinter as tk
master = tk.Tk()
variable = tk.StringVar(master)
options = {"one": 1, "two": 2}
tk.OptionMenu(master, variable, *options.keys()).pack()
...
wanted = options[variable.get()]
Please note the splat operator, *, used to unpack the keys as a comma separated list of arguments to OptionMenu. Later when you want the option's "value" use variable.get() as the "key" in the dictionary.

Python - function that c

I'm working on a choose-your-own-adventure project whose main function accepts 3 paramters.
First parameter is printed by the function. This is always a variable containing text describing the scenario
The second parameter is always a variable containing choices pertaining to the current scenario, which is printed by the function
The third parameter is a dictionary whose keys correspond to the scenario choices, and are compared against user's input. The value of the keys contain the next 3 arguments.
Example.
text1 = 'This is the scenario'
text2 = 'These are the choices'
dict2 = {'1':[text2, text1, dict1]}
dict1 = {'1':[text1, text2, dict2]}
def mainloop(scenario, choice, consequence):
print scenario
print choice
answer = raw_input('Please input 1, 2 or 3>>> ')
if answer in consequence.keys():
mainloop(*consequence[answer])
mainloop(text3, text2, dict1)
I thought this would be a good way to design my project, however I am running into a problem with the dictionary parameter. Because the dictionary values contain a list of arguments, which include other dictionaries I keep getting the error:
NameError: name 'dict1' is not defined
Flipflopping the order I define the dictionaries in expectedly results in the same error, only with 'dict2' not being defined. Any advice on how I can get this concept to work? Or is it time to take a completely different approach?
I'm not quite sure why you need two dicts, but assuming you do, it is possible to define dicts with circular references:
text1 = 'This is the scenario'
text2 = 'These are the choices'
dict1, dict2 = {}, {}
dict2['1'] = [text2, text1, dict1]
dict1['1'] = [text1, text2, dict2]
There are a couple of things that I would suggest rethinking about the approach as a whole.
As others pointed out, mainloop() isn't actually a loop; it's a recursive function. Ideally, if this is a game loop, you'd want it to be more likeā€¦
def myGameLoop():
gameRunning = True
while gameRunning:
# code that displays your rooms, gets user input,
#and checks to make sure that gameRunning is not False.
#for an example, something like:
if somethingAwfulHappened
gameRunning = False
In this way you don't have to call the mainloop more than once, and there's no reason for it to call itself.
Additionally, the reason your room dicts/lists keep telling you they don't exist is because, well, they don't :) This is actually a fairly good example of why it's a good idea to separate your concerns!
Think of it this way: why does a 'room' object - whether it is a dict, a list, an object, etc - need to contain any data about other rooms? Your kitchen might lead to your bathroom, sure - but does your kitchen KNOW that it leads to the bathroom? Does it serve a new purpose by leading to the bathroom instead of your bedroom? In the same way that your kitchen does not "care" that it is connected to your bathroom, neither do your dicts need to "know" that they are connected by explicitly naming each other in their own data.
An ideal approach might be instead to go and define all your rooms, and then create a 'map' which describes the relationship between your rooms. So for example:
kitchen = {
"name":"Kitchen",
"description": "This is the kitchen! It's nice and clean.",
"exits": "S", "E"
}
bathroom = {
"name":"Bathroom",
"description":"This is the bathroom. Someone left a towel on the floor.",
"exits":"W", "S"
}
#and so on, creating rooms
Now create a map dict that just holds all this info and describes how those exits work.
mapOfHouse = {
"kitchen": kitchen,
"leadsTo": {
"S": bathroom,
"E": someOtherRoom ##some other place you've defined
},
"bathroom": bathroom,
"leadsTo": {
"S": otherAwesomePlaces,
"E": kitchen
},
#and so on for other rooms in the house/on the map/etc
}
Once you sketch all this out, you then set your gameloop to get your player's input, check it against the room's exits, and if there's a match, as long as the loop is still True it heads back up to the top, displaying the updated information.
It seems like a lot more work, but really this gives you an immense amount of freedom. It lets you focus strictly on rooms as you design them, then focus strictly on the map as you update it, and then lets you design a game loop which doesn't really care a lot about the contents of what it's looping over, as long as it stays True the whole time and gets good instructions from the player.
You referencing the dict1 within dict2, but dict1 doesn't exist yet.
Your problem is the following:
dict2 = {'1':[text2, text1, dict1]} # NameError: Here dict1 does not exist yet
dict1 = {'1':[text1, text2, dict2]}
You can only solve this by defining the objects before referencing them.
In order to achieve that, you can do as follows:
dict1, dict2 = {}, {} # Define both objects
dict1[1] = [text2, text1, dict2] # Here dict2 is already defined, no NameError
dict1[2] = None # ...
dict2[1] = [text1, text2, dict1] # And here is dict1 already defined too
dict2[2] = None # ...
The question we could ask is: does referencing an object and later changing it alter the reference?
The answer is yes, it does; provided you're using references and not copies (asigning dictonaries always references them).
What if you wanted to make a copy and not a reference?
Well, you can do that via the deepcopy and copy functions from the copy module.
The problem with NameErrors is that there is no such name defined; Python does not use names to track variables: the same name can correspond to different variables (in different scopes, or at different times) and the same variable can be accessed via different names (by assigning one name's value to the other).
When you do del x, you unlink a variable from its name, but you don't always eliminate the object.
For example, the following code works and prints stash correctly, altough via the del operator we unlinked the str object 'hello' from the object x that contained it.
x = {1: 'hello'}
stash = x[1]
del x[1]
print stash
By deleting the stash object too the 'hello' object may be now prone to be garbage-collected at some point.
Reminder
If you want to refer to other objects via their names, they have to be named before that reference happens.

Categories

Resources