Learn to Code via Tutorials on Repl.it!

← Back to all posts
📝 How to Make a Todo List! [Tutorial + Template]
Bookie0 (6187)

Hi!!!

Welcome to a tutorial/template on how to make a Todo List Organizer in Python! Originally for @Vandesm14's Jam (sorry for ping lol), I've modified it a bit so that it's a tad simpler and fit for a tutorial/template.


How it'll look like

Here are some snapshots:

This shall be the home page or main menu:

Making a new task:

Completing a task:


Features of the Todo List

The Todo List shall have the following features:

  • Make a new task

  • Edit tasks

  • View tasks

  • Display how many total tasks (and how many are completed/un-completed)

  • Delete tasks

  • Complete tasks

  • Un-complete tasks

  • A full box ((▣)) to indicate completedness

  • An empty box ((☐)) to indicate un-completedness


Outline of Code

First of all, let's start with a basic outline, or summary of the code.

We'll need something to hold all the tasks. At first thought, we could use a list and add all the tasks to do inside. However, we also want to assign to each task either an empty box ((☐)) or a full one ((▣)) to indiate that it is not completed or completed, respectfully. So instead of using a list, we'll use a dict, todo_name_done (with the "name" of task as key, and the "doneness", or "completedness" as value. Hence the name). This is how it'll look like:

todo_name_done = {"Task_name": "(▣)", "Task_name": "(☐)"}
# (▣) = completed, (☐) = not completed

To count and keep track of how many tasks are completed or not completed, this time we could use arrays. We shall have one list holding tasks that are completed, completed_tasks and another holding tasks that aren't completed, not_completed_tasks. Finally, there's also the variable no_tasks which we'll use to determine if the user has 0 tasks (more on that later).

Also by the way, don't worry about noting down those lists, dicts, and variable. I'll say it again below for the full tutorial/template!

So the different key features that I mentioned above will be in a function, like to view your tasks, to make a new task, to complete/un-complete a task, etc. so that we can use it several times. Then we'll also have the main_menu() function show the things you can do as well as the main input and conditionals which will determine what the user wants to do.

The very last will have a while True loop with the function calling the main menu and yea that's when everything happens.

Also, I'll be showing the code step by step, and I may sometimes have the previous step's code in the current one. I will seperate what's new and old with ### NEW ###. And when I add ..., it means I skipped a bit of the code.


The Tutorial (Finally!)

Ok, so first, like I explained above, we'l have the dicts, lists, and variable:

todo_name_done = {} # dict holding the task name as key and the square as value
# format:
# todo_name_done = {"Task_name": "(▣)", "Task_name": "(☐)"}
# (▣) = completed, (☐) = not completed

completed_tasks = [] # list holding tasks that are completed

not_completed_tasks = [] # list holding tasks that aren't completed

no_tasks = False # for conditionals later on to determine if dict is empty or not

view_tasks() Function

Next, let's start with one of the features we'll use several times; to view all your tasks.

We'll put it in a function called view_tasks():

def view_tasks(): # function holding code to output the user's task

In that function, we'll output some useful numbers, like the total number of tasks, the number of completed tasks, and the number of un-completed tasks. To find out the numbers, we shall use len() to get the length of items in the lists/dicts. This will return an int, and to concatenate it successfully in the print() statement, we convert it to str. At the end, you'll notice another print statement that will output "Your Tasks" with a few \n for new lines (for some space in between):

def view_asks(): 

### NEW ###

	# outputs total number of tasks
	print("Total number of tasks: " + str(len(todo_name_done)))
	
	# outputs total number of un-completed tasks
	print("Number of uncompleted tasks: " + str(len(not_completed_tasks)))

	# outputs total number of completed tasks
	print("Number of completed tasks: " + str(len(completed_tasks)))

	print("\n=== Your Tasks ===\n")

Now, to actually output each task, we'll have a for loop that shall loop through each item in the dict todo_name_done and output the key and it's value. We'll be using enumerate that numbers each item it loops through, starting from 0. That's why instead of a usual for i in (todo_name_done):, we'll have for n, i in enumerate(todo_name_done): The n will be the variable for enumerate, and the i shall be the variable holding the current key in the dict todo_name_done.

def view_asks(): # called to show all of the user's tasks

	# outputs total number of tasks
	print("Total number of tasks: " + str(len(todo_name_done)))
	
	# outputs total number of un-completed tasks
	print("Number of uncompleted tasks: " + str(len(not_completed_tasks)))

	# outputs total number of completed tasks
	print("Number of completed tasks: " + str(len(completed_tasks)))

	print("\n=== Your Tasks ===\n\n")

### NEW ###
        # for loop to output each task   
	for n, i in enumerate(todo_name_done): 

Next, we'll have some conditionals. Now, the conditionals are not needed because this tutorial will only give you the basic program, meaning no colors or fancy design (thought I'll link a repl for that as well). However, if you do use colors later on, that small branch of conditionals shall be useful. First, here's the code:

def view_asks(): # called to show all of the user's tasks

...

	for n, i in enumerate(todo_name_done): 

### NEW ###

		# conditionals to determine if the current task is completed or not_completed_tasks
		# if yes, it will output the task name with a different color than if no
 		if todo_name_done[i] == "(▣)":
			
# outputs the number, the value of the key (empty or full square), and then the name of the task
			print(str(n+1) + ") " + todo_name_done[i] + "  " + i) 
		
		elif todo_name_done[i] == "(☐)":
			
                        # outputs the number, the value of the key (empty or full square), and then the name of the task
			print(str(n+1) + ") " + todo_name_done[i] + "  " + i) 
	print("\n") # new line (for spacing)

Let's break it up. First, we have the if statement: if todo_name_done[i] == "(▣)":. This checks if the current task in the for loop is equal to the full square; it basically checks if that task is completed. Remember this is how our dict is shaped:

todo_name_done = {"Task_name": "(▣)", "Task_name": "(☐)"}

The task name is the key, and the empty/full square is the value. And because the for loop loops through the dict's keys, to get the value of a key you do name_of_dict[key_name]. So in this case, to get the value (the square) of a task name (the key), we do todo_name_done[i]. And i is task name that the for loop is currently on. It's the same thing with the elif, except that it checks if the task is not completed (the empty square).

Both branches of the conditionals output the same thing: print(str(n+1) + ") " + todo_name_done[i] + " " + i). The only thing is that you could insert colors inside the print statement; so for example, in the if statement, you could color that print green to indicate that the following task is completed. In the elif statement, you could color the print red to indicate that the following task is not completed. I'll be using ANSI escape codes but I'll cover how exactly to implement the colors somewhere, later.

Back on that print; using concatenation again, it first outputs the n+1 (converted into str), because remember, enumerate starts at 0, and we don't wwant the first task to be labeled as 0. Then, it outputs todo_name_done[i] which gets the value of the key (the empty/full squares). Finally, it outputs the key (the name of the task); i.

Wooh, you've done it! We can now view tasks. This is how it will look like:

You can see that we have 4 tasks, as said from the total number of tasks, and we also have 2 completed tasks and 2 un-completed tasks, again indicated above. Then, with the for loop it prints each task, numbered, with either a full square if it's completed and a empty square if it's not.

But how did those tasks end up there? And how do you make a task? That's what we shall cover next!


new_task() Function

The new_task() function will be the function holding the code for, well, making a new task! So first, let's define the function and indicate that this is to make a new task:

def new_task(): # to make a new task

	print("=== New Task ===")

Now when you make a task, the program should ask the user to enter a name for the task. For that, we'll use an input().

But say you already have this task name, what if the user makes another new task with that same name? That would kind of mess up/break the program, so we should use conditionals to determine if what the user wrote is already used. Also, we'll need a while True: loop that will loop contineously as long as the user enters a name for the task that is already used. Once they enter a new name, we'll use break to "break" the loop, or get out of it, and continue with the program. Otherwise, we'll use pass to "pass", or to continue with the loop we're in.

The expression to determine if the task name is already used will be somethign like this: if something in something:. We'll just have to replace those 2 "something"s!

This is how it'll look like:

def new_task():

	print("=== New Task ===")

### NEW ###

	while True: # loop to continue as long as user enters invalid name for task

                # asks user what the name of the new task should be. 
		new_task_name = input("New task? Let's add a name\n¬") 

		# checks if name of task is already used
		if new_task_name in todo_name_done: 
			
			print("Sorry, you already used this task name!")
			pass 

So first, we have the input(), and we store what the user says in the variable new_task_name. Then, using an if statement, we check if the name of that new task, new_task_name is already in the dict holding all the tasks, todo_name_done. If this expression evaluates to True, then the code inside that if statement shall execute; the program shall tell the user (with a print() statement) that they already have a task with the same name. Then, we use pass to kind of "pass" and carry on with the loop.

Now, let's add an else statement so that when the user enters a valid name for the task (a name not the same to any other existing tasks), they can continue with the rest of the program. In this else statement, we'll have quite a few things.

The most important parts will be actually creating the new task; meaning that we add it inside the dict as a key, and add the empty square as value. This is because when you initially create a task, the task is by default not completed yet, that's why we automatically put the value as an empty square.

The second most important thing is to add this new task to the not_completed_tasks; which, if you recall, is the list holding all the not completed tasks. We'll use append() to add it to the list.

Finally, we'll have a break, which will cause the program to exit the while True: loop and carry on with the rest of the program.

You shall all notice that I'll add some other small pieces of code; those aren't really neccessary, but make the program a bit cooler. I'll explain them a bit more in detail in the code with comments and also after this code block:

def new_task():

	print("=== New Task ===")
        
        # loop to continue as long as user enters invalid name for task
	while True: 
	
...

### NEW ###

		else: # if name of new task is really new
		
			# adds new task to the dict
			todo_name_done[new_task_name] = "(☐)" # default (☐) not done 


			# not neccessary part
			# starts here
			print("Creating...")

			sleep(0.4)

			os.system('clear') # clears console

			print("Alrighty, new task created!")
			
			sleep(1.3)
			# ends here


			not_completed_tasks.append(new_task_name) # adds, or appends that new task to the not)completed_tasks list

			break # breaks While True loop and exits to main menu

As you can see, we've added the new important parts I've talked about above, and also a few extra things:

# not neccessary part
# starts here
print("Creating...")

sleep(0.4)

os.system('clear') # clears console

print("Alrighty, new task created!")

sleep(1.3)
# ends here

First, there's a little print() statement that says "Creating...", and the program "sleeps", or waits, 0.4 seconds before continuing. Right after, with os.system('clear'), it clears the console screen. This makes a cool kind of effect. Then there's a little message of confirmation affirm that the task was created. The program waits 1.3 seconds , and then you see break at the end which makes the program exit the loop and continue with the rest of the program.

All those parts are just for design and small, cool, effects, and like I said, they are not needed. You can change how long the program waits and what the print() statement exactly says; it's completly up to you!

Great job, we finished this section!

This is how it looks like, you can write whatever name for the task you want, but...

As you can see, because we already have this task, we can't make another task that's the same!

Oh no! I actually don't want to do this task. How do I get rid of it? Onto the next section!


delete_task() Function

What if we need to delete a task that we deem to useless like homework or studying? We'll need a function to delete a task!

Let's call it delete_task() (because it's an appropriate name!), and we'll also have a print() statement with the same style as the previous functions to show what this is.

But what task to delete? What if the user can't remember what tasks they have or what they're called? That's why we will call the view_tasks() function so the user can see all their tasks:

def delete_tasks():

	print("=== Delete Task ===“)
	
	view_tasks() # calls function to view all tasks

Now, with an input(), we'll ask the user to enter the name of the task to delete. We will also use some conditionals as well to determine if that task exists or not; we don't want to delete non-existant tasks right?

We'll be using the same expression as above: if something in something:, but again, we'll replace the somethings with acutal variables. All this will also be in a while True: loop to keep on going until the user enters a valid name of task.

Then, we'll delete that task using del; and we'll select the key (the task name) from the dict (todo_name_done) and remove it.

You'll also see some not neccessary parts, like above. It's only to make the program look good. I'll indicate what's neccessary and what isn't:

def delete_tasks():

	print("=== Delete Task ===“)
	
	view_tasks() # calls function to view all tasks

### NEW ###

	while True:
                # asks user to enter task to delete 
		remove_task_name = input("\nTask too difficult? Which task do you want to delete\n¬") 

		if remove_task_name in todo_name_done: # checks if the task exists

			del todo_name_done[remove_task_name] # deletes that task name


			# not neccessary part
			# starts here
			print("Deleting...")
			sleep(0.4)

			os.system('clear')

			print("Bye! Task deleted.")

			sleep(1.3)
			# ends here

As you can see, using del, we've got key (remove_task_name) from the input(), and then used it to find it in the dict todo_name_done, and it's removed.

Now remember, we've successfully deleted that task from the dict, but not from the 2 lists completed_tasks and not_completed_tasks! So if we guess, and decide to always remove that task from say, the not_completed_tasks list. What if it's not in that list, meaning that it is completed and is instead in the completed_tasks list? The program will halt, and an error will come:

But we won't know if the task that is being deleted is completed or not. That's why we'll use try and except. What does try and except do? Well, first, you put the code that you want to execute inside the try branch. The program shall then execute that part like normal, however, if it encounters an error, then it will go to the except part. This is useful as it kind of "conceals" errors to the user and keeps the program running.

So we'll have in the try block the code that takes off the deleted task from the not_completed_tasks, then, if the program encounters an error (like if the task is already completed and is in the completed_tasks list, hence not in the previous one), we'll know the task is in the completed_tasks list and remove it from there.

At the end, we'll have break to go back to the main program:

def delete_tasks():

	print("=== Delete Task ===“)
	
	view_tasks() # calls function to view all tasks


	while True:

...

### NEW ###

			# try and except
			# to determine if task is completed or not
			# and then remove it from the corresponding list
			
			try: # if this works, it'll remove the task from the completed tasks
					 # if not, it will go on to the except

# removes the task from the completed_tasks list				completed_tasks.remove(remove_task_name) 
			
			except: # if the program encountered an error above
				
# removes the task from the not_completed_tasks list				not_completed_tasks.remove(remove_task_name) 

			break # exits back to menu

Now, we're almost done with the delete_task function; we just have to add a final else statement (that comes from:
if remove_task_name in todo_name_done: # checks if the task exists) so that the program alerts the user if they selected a task to delete that isn't found in that dict todo_name_done (meaning it doesn't exist).

def delete_tasks():

	print("=== Delete Task ===“)
	
	view_tasks() # calls function to view all tasks


	while True:

		remove_task_name = input("\nTask too difficult? Which task do you want to delete\n¬")

                # checks if the task exists
		if remove_task_name in todo_name_done: 

...

### NEW ###

		else:
			
			# alerts user task they entered doesn't exist
			print("Hum, I can't seem to find that task. Typo?) 
			pass # passes and continues with program

Nice! We've completed this section, now the user shall be able to remove their tasks! How it's going to look like when select a task to be deleted:

And this confirms we deleted the task!

Wait! What if I've made a mistake, but I don't want to delete the task? How do I edit? Let's continue!


edit_task Function

We'll use this function edit a task to rename it. This section will probably be a bit longer/complicated, but we'll do it!

Starting with the same kind of model/design as the previous functions (and we'll also add view_tasks so user can see their tasks):

def edit_task(): # out edit_task function to edit/rename tasks

	print("=== Edit Task ===")

	view_tasks() # calls function to view all tasks

Next, we'll have a while True: loop with an input() and conditionals to determine if the task the user typed is a valid one. If yes, we'll use break to get out of that loop. If the user makes a typo or writes a non-existant task name, there shall be an else statement to output an error message and return back to the input():

def edit_task(): 

	print("=== Edit Task ===")

	view_tasks() 

### NEW ###

	while True: # while True loop to contineously loop in case of error
		
		# asks user to type in the task name to edit
		task_to_edit = input("\nMade a typo? No problem! Which task name to edit?\n¬ ") 

		# conditionals
		if task_to_edit in todo_name_done: # checks if task user wants to delete exists in dict

			break # if yes, breaks loop 

		else: # if no, continues with loop 
			
			print("Oh no, I can't seem to find that task. Make sure it's the correct name!")
			pass 

Then, another while True: loop as well as another input() to ask the user what they want to change their task to. It'll use some concatenation to show the old name of the task:

# we convert task_to_edit to str to be able to use it in the input
# remember, \n is for a new line
new_task_name = input("\nChange " + str(task_to_edit) + " to\n¬ " )

After, same thing as just before; an if and else statement, but this time to check if the new name of the task is already used. If yes, we'll use pass to loop back for the user to use another name, and if it's valid (meaning the task name is not used)...we'll get to that soon!

def edit_task():

...

	while True: # the first loop

	...

	if task_to_edit in todo_name_done:
		...
	
	else:
		...

### NEW ###

	while True: # the second loop

		# new name for the task
		new_task_name = input("\nChange " + str(task_to_edit) + " to\n¬ ")

		# if user chose to edit their task to a name already used, alerts the user
		if new_task_name in todo_name_done: 
			
			print("Heads up, you already used this task name!")
			pass # and passes back to the input
		
		else: # if the task name is new
			
			# we'll get into the next part shortly!

Alright, as you can, we have 2 while True loops; just to clarify, the first one is for getting the name of the first task. The second one is for getting the new name of the task.

An uneccessary but still cool small part (outputs momentarily "Editing...", then clears the console, and outputs with concatenation the old task name as well as the new task name).

def edit_task():

...

while True: # the second loop

		...

		if new_task_name in todo_name_done: 
			...
		
		else: 

### NEW ###

			# not neccessary part
			# starts here
			print("Editing...") 
			sleep(0.4)

			os.system('clear')

			print(str(task_to_edit) + " edited to " + str(new_task_name))

			sleep(1.3)
			# ends here
	

Now, time for the actual editing part! Since the task name is the key of the dict todo_name_done; reminder, this is how it looks like:

todo_name_done = {"Task_name": "(▣)", "Task_name": "(☐)"}

So since the user will be changing the name of the task, the key of the dict, we'll first make a new item with the same value (the empty/full box) but a different key which will be new_task_name. Then, like in the delete_task function, using del, we delete the old item with the old key.

Next, we'll have to update this new name in the completed_tasks and not_completed_tasks list. Since we don't know if the task is completed or not, we'll achieve this with some for loops which will also use enumerate. In the for loop, we'll use conditionals to find the old name of the task, and then replace it with the new name of the task. Therefore, when we find it in one of the lists, say completed_tasks, we will update it there, and since that task doesn't exist in the other list, not_completed_tasks, nothing will be done there.

Finally, we'll use break to get out of the while loop.

def edit_task():

	...

	while True: # the second loop

		...

		if new_task_name in todo_name_done: 
			
			...
		
		else: 
			
			...

### NEW ###

			# making new task with different task name and same value (box)
			todo_name_done[new_task_name] = todo_name_done[task_to_edit] 

			del todo_name_done[task_to_edit] # deletes old item with old task name

			# for loops to update the task to the new name

			# loops through each item in completed_tasks
			for n, i in enumerate(completed_tasks): 
				if i == task_to_edit: # if it finds the old name of the task in that list
					
					completed_tasks[n] = new_task_name # replaces the old name with the new name

			# loops through each item in not_completed_tasks
			for n, i in enumerate(not_completed_tasks):
				if i == task_to_edit: # if it finds the old name of the task in that list
					
					not_completed_tasks[n] = new_task_name # replaces the old name with the new name

			break # goes out of the while loops

So as you can see, we are updating the name of the task both in the dict todo_name_done as well as in the lists completed_tasks and not_completed_tasks. Remember, when we use enumerate, it's to number each "loop" or each time the program goes back to the start of the for loop. n holds the variable holding the current number from enumerate, and i holds the current item in the list.

We use enumerate (which conveniently starts at 0, like lists' indexing) so that with indexing we can find the task and change it to a new name.

Woah, we've just completed the edit_task function! Good job!

This is how it looks like when you edit a task:

And if you select a task that doesn't exist to edit:

And finally if you choose to rename a task to a name already used:

Now we can make new tasks, delete them, and edit them, what else? Well, we have to complete tasks! That's the whole point of this program!


complete_task() Function

This useful function is probably one of the most important functions of the program, because, like I just said, it's the whole point of this Todo list organizer.

As ya'll know, I'll be using the same "style" as the previous functions, also calling the view_task() function so the user can see their tasks:

def complete_task(): # function holding code to complete a task

	print("=== Complete Task === ")

	view_tasks() # shows user their tasks

Now, we're going to have a while True: loop which will contain some conditional branches. The first conditionals will check if the user has any tasks; by using len() we'll check if the todo_name_done dict is empty.

After this, there'll be an input() to get the task that the user wants to complete.

Next, the second part of the conditionals where we check if the task the user wants to complete is a valid one (if it exists in the dict todo_name_done):

def complete_task():
	...

### NEW ###

	while True: # while True loop to loop on
		
		# first set of conditionals

		# checks if dict is empty
		if len(todo_name_done) == 0: 
			
			# if yes, alerts user
			print("You don't have any tasks. Create one!")
			no_tasks = True # sets this variable to True, more on this later
			
			sleep(2) # pauses program
			break # breaks loop
		
		else: # if does have task
			
			no_tasks = False # sets this variable to False, more on this later

		# input to ask user which task they want to complete
		task_to_complete = input("Hurrah! Which task did you complete?\n¬ ")

		# second set of conditionals

		# checks if task user wants to complete is a valid one
		if task_to_complete in todo_name_done: 
			
			break # breaks loop

		# if task doesn't exist
		else: 
		
			print("You sure that's the correct task name? I can't find it.") # alerts user
			pass # continues back to beginning of loop

That's quite a bunch of code at once, you can read the comments to understand more! As you can see, I've used the no_tasks variable. If you recall, we've defined it waaaay above in the beginning. We shall use it next to determine if we should complete a task or not (because if the dict is empty, there's nothing to complete).

After, we'll notify the user that we're completing the task, pause the program, and clear the console.

Now remember, when the user enters the task, we won't know if it's completed or not. That's why we'll use try and except in case the user wants to complete a task that is already completed. In the try block, we'll first "try" to make the value of the item (the task) a full square ("(▣)") to indicate that it's completed.

Then, we'll remove that task from the not_completed_tasks list (using .remove) and add it to the complete_tasks list (using .append).

Finally, we'll output a confirmation message to the user, pause the program for 2 seconds to let the user read it, and then it'll automatically exit the function.

Here's how it'll look like:

def complete_task():
	...

	while True:
		
		if len(todo_name_done) == 0: 
			
			...
		
		else: 
			
			...

		task_to_complete = input("Hurrah! Which task did you complete?\n¬ ")

		# second set of conditionals

		
		if task_to_complete in todo_name_done: 
			
			...

		else: 
			
			...

### NEW ###

	# if dict isn't empty
	if no_tasks == False:

		# notify user we're completing task, pause program, and clear console
		print("Completing...")
		sleep(0.4)

		os.system('clear')
		
		# try to execute this block of code
		# if there's an error, it will go to the except part later on
		try:
                        
                        # replaces empty square with the full square to indicate completedness
			todo_name_done[task_to_complete] = "(▣)" 
 
                        # removes task from not completed list
			not_completed_tasks.remove(task_to_complete) 

                        # and adds it in the completed list
			completed_tasks.append(task_to_complete) 

			# confirms to user task is completed
			print(str(task_to_complete) + " completed!\nWell done!")

			sleep(2) # sleeps for 2 seconds then continues back to main program

Now, for the last part of our complete_task() function, we'll have the except to join our previous try, and a final else statement (from the if no_tasks == False:).

def complete_task():
	...

	while True:
		
		if len(todo_name_done) == 0: 
			
			...
		
		else: 
			
			...

		task_to_complete = input("Hurrah! Which task did you complete?\n¬ ")

		# second set of conditionals

		
		if task_to_complete in todo_name_done: 
			
			...

		else: 
			
			...


	if no_tasks == False:

		...
		try:

			...

### NEW ###		
		
		except: # if task is already completed
			
			print("Hey, you already completed this task!")
			sleep(2) # pauses program for 2 seconds

	else:
		pass

So like I said above, if the program encounters an error in the try block (if it tries to remove the task from the not_completed_tasks list with not_completed_tasks.remove(task_to_complete)), then it will go skip directly to the except. In there, it'll output an error message and pause the program for 2 seconds to let the user read it, then will exit back to the main menu (we'll cover this soon).

Good job, we completed the complete_task() function. A few pictures:

Confirmation message when we've completed a task:

And if we try to complete a task that is already completed:

One of the final sections of our program, the un_complete_task() function to un-complete tasks! Almost done, let's continue!


un_complete_task() Function

If the user wants to change their mind about a task, or completed one by accident, we'll need a function to un-complete a task. This function will really be almost the same as the complete_task() function so we're just gonna copy paste the previous code, and with the comments I'll indicate what's different/new:

def un_complete_task():

	print("=== Un-Complete Task === ") ### DIFFERENT ###

	view_tasks() # shows user their tasks


	while True:
		
		if len(todo_name_done) == 0: 
			
			print("You have 0 tasks. Go back and start one!") ### DIFFERENT ###
			no_tasks = True
			sleep(2)
			break
		
		else: 
			
			no_tasks = False

		task_to_un_complete = input("Changed your mind? Which task did you not complete?\n¬ ") ### DIFFERENT ###


		if task_to_un_complete in todo_name_done: ### DIFFERENT ###
			
			break

		else: 
			
			print("I can't seem to find that task. Try again!") ### DIFFERENT ###
			pass


	if no_tasks == False:

		print("Un-Completing...") ### DIFFERENT ###
		sleep(0.4)
		os.system('clear')

		# try and except to see if that task is already uncompleted
		try:
			
			### DIFFERENT ###
                        # replaces full square with empty square to indicate not completed
			todo_name_done[task_to_un_complete] = "(☐)" 

			### DIFFERENT ###
                        # removes task from completed list

			completed_tasks.remove(task_to_un_complete) 
			### DIFFERENT ###
			# and adds it to not completed list
not_completed_tasks.append(task_to_un_complete) 

			### DIFFERENT ###
			print(str(task_to_un_complete) + " Un-Completed!\nMake sure to complete it!")
			
			sleep(2)
		
		except: # if task is already un_completed

			print("Wait a minute, you already un-completed this task!") ### DIFFERENT ###
			sleep(2)


	else:
		pass

As you can see, many of this code is the same as the complete_task() function. The only things that are different are the print() statements that alert the user (they're different for some 'personality' of the program).

Then, in the try block, most of the code is different; in the todo_name_done dict we replace the full square with the empty square to indicate un-completedness. Then, we remove that task from the completed_tasks list, and append it to the un_completed_tasks list. We then output a message of confirmation, pause the program for 2 seconds, and that's all.

In the except block, the code will run if the program finds an error in the above try, and if so, we'll have a print() statememt to tell the user the task is already un-completed, or not completed.

And that's it for the un_complete_task() function! A few pictures:

Un-completing a task:

And the error message if we un-complete a already un-completed task:

And yay, we're onto the last part of our progam, the main_menu() function! Almost done!


main_menu() Function

This function will hold the code for the main menu where the user can choose to do their actions. We'll first define the function, clear the console so the menu looks clean, and show the user's tasks:

def main_menu(): # main menu with different choices

	os.system('clear') # clears console
	
	view_tasks() # outputs all the user's task

Now, we're going to show the different options the user can do with a print() statement using triple double quotes ("""). Then, we'll have an input() to ask the user what they want to do (with the ¬ to prompt the user), and another os.system('clear') to clear the console.

Next, some conditionals to determine what the user wants to do, and call the appropriate function:

def main_menu(): 

	os.system('clear') 
	
	view_tasks() 

### NEW ###

	# outputs different choices 
		print("""                           
													
	(1) New Task               
	(2) Delete Task         
	(3) Edit Task           
												
	(4) Complete Task       
	(5) Un-Complete Task                    
															
	""")

	menu_choice = input("¬ ") # asks and prompts user to choose something
	
	os.system('clear') # clears console

	# conditionals to determine what user wants to do
	if menu_choice == "1": # if user wants to make a new task

		new_task()
		
	if menu_choice == "2": # if user wants to remove a task

		delete_task()

	elif menu_choice == "3": # if user wants to edit a task

		edit_task()

	elif menu_choice == "4": # if user wants to complete a task
		
		complete_task()

	elif menu_choice == "5": # if user wants un-complete a task
		
		un_complete_task

Those if and elif statements check if what the user wrote is equal to one of the numbers; where each of those numbers from 1 to 5 represent a different action.

The last bit of code!!

For the last part of our program, we'll have a while True: loop so this program goes on and on, and we'll call the main_menu() function (which in turn calls the other functions on the user's input):

while True: # for the program to go on and on

	main_menu() # calls function to show main menu page

# done! 

Woah! That's it!! You've made it Todo List Organizer in Python! You can add a bunch of other cool things to customize your program, like changiing the error message/confirmation message strings, changing the time the program pauses, etc.


Colors (optional)

You can also add colors! I won't exactly be making a tutorial, but here's a quick crash course:

# colors
Red = "\033[0;31m"
Green = "\033[0;32m"
Orange = "\033[0;33m"
Blue = "\033[0;34m"
Purple = "\033[0;35m"
Cyan = "\033[0;36m"
White = "\033[0;37m" 
black = "\033[0;30m"
black = "\033[0;90m"
red = "\033[0;91m"
green = "\033[0;92m"
yellow = "\033[0;93m"
blue = "\033[0;94m"
magenta = "\033[0;95m"
cyan = "\033[0;96m"
white = "\033[0;97m"
cyan_back="\033[0;46m"
pink_back="\033[0;45m"
white_back="\033[0;47m"
blue_back="\033[0;44m"
orange_back="\033[0;43m"
green_back="\033[0;42m"
red_back="\033[0;41m"
grey_back="\033[0;40m"
bold = "\033[1m"
underline = "\033[4m"
italic = "\033[3m"
darken = "\033[2m"
reset = "\033[0m"

# to use:

print(Red + "Hello " + cyan_back + "World" + magenta + italic + "!" + reset) 

# use reset to reset the colors back to the inital white 
# and take off any text decoration like italic, bold, underline, or darken

Output:

You can apply them for example in the main menu where we show the different options the user can do, using .format() so it's easier. Here's an example of using .format():

print("""
{gb}This has a background of grey{r}And text is back plain!

""".format(gb=grey_back,r=reset))

As you can see, in the .format(), we say that gb equals the variable grey_back, which holds the ANSI escape codes for a grey background. It's the same thing for r and reset. The reason you would want to do that is to make your life easier and not have to write the whole grey_back or reset each time.

Here's a bigger example with the main menu:

print("""
{r}{ob}                                 
{r}{ob}    {bb}                         {bb}{r}{ob}    
{r}{ob}    {bb} (1) New Task            {r}{ob}    
{r}{ob}    {bb} (2) Delete Task         {r}{ob}    
{r}{ob}    {bb} (3) Edit Task           {r}{ob}    
{r}{ob}    {bb}                         {r}{ob}    
{r}{ob}    {bb} (4) Complete Task       {r}{ob}    
{r}{ob}    {bb} (5) Un-Complete Task    {r}{ob}    
{r}{ob}    {bb}                         {r}{ob}    
{r}{ob}                                 
	{r}
""".format(bb=blue_back,ob=orange_back,r=reset))

I'm using blue_back, orange_back, and reset. You can see that there is an empty space on the first line after the print() statement ({r}{ob}); well if you highlight it, you can see that I've pressed space a lot of times so that the orange background creates a kind of wall.

This is the output:

Very pretty! You can use this for many other parts of the program and of course choose your own colors! :D


Closing

Well, that's about it! You now know how to create your own Todo List Organizer program! Hope you've enjoyed, and if you have any questions, comment them down below and I'll do my best to help you! =)

  • Have a super day!

Goodbye, and cya soon! 👋

(Pretty soon because I think I'll be posting a Typing Speed Test repl soon! xD)

PS: Video on how to make your repls more efficient and use less CPU Enjoy! ;)

Original repl with colors and everything: https://repl.it/@Bookie0/Todo-list-Jam

Comments
hotnewtop
Bookie0 (6187)

Ugh I have a monitor and it bugs me that the text of the tutorial only fits like a third of my screen, when it could be bigger!! So much blank space around it lol :P

DSAJagat20 (1)

Awesome! I made this an among us tasks list

Bookie0 (6187)

Cool! Glad you liked this! :) @DSAJagat20

LeoXu2 (40)

How long did that take XD

Bookie0 (6187)

@LeoXu2 The repl took a few days (for a jam), and the tutorial another few days lol :)

LingWu1 (91)

I love it! It had so many words that I lost track for a second, but it explains everything very well.

Bookie0 (6187)

Haha, thanks, glad you enjoyed it!! :) @LingWu1

LingWu1 (91)

U said you will be posting a Typing speed test soon? @Bookie0
That is EXACTLY what I am trying to make right now.

Bookie0 (6187)

@LingWu1 haha lol I'll soon be done hopefully lol

LingWu1 (91)

Yea, i still cant manage to make it. It has a BUNCH of errors every [email protected] and check out w3schools! Its helped me alot with coding and explanations of funtions and commands

Bookie0 (6187)

@LingWu1 um if you just follow my steps there should not be any errors. try to copy the code from the repl. also I know w3 schools lol

LingWu1 (91)

No, I mean in my OWN code, without tutorials @Bookie0

LingWu1 (91)

I am trying to make a code by myself and errors pop up. I am not using tutorials when I am making this code @Bookie0

Bookie0 (6187)

@LingWu1 ok well if you follow this tutorial, there shouldn't be an error.

What's the error?

LingWu1 (91)

Ok this is confusing @Bookie0
I am trying to make a Typing Speed Test not your code, do you get that?
So there are no errors for the tutorial

Bookie0 (6187)

@LingWu1 okay, so why are you commenting lol..?

LingWu1 (91)

Because I am apparently trying to make one @Bookie0
Errors are just very annoying. I can usually fix Syntax errors and indication errors though.

Bookie0 (6187)

@LingWu1 hum ok best of luck! :D

LingWu1 (91)

Ok we should stop commenting... @Bookie0

SudhanshuMishra (235)

aweeeessooooommmmmeeeee !

[deleted]

hi awesome project cub

SilvermoonCat (442)

this might be comlciated but maybe a db? nice tutorial i can tell u worked hard!!

Dov4k (0)

@Bookie0 How do I make the checkboxes; I know I can just copy and paste but that feels more time consuming.

Bookie0 (6187)

@Dov4k uh just copy paste, or search up ascii boxes on internet.

[deleted]

buying bread is superior to everything else

tad1wanashe (0)

Thank you for this!!! Reeally enjoyed it. If i wanted to connect this python app to a database how would I do that? And do you have any recommendations on which database to use?

Bookie0 (6187)

@tad1wanashe np, glad this helped! :D

Hum, I don't really do DBs, but you can use repl.it's DB that comes with every repl!

Bookie0 (6187)

Hm? What do you need help with? :) @TijnRheiter

IntellectualGuy (797)

How do you do checkboxes in markdown? I never knew you could do that.

CodingCactus (4208)

Originally for @Vandesm14's Jam (sorry for ping lol)

erm the @ isn't part of their name it's only there to ping them, so why would you intentionally ping them and then say sorry for pinging lol

Bookie0 (6187)

@CodingCactus 'twas just to ping them so they know about it, and also so people know there are jams going on