پروژه روی کامپیوتر خودت اجرا میشه و باید دستی بررسیش کنی؛ این کد رو در مرورگر اجرا نکن. کد زیر رو در فایل tasks_app.py ذخیره کن و با پایتون محیط مجازی اجراش کن. هر بخش یک کار مشخص داره:
Taskعنوان رو بررسی میکنه و اطلاعات یک کار رو نگه میداره.TaskManagerکارها رو میخونه، تغییر میده و ذخیره میکنه.- تابعهای دکمهها یا callbackها، ورودی پنجره رو به این بخشها میدن و نمایش رو بهروز میکنن.
داده در codetamrin_tasks.json در پوشهٔ خانگی کاربر ذخیره میشه. برنامهٔ اولیه هم در حالت فایل .py و هم در بستهٔ اجرایی، مسیر قابل نوشتن و ثابتی برای داده داره. در اولین اجرا نبود فایل طبیعیه؛ اما فایل خراب یا ساختار نامعتبر باعث پیام خطا و پایان برنامه میشه تا دادهٔ قبلی با لیست خالی جایگزین نشه.
import json
from pathlib import Path
import tkinter as tk
from tkinter import messagebox
class Task:
def __init__(self, title, done=False):
self.title = title
self.done = done
@property
def title(self):
return self._title
@title.setter
def title(self, value):
if not isinstance(value, str) or not value.strip():
raise ValueError("عنوان نامعتبر است")
self._title = value.strip()
def record(self):
return {"title": self.title, "done": self.done}
class TaskManager:
def __init__(self, path):
self.path = Path(path)
self.tasks = []
def load(self):
try:
with self.path.open("r", encoding="utf-8") as file:
rows = json.load(file)
except FileNotFoundError:
return # اولین اجرا؛ هنوز فایلی نداریم.
if not isinstance(rows, list):
raise ValueError("ساختار فایل باید فهرست باشد")
loaded = []
for row in rows:
if (not isinstance(row, dict)
or not isinstance(row.get("title"), str)
or type(row.get("done")) is not bool):
raise ValueError("ساختار یکی از کارها نامعتبر است")
loaded.append(Task(row["title"], row["done"]))
self.tasks = loaded
def save(self):
# اول فایل موقت را کامل مینویسیم، بعد جایگزین میکنیم.
temporary = self.path.with_suffix(self.path.suffix + ".tmp")
with temporary.open("w", encoding="utf-8") as file:
json.dump([task.record() for task in self.tasks], file,
ensure_ascii=False, indent=2)
temporary.replace(self.path)
def add(self, title):
task = Task(title)
self.tasks.append(task)
try:
self.save()
except OSError:
self.tasks.pop()
raise
def complete(self, index):
old = self.tasks[index].done
self.tasks[index].done = True
try:
self.save()
except OSError:
self.tasks[index].done = old
raise
def main():
root = tk.Tk()
root.title("کارهای من")
# داده کنار مفسر یا فایل exe ذخیره نمیشود؛ پوشهٔ خانگی کاربر است.
manager = TaskManager(Path.home() / "codetamrin_tasks.json")
try:
manager.load()
except (OSError, UnicodeError, ValueError) as error:
messagebox.showerror("خواندن فایل", str(error), parent=root)
root.destroy()
return # فایل خراب را با دادهٔ خالی بازنویسی نمیکنیم.
entry = tk.Entry(root, width=40)
entry.pack(padx=12, pady=8)
items = tk.Listbox(root, width=50)
items.pack(padx=12, pady=8)
def refresh():
items.delete(0, tk.END)
for task in manager.tasks:
mark = "✓" if task.done else "○"
items.insert(tk.END, f"{mark} {task.title}")
def add_task():
try:
manager.add(entry.get())
except (OSError, ValueError) as error:
messagebox.showerror("افزودن انجام نشد", str(error), parent=root)
return
entry.delete(0, tk.END)
refresh()
def complete_task():
selected = items.curselection()
if not selected:
messagebox.showinfo("انتخاب کار", "ابتدا یک کار را انتخاب کن", parent=root)
return
try:
manager.complete(selected[0])
except OSError as error:
messagebox.showerror("ذخیره انجام نشد", str(error), parent=root)
return
refresh()
tk.Button(root, text="افزودن", command=add_task).pack(pady=3)
tk.Button(root, text="انجام شد", command=complete_task).pack(pady=3)
refresh()
root.mainloop()
if __name__ == "__main__":
main()
برنامه اول داده رو کامل در یک فایل موقت در همون پوشه مینویسه، بعد اون رو جای فایل اصلی میذاره. این کار احتمال ناقص موندن فایل اصلی رو کمتر میکنه، ولی جای نسخه پشتیبان رو نمیگیره. اگه ذخیره شکست بخوره، تغییر داخل حافظه هم به حالت قبل برمیگرده. این نسخه برای یک اجرای برنامه طراحی شده؛ دو پنجرهٔ مستقل ممکنه تغییرهای همدیگه رو بازنویسی کنن.
آزمون دستی رو به ترتیب انجام بده: ۱) یک عنوان فارسی اضافه کن و برنامه رو ببند و باز کن؛ ۲) اون رو انجامشده کن و دوباره باز کن؛ ۳) عنوان خالی رو امتحان کن؛ ۴) بدون انتخاب کار روی «انجام شد» بزن؛ ۵) بعد از تهیهٔ کپی پشتیبان از JSON، با فایل آزمایشی خراب و ساختار اشتباه رفتار خطا رو ببین. برای این آزمونها دادهٔ مهم شخصی استفاده نکن. در این نسخه میتونی کار اضافه کنی و انجامشدنش رو ثبت کنی. برای تمرین بیشتر، بعداً حذف، ویرایش و جستوجو رو هم اضافه کن.
فعالیت شما
اجزای آموختهشده رو در یک برنامهٔ محلی کامل و قابل اجرای دوباره کنار هم قرار بده.