I want to make a to do list app with kivy/kivymd and I want to make it with multiple screens, and all it's good until I add a widget in the list and I get this error when i press on SAVE.
This is main.py
from cgitb import small
from kivymd.app import MDApp
from kivymd.uix.dialog import MDDialog
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.picker import MDDatePicker
from datetime import datetime
from kivymd.uix.list import TwoLineAvatarIconListItem, ILeftBodyTouch
from kivymd.uix.selectioncontrol import MDCheckbox
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.text import LabelBase
from kivy.utils import platform
from kivy.core.window import Window
import sqlite3
Window.size = (360, 640)
class Database:
def __init__(self):
self.con = sqlite3.connect('todo.db')
self.cursor = self.con.cursor()
self.create_task_table()
def create_task_table(self):
"""Create tasks table"""
self.cursor.execute("CREATE TABLE IF NOT EXISTS tasks(id integer PRIMARY KEY AUTOINCREMENT, task varchar(50) NOT NULL, due_date varchar(50), completed BOOLEAN NOT NULL CHECK (completed IN (0, 1)))")
self.con.commit()
def create_task(self, task, due_date=None):
"""Create a task"""
self.cursor.execute("INSERT INTO tasks(task, due_date, completed) VALUES(?, ?, ?)", (task, due_date, 0))
self.con.commit()
# GETTING THE LAST ENTERED ITEM SO WE CAN ADD IT TO THE TASK LIST
created_task = self.cursor.execute("SELECT id, task, due_date FROM tasks WHERE task = ? and completed = 0", (task,)).fetchall()
return created_task[-1]
def get_tasks(self):
"""Get tasks"""
uncomplete_tasks = self.cursor.execute("SELECT id, task, due_date FROM tasks WHERE completed = 0").fetchall()
completed_tasks = self.cursor.execute("SELECT id, task, due_date FROM tasks WHERE completed = 1").fetchall()
return completed_tasks, uncomplete_tasks
def mark_task_as_complete(self, taskid):
"""Marking tasks as complete"""
self.cursor.execute("UPDATE tasks SET completed=1 WHERE id=?", (taskid,))
self.con.commit()
def mark_task_as_incomplete(self, taskid):
"""Mark task as uncomplete"""
self.cursor.execute("UPDATE tasks SET completed=0 WHERE id=?", (taskid,))
self.con.commit()
# return the text of the task
task_text = self.cursor.execute("SELECT task FROM tasks WHERE id=?", (taskid,)).fetchall()
return task_text[0][0]
def delete_task(self, taskid):
"""Delete a task"""
self.cursor.execute("DELETE FROM tasks WHERE id=?", (taskid,))
self.con.commit()
def close_db_connection(self):
self.con.close()
db = Database()
class DialogContent(MDBoxLayout):
"""OPENS A DIALOG BOX THAT GETS THE TASK FROM THE USER"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.ids.date_text.text = str(datetime.now().strftime('%A %d %B %Y'))
def show_date_picker(self):
"""Opens the date picker"""
date_dialog = MDDatePicker()
date_dialog.bind(on_save=self.on_save)
date_dialog.open()
def on_save(self, instance, value, date_range):
"""This functions gets the date from the date picker and converts its it a
more friendly form then changes the date label on the dialog to that"""
date = value.strftime('%A %d %B %Y')
self.ids.date_text.text = str(date)
class ListItemWithCheckbox(TwoLineAvatarIconListItem):
'''Custom list item'''
def __init__(self, pk=None, **kwargs):
super().__init__(**kwargs)
# state a pk which we shall use link the list items with the database primary keys
self.pk = pk
def mark(self, check, the_list_item):
'''mark the task as complete or incomplete'''
if check.active == True:
the_list_item.text = '[s]' the_list_item.text '[/s]'
db.mark_task_as_complete(the_list_item.pk)# here
else:
the_list_item.text = str(db.mark_task_as_incomplete(the_list_item.pk))# Here
def delete_item(self, the_list_item):
'''Delete the task'''
self.parent.remove_widget(the_list_item)
db.delete_task(the_list_item.pk)# Here
class LeftCheckbox(ILeftBodyTouch, MDCheckbox):
'''Custom left container'''
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class MainApp(MDApp):
task_list_dialog = None
def build(self):
self.theme_cls.primary_palette = "Blue"
sm = ScreenManager()
sm.add_widget(Screen1(name='screen1'))
sm.add_widget(Screen2(name='screen2'))
return sm
def show_task_dialog(self):
if not self.task_list_dialog:
self.task_list_dialog = MDDialog(
title="Create Task",
type="custom",
content_cls=DialogContent(),
)
self.task_list_dialog.open()
def close_dialog(self, *args):
self.task_list_dialog.dismiss()
def on_start(self):
"""Load the saved tasks and add them to the MDList widget when the application starts"""
try:
completed_tasks, uncomplete_tasks = db.get_tasks()
if uncomplete_tasks != []:
for task in uncomplete_tasks:
add_task = ListItemWithCheckbox(pk=task[0],text=task[1], secondary_text=task[2])
self.root.ids.container.add_widget(add_task)
if completed_tasks != []:
for task in completed_tasks:
add_task = ListItemWithCheckbox(pk=task[0],text='[s]' task[1] '[/s]', secondary_text=task[2])
add_task.ids.check.active = True
self.root.ids.container.add_widget(add_task)
except Exception as e:
print(e)
pass
def add_task(self, task, task_date):
'''Add task to the list of tasks'''
# Add task to the db
created_task = db.create_task(task.text, task_date)# Here
# return the created task details and create a list item
self.root.ids['container'].add_widget(ListItemWithCheckbox(pk=created_task[0], text='[b]' created_task[1] '[/b]', secondary_text=created_task[2]))# Here
task.text = ''
if __name__ == '__main__':
app = MainApp()
app.run()
This is main.kv I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted I have to make an description for my code to be posted
#:kivy 1.10.1
<Screen1>:
MDTextButton:
text: "Next Screen"
theme_text_color: "Custom"
text_color: rgba(168, 168, 168, 225)
font_size: "50sp"
on_press: app.root.current = "screen2"
<Screen2>:
MDFloatLayout:
MDLabel:
id: task_label
halign: 'center'
markup: True
text: "My Tasks"
font_name: "Poppins-Bold"
font_size: 40
pos_hint: {'y': .45}
ScrollView:
pos_hint: {'center_y': .5, 'center_x': .5}
size_hint: .9, .8
MDList:
id: container
MDFloatingActionButton:
icon: 'plus-thick'
on_release: app.show_task_dialog()
elevation_normal: 12
pos_hint: {'x': .8, 'y':.05}
<DialogContent>:
orientation: "vertical"
spacing: "10dp"
size_hint: 1, None
height: "130dp"
GridLayout:
rows: 1
MDTextField:
id: task_text
hint_text: "Add Task..."
pos_hint: {"center_y": .4}
max_text_length: 50
on_text_validate: (app.add_task(task_text, date_text.text), app.close_dialog())
MDIconButton:
icon: 'calendar'
on_release: root.show_date_picker()
padding: '10dp'
MDLabel:
spacing: '10dp'
id: date_text
BoxLayout:
orientation: 'horizontal'
MDRaisedButton:
text: "SAVE"
on_release: (app.add_task(task_text, date_text.text), app.close_dialog())
MDFlatButton:
text: 'CANCEL'
on_release: app.close_dialog()
<ListItemWithCheckbox>:
id: the_list_item
markup: True
LeftCheckbox:
id: check
on_release:
root.mark(check, the_list_item)
IconRightWidget:
icon: 'trash-can-outline'
theme_text_color: "Custom"
text_color: 1, 0, 0, 1
on_release:
root.delete_item(the_list_item)
CodePudding user response:
In your method add_task
you did self.root.ids['container']
which suggests that you want to access the id (from kvlang
) named container
from you root widget (here ScreenManager
).
But it's evident that this root has no ids assigned. What it appears is that you want to access the id container
from your Screen2
. In order to achieve that you can go as,
def add_task(self, task, task_date):
'''Add task to the list of tasks'''
# Add task to the db
created_task = db.create_task(task.text, task_date)# Here
# return the created task details and create a list item
# Access the relevant screen first.
screen2 = self.root.get_screen("screen2")
# Now access the `MDList` and add the items.
screen2.ids['container'].add_widget(ListItemWithCheckbox(pk=created_task[0], text='[b]' created_task[1] '[/b]', secondary_text=created_task[2]))# Here
task.text = ''