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.