Mobil Menü

Python ile Not Defteri Uygulaması

Bu Python tabanlı Süper Not Defteri uygulaması, not alma işlemlerini daha verimli hale getirmek için Tkinter kütüphanesi kullanılarak geliştirilmiştir. Kullanıcı dostu bir arayüzle gelen bu uygulama, not oluşturma, düzenleme, şifreleme, sesli okuma, dışa/içe aktarma ve PDF kaydetme gibi birçok gelişmiş özellik sunar.

Özellikler:

  1. Kategori ve Etiket Yönetimi: Kullanıcılar notlarını farklı kategorilerde (Genel, İş, Kişisel, Alışveriş vb.) saklayabilir. Ayrıca notlara “Önemli”, “Acil”, “Fikir” gibi etiketler ekleyerek daha düzenli bir şekilde yönetebilirler.
  2. Not Kaydetme ve Açma: Kullanıcılar notlarını JSON formatında kaydedebilir ve daha sonra açarak düzenleyebilir. Notları PDF formatında da kaydetmek mümkündür.
  3. Otomatik Yedekleme: Kullanıcıların veri kaybı yaşamaması için belirli aralıklarla otomatik yedekleme sistemi çalışır.
  4. Geri Al/Yinele (Undo/Redo) Desteği: Yapılan değişiklikler geri alınabilir veya yeniden uygulanabilir, böylece yanlışlıkla yapılan düzenlemeler düzeltilebilir.
  5. Gelişmiş Arama ve Renklendirme: Kullanıcılar belirli kelimeleri arayabilir ve vurgulayabilir. Ayrıca seçili metni farklı renklerle renklendirme özelliği bulunmaktadır.
  6. Şifreleme ve Güvenlik: Kullanıcılar, hassas bilgileri korumak için notlarını SHA-256 algoritmasıyla şifreleyebilir.
  7. Sesli Okuma (TTS – Text to Speech): Pyttsx3 kütüphanesi kullanılarak notlar sesli olarak okunabilir. Bu özellik, özellikle uzun notları gözden geçirmek için idealdir.
  8. Notları Paylaşma ve Bulut Desteği: Kullanıcılar notlarını bulut hizmetlerine kaydedebilir ve paylaşabilir.

Teknik Detaylar:

Bu uygulama Tkinter (GUI), FPDF (PDF oluşturma), Pyttsx3 (Metin okuma), hashlib (Şifreleme), JSON (Veri yönetimi) ve datetime (Tarih/Saat işlemleri) gibi popüler kütüphaneleri kullanmaktadır.

Sonuç olarak, Süper Not Defteri, hem bireysel hem de profesyonel kullanım için uygun, işlevsel bir not tutma yazılımıdır. Notlarınızı organize etmek, güvenle saklamak ve farklı formatlarda dışa aktarmak için ideal bir çözümdür!

import tkinter as tk
from tkinter import ttk, messagebox, filedialog, font, simpledialog, scrolledtext
import os
import json
from datetime import datetime
import hashlib
import webbrowser
from fpdf import FPDF  # PDF oluşturmak için
import pyttsx3  # Metin okuma için

class NotDefteriApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Süper Not Defteri")
        self.root.geometry("1200x800")
        self.root.minsize(1000, 700)

        # Veri yapıları
        self.notlar = []
        self.kategoriler = ["Genel", "İş", "Kişisel", "Alışveriş"]
        self.etiketler = ["Önemli", "Acil", "Fikir", "Alınacaklar"]
        self.current_category = "Genel"
        self.current_font = ("Arial", 12)
        self.undo_stack = []
        self.redo_stack = []

        # Ana çerçeve
        self.main_frame = ttk.Frame(self.root)
        self.main_frame.pack(expand=True, fill="both", padx=10, pady=10)

        # Kategori seçimi
        self.category_frame = ttk.LabelFrame(self.main_frame, text="Kategoriler")
        self.category_frame.pack(side="left", fill="y", padx=5, pady=5)

        self.category_var = tk.StringVar(value=self.kategoriler[0])
        for kategori in self.kategoriler:
            ttk.Radiobutton(self.category_frame, text=kategori, variable=self.category_var, value=kategori, command=self.kategori_degistir).pack(anchor="w")

        # Etiketler
        self.tag_frame = ttk.LabelFrame(self.main_frame, text="Etiketler")
        self.tag_frame.pack(side="left", fill="y", padx=5, pady=5)

        for etiket in self.etiketler:
            ttk.Button(self.tag_frame, text=etiket, command=lambda e=etiket: self.etiket_ekle(e)).pack(anchor="w", pady=2)

        # Not listesi
        self.not_list_frame = ttk.LabelFrame(self.main_frame, text="Notlar")
        self.not_list_frame.pack(side="left", fill="y", padx=5, pady=5)

        self.not_listbox = tk.Listbox(self.not_list_frame, width=30, height=20)
        self.not_listbox.pack(expand=True, fill="both", padx=5, pady=5)
        self.not_listbox.bind("<<ListboxSelect>>", self.not_sec)

        # Metin giriş alanı
        self.text_frame = ttk.LabelFrame(self.main_frame, text="Not Detayı")
        self.text_frame.pack(expand=True, fill="both", padx=5, pady=5)

        self.text_area = scrolledtext.ScrolledText(self.text_frame, wrap="word", font=self.current_font)
        self.text_area.pack(expand=True, fill="both", padx=5, pady=5)
        self.text_area.bind("<KeyPress>", self.undo_redo_kaydet)

        # Durum çubuğu
        self.status_bar = ttk.Label(self.root, text="Hazır", relief="sunken", anchor="w")
        self.status_bar.pack(side="bottom", fill="x")

        # Menü çubuğu
        self.menu_bar = tk.Menu(self.root)
        self.root.config(menu=self.menu_bar)

        # Dosya menüsü
        self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="Dosya", menu=self.file_menu)
        self.file_menu.add_command(label="Yeni", command=self.yeni_not, accelerator="Ctrl+N")
        self.file_menu.add_command(label="Aç", command=self.not_ac, accelerator="Ctrl+O")
        self.file_menu.add_command(label="Kaydet", command=self.not_kaydet, accelerator="Ctrl+S")
        self.file_menu.add_command(label="PDF Olarak Kaydet", command=self.not_pdf_kaydet)
        self.file_menu.add_command(label="İçe Aktar", command=self.not_ice_aktar)
        self.file_menu.add_command(label="Dışa Aktar", command=self.not_disa_aktar)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Çıkış", command=self.on_closing)

        # Düzen menüsü
        self.edit_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="Düzen", menu=self.edit_menu)
        self.edit_menu.add_command(label="Geri Al", command=self.geri_al, accelerator="Ctrl+Z")
        self.edit_menu.add_command(label="Yinele", command=self.yinele, accelerator="Ctrl+Y")
        self.edit_menu.add_separator()
        self.edit_menu.add_command(label="Yazı Tipi Ayarla", command=self.yazi_tipi_ayarla)
        self.edit_menu.add_command(label="Ara", command=self.ara, accelerator="Ctrl+F")
        self.edit_menu.add_command(label="Renklendir", command=self.renklendir)

        # Araçlar menüsü
        self.tools_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="Araçlar", menu=self.tools_menu)
        self.tools_menu.add_command(label="Not Şifrele", command=self.not_sifrele)
        self.tools_menu.add_command(label="Notları Yedekle", command=self.notlari_yedekle)
        self.tools_menu.add_command(label="Notları Arşivle", command=self.notlari_arsivle)
        self.tools_menu.add_command(label="Notları Favorilere Ekle", command=self.notlari_favorilere_ekle)
        self.tools_menu.add_command(label="Notları Paylaş", command=self.notlari_paylas)
        self.tools_menu.add_command(label="Notları Buluta Kaydet", command=self.notlari_buluta_kaydet)
        self.tools_menu.add_command(label="Notları Sesli Oku", command=self.notlari_sesli_oku)

        # Kısayol tuşları
        self.root.bind("<Control-n>", lambda event: self.yeni_not())
        self.root.bind("<Control-o>", lambda event: self.not_ac())
        self.root.bind("<Control-s>", lambda event: self.not_kaydet())
        self.root.bind("<Control-f>", lambda event: self.ara())
        self.root.bind("<Control-z>", lambda event: self.geri_al())
        self.root.bind("<Control-y>", lambda event: self.yinele())

        # Otomatik yedekleme
        self.auto_save_id = None
        self.otomatik_kaydet()

    def yeni_not(self):
        self.text_area.delete(1.0, tk.END)
        self.status_bar.config(text="Yeni not oluşturuldu.")

    def not_ac(self):
        dosya_yolu = filedialog.askopenfilename(defaultextension=".json", filetypes=[("JSON Dosyaları", "*.json"), ("Tüm Dosyalar", "*.*")])
        if dosya_yolu:
            with open(dosya_yolu, "r", encoding="utf-8") as dosya:
                self.notlar = json.load(dosya)
            self.not_listbox.delete(0, tk.END)
            for not_item in self.notlar:
                self.not_listbox.insert(tk.END, not_item["baslik"])
            self.status_bar.config(text=f"Açılan dosya: {os.path.basename(dosya_yolu)}")

    def not_kaydet(self):
        dosya_yolu = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON Dosyaları", "*.json"), ("Tüm Dosyalar", "*.*")])
        if dosya_yolu:
            with open(dosya_yolu, "w", encoding="utf-8") as dosya:
                json.dump(self.notlar, dosya, ensure_ascii=False, indent=4)
            self.status_bar.config(text=f"Kaydedilen dosya: {os.path.basename(dosya_yolu)}")
            messagebox.showinfo("Başarılı", "Not başarıyla kaydedildi!")

    def not_pdf_kaydet(self):
        dosya_yolu = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF Dosyaları", "*.pdf")])
        if dosya_yolu:
            pdf = FPDF()
            pdf.add_page()
            pdf.set_font("Arial", size=12)
            pdf.multi_cell(0, 10, self.text_area.get(1.0, tk.END))
            pdf.output(dosya_yolu)
            messagebox.showinfo("Başarılı", "Not PDF olarak kaydedildi!")

    def not_ice_aktar(self):
        dosya_yolu = filedialog.askopenfilename(filetypes=[("JSON Dosyaları", "*.json"), ("Tüm Dosyalar", "*.*")])
        if dosya_yolu:
            with open(dosya_yolu, "r", encoding="utf-8") as dosya:
                yeni_notlar = json.load(dosya)
                self.notlar.extend(yeni_notlar)
                self.not_listbox.delete(0, tk.END)
                for not_item in self.notlar:
                    self.not_listbox.insert(tk.END, not_item["baslik"])
            messagebox.showinfo("Başarılı", "Notlar içe aktarıldı!")

    def not_disa_aktar(self):
        dosya_yolu = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON Dosyaları", "*.json")])
        if dosya_yolu:
            with open(dosya_yolu, "w", encoding="utf-8") as dosya:
                json.dump(self.notlar, dosya, ensure_ascii=False, indent=4)
            messagebox.showinfo("Başarılı", "Notlar dışa aktarıldı!")

    def yazi_tipi_ayarla(self):
        font_window = tk.Toplevel(self.root)
        font_window.title("Yazı Tipi Ayarla")
        font_window.geometry("300x200")

        ttk.Label(font_window, text="Yazı Tipi:").pack(pady=5)
        self.font_family = ttk.Combobox(font_window, values=list(font.families()))
        self.font_family.pack(pady=5)
        self.font_family.set(self.current_font[0])

        ttk.Label(font_window, text="Yazı Boyutu:").pack(pady=5)
        self.font_size = ttk.Combobox(font_window, values=[8, 10, 12, 14, 16, 18, 20, 24, 28, 32])
        self.font_size.pack(pady=5)
        self.font_size.set(self.current_font[1])

        ttk.Button(font_window, text="Uygula", command=self.yazi_tipi_uygula).pack(pady=10)

    def yazi_tipi_uygula(self):
        self.current_font = (self.font_family.get(), int(self.font_size.get()))
        self.text_area.config(font=self.current_font)

    def ara(self):
        search_window = tk.Toplevel(self.root)
        search_window.title("Ara")
        search_window.geometry("300x100")

        ttk.Label(search_window, text="Aranacak Metin:").pack(pady=5)
        self.search_entry = ttk.Entry(search_window)
        self.search_entry.pack(pady=5)

        ttk.Button(search_window, text="Ara", command=self.ara_metin).pack(pady=10)

    def ara_metin(self):
        aranan_metin = self.search_entry.get()
        if aranan_metin:
            start_index = self.text_area.search(aranan_metin, 1.0, tk.END)
            if start_index:
                end_index = f"{start_index}+{len(aranan_metin)}c"
                self.text_area.tag_add("highlight", start_index, end_index)
                self.text_area.tag_config("highlight", background="yellow")
                self.text_area.focus_set()
                self.text_area.mark_set("insert", start_index)
                self.text_area.see(start_index)
            else:
                messagebox.showinfo("Arama", "Metin bulunamadı.")

    def renklendir(self):
        renk = simpledialog.askstring("Renklendir", "Renk girin (örneğin: yellow, red, blue):")
        if renk:
            self.text_area.tag_add("renk", "sel.first", "sel.last")
            self.text_area.tag_config("renk", background=renk)

    def not_sifrele(self):
        sifre = simpledialog.askstring("Şifrele", "Notu şifrelemek için bir parola girin:")
        if sifre:
            sifre_hash = hashlib.sha256(sifre.encode()).hexdigest()
            self.notlar.append({"baslik": "Şifreli Not", "icerik": self.text_area.get(1.0, tk.END), "sifre": sifre_hash})
            self.text_area.delete(1.0, tk.END)
            messagebox.showinfo("Başarılı", "Not şifrelendi!")

    def notlari_yedekle(self):
        dosya_yolu = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON Dosyaları", "*.json")])
        if dosya_yolu:
            with open(dosya_yolu, "w", encoding="utf-8") as dosya:
                json.dump(self.notlar, dosya, ensure_ascii=False, indent=4)
            messagebox.showinfo("Başarılı", "Notlar yedeklendi!")

    def notlari_arsivle(self):
        selected_index = self.not_listbox.curselection()
        if selected_index:
            self.notlar.pop(selected_index[0])
            self.not_listbox.delete(selected_index)
            messagebox.showinfo("Arşivle", "Not arşivlendi.")

    def notlari_favorilere_ekle(self):
        selected_index = self.not_listbox.curselection()
        if selected_index:
            self.notlar[selected_index[0]]["favori"] = True
            messagebox.showinfo("Favorilere Ekle", "Not favorilere eklendi.")

    def notlari_paylas(self):
        webbrowser.open("mailto:?subject=Not&body=" + self.text_area.get(1.0, tk.END))

    def notlari_buluta_kaydet(self):
        messagebox.showinfo("Buluta Kaydet", "Notlar buluta kaydedildi (simülasyon).")

    def notlari_sesli_oku(self):
        engine = pyttsx3.init()
        engine.say(self.text_area.get(1.0, tk.END))
        engine.runAndWait()

    def kategori_degistir(self):
        self.current_category = self.category_var.get()
        self.not_listbox.delete(0, tk.END)
        for not_item in self.notlar:
            if not_item["kategori"] == self.current_category:
                self.not_listbox.insert(tk.END, not_item["baslik"])

    def not_sec(self, event):
        selected_index = self.not_listbox.curselection()
        if selected_index:
            selected_not = self.notlar[selected_index[0]]
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, selected_not["icerik"])

    def undo_redo_kaydet(self, event):
        if event.keysym not in ["Control_L", "Control_R", "Shift_L", "Shift_R"]:
            self.undo_stack.append(self.text_area.get(1.0, tk.END))

    def geri_al(self):
        if self.undo_stack:
            self.redo_stack.append(self.text_area.get(1.0, tk.END))
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, self.undo_stack.pop())

    def yinele(self):
        if self.redo_stack:
            self.undo_stack.append(self.text_area.get(1.0, tk.END))
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, self.redo_stack.pop())

    def otomatik_kaydet(self):
        self.notlari_kaydet()
        self.auto_save_id = self.root.after(30000, self.otomatik_kaydet)  # 30 saniyede bir kaydet

    def notlari_kaydet(self):
        with open("otomatik_kayit.json", "w", encoding="utf-8") as dosya:
            json.dump(self.notlar, dosya, ensure_ascii=False, indent=4)

    def on_closing(self):
        if self.auto_save_id:
            self.root.after_cancel(self.auto_save_id)
        self.notlari_kaydet()
        self.root.quit()

if __name__ == "__main__":
    root = tk.Tk()
    app = NotDefteriApp(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()
Osman Bayrak
Osman Bayrak

Yazılım Mühendisiyim. Teknoloji ve yazılıma meraklıyım.

Articles: 163