I'm creating an app to store tasks. However, I can't find a working solution that stores more than one task. This is the code I'm running that you should need:
def newtaskwin():
newtaskwin=Toplevel(todohome)
newtaskwin.geometry()
taskbxfrme=tk.Frame(newtaskwin)
TaskNameLbl=tk.Label(newtaskwin, text='Task Name:').grid(row=1, column=0, pady=5)
TaskNameEntry=ttk.Entry(newtaskwin, width=100)
TaskNameEntry.grid(row=1, column=1, pady=5)
GroupNameLbl=tk.Label(newtaskwin, text='Group:').grid(row=2, column=0, pady=5)
GroupNameEntry=ttk.Entry(newtaskwin, width=100)
GroupNameEntry.grid(row=2, column=1, pady=5)
PriorityLbl=tk.Label(newtaskwin, text='Priority:').grid(row=3, column=0, pady=5)
PriorityEntry=ttk.Entry(newtaskwin, width=100)
PriorityEntry.grid(row=3, column=1, pady=5)
start_date_lbl=tk.Label(newtaskwin, text='Start Date:').grid(row=4, column=0, pady=5)
start_date_entry=ttk.Entry(newtaskwin, width=100)
start_date_entry.grid(row=4, column=1, pady=5)
end_date_lbl=tk.Label(newtaskwin, text='End Date:').grid(row=5, column=0, pady=5)
end_date_entry=ttk.Entry(newtaskwin, width=100)
end_date_entry.grid(row=5, column=1, pady=5)
start_time_lbl=tk.Label(newtaskwin, text='Start Time:').grid(row=6, column=0, pady=5)
start_time_entry=ttk.Entry(newtaskwin, width=100)
start_time_entry.grid(row=6, column=1, pady=5)
end_time_lbl=tk.Label(newtaskwin, text='End Time:').grid(row=7, column=0, pady=5)
end_time_entry=ttk.Entry(newtaskwin, width=100)
end_time_entry.grid(row=7, column=1, pady=5)
def create_task():
with dbm.open('taskstorage.db' , 'n') as taskdb:
taskdb['name']=TaskNameEntry.get()
taskdb['Group']=GroupNameEntry.get()
taskdb['Priority']=PriorityEntry.get()
taskdb['Start_Date']=start_date_entry.get()
taskdb['End_Date']=end_date_entry.get()
taskdb['Start_Time']=start_time_entry.get()
taskdb['end_time']=end_time_entry.get()
messagebox.showinfo('eTasks', 'Task saved')
create_task_btn=tk.Button(newtaskwin, command=create_task, width=10, text='Save').grid(row=9, column=0)
I tried using json and dbm; json didn't allow me to store the dictionary with variables, dbm overwrote the previous data and json didn't dump the data because of the immutable and mutable data type rules in dictionaries in python.
JSON files cannot be appended to [afaik] without loading the previous data and then updating with new data before saving again [so, the file will be over-written, but since the old data was loaded, it will be saved again but with the addition of the new data]. You could have a function for loading lists from JSON files - something like
# import json
def list_fromJsonFile(filename, printWarnings=False):
try:
with open(filename, 'r') as f: retList = json.load(f)
except Exception as e:
retList = []
if printWarnings: print('Failed to load', repr(e))
if not isinstance(retList, list):
if printWarnings: print('Returning as', [{'previous_data': type(retList)}])
retList = [{'previous_data': retList}]
return retList
With that, I don't see why this should cause any issues [in terms of JSON handling]:
def create_task():
storageFilePath = 'taskstorage.json' # path to storage
tasksList = list_fromJsonFile(storageFilePath) # load previous data
taskDict = {
'name': TaskNameEntry.get()
'Group': GroupNameEntry.get()
'Priority': PriorityEntry.get()
'Start_Date': start_date_entry.get()
'End_Date': end_date_entry.get()
'Start_Time': start_time_entry.get()
'end_time': end_time_entry.get()
}
with open(storageFilePath, 'w') as jsonfile:
json.dump(
tasksList+[taskDict], jsonfile, indent=4,
default=lambda o: getattr(o, '__dict__', str(o))
)
messagebox.showinfo('eTasks', 'Task saved')
[ As far as I'm aware, the values returned by Entry.get()
are JSON-serializable, but still I passed default=lambda o: getattr(o, '__dict__', str(o))
to .dump
just in case, so that any non-serializable values are converted to dictionaries (or, if that's not possible, to string).]
Since you're saving tasksList+[taskDict]
, you should not be losing any of the old data; and then, if you want to
be able to call each task separately by name
You can load the saved list and convert that list to a [nested] dictionary keyed to name
.
tasks_list = list_fromJsonFile('taskstorage.json', printWarnings=True)
tasks_by_name = {t['name']: t for t in tasks_list if isinstance(t,dict) and 'name' in t}
Note that if there are any duplicate name
s, only the last task t
of each name
will remain in tasks_by_name
.
You can loop through the task dictionaries with for task in tasks_list
, or get a task (named task_name='task_1'
, for example) with tasks_by_name[task_name]
, or tasks_by_name['task_1']
, or [if you're not sure that there's a task with that name] tasks_by_name.get(task_name)
.
If you don't want to have to load previous data every time you want to add a new task, I suggest using CSV files for storage instead.
# import os
# import pandas as pd
def create_task():
storageFilePath = 'taskstorage.csv' # path to storage
# taskDict = {...} ## AS BEFORE
wHed = not os.path.isfile(storageFilePath)
pd.DataFrame([taskDict]).to_csv(storageFilePath,mode='a',index=False,header=wHed)
messagebox.showinfo('eTasks',f'Task saved to {os.path.abspath(storageFilePath)}')
[ Passing mode='a'
appends to file instead of over-writing old data, and the wHed
flag is for adding the headers row if a new file is created. ]
In this case, a function like list_fromJsonFile
is no longer needed as the tasks can be loaded with .read_csv
.
# tasks_by_name = pd.read_csv('taskstorage.csv').set_index('name').to_dict('index')#maybe
tasks_list = pd.read_csv('taskstorage.csv').to_dict('records')
tasks_by_name = {t['name']: t for t in tasks_list if isinstance(t,dict) and 'name' in t}
dbm overwrote the previous data
From what I understand, you can only have one instance of each key in a dbm file. You could make the name part of the keys to keep from overwriting other tasks [as long as the names are unique].
def create_task():
with dbm.open('taskstorage.db' , 'c') as taskdb:
tName = TaskNameEntry.get()
taskdb[f'{tName} name']=tName
taskdb[f'{tName} Group']=GroupNameEntry.get()
taskdb[f'{tName} Priority']=PriorityEntry.get()
taskdb[f'{tName} Start_Date']=start_date_entry.get()
taskdb[f'{tName} End_Date']=end_date_entry.get()
taskdb[f'{tName} Start_Time']=start_time_entry.get()
taskdb[f'{tName} end_time']=end_time_entry.get()
messagebox.showinfo('eTasks', 'Task saved')