Make Mobile App using Python Part3

To Do List App

Posted by Afsal on 09-Jun-2023

Hi Pythonistas!

Today will make a very simple to do list app using Kivy. If you are new to Kivy please refer the part1, part2 and SQLite operation with Python . Let us dive into the code.

Creating database and table

conn = sqlite3.connect('todo.db')
c = conn.cursor()

c.execute('''CREATE TABLE IF NOT EXISTS tasks
          (id INTEGER PRIMARY KEY AUTOINCREMENT, task text)''')

conn.commit()

This will creates a database with name todo.db and table task with columns with name id and task.

Create a basic layout

def build(self):

     layout = BoxLayout(orientation='vertical', spacing=10, padding=40)   
     self.task_input = TextInput(hint_text='Enter a task', size_hint=(1, None), height=100)
     layout.add_widget(self.task_input)

     add_button = Button(text='Add Task', size_hint=(1, None), height=60)
     add_button.bind(on_press=self.add_task)
     layout.add_widget(add_button)

     self.task_list = BoxLayout(orientation='vertical', spacing=10, size_hint=(1, None))
     self.task_list.bind(minimum_height=self.task_list.setter('height'))
     scroll_view = ScrollView()
     scroll_view.add_widget(self.task_list)

     layout.add_widget(scroll_view)
     self.update_task_list()
     return layout

Here we add a text input and submit button in BoxLayout. Then below the add button we have added a scroll view to list the existing tasks. We have binded add_task on button click and also called update_task_list to show the existing task on the scroll view now we can see what add_task and update_task_list do

Add Task function

def add_task(self, instance):
     task = self.task_input.text
     if task:
             c.execute("INSERT INTO tasks (task) VALUES (?)", (task,))
             conn.commit()
         self.task_input.text = ''
         self.update_task_list()

Add task get the text from the input box and insert this into database. Then it clears the input box and call update_task_list

Updating the scroll view content

class IconButton(ButtonBehavior, Image):
    pass

def update_task_list(self):
     self.task_list.clear_widgets()
     c.execute("SELECT id, task FROM tasks")
     tasks = c.fetchall()
     for task in tasks:
             task_label = Label(text=task[1], size_hint=(1, None), height=40)
             delete_button = IconButton(source='delete.png')
             delete_button.task_id = task[0]  # Set task_id as an attribute
             delete_button.bind(on_press=self.delete_task)

             task_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
             task_layout.add_widget(task_label)
             task_layout.add_widget(delete_button)
             self.task_list.add_widget(task_layout)

This will clears the scroll view then fetch all the saved item from the database. Then adding each row as a box layout with text and delete button. IconButton is class derived from ButtonBehavior and Image this will enable us to show delete icon with clickable action. Source of the button image will be delete.png. We have binded the delete_task with the delete button.

Now we can check what delete_task function looks like

Deleting a task

def delete_task(self, instance):
     task_id = instance.task_id
     c.execute("DELETE FROM tasks WHERE id=?", (task_id,))
     conn.commit()
     self.update_task_list()

In this function we are getting the task_id from the button and delete it from database and then call the update_task_list to show updated list.

Run the App

if __name__ == '__main__':
    TodoListApp().run()

conn.close()

It run the app. When it is exited then db close is called.

Complete Code

import sqlite3
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.textinput import TextInput
from kivy.core.window import Window
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image

conn = sqlite3.connect('todo.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS tasks
          (id INTEGER PRIMARY KEY AUTOINCREMENT, task text)''')
conn.commit()

class IconButton(ButtonBehavior, Image):
    pass

class TodoListApp(App):
    def build(self):
        layout = BoxLayout(orientation='vertical', spacing=10, padding=40)
        self.task_input = TextInput(hint_text='Enter a task', size_hint=(1, None), height=100)
        layout.add_widget(self.task_input)
        add_button = Button(text='Add Task', size_hint=(1, None), height=60)
        add_button.bind(on_press=self.add_task)
        layout.add_widget(add_button)
        self.task_list = BoxLayout(orientation='vertical', spacing=10, size_hint=(1, None))
        self.task_list.bind(minimum_height=self.task_list.setter('height'))
        scroll_view = ScrollView()
        scroll_view.add_widget(self.task_list)
        layout.add_widget(scroll_view)
        self.update_task_list()
        return layout

    def add_task(self, instance):
        task = self.task_input.text
        if task:
            c.execute("INSERT INTO tasks (task) VALUES (?)", (task,))
        conn.commit()
        self.task_input.text = ''
        self.update_task_list()

    def delete_task(self, instance):
        task_id = instance.task_id
        c.execute("DELETE FROM tasks WHERE id=?", (task_id,))
        conn.commit()
        self.update_task_list()

    def update_task_list(self):

        self.task_list.clear_widgets()
        c.execute("SELECT id, task FROM tasks")
        tasks = c.fetchall()
        for task in tasks:
            task_label = Label(text=task[1], size_hint=(1, None), height=40)
            delete_button = IconButton(source='delete.png')
            delete_button.task_id = task[0]  # Set task_id as an attribute
            delete_button.bind(on_press=self.delete_task)
            task_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
            task_layout.add_widget(task_label)
            task_layout.add_widget(delete_button)
            self.task_list.add_widget(task_layout)

if __name__ == '__main__':
    TodoListApp().run()

conn.close()

Ouput

Hope you have learned something from this post. Please share you suggestions with afsal@parseltongue.co.in. In the upcoming post we will learn how to make this python script into an Android APK.