بناء تطبيق مهام كامل بلغة Python — مع قاعدة بيانات
دليل عملي لبناء تطبيق إدارة مهام احترافي بلغة Python — مع قاعدة بيانات SQLite وواجهة رسومية Tkinter
بعد أن بنينا آلة حاسبة احترافية، حان الوقت لمشروع أكبر: تطبيق إدارة مهام كامل.
هذا المشروع سيعلمك:
- التعامل مع قاعدة بيانات حقيقية (SQLite)
- بناء واجهة رسومية متعددة النوافذ
- إدارة البيانات (إضافة، تعديل، حذف)
- تصدير البيانات إلى ملفات
- تنظيم الكود في ملفات متعددة
بنهاية هذا المقال، سيكون لديك تطبيق مهام كامل يمكنك استخدامه يومياً.
1. التخطيط للمشروع
المتطلبات
قبل كتابة أي كود، دعنا نحدد ماذا نريد:
| الميزة | الوصف |
|---|---|
| إضافة مهمة | عنوان، وصف، أولوية، تاريخ استحقاق |
| عرض المهام | قائمة بكل المهام مع حالتها |
| تعديل مهمة | تعديل أي حقل من حقول المهمة |
| حذف مهمة | حذف مهمة نهائياً |
| إكمال مهمة | وضع علامة "مكتملة" |
| فلترة | الكل، النشطة، المكتملة |
| تصدير | تصدير المهام إلى CSV |
| قاعدة بيانات | حفظ دائم للبيانات |
هيكل قاعدة البيانات
سنحتاج جدول واحد فقط للمهام:
| العمود | النوع | الوصف |
|---|---|---|
id | INTEGER | معرّف فريد (تلقائي) |
title | TEXT | عنوان المهمة |
description | TEXT | وصف تفصيلي |
priority | TEXT | الأولوية (عالية/متوسطة/منخفضة) |
due_date | TEXT | تاريخ الاستحقاق |
completed | INTEGER | 0 أو 1 (منجزة أم لا) |
created_at | TEXT | تاريخ الإنشاء |
هيكل المشروع
سنقسم المشروع إلى ملفات:
task_manager/
├── main.py # نقطة البداية
├── database.py # إدارة قاعدة البيانات
├── app.py # واجهة التطبيق
└── utils.py # دوال مساعدة
2. ملف قاعدة البيانات — database.py
هذا الملف يتعامل مع قاعدة بيانات SQLite:
"""
database.py
إدارة قاعدة بيانات تطبيق المهام
"""
import sqlite3
from datetime import datetime
from typing import List, Dict, Optional
class TaskDatabase:
"""فئة للتعامل مع قاعدة بيانات المهام"""
def __init__(self, db_path: str = "tasks.db"):
"""تهيئة قاعدة البيانات"""
self.db_path = db_path
self.init_database()
def get_connection(self):
"""الحصول على اتصال بقاعدة البيانات"""
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row # للوصول للأعمدة بالأسماء
return conn
def init_database(self):
"""إنشاء الجداول إذا لم تكن موجودة"""
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT DEFAULT '',
priority TEXT DEFAULT 'متوسطة',
due_date TEXT DEFAULT '',
completed INTEGER DEFAULT 0,
created_at TEXT NOT NULL
)
""")
conn.commit()
conn.close()
def add_task(
self,
title: str,
description: str = "",
priority: str = "متوسطة",
due_date: str = ""
) -> int:
"""إضافة مهمة جديدة وإرجاع ID"""
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("""
INSERT INTO tasks (title, description, priority, due_date, created_at)
VALUES (?, ?, ?, ?, ?)
""", (
title,
description,
priority,
due_date,
datetime.now().strftime("%Y-%m-%d %H:%M")
))
task_id = cursor.lastrowid
conn.commit()
conn.close()
return task_id
def get_all_tasks(self) -> List[Dict]:
"""الحصول على كل المهام"""
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM tasks ORDER BY completed ASC, id DESC")
rows = cursor.fetchall()
conn.close()
return [dict(row) for row in rows]
def get_task(self, task_id: int) -> Optional[Dict]:
"""الحصول على مهمة واحدة"""
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM tasks WHERE id = ?", (task_id,))
row = cursor.fetchone()
conn.close()
return dict(row) if row else None
def update_task(self, task_id: int, **kwargs) -> bool:
"""تحديث مهمة"""
if not kwargs:
return False
# بناء استعلام التحديث
fields = []
values = []
for key, value in kwargs.items():
fields.append(f"{key} = ?")
values.append(value)
values.append(task_id)
query = f"UPDATE tasks SET {', '.join(fields)} WHERE id = ?"
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute(query, values)
affected = cursor.rowcount
conn.commit()
conn.close()
return affected > 0
def delete_task(self, task_id: int) -> bool:
"""حذف مهمة"""
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM tasks WHERE id = ?", (task_id,))
affected = cursor.rowcount
conn.commit()
conn.close()
return affected > 0
def toggle_complete(self, task_id: int) -> bool:
"""تبديل حالة الإكمال"""
task = self.get_task(task_id)
if not task:
return False
new_status = 0 if task["completed"] else 1
return self.update_task(task_id, completed=new_status)
def get_statistics(self) -> Dict:
"""إحصائيات المهام"""
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM tasks")
total = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM tasks WHERE completed = 1")
completed = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM tasks WHERE completed = 0")
active = total - completed
cursor.execute("SELECT COUNT(*) FROM tasks WHERE priority = 'عالية'")
high_priority = cursor.fetchone()[0]
conn.close()
return {
"total": total,
"completed": completed,
"active": active,
"high_priority": high_priority,
}
def export_to_csv(self, file_path: str) -> bool:
"""تصدير المهام إلى ملف CSV"""
import csv
tasks = self.get_all_tasks()
if not tasks:
return False
try:
with open(file_path, "w", encoding="utf-8-sig", newline="") as f:
writer = csv.DictWriter(f, fieldnames=tasks[0].keys())
writer.writeheader()
writer.writerows(tasks)
return True
except Exception as e:
print(f"خطأ في التصدير: {e}")
return False
شرح الأجزاء الأساسية
| الدالة | الوظيفة |
|---|---|
init_database() | إنشاء جدول المهام إذا لم يكن موجوداً |
add_task() | إضافة مهمة جديدة وإرجاع رقمها |
get_all_tasks() | جلب كل المهام مرتبة |
update_task() | تحديث أي حقل في المهمة |
delete_task() | حذف مهمة نهائياً |
toggle_complete() | تبديل حالة "منجزة" |
get_statistics() | إحصائيات للعرض في الواجهة |
export_to_csv() | تصدير المهام لملف Excel |
3. الواجهة الرسومية — app.py
هذا الملف يحتوي على واجهة Tkinter:
"""
app.py
الواجهة الرسومية لتطبيق المهام
"""
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from database import TaskDatabase
class TaskManagerApp:
"""تطبيق إدارة المهام"""
def __init__(self, root):
"""تهيئة التطبيق"""
self.root = root
self.root.title("📝 مدير المهام | NahdaAI")
self.root.geometry("900x650")
self.root.configure(bg="#f0f2f5")
self.db = TaskDatabase()
self.current_filter = "الكل"
self.selected_task_id = None
# تحسين المظهر
self.setup_styles()
# بناء الواجهة
self.create_header()
self.create_toolbar()
self.create_tasks_area()
self.create_footer()
# تحميل المهام
self.load_tasks()
def setup_styles(self):
"""إعداد الأنماط"""
style = ttk.Style()
style.theme_use("clam")
style.configure(
"Task.Treeview",
background="white",
fieldbackground="white",
rowheight=35,
font=("Arial", 11),
)
style.configure(
"Task.Treeview.Heading",
background="#2563eb",
foreground="white",
font=("Arial", 12, "bold"),
borderwidth=0,
)
style.map(
"Task.Treeview.Heading",
background=[("active", "#1e40af")]
)
def create_header(self):
"""إنشاء الهيدر"""
header = tk.Frame(self.root, bg="#2563eb", height=80)
header.pack(fill="x")
header.pack_propagate(False)
title = tk.Label(
header,
text="📝 مدير المهام",
font=("Arial", 24, "bold"),
bg="#2563eb",
fg="white",
)
title.pack(side="right", padx=30, pady=20)
self.stats_label = tk.Label(
header,
text="",
font=("Arial", 12),
bg="#2563eb",
fg="white",
)
self.stats_label.pack(side="left", padx=30, pady=20)
def create_toolbar(self):
"""إنشاء شريط الأدوات"""
toolbar = tk.Frame(self.root, bg="#f0f2f5", pady=15)
toolbar.pack(fill="x", padx=20)
# زر إضافة مهمة
add_btn = tk.Button(
toolbar,
text="➕ مهمة جديدة",
font=("Arial", 12, "bold"),
bg="#10b981",
fg="white",
activebackground="#059669",
activeforeground="white",
border=0,
padx=20,
pady=10,
cursor="hand2",
command=self.open_add_dialog,
)
add_btn.pack(side="right", padx=5)
# زر تصدير
export_btn = tk.Button(
toolbar,
text="📥 تصدير CSV",
font=("Arial", 12),
bg="#6366f1",
fg="white",
activebackground="#4f46e5",
activeforeground="white",
border=0,
padx=20,
pady=10,
cursor="hand2",
command=self.export_tasks,
)
export_btn.pack(side="right", padx=5)
# فلتر
filter_frame = tk.Frame(toolbar, bg="#f0f2f5")
filter_frame.pack(side="left")
tk.Label(
filter_frame,
text="عرض:",
font=("Arial", 11),
bg="#f0f2f5",
).pack(side="right", padx=5)
self.filter_var = tk.StringVar(value="الكل")
filter_menu = ttk.Combobox(
filter_frame,
textvariable=self.filter_var,
values=["الكل", "النشطة", "المكتملة"],
state="readonly",
font=("Arial", 11),
width=12,
)
filter_menu.pack(side="right")
filter_menu.bind("<<ComboboxSelected>>", lambda e: self.load_tasks())
def create_tasks_area(self):
"""إنشاء منطقة عرض المهام"""
container = tk.Frame(self.root, bg="#f0f2f5")
container.pack(fill="both", expand=True, padx=20, pady=10)
# Treeview لعرض المهام
columns = ("id", "title", "priority", "due_date", "status")
self.tree = ttk.Treeview(
container,
columns=columns,
show="headings",
style="Task.Treeview",
selectmode="browse",
)
# رؤوس الأعمدة
self.tree.heading("id", text="ID")
self.tree.heading("title", text="المهمة")
self.tree.heading("priority", text="الأولوية")
self.tree.heading("due_date", text="تاريخ الاستحقاق")
self.tree.heading("status", text="الحالة")
# عرض الأعمدة
self.tree.column("id", width=50, anchor="center")
self.tree.column("title", width=350, anchor="e")
self.tree.column("priority", width=100, anchor="center")
self.tree.column("due_date", width=150, anchor="center")
self.tree.column("status", width=100, anchor="center")
# شريط التمرير
scrollbar = ttk.Scrollbar(container, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
self.tree.pack(side="right", fill="both", expand=True)
scrollbar.pack(side="left", fill="y")
# أزرار التحكم
controls = tk.Frame(self.root, bg="#f0f2f5", pady=10)
controls.pack(fill="x", padx=20)
buttons = [
("✏️ تعديل", "#f59e0b", self.edit_task),
("✅ تبديل الحالة", "#10b981", self.toggle_complete),
("🗑️ حذف", "#ef4444", self.delete_task),
]
for text, color, command in buttons:
btn = tk.Button(
controls,
text=text,
font=("Arial", 11),
bg=color,
fg="white",
activebackground=color,
activeforeground="white",
border=0,
padx=20,
pady=8,
cursor="hand2",
command=command,
)
btn.pack(side="right", padx=5)
# ربط النقر المزدوج
self.tree.bind("<Double-1>", lambda e: self.edit_task())
def create_footer(self):
"""إنشاء الفوتر"""
footer = tk.Frame(self.root, bg="#1a1a1a", height=40)
footer.pack(fill="x", side="bottom")
footer.pack_propagate(False)
tk.Label(
footer,
text="© 2026 NahdaAI — مدير المهام بلغة Python",
font=("Arial", 10),
bg="#1a1a1a",
fg="#888",
).pack(pady=10)
def load_tasks(self):
"""تحميل المهام وعرضها"""
# حذف القديم
for item in self.tree.get_children():
self.tree.delete(item)
# جلب المهام
tasks = self.db.get_all_tasks()
# الفلترة
filter_type = self.filter_var.get()
if filter_type == "النشطة":
tasks = [t for t in tasks if not t["completed"]]
elif filter_type == "المكتملة":
tasks = [t for t in tasks if t["completed"]]
# عرض المهام
for task in tasks:
status = "✅ مكتملة" if task["completed"] else "⏳ نشطة"
priority_symbol = {
"عالية": "🔴 عالية",
"متوسطة": "🟡 متوسطة",
"منخفضة": "🟢 منخفضة",
}.get(task["priority"], task["priority"])
self.tree.insert(
"",
"end",
values=(
task["id"],
task["title"],
priority_symbol,
task["due_date"] or "—",
status,
),
)
# تحديث الإحصائيات
self.update_stats()
def update_stats(self):
"""تحديث الإحصائيات"""
stats = self.db.get_statistics()
text = f"📊 المجموع: {stats['total']} | نشطة: {stats['active']} | مكتملة: {stats['completed']}"
self.stats_label.config(text=text)
def get_selected_task_id(self):
"""الحصول على ID المهمة المحددة"""
selection = self.tree.selection()
if not selection:
messagebox.showwarning("تنبيه", "الرجاء اختيار مهمة أولاً")
return None
values = self.tree.item(selection[0], "values")
return int(values[0])
def open_add_dialog(self):
"""فتح نافذة إضافة مهمة"""
TaskDialog(self.root, self.db, on_save=self.load_tasks)
def edit_task(self):
"""تعديل مهمة"""
task_id = self.get_selected_task_id()
if not task_id:
return
task = self.db.get_task(task_id)
if task:
TaskDialog(self.root, self.db, task=task, on_save=self.load_tasks)
def toggle_complete(self):
"""تبديل حالة الإكمال"""
task_id = self.get_selected_task_id()
if not task_id:
return
if self.db.toggle_complete(task_id):
self.load_tasks()
def delete_task(self):
"""حذف مهمة"""
task_id = self.get_selected_task_id()
if not task_id:
return
task = self.db.get_task(task_id)
if not task:
return
answer = messagebox.askyesno(
"تأكيد الحذف",
f"هل أنت متأكد من حذف المهمة:\n\n'{task['title']}'؟"
)
if answer:
if self.db.delete_task(task_id):
self.load_tasks()
def export_tasks(self):
"""تصدير المهام"""
file_path = filedialog.asksaveasfilename(
defaultextension=".csv",
filetypes=[("CSV Files", "*.csv")],
initialfile="tasks.csv",
)
if file_path:
if self.db.export_to_csv(file_path):
messagebox.showinfo("نجاح", f"تم التصدير بنجاح إلى:\n{file_path}")
else:
messagebox.showerror("خطأ", "لا توجد مهام للتصدير")
class TaskDialog:
"""نافذة إضافة/تعديل مهمة"""
def __init__(self, parent, db, task=None, on_save=None):
self.db = db
self.task = task
self.on_save = on_save
self.dialog = tk.Toplevel(parent)
self.dialog.title("تعديل مهمة" if task else "مهمة جديدة")
self.dialog.geometry("500x450")
self.dialog.configure(bg="#f0f2f5")
self.dialog.transient(parent)
self.dialog.grab_set()
self.create_widgets()
if task:
self.load_task_data()
def create_widgets(self):
"""إنشاء عناصر النافذة"""
# العنوان
tk.Label(
self.dialog,
text="عنوان المهمة *",
font=("Arial", 11, "bold"),
bg="#f0f2f5",
).pack(anchor="e", padx=30, pady=(20, 5))
self.title_entry = tk.Entry(
self.dialog,
font=("Arial", 12),
justify="right",
)
self.title_entry.pack(fill="x", padx=30, ipady=8)
# الوصف
tk.Label(
self.dialog,
text="الوصف",
font=("Arial", 11, "bold"),
bg="#f0f2f5",
).pack(anchor="e", padx=30, pady=(15, 5))
self.desc_text = tk.Text(
self.dialog,
font=("Arial", 11),
height=5,
wrap="word",
)
self.desc_text.pack(fill="x", padx=30)
# الأولوية
tk.Label(
self.dialog,
text="الأولوية",
font=("Arial", 11, "bold"),
bg="#f0f2f5",
).pack(anchor="e", padx=30, pady=(15, 5))
self.priority_var = tk.StringVar(value="متوسطة")
priority_frame = tk.Frame(self.dialog, bg="#f0f2f5")
priority_frame.pack(fill="x", padx=30)
for priority in ["عالية", "متوسطة", "منخفضة"]:
tk.Radiobutton(
priority_frame,
text=priority,
variable=self.priority_var,
value=priority,
font=("Arial", 11),
bg="#f0f2f5",
activebackground="#f0f2f5",
).pack(side="right", padx=10)
# تاريخ الاستحقاق
tk.Label(
self.dialog,
text="تاريخ الاستحقاق (YYYY-MM-DD)",
font=("Arial", 11, "bold"),
bg="#f0f2f5",
).pack(anchor="e", padx=30, pady=(15, 5))
self.date_entry = tk.Entry(
self.dialog,
font=("Arial", 11),
justify="right",
)
self.date_entry.pack(fill="x", padx=30, ipady=6)
# الأزرار
buttons = tk.Frame(self.dialog, bg="#f0f2f5")
buttons.pack(fill="x", padx=30, pady=25)
tk.Button(
buttons,
text="حفظ",
font=("Arial", 12, "bold"),
bg="#10b981",
fg="white",
border=0,
padx=30,
pady=10,
cursor="hand2",
command=self.save,
).pack(side="right", padx=5)
tk.Button(
buttons,
text="إلغاء",
font=("Arial", 12),
bg="#94a3b8",
fg="white",
border=0,
padx=30,
pady=10,
cursor="hand2",
command=self.dialog.destroy,
).pack(side="right", padx=5)
def load_task_data(self):
"""تحميل بيانات المهمة للتعديل"""
self.title_entry.insert(0, self.task["title"])
self.desc_text.insert("1.0", self.task["description"])
self.priority_var.set(self.task["priority"])
self.date_entry.insert(0, self.task["due_date"])
def save(self):
"""حفظ المهمة"""
title = self.title_entry.get().strip()
if not title:
messagebox.showwarning("تنبيه", "عنوان المهمة مطلوب")
return
description = self.desc_text.get("1.0", "end-1c").strip()
priority = self.priority_var.get()
due_date = self.date_entry.get().strip()
if self.task:
# تعديل
self.db.update_task(
self.task["id"],
title=title,
description=description,
priority=priority,
due_date=due_date,
)
else:
# إضافة جديدة
self.db.add_task(title, description, priority, due_date)
if self.on_save:
self.on_save()
self.dialog.destroy()
4. نقطة البداية — main.py
هذا الملف يشغّل التطبيق:
"""
main.py
نقطة بداية تطبيق المهام
"""
import tkinter as tk
from app import TaskManagerApp
def main():
"""تشغيل التطبيق"""
root = tk.Tk()
app = TaskManagerApp(root)
root.mainloop()
if __name__ == "__main__":
main()
5. تشغيل التطبيق
الخطوة 1: احفظ الملفات
احفظ الملفات الأربعة في مجلد task_manager/:
task_manager/
├── main.py
├── database.py
├── app.py
└── utils.py (اختياري)
الخطوة 2: شغّل التطبيق
cd task_manager
python main.py
الخطوة 3: الاستخدام
| الإجراء | الطريقة |
|---|---|
| إضافة مهمة | اضغط "➕ مهمة جديدة" |
| عرض المهام | تظهر تلقائياً في الجدول |
| تعديل مهمة | اختر المهمة + اضغط "✏️ تعديل" |
| إكمال مهمة | اختر المهمة + اضغط "✅ تبديل الحالة" |
| حذف مهمة | اختر المهمة + اضغط "🗑️ حذف" |
| فلترة | اختر من قائمة "عرض" |
| تصدير | اضغط "📥 تصدير CSV" |
6. شرح المفاهيم المتقدمة
1. لماذا فصلنا الكود في ملفات؟
| السبب | الشرح | |-------|-------| | التنظيم | كل ملف له مسؤولية واحدة | | الاختبار | يمكن اختبار database.py منفصلاً | | إعادة الاستخدام | يمكن استخدام database.py في تطبيق آخر | | الصيانة | تعديل الواجهة لا يؤثر على البيانات |
2. مفهوم Context Manager في SQLite
conn = self.get_connection()
cursor = conn.cursor()
# ... عمليات
conn.commit()
conn.close()
الأفضل: استخدام with:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
# ... عمليات
# commit و close تلقائيان
3. معامل **kwargs في update_task
يسمح بتحديث أي حقل دون كتابة دوال منفصلة:
# كل هذه تعمل بنفس الدالة
db.update_task(1, title="عنوان جديد")
db.update_task(1, priority="عالية")
db.update_task(1, title="جديد", priority="عالية", completed=1)
4. استخدام sqlite3.Row
conn.row_factory = sqlite3.Row
الفائدة: الوصول للأعمدة بالأسماء بدلاً من الأرقام:
# بدون Row
task[1] # غامض
# مع Row
task["title"] # واضح
7. التوسعات المقترحة
1. إضافة تصنيفات
# في قاعدة البيانات
cursor.execute("""
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
color TEXT DEFAULT '#2563eb'
)
""")
# إضافة عمود للمهام
cursor.execute("ALTER TABLE tasks ADD COLUMN category_id INTEGER")
2. تكرار المهام
def create_recurring_task(task_id, pattern):
"""إنشاء مهمة متكررة"""
patterns = {
"daily": 1,
"weekly": 7,
"monthly": 30,
}
# ...
3. الإشعارات
import plyer
def send_notification(title, message):
"""إرسال إشعار"""
plyer.notification.notify(
title=title,
message=message,
timeout=10
)
4. الوضع الليلي
def toggle_dark_mode(self):
"""تبديل الوضع الليلي"""
if self.dark_mode:
bg = "#1a1a1a"
fg = "#ffffff"
else:
bg = "#f0f2f5"
fg = "#000000"
self.root.configure(bg=bg)
# تحديث باقي المكونات...
8. تحويل التطبيق إلى .exe
باستخدام PyInstaller
pip install pyinstaller
pyinstaller --onefile --windowed --name "TaskManager" main.py
النتيجة
ستجد ملف TaskManager.exe في مجلد dist/.
⚠️ ملاحظة: الملف سيكون بحجم ~10MB بسبب مكتبات Python المضمنة.
9. أخطاء شائعة
الخطأ 1: نسيان commit
# خطأ — التعديلات لم تُحفظ
cursor.execute("INSERT INTO tasks ...")
# نسيت conn.commit()
# صحيح
cursor.execute("INSERT INTO tasks ...")
conn.commit()
الخطأ 2: عدم إغلاق الاتصال
# خطأ — تسرب في الذاكرة
conn = sqlite3.connect("tasks.db")
# ... عمليات
# نسيت conn.close()
# صحيح — استخدم try/finally
try:
conn = sqlite3.connect("tasks.db")
# ...
finally:
conn.close()
الخطأ 3: SQL Injection
# خطأ — خطر أمني
cursor.execute(f"SELECT * FROM tasks WHERE id = {user_id}")
# صحيح — استخدام placeholders
cursor.execute("SELECT * FROM tasks WHERE id = ?", (user_id,))
10. تمارين عملية
التمرين 1: إضافة البحث
أضف مربع بحث في الأعلى يفلتر المهام بالعنوان.
التمرين 2: الألوان حسب الأولوية
اجعل كل مهمة بلون مختلف حسب أولويتها:
- عالية → خلفية حمراء فاتحة
- متوسطة → خلفية صفراء فاتحة
- منخفضة → خلفية خضراء فاتحة
التمرين 3: عداد تنازلي
أضف عموداً يعرض عدد الأيام المتبقية حتى تاريخ الاستحقاق.
التمرين 4: إشعارات سطح المكتب
عند إضافة مهمة بتاريخ اليوم، اعرض إشعاراً.
التمرين 5: نسخ احتياطي
أضف زر لإنشاء نسخة احتياطية من قاعدة البيانات.
قائمة تحقق — هل بنيت التطبيق؟
| المهارة | الحالة |
|---|---|
| فهم SQLite وقواعد البيانات | ⬜ |
| إنشاء جدول وعمود | ⬜ |
| عمليات CRUD (إضافة، قراءة، تعديل، حذف) | ⬜ |
| واجهة Tkinter متعددة النوافذ | ⬜ |
| Treeview لعرض البيانات | ⬜ |
| تنظيم الكود في ملفات | ⬜ |
| تصدير البيانات إلى CSV | ⬜ |
| تحويل التطبيق إلى .exe | ⬜ |
الخلاصة
في هذا المقال، بنيت تطبيق مهام احترافياً كاملاً:
- ✅ التخطيط — قبل كتابة أي كود
- ✅ قاعدة بيانات SQLite — حفظ دائم للبيانات
- ✅ واجهة رسومية متقدمة — Tkinter
- ✅ فصل الكود — database.py و app.py و main.py
- ✅ عمليات CRUD كاملة — إضافة، عرض، تعديل، حذف
- ✅ الفلاتر والإحصائيات — تجربة أفضل
- ✅ تصدير CSV — لمشاركة البيانات
- ✅ تحويل لـ .exe — تطبيق مستقل
هذا المشروع يمكنك إضافته لمعرض أعمالك كتطبيق حقيقي!
في المقال الأخير من السلسلة: سنتعلم Git و GitHub — كيف تحفظ كودك وتشاركه مع العالم!
ما الميزة التي ستضيفها لتطبيقك؟ شاركنا في التعليقات!
فريق نهضة
فريق عربي متخصص في التقنية والذكاء الاصطناعي وتطوير الذات
📚 مقالات ذات صلة
بناء آلة حاسبة بلغة Python — مشروع كامل خطوة بخطوة
دليل عملي كامل لبناء آلة حاسبة احترافية بلغة Python — من التخطيط إلى التنفيذ، مع واجهة رسومية بـ Tkinter
5 مشاريع Python للمبتدئين — ابنِ أول أعمالك
5 مشاريع Python عملية للمبتدئين مع الكود الكامل والشرح خطوة بخطوة — ابنِ معرض أعمالك الأول وابدأ رحلتك البرمجية
الدوال (Functions) في Python — نظّم كودك باحتراف
دليل شامل لتعلم الدوال في Python — تعريف الدوال، المعاملات، القيم المُرجعة، الدوال المجهولة، مع مشاريع تطبيقية خطوة بخطوة