البرمجة📅 2026-09-2513 دقائق قراءة
بناء تطبيق مهام كامل بلغة Python — مع قاعدة بيانات

بناء تطبيق مهام كامل بلغة Python — مع قاعدة بيانات

دليل عملي لبناء تطبيق إدارة مهام احترافي بلغة Python — مع قاعدة بيانات SQLite وواجهة رسومية Tkinter

بعد أن بنينا آلة حاسبة احترافية، حان الوقت لمشروع أكبر: تطبيق إدارة مهام كامل.

هذا المشروع سيعلمك:

  • التعامل مع قاعدة بيانات حقيقية (SQLite)
  • بناء واجهة رسومية متعددة النوافذ
  • إدارة البيانات (إضافة، تعديل، حذف)
  • تصدير البيانات إلى ملفات
  • تنظيم الكود في ملفات متعددة

بنهاية هذا المقال، سيكون لديك تطبيق مهام كامل يمكنك استخدامه يومياً.

1. التخطيط للمشروع

المتطلبات

قبل كتابة أي كود، دعنا نحدد ماذا نريد:

الميزةالوصف
إضافة مهمةعنوان، وصف، أولوية، تاريخ استحقاق
عرض المهامقائمة بكل المهام مع حالتها
تعديل مهمةتعديل أي حقل من حقول المهمة
حذف مهمةحذف مهمة نهائياً
إكمال مهمةوضع علامة "مكتملة"
فلترةالكل، النشطة، المكتملة
تصديرتصدير المهام إلى CSV
قاعدة بياناتحفظ دائم للبيانات

هيكل قاعدة البيانات

سنحتاج جدول واحد فقط للمهام:

العمودالنوعالوصف
idINTEGERمعرّف فريد (تلقائي)
titleTEXTعنوان المهمة
descriptionTEXTوصف تفصيلي
priorityTEXTالأولوية (عالية/متوسطة/منخفضة)
due_dateTEXTتاريخ الاستحقاق
completedINTEGER0 أو 1 (منجزة أم لا)
created_atTEXTتاريخ الإنشاء

هيكل المشروع

سنقسم المشروع إلى ملفات:

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

الخلاصة

في هذا المقال، بنيت تطبيق مهام احترافياً كاملاً:

  1. التخطيط — قبل كتابة أي كود
  2. قاعدة بيانات SQLite — حفظ دائم للبيانات
  3. واجهة رسومية متقدمة — Tkinter
  4. فصل الكود — database.py و app.py و main.py
  5. عمليات CRUD كاملة — إضافة، عرض، تعديل، حذف
  6. الفلاتر والإحصائيات — تجربة أفضل
  7. تصدير CSV — لمشاركة البيانات
  8. تحويل لـ .exe — تطبيق مستقل

هذا المشروع يمكنك إضافته لمعرض أعمالك كتطبيق حقيقي!

في المقال الأخير من السلسلة: سنتعلم Git و GitHub — كيف تحفظ كودك وتشاركه مع العالم!

ما الميزة التي ستضيفها لتطبيقك؟ شاركنا في التعليقات!

فريق نهضة

فريق نهضة

فريق عربي متخصص في التقنية والذكاء الاصطناعي وتطوير الذات

📚 مقالات ذات صلة

بناء آلة حاسبة بلغة Python — مشروع كامل خطوة بخطوة
البرمجة

بناء آلة حاسبة بلغة Python — مشروع كامل خطوة بخطوة

دليل عملي كامل لبناء آلة حاسبة احترافية بلغة Python — من التخطيط إلى التنفيذ، مع واجهة رسومية بـ Tkinter

5 مشاريع Python للمبتدئين — ابنِ أول أعمالك
البرمجة

5 مشاريع Python للمبتدئين — ابنِ أول أعمالك

5 مشاريع Python عملية للمبتدئين مع الكود الكامل والشرح خطوة بخطوة — ابنِ معرض أعمالك الأول وابدأ رحلتك البرمجية

الدوال (Functions) في Python — نظّم كودك باحتراف
البرمجة

الدوال (Functions) في Python — نظّم كودك باحتراف

دليل شامل لتعلم الدوال في Python — تعريف الدوال، المعاملات، القيم المُرجعة، الدوال المجهولة، مع مشاريع تطبيقية خطوة بخطوة