I am trying to append different dictionaries based on what raidoButton is selected in the GUI.
Currently I can append a select dictionary however I am trying to create something that will check the value of the selected raidButton ( 0,1, or 2)
and then with that value check another dict for the name of the dictionaries that need to be append.
Note: My dictionaries are stored on files outside of the program in the same directory as the script.
Here is my code.
thislib = IntVar() # thislib is the variable for the raidoButtons
def append_notes(event=None):
buttonValue = thislib.get()
list_of_dicts = {0:vzt_notes, 1:rms_notes, 2:nsr_notes}
dict_to_be_updated = list_of_dicts[buttonValue]
e1Current = keywordEntry.get().lower()
e1Upper = keywordEntry.get().upper()
e2Current = root.text.get(1.0, END)
answer = tkinter.messagebox.askquestion("Update Notes!","Are you sure you want update your Notes for "+e1Upper+" This cannot be undone!")
if answer == "yes":
dict_to_be_updated[e1Current] = e2Current
with open("%s" %dict_to_be_updated, "r+" ) as working_temp_var:
json.dump(dict_to_be_updated, working_temp_var, indent = "")
else:
tkinter.messagebox.showinfo('...','That was close!')
When I get the below error I think its saying that during my with open statement it is trying to pass the content of the dictionary as a filename/path. I am not sure how to get around this. I just want to get the file name from list_of_dicts by using the raidoButton value as the index value.
with open("%s" %dict_to_be_updated, "r+" ) as working_temp_var:
FileNotFoundError: [Errno 2] No such file or directory: '{\'dss\': \'# DSS: Decision Support Systems\\n# U
Maybe I cant do it like this but it would be nice to know if I can do something simaler. Otherwise I have to create a long if: elif: elif: else: kind of statement for each dictionary I add as a note storage location.
Thanks to the advise of Daniel Roseman, I was able to use a separate list to perform the function I was looking for.
Below is the solution to my question if anyone is interested.
# This is the variable that stores the value of the selected radioButton
thislib = intVar()
def append_notes(event=None):
# This is getting the current value of the radooButton selected. Currently the posible values are 0,1, and 2.
buttonValue = thislib.get()
# This list is used during out "with open" statement below.
list_of_current_keys=["vzt_keys","rms_keys","nsr_keys"]
# This list is used during out "with open" statement below.
list_of_current_dicts=["vzt_notes","rms_notes","nsr_notes"]
# The two dictionaries and lists below are used in the below if statement in order to
# update the correct dictionary and list based of the returned radioButton value.
list_of_dicts = {0:vzt_notes, 1:rms_notes, 2:nsr_notes}
list_of_keys = {0:vzt_keys, 1:rms_keys, 2:nsr_keys}
dict_to_be_updated = list_of_dicts[buttonValue]
keys_to_be_updated = list_of_keys[buttonValue]
# This variable is getting the lower case version of the typed keyword in the keyword
# search box. This lets us search the key value of the dictionary without
# having to worry about case being an issue to see if the keyword already exist
e1Current = keywordEntry.get().lower()
# This is used to store the keyword in the format typed by the used.
# So the user can decide on the case they want displayed in the keyword list.
e1allcase = keywordEntry.get()
# e2Current is where all the notes are displayed or typed. we get the notes that are
# in the textbox to update or add to the selected dictionary as the keys : string value.
e2Current = root.text.get(1.0, END)
# This is a simple pop up window that will ask if you are sure you wish to update your notes.
# currently I have not written a way to undo the last update so for now this message is to
# warn you there is no going back if you hit the "yes" button.
answer = tkinter.messagebox.askquestion("Update Notes!","Are you sure you want update your Notes for "+e1allcase+" This cannot be undone!")
# This if statement will check take the currently selected dictionary & list and update them.
# If the keyword does not exist in the selected dictionary then It will add the keyword:"notes".
if answer == "yes":
current_library = list_of_dicts[buttonValue]
selected_dict = list_of_current_dicts[buttonValue]
selected_libkey = list_of_current_keys[buttonValue]
if e1Current in current_library:
statusW.config(text = "Updating Keyword & Notes for the "+selected_dict+" Library!")
dict_to_be_updated[e1Current] = e2Current
with open(selected_dict, "r+" ) as working_temp_var:
json.dump(dict_to_be_updated, working_temp_var, indent = "")
statusW.config(text = "Update Complete")
else:
statusW.config(text= "Creating New Keyword & Notes for the "+selected_dict+" Library!")
dict_to_be_updated[e1Current] = e2Current
with open(selected_dict, "r+" ) as working_temp_var:
json.dump(dict_to_be_updated, working_temp_var, indent = "")
keys_to_be_updated.append(e1allcase)
with open(selected_libkey, "r+" ) as working_temp_keys:
json.dump(keys_to_be_updated, working_temp_keys, indent = "")
statusW.config(text = "Update Complete")
# This calles a function that will update the display
update_kw_display()
else:
tkinter.messagebox.showinfo('...','That was close!')
Related
Hello I a developing a text based game and I'm struggling with the drop command ; the way it works is you write "D name of the item" then it checks if the item is actually in the inventory and if it is, it puts it in a variable, deletes it from the inventory and I want it to append to the content indice of a room (a dictionary) and that dictionary is in a list, and I can't append to it.
this is the code(some of it):
room = []
room.append({'number': 1, 'content': ""})
roomnumber = 1
inv = ["sword"]
command = input(": ")
first_letter = command(0)
if first_letter == "D":
item = command.split(" ", 2)
item.remove("D")
for i in range(0, len(inv):
inv.pop(i)
#this doesn't work
` room[roomnumber]['content'].append(item[0])`
item.pop(0)
After I have entered: "D sword", it gives me this error:
Traceback (most recent call last):
File "/Users/antony/PycharmProjects/TextBased/Main.py", line 54, in <module>
room[roomnumber]['content'].append(item[0])
AttributeError: 'str' object has no attribute 'append'
I don't get it, please help !
Do you want a room to be able to contain more than one thing? If so, make your content field a list rather than a string:
room.append({'number': 1, 'content': []})
Now you can append any number of things to the content.
room = []
room.append({'number': 1, 'content': ""})
## room = [{'number': 1, 'content': ""}]
roomnumber = 1
## you should actually change this to 0. Otherwise you will get an "index out
## of range" error (see comment below)
inv = ["sword"]
command = input(": ")
first_letter = command(0)
if first_letter == "D":
item = command.split(" ", 2)
## why are you adding a max split here??
item.remove("D")
for i in range(0, len(inv)):
## equals for i in range(0, 5):, so iterates 0,1,2,3,4
## you forgot to add a closing bracket for the range
inv.pop(i)
## what are you trying to do here? This looks strange to me, given that after each
## pop, the value of your inv will be shortened..?
#this doesn't work
room[roomnumber]['content'].append(item[0])
## this does not work because your room list only contains one element, a
## dictionary, at index position 0. Probably THIS is your biggest issue here.
## Second, you're trying to change the value of 'content'. To change the value of a
## dictionarie's key, you need to use "=", and not ".append", as ".append" is used
## to append an element at the end of a list, to a list, and not to a dictionary.
## So, use room[roomnumber]['content'] = item[0]
item.pop(0)
From what I understood, you want to add a content, in function of the roomnumber, to the content value of the dictionary of the corresponding room number. In this case, your whole syntax is wrong, and you must rather use:
room = []
room.append({1:""})
## and then, after the rest of the code:
room[roomnumber-1][roomnumber] = item[0]
or, even simpler, given that the simultaneous use of lists and dictionaries is actually obsolete here
## initiate one dictionary, containing all the rooms in key = roomnumber
## value = content pairs
rooms = {}
## the syntax to add a new room number with a new content into the dictionary
## would then simply be: rooms[number] = content, e.g.:
rooms[1] = ""
## to then set the value of the content for a given roomnumber, you simply use
rooms[roomnumber] = item[0]
I recommend you to learn about the basic differences in between lists and dictionaries in python, you seem to lack some basic understanding of how elements of lists and dictionaries are accessed / modified (no offense of course)
ok thanks a lot guys :), I just wanted to say that the reason that some things are a bit weird is because that is only about 20 % of the code ( there are more rooms for example witch is why I needed to use dictionaries in a list) and my room number does start with 0 in my actual code :), the pop is to remove the item from the inventory (because this is the drop command) and I remove it from the item variable just to be safe that it doesn't cause any unwanted bugs. and otherwise, yes the content WAS supposed to be a list, I forgot, thanks for pointing it out and the for loop is actually closed it ( I was just in a bit of a rush when I wrote this ). Anyway, thanks everyone :)
I am running with some issue. I will like to view the number of times a user has delete a value from a key even if the user exits the program, it will still retain that number. So that in future if the user will to delete any values again, it will use the existing number and add on from there.
edited: Just to add. All the dict will be store on a .txt file
dict= {} #start off with an empty list
key_search = ("Enter to find a key")
if options_choose == 2:
c = input('Which value would you like to change? ')
c = change.lower()
if change in list_of_value:
loc = list_of_value.index(c)
list_of_value.remove(c)
correction = input("Enter correction: ")
correction = correction.lower()
print(f"value(s) found relating to the key '{key_search}' are:")
list_of_value.insert(loc, correction)
list_of_value = dict[key_search]
for key, value in enumerate(list_of_value, 1):
print(f"{key}.) {value}")
else:
print('Entry invalid')
As you can see in the below screenshot I have exited and re-entered the program with the same counter.
You can adapt this to fit the features of your program. Since it seems like the code you provided is incomplete, I have substituted it with an example of dictionary modification to show how you can store and read a value after program termination.
You have to have a folder with test.py and modification.value.txt inside. Write "0" inside the text file.
ex_dict= {'example_entry':1}
#Read in the value stored into the text file.
with open('modification_value.txt','r') as file:
counter = int(file.readline()) #counter value imported from file.
print('Counter: ', counter)
modify_dict = True #boolean to check when user wants to stop making changes
while modify_dict == True:
for key in ex_dict:
dict_key = key
new_value = input('Enter value for new key.\n')
ex_dict[dict_key] = new_value
counter+=1
print('New dictionary: ', ex_dict, '\n')
response = input("Do you still want to modify Y/N?\n")
if (response =='Y'):
continue
elif(response =='N'):
modify_dict=False
#Write the value of the counter recorded by the program to the text file so the program can access it when it is run again after termination.
with open('modification_value.txt','w+') as file:
file.write(str(counter))
back again with another problem. I am trying to make a "table maker" - for now just creating files with dictionaries and checking keys.
I have two problems: firstly i don't know if there is a file format for writing in binary but not overwriting a previous file if it already exists? I tried the (try, except) method but the excepted FileExistsError does never come up :/ (in the createTable function)
Secondly I have a problem with creating lists. I made a loop which asks for entries and values for them to be stored in separate lists. Those lists will later be zipped into a dictionary and pickled into a file. (in the createTable function)
Of course if there are other mistakes I'd love them pointed out :)
import pickle
def checkTable(nameOfTable) :
try :
#seeing what is it they are looking for
prompt = input("What do you want to check?\n")
with open("%s.pkl" % nameOfTable, "rb") as f:
data = pickle.load(f)
#getting what they want from the data from table
whatTheyWant = data.get(prompt, "There is nothing like that in the table.\n")
print(whatTheyWant)
#if table doesn't exist
except IOError as e:
print("Sorry such a directory doesn't exist.\n")
def createTable(nameOfYourTable) :
try :
#opens a new file with the table
with open("%s.pkl" %nameOfYourTable, "wb+") as f :
decision = "yes"
if decision == "yes" :
#asking for entries and keys to put into the table
#creates lists with entries and values to be zipped together
entry.append = input("What is the entry?\n")
value.append = input("What is the value of the entry?\n")
decision = input("Do you want to go on? (yes/no)\n")
i += 1
else :
#getting it all into a dictionary and putting it into a file
table={dict(zip(entry, value))}
pickle.dump(table, f)
#if a file with the name already exists
except FileExistsError as e :
print("Sorry, a file with this name already exists.")
#what the person wants to do
answer = input("Hello. Do you want to create a table or check an existing one?\n")
#asking for the name of the new table
if answer == "create" :
nameOfYourTable = input("What do you want the table to be called?\n")
createTable(nameOfYourTable)
#asking what table to look in
elif answer == "check" :
nameOfTable = input("What is the name of the table?\n")
checkTable(nameOfTable)
else :
print("\nThat's not a valid option.\n")
print("Thank you for using me. It was very nice.")
There is a file mode just for what you want, open(file, "x"), add b or t as per your need.
In x mode a file is created only when it does not already exist, raises exception otherwise. The createTable function does not really make sense to me. decision = "yes" then if decision == "yes":? decision is supposed to be a global? It's very fuzzy.
You need to pickle read the list from the file first, add to the list, and then write a new pickle file at the end. Btw, you can check if a file exists with os.path.exists. Appending items to a list is done with list_name.append(new_item). You need to have initialized the list first with list_name = [].
There is an error in my code :
_csv.Error: sequence expected
which i believe is because i am trying to write only one value not a list etc.
exRtFile = open ('exchangeRate.csv')
exchReader = csv.reader(exRtFile)
exchWriter = csv.writer(exRtFile)
loop2=0
while loop2==0:
selected=int(input("Please select an option: "))
if selected == 1:
change = input("What rate would you like to change: ")
changeRt = float(input("What would you like to change the rate to: "))
for row in exchReader:
currency = row[0]
if currency == change:
crntRt = row[1]
crntRt = changeRt
exchWriter.writerow(crntRt)
exRtFile.close()
what would be the best way to fix this, or is there a better wayy to change a value in an CSV file?
csv file:
Pound Sterling,1
Euro,1.22
US Dollar,1.67
Japanese Yen,169.948
Here is some code, not tested, that will do what you want. The idea is to read the text into memory, apply the updates, then write out the results over the original file.
You can further enhance this ask the user if they want to save their changes, and to add new currencies instead of just telling the user they're not known.
In the real world, I would break this code into three separate functions (or even classes), one for reading, one for writing, and one for editing the list.
import csv
rates = {}
# read file into dictionary
with open('csv_file.csv', 'r') as in_file:
rdr = csv.reader(in_file)
for item in reader:
rates[row[0]] = row[1]
# ask user for updates and apply to dictionary
while true:
cmd = raw_input('Enter exchange rate to adjust, or blank to exit')
if cmd is None or cmd.strip() == '':
break
if rates.has_key(cmd):
new_rate = float(raw_input('Enter new exchange rate:'))
rates[cmd] = new_rate
else:
print 'Currency {} is not known.'.format(cmd)
# Write the updated dictionary back over the same file.
with open('csv_file.csv', 'w') as out_file:
wrtr = csv_writer(out_file)
wrtr.writerows(rates)
Answering your question: Yes, the problem is that you were trying to write only a value, while writerow expects a list.
That said... Would you consider changing a bit the way your code works?
Here's what I've done (I've tested it now, so I know it works):
First, ask the user for all the changes to make and keep them in a dict where keys are the currency names (Euro, for instance) and the value is the new currency value (5.0, for instance) The user can get out of the loop pressing 0
Second, open and read your exchangeRate.csv file row by row. If the row[0] (name of the currency) is among the values to change, then change it in that row.
No matter what happens (regardless of whether the row needed to be changed or not) write that row in a new temporary file exchangeRate.csv.tmp
When all the rows in the original file are read, you'll have exchangeRate.csv.tmp with some rows unchanged and some rows changed. Swap (move) the .tmp file to exchangeRate.csv
Dunno... might be too much change maybe? Here it is, anyway:
import csv
import shutil
change_rates = {}
selected = 1
while selected:
selected=int(raw_input("Please select an option: (1 to change, 0 to exit)"))
if selected == 1:
change = raw_input("What rate would you like to change?: ")
changeRt = float(raw_input("What would you like to change the rate to: "))
change_rates[change] = changeRt
if len(change_rates) > 0:
with open('exchangeRate.csv', 'r') as f_in,\
open('exchangeRate.csv.tmp', 'w') as f_out:
exchReader = csv.reader(f_in)
exchWriter = csv.writer(f_out)
for row in exchReader:
if row[0] in change_rates:
row[1] = change_rates[row[0]]
exchWriter.writerow(row)
shutil.move('exchangeRate.csv.tmp', 'exchangeRate.csv')
And a sample execution below:
Please select an option: (1 to change, 0 to exit)1
What rate would you like to change?: Euro
What would you like to change the rate to: 5
Please select an option: (1 to change, 0 to exit)0
borrajax#borrajax:~/Documents/Tests$ cat ./exchangeRate.csv
Pound Sterling,1
Euro,5.0
US Dollar,1.67
Japanese Yen,169.948
You can always make more optimizations, such as... allow case insensitive searches, or check that the currency has actually been changed (like even if the user says he wants to change the currency Euro to 5.0, if that was the Euro's exchange rate then don't do anything)... Things like that.
EDIT 1:
I've just seen Larry Lustig's answer and I agree that for small files as it seems to be your case (files that you can fully load in memory) the continuous reading and writing from disk I posted is not optimal. His idea of keeping everything in memory and then do a bulk write to the same exchangeRate.csv file probably is a better fit for your needs.
EDIT 2:
To answer your questions in a comment to this answer:
what does .tmp do at the end of: exchangeRate.csv.tmp:
It's just a new name. I add the suffix .tmp to avoid a naming conflict with your original file (exchangeRate.csv). You could name it whatever you want, though (even foobar.baz)
What is the purpose of 'change' in the variable: change_rates[change] = changeRt:
change is a variable that contains the name of the currency to change (in the usage example I posted, change contains the string "Euro", because that's what the user (erm... me) typed on the console. Is just a way of accessing a dict.
What is the prupose of '[row[0]]' in: row1=change_rates[row[0]].
We agreed that when reading the file, row[0] (just like that, not [row[0]]) contains the name of the currency in the file (Euro, Pound Sterling... etcetera) right? So at a certain point of the execution row[0] will contain the string "Euro", which (in my test example) is the currency the user wanted to change. That string ("Euro") is also a key in the change_rates dictionary (because the user said he wanted to change it) so you are querying the value for the item with key "Euro" in the change_rates dictionary (which will give you 5.0). Is pretty much doing change_rates["Euro"] To see it a bit more clearer add the line print "Currencies to change: %s" % change_rates on the line right above if len(change_rates) > 0: (that'll show you how the dictionary looks like)
what does shutil.move('exchangeRate.csv.tmp', 'exchangeRate.csv') do?
It copies the file with the new currencies to exchangeRate.csv (see the shutil documentation)
I have a method that is suppose to take a search parameter and remove everything from the list that does not meet the parameter. But when it runs it removes list items at almost random. I've debugged it and it correctly determines if an item needs to be removed but it doesn't remove the right one. I think it has something to do with when I remove one item it messes up the indexes of the rest of the list, which doesn't with with my method of tracking the index.
I posted the whole class but the relevant code is towards the bottom
class StudentFinderWindow(Tkinter.Toplevel):
def __init__(self):
Tkinter.Toplevel.__init__(self) # Create Window
##### window attributes
self.title('Edit Students') #sets window title
##### puts stuff into the window
# text
editStudentInfoLabel = Tkinter.Label(self,text='Select the student from the list below or search for one in the search box provided')
editStudentInfoLabel.grid(row=0, column=0)
# entry box
self.searchRepositoryEntry = Tkinter.Entry(self)
self.searchRepositoryEntry.grid(row=1, column=0)
# list box
self.searchResults = Tkinter.Listbox(self)
self.searchResults.grid(row=2, column=0)
# search results initial updater
self.getStudentList()
for student in self.studentList:
self.searchResults.insert(Tkinter.END, student)
##### event handler
self.searchRepositoryEntry.bind('<KeyRelease>', self.updateSearch)
This is the relevant code
def updateSearch(self, event):
parameters = self.searchRepositoryEntry.get()
int = 0
currentList = self.searchResults.get(0, Tkinter.END)
length = len(parameters)
print(parameters)
print(length)
for i in currentList:
if not i[0:length] == parameters:
self.searchResults.delete(int)
print(i[0:length] == parameters)
print(i[0:length])
print(int)
int += 1
def getStudentList(self):
global fileDirectory # gets the directory that all the files are in
fileList = listdir(fileDirectory) # makes a list of files from the directory
self.studentList = [] # makes a new list
for file in fileList: # for loop that adds each item from the file list to the student list
self.studentList.append(file[:-4])
When you delete an item, everything below it moves up causing the index of all following items to change. The simplest solution to this sort of a problem (it's also common when deleting words from a text widget) is to delete backwards, starting at the end.
I think you already know the problem. When you delete an item, the index for the rest of the items change. For example, if you delete the 4th item, then the 5th item becomes the "new" 4th item. So you don't want to increment int whenever you delete an item. You can implement that with continue:
for i in currentList:
if not i[0:length] == parameters:
self.searchResults.delete(int)
continue # <-- Use continue so `int` does not increment.
int += 1
PS. It's not good coding style to use int as a variable name -- in Python it masks the built-in function of the same name.