import json import os from datetime import datetime, timedelta import tkinter as tk from tkinter import ttk, scrolledtext, messagebox, filedialog import sys import webbrowser class WindowsDiary: def __init__(self, root): self.root = root self.root.title("My Personal Diary") self.root.geometry("1100x750") # Set Windows icon (if you have an .ico file) try: self.root.iconbitmap('diary.ico') except: pass # Set Windows theme try: from ctypes import windll windll.shcore.SetProcessDpiAwareness(1) except: pass # Data storage in user's Documents for easy access self.data_dir = os.path.join(os.path.expanduser("~"), "Documents", "MyDiary") self.entries_file = os.path.join(self.data_dir, "entries.json") # Create data directory os.makedirs(self.data_dir, exist_ok=True) # Load entries self.entries = [] self.current_entry = None self.load_entries() # Setup UI self.setup_ui() self.setup_shortcuts() # Auto-save self.start_auto_save() def setup_ui(self): # Create main container with notebook self.notebook = ttk.Notebook(self.root) self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # Create tabs self.editor_tab = ttk.Frame(self.notebook) self.list_tab = ttk.Frame(self.notebook) self.stats_tab = ttk.Frame(self.notebook) self.notebook.add(self.editor_tab, text="✏️ Write Entry") self.notebook.add(self.list_tab, text="πŸ“š View Entries") self.notebook.add(self.stats_tab, text="πŸ“Š Statistics") # Setup all tabs self.setup_editor_tab() self.setup_list_tab() self.setup_stats_tab() # Bottom toolbar self.setup_toolbar() def setup_editor_tab(self): # Main frame main_frame = ttk.Frame(self.editor_tab) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # Entry details frame details_frame = ttk.LabelFrame(main_frame, text="Entry Information", padding=10) details_frame.pack(fill=tk.X, pady=(0, 10)) # Row 1: Date ttk.Label(details_frame, text="Date:", font=("Segoe UI", 10, "bold")).grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) self.date_var = tk.StringVar(value=datetime.now().strftime("%Y-%m-%d")) date_entry = ttk.Entry(details_frame, textvariable=self.date_var, width=12, font=("Segoe UI", 10)) date_entry.grid(row=0, column=1, sticky=tk.W, padx=5, pady=5) # Date buttons date_btn_frame = ttk.Frame(details_frame) date_btn_frame.grid(row=0, column=2, sticky=tk.W, padx=5, pady=5) ttk.Button(date_btn_frame, text="Today", width=8, command=lambda: self.date_var.set(datetime.now().strftime("%Y-%m-%d"))).pack(side=tk.LEFT, padx=2) ttk.Button(date_btn_frame, text="Yesterday", width=10, command=lambda: self.date_var.set((datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d"))).pack(side=tk.LEFT, padx=2) # Row 2: Category and Mood ttk.Label(details_frame, text="Category:", font=("Segoe UI", 10, "bold")).grid(row=1, column=0, sticky=tk.W, padx=5, pady=5) self.category_var = tk.StringVar() categories = ["πŸ“ Personal", "πŸ’Ό Work", "πŸ’‘ Ideas", "✈️ Travel", "❀️ Health", "🎨 Creative", "πŸ“– Learning", "🏠 Family", "πŸ’° Finance", "Other"] self.category_combo = ttk.Combobox(details_frame, textvariable=self.category_var, values=categories, width=18) self.category_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5) self.category_combo.set("πŸ“ Personal") ttk.Label(details_frame, text="Mood:", font=("Segoe UI", 10, "bold")).grid(row=1, column=2, sticky=tk.W, padx=5, pady=5) self.mood_var = tk.StringVar() moods = ["😊 Happy", "😒 Sad", "πŸ€” Thoughtful", "⚑ Energetic", "😌 Calm", "😰 Anxious", "😴 Tired", "πŸŽ‰ Excited", "😀 Frustrated"] self.mood_combo = ttk.Combobox(details_frame, textvariable=self.mood_var, values=moods, width=18) self.mood_combo.grid(row=1, column=3, sticky=tk.W, padx=5, pady=5) self.mood_combo.set("😊 Happy") # Row 3: Title ttk.Label(details_frame, text="Title:", font=("Segoe UI", 10, "bold")).grid(row=2, column=0, sticky=tk.W, padx=5, pady=5) self.title_var = tk.StringVar() title_entry = ttk.Entry(details_frame, textvariable=self.title_var, width=60, font=("Segoe UI", 11)) title_entry.grid(row=2, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5, pady=5) # Content frame content_frame = ttk.LabelFrame(main_frame, text="Your Entry", padding=10) content_frame.pack(fill=tk.BOTH, expand=True) # Formatting toolbar format_frame = ttk.Frame(content_frame) format_frame.pack(fill=tk.X, pady=(0, 5)) ttk.Button(format_frame, text="Bold", width=6, command=self.make_bold).pack(side=tk.LEFT, padx=2) ttk.Button(format_frame, text="Italic", width=6, command=self.make_italic).pack(side=tk.LEFT, padx=2) ttk.Button(format_frame, text="Underline", width=8, command=self.make_underline).pack(side=tk.LEFT, padx=2) ttk.Separator(format_frame, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=5) ttk.Button(format_frame, text="πŸ“‹ Word Count", command=self.show_word_count).pack(side=tk.LEFT, padx=2) # Text editor self.content_text = scrolledtext.ScrolledText(content_frame, wrap=tk.WORD, font=("Segoe UI", 11), height=18, bg="white", undo=True) self.content_text.pack(fill=tk.BOTH, expand=True) # Word count label self.word_count_var = tk.StringVar(value="Words: 0 | Characters: 0") word_count_label = ttk.Label(content_frame, textvariable=self.word_count_var, font=("Segoe UI", 9)) word_count_label.pack(anchor=tk.E, pady=2) # Update word count self.content_text.bind('', self.update_word_count) # Action buttons button_frame = ttk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=10) ttk.Button(button_frame, text="πŸ’Ύ Save Entry", command=self.save_entry, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="πŸ—‘οΈ Delete Entry", command=self.delete_entry, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="πŸ“‹ Clear Form", command=self.new_entry, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="πŸ“ Open Data Folder", command=self.open_data_folder, width=18).pack(side=tk.RIGHT, padx=5) def setup_list_tab(self): # Search and filter frame filter_frame = ttk.LabelFrame(self.list_tab, text="Search & Filter", padding=10) filter_frame.pack(fill=tk.X, padx=10, pady=10) # Search row search_row = ttk.Frame(filter_frame) search_row.pack(fill=tk.X, pady=5) ttk.Label(search_row, text="πŸ” Search:", font=("Segoe UI", 10)).pack(side=tk.LEFT, padx=5) self.search_var = tk.StringVar() self.search_var.trace('w', lambda *args: self.filter_entries()) search_entry = ttk.Entry(search_row, textvariable=self.search_var, width=40, font=("Segoe UI", 10)) search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) # Filter row filter_row = ttk.Frame(filter_frame) filter_row.pack(fill=tk.X, pady=5) ttk.Label(filter_row, text="Category:", font=("Segoe UI", 10)).pack(side=tk.LEFT, padx=5) self.filter_category = tk.StringVar() categories = ["All", "πŸ“ Personal", "πŸ’Ό Work", "πŸ’‘ Ideas", "✈️ Travel", "❀️ Health", "🎨 Creative", "πŸ“– Learning", "🏠 Family", "πŸ’° Finance", "Other"] filter_combo = ttk.Combobox(filter_row, textvariable=self.filter_category, values=categories, width=15) filter_combo.pack(side=tk.LEFT, padx=5) filter_combo.set("All") filter_combo.bind('<>', lambda e: self.filter_entries()) ttk.Label(filter_row, text="Mood:", font=("Segoe UI", 10)).pack(side=tk.LEFT, padx=5) self.filter_mood = tk.StringVar() moods = ["All", "😊 Happy", "😒 Sad", "πŸ€” Thoughtful", "⚑ Energetic", "😌 Calm", "😰 Anxious", "😴 Tired"] mood_combo = ttk.Combobox(filter_row, textvariable=self.filter_mood, values=moods, width=12) mood_combo.pack(side=tk.LEFT, padx=5) mood_combo.set("All") mood_combo.bind('<>', lambda e: self.filter_entries()) # Entry list and preview list_frame = ttk.Frame(self.list_tab) list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) # Left side - Entry list left_frame = ttk.Frame(list_frame) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5)) ttk.Label(left_frame, text="Entries:", font=("Segoe UI", 10, "bold")).pack(anchor=tk.W) list_container = ttk.Frame(left_frame) list_container.pack(fill=tk.BOTH, expand=True, pady=5) scrollbar = ttk.Scrollbar(list_container) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.entry_listbox = tk.Listbox(list_container, yscrollcommand=scrollbar.set, font=("Segoe UI", 10), height=15, selectbackground="#0078D4", selectforeground="white") self.entry_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.config(command=self.entry_listbox.yview) self.entry_listbox.bind('<>', self.on_entry_select) self.entry_listbox.bind('', self.open_selected_entry) # Right side - Preview right_frame = ttk.Frame(list_frame) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0)) ttk.Label(right_frame, text="Preview:", font=("Segoe UI", 10, "bold")).pack(anchor=tk.W) self.preview_text = scrolledtext.ScrolledText(right_frame, wrap=tk.WORD, font=("Segoe UI", 10), height=15, state=tk.DISABLED, bg="#F5F5F5") self.preview_text.pack(fill=tk.BOTH, expand=True, pady=5) # Action buttons action_frame = ttk.Frame(self.list_tab) action_frame.pack(fill=tk.X, padx=10, pady=10) ttk.Button(action_frame, text="πŸ”„ Refresh", command=self.refresh_entry_list, width=12).pack(side=tk.LEFT, padx=5) ttk.Button(action_frame, text="✏️ Edit Selected", command=self.edit_selected_entry, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(action_frame, text="πŸ—‘οΈ Delete Selected", command=self.delete_selected_entry, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(action_frame, text="πŸ“Š Export All", command=self.export_entries, width=12).pack(side=tk.RIGHT, padx=5) self.refresh_entry_list() def setup_stats_tab(self): # Statistics display with nicer formatting stats_container = ttk.Frame(self.stats_tab) stats_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=20) # Create a canvas for scrolling canvas = tk.Canvas(stats_container) scrollbar = ttk.Scrollbar(stats_container, orient="vertical", command=canvas.yview) scrollable_frame = ttk.Frame(canvas) scrollable_frame.bind( "", lambda e: canvas.configure(scrollregion=canvas.bbox("all")) ) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") # Stats text widget self.stats_text = tk.Text(scrollable_frame, font=("Consolas", 10), height=25, width=70, bg="#F5F5F5") self.stats_text.pack(pady=10) # Button frame button_frame = ttk.Frame(self.stats_tab) button_frame.pack(fill=tk.X, padx=20, pady=10) ttk.Button(button_frame, text="πŸ“ˆ Update Statistics", command=self.update_stats, width=20).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="πŸ“Š Export Statistics", command=self.export_stats, width=20).pack(side=tk.LEFT, padx=5) self.update_stats() def setup_toolbar(self): toolbar = ttk.Frame(self.root) toolbar.pack(side=tk.BOTTOM, fill=tk.X) # Status bar self.status_var = tk.StringVar(value="Ready βœ“") status_label = ttk.Label(toolbar, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2, pady=2) # Quick stats self.quick_stats_var = tk.StringVar(value="") quick_stats = ttk.Label(toolbar, textvariable=self.quick_stats_var, relief=tk.SUNKEN, anchor=tk.E, width=30) quick_stats.pack(side=tk.RIGHT, padx=2, pady=2) self.update_quick_stats() def setup_shortcuts(self): self.root.bind('', lambda e: self.save_entry()) self.root.bind('', lambda e: self.new_entry()) self.root.bind('', lambda e: self.notebook.select(self.list_tab)) self.root.bind('', lambda e: self.open_data_folder()) self.root.bind('', lambda e: self.show_help()) def make_bold(self): try: self.content_text.tag_configure("bold", font=("Segoe UI", 11, "bold")) self.content_text.insert(tk.INSERT, "**", "bold") except: pass def make_italic(self): try: self.content_text.insert(tk.INSERT, "*italic*") except: pass def make_underline(self): try: self.content_text.insert(tk.INSERT, "_underlined_") except: pass def show_word_count(self): text = self.content_text.get(1.0, tk.END).strip() words = len(text.split()) chars = len(text) messagebox.showinfo("Word Count", f"Words: {words}\nCharacters: {chars}") def update_word_count(self, event=None): text = self.content_text.get(1.0, tk.END).strip() words = len(text.split()) chars = len(text) self.word_count_var.set(f"Words: {words} | Characters: {chars}") def update_quick_stats(self): total = len(self.entries) self.quick_stats_var.set(f"πŸ“Š Total Entries: {total}") self.root.after(5000, self.update_quick_stats) def start_auto_save(self): self.root.after(30000, self.auto_save) def auto_save(self): if self.content_text.get(1.0, tk.END).strip(): self.save_entry(quiet=True) self.start_auto_save() def load_entries(self): try: if os.path.exists(self.entries_file): with open(self.entries_file, 'r', encoding='utf-8') as f: self.entries = json.load(f) else: self.entries = [] except Exception as e: messagebox.showerror("Error", f"Could not load entries: {str(e)}") self.entries = [] def save_entries(self): try: with open(self.entries_file, 'w', encoding='utf-8') as f: json.dump(self.entries, f, indent=2, ensure_ascii=False) return True except Exception as e: messagebox.showerror("Error", f"Could not save entries: {str(e)}") return False def save_entry(self, quiet=False): try: entry = { 'title': self.title_var.get().strip(), 'date': self.date_var.get(), 'category': self.category_var.get(), 'mood': self.mood_var.get(), 'content': self.content_text.get(1.0, tk.END).strip(), 'last_modified': datetime.now().isoformat() } if not entry['title']: if not quiet: messagebox.showwarning("Warning", "Please enter a title") return False if self.current_entry: # Update existing entry for i, e in enumerate(self.entries): if e.get('id') == self.current_entry.get('id'): entry['id'] = self.current_entry.get('id') self.entries[i] = entry break else: # Add new entry entry['id'] = datetime.now().strftime("%Y%m%d%H%M%S") self.entries.append(entry) if self.save_entries(): self.current_entry = entry if not quiet: self.status_var.set(f"βœ“ Entry saved: {entry['title']}") self.refresh_entry_list() self.update_stats() return True except Exception as e: if not quiet: messagebox.showerror("Error", f"Failed to save: {str(e)}") return False def refresh_entry_list(self): self.filter_entries() def filter_entries(self): search_term = self.search_var.get().lower() if hasattr(self, 'search_var') else "" category_filter = self.filter_category.get() if hasattr(self, 'filter_category') else "All" mood_filter = self.filter_mood.get() if hasattr(self, 'filter_mood') else "All" self.entry_listbox.delete(0, tk.END) # Sort entries by date (newest first) sorted_entries = sorted(self.entries, key=lambda x: x['date'], reverse=True) for entry in sorted_entries: # Apply filters if category_filter != "All" and entry.get('category', '') != category_filter: continue if mood_filter != "All" and entry.get('mood', '') != mood_filter: continue if search_term and search_term not in entry['title'].lower() and search_term not in entry.get('content', '').lower(): continue display_text = f"{entry['date']} | {entry['title']}" self.entry_listbox.insert(tk.END, display_text) def on_entry_select(self, event): selection = self.entry_listbox.curselection() if selection: # Get the selected entry filtered_entries = self.get_filtered_entries() if selection[0] < len(filtered_entries): self.current_entry = filtered_entries[selection[0]] # Show in preview self.preview_text.config(state=tk.NORMAL) self.preview_text.delete(1.0, tk.END) preview_content = f"Title: {self.current_entry['title']}\n" preview_content += f"Date: {self.current_entry['date']}\n" preview_content += f"Category: {self.current_entry.get('category', 'N/A')}\n" preview_content += f"Mood: {self.current_entry.get('mood', 'N/A')}\n" preview_content += "-" * 50 + "\n\n" preview_content += self.current_entry.get('content', '') self.preview_text.insert(1.0, preview_content) self.preview_text.config(state=tk.DISABLED) def get_filtered_entries(self): search_term = self.search_var.get().lower() if hasattr(self, 'search_var') else "" category_filter = self.filter_category.get() if hasattr(self, 'filter_category') else "All" mood_filter = self.filter_mood.get() if hasattr(self, 'filter_mood') else "All" filtered = [] for entry in sorted(self.entries, key=lambda x: x['date'], reverse=True): if category_filter != "All" and entry.get('category', '') != category_filter: continue if mood_filter != "All" and entry.get('mood', '') != mood_filter: continue if search_term and search_term not in entry['title'].lower() and search_term not in entry.get('content', '').lower(): continue filtered.append(entry) return filtered def open_selected_entry(self, event): self.edit_selected_entry() def edit_selected_entry(self): if self.current_entry: # Switch to editor tab self.notebook.select(self.editor_tab) # Load entry into editor self.title_var.set(self.current_entry['title']) self.date_var.set(self.current_entry['date']) self.category_var.set(self.current_entry.get('category', 'πŸ“ Personal')) self.mood_var.set(self.current_entry.get('mood', '😊 Happy')) self.content_text.delete(1.0, tk.END) self.content_text.insert(1.0, self.current_entry.get('content', '')) self.status_var.set(f"Editing: {self.current_entry['title']}") def delete_selected_entry(self): if self.current_entry: if messagebox.askyesno("Confirm Delete", f"Delete '{self.current_entry['title']}'?"): self.entries = [e for e in self.entries if e.get('id') != self.current_entry.get('id')] self.save_entries() self.current_entry = None self.refresh_entry_list() self.preview_text.config(state=tk.NORMAL) self.preview_text.delete(1.0, tk.END) self.preview_text.config(state=tk.DISABLED) self.status_var.set("βœ“ Entry deleted") self.update_stats() def new_entry(self): self.current_entry = None self.title_var.set("") self.date_var.set(datetime.now().strftime("%Y-%m-%d")) self.category_var.set("πŸ“ Personal") self.mood_var.set("😊 Happy") self.content_text.delete(1.0, tk.END) self.status_var.set("New entry ready") def delete_entry(self): self.delete_selected_entry() def export_entries(self): filename = filedialog.asksaveasfilename( defaultextension=".json", filetypes=[("JSON files", "*.json"), ("All files", "*.*")], initialfile=f"diary_backup_{datetime.now().strftime('%Y%m%d')}.json" ) if filename: try: with open(filename, 'w', encoding='utf-8') as f: json.dump(self.entries, f, indent=2, ensure_ascii=False) self.status_var.set(f"βœ“ Backup saved to {os.path.basename(filename)}") messagebox.showinfo("Success", f"Backup saved to:\n{filename}") except Exception as e: messagebox.showerror("Error", f"Backup failed: {str(e)}") def export_stats(self): filename = filedialog.asksaveasfilename( defaultextension=".txt", filetypes=[("Text files", "*.txt"), ("All files", "*.*")], initialfile=f"diary_stats_{datetime.now().strftime('%Y%m%d')}.txt" ) if filename: try: stats_content = self.stats_text.get(1.0, tk.END) with open(filename, 'w', encoding='utf-8') as f: f.write(stats_content) self.status_var.set(f"βœ“ Statistics exported") messagebox.showinfo("Success", f"Statistics saved to:\n{filename}") except Exception as e: messagebox.showerror("Error", f"Export failed: {str(e)}") def open_data_folder(self): try: os.startfile(self.data_dir) except: try: os.system(f'explorer "{self.data_dir}"') except: messagebox.showerror("Error", "Could not open folder") def update_stats(self): self.stats_text.delete(1.0, tk.END) if not self.entries: self.stats_text.insert(1.0, "No entries yet. Start writing your first entry!") return total_entries = len(self.entries) # Category statistics categories = {} for entry in self.entries: cat = entry.get('category', 'Uncategorized') categories[cat] = categories.get(cat, 0) + 1 # Mood statistics moods = {} for entry in self.entries: mood = entry.get('mood', 'Not specified') moods[mood] = moods.get(mood, 0) + 1 # Date range dates = [entry['date'] for entry in self.entries] oldest = min(dates) if dates else "N/A" newest = max(dates) if dates else "N/A" # Word count total_words = sum(len(entry.get('content', '').split()) for entry in self.entries) # Most active months months = {} for entry in self.entries: month = entry['date'][:7] # YYYY-MM months[month] = months.get(month, 0) + 1 most_active_month = max(months.items(), key=lambda x: x[1]) if months else ("N/A", 0) stats = f""" ╔══════════════════════════════════════════════════════════════════╗ β•‘ DIARY STATISTICS β•‘ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• πŸ“Š OVERVIEW Total Entries: {total_entries} Total Words Written: {total_words:,} Average Words per Entry: {total_words//total_entries if total_entries > 0 else 0:,} πŸ“… DATE RANGE First Entry: {oldest} Latest Entry: {newest} Journal Duration: {(datetime.strptime(newest, "%Y-%m-%d") - datetime.strptime(oldest, "%Y-%m-%d")).days if newest != "N/A" else 0} days πŸ“ˆ ACTIVITY Most Active Month: {most_active_month[0]} ({most_active_month[1]} entries) Average Entries per Month: {total_entries / len(months) if months else 0:.1f} πŸ“‚ ENTRIES BY CATEGORY """ for cat, count in sorted(categories.items(), key=lambda x: x[1], reverse=True): percentage = (count / total_entries) * 100 bar_length = int(percentage / 2) bar = "β–ˆ" * bar_length + "β–‘" * (50 - bar_length) stats += f" {cat:<15} {bar} {count} ({percentage:.1f}%)\n" stats += "\n😊 ENTRIES BY MOOD\n" for mood, count in sorted(moods.items(), key=lambda x: x[1], reverse=True): percentage = (count / total_entries) * 100 bar_length = int(percentage / 2) bar = "β–ˆ" * bar_length + "β–‘" * (50 - bar_length) stats += f" {mood:<15} {bar} {count} ({percentage:.1f}%)\n" stats += f""" πŸ’ͺ CONSISTENCY Total Days with Entries: {len(set(dates))} Longest Streak: {self.calculate_streak()} days 🎯 GOALS Target: 365 entries/year Progress: {min(100, int((total_entries / 365) * 100))}% complete """ self.stats_text.insert(1.0, stats) def calculate_streak(self): if not self.entries: return 0 dates = sorted(set([entry['date'] for entry in self.entries])) if not dates: return 0 streak = 1 current_streak = 1 prev_date = datetime.strptime(dates[0], "%Y-%m-%d") for date_str in dates[1:]: current_date = datetime.strptime(date_str, "%Y-%m-%d") if (current_date - prev_date).days == 1: current_streak += 1 streak = max(streak, current_streak) else: current_streak = 1 prev_date = current_date return streak def show_help(self): help_text = """My Personal Diary - Help Quick Start: 1. Click "Write Entry" tab to create a new entry 2. Enter title, date, category, and mood 3. Write your content in the text area 4. Click "Save Entry" to save Features: β€’ Auto-save every 30 seconds β€’ Search and filter entries β€’ Statistics and progress tracking β€’ Export backups β€’ Open data folder for manual backup Keyboard Shortcuts: β€’ Ctrl+S - Save entry β€’ Ctrl+N - New entry β€’ Ctrl+F - View entries β€’ Ctrl+O - Open data folder β€’ F1 - Show this help Your data is stored in: Documents\\MyDiary\\entries.json Enjoy journaling! πŸ“""" messagebox.showinfo("Help", help_text) def main(): root = tk.Tk() app = WindowsDiary(root) # Center window root.update_idletasks() width = root.winfo_width() height = root.winfo_height() x = (root.winfo_screenwidth() // 2) - (width // 2) y = (root.winfo_screenheight() // 2) - (height // 2) root.geometry(f'{width}x{height}+{x}+{y}') root.mainloop() if __name__ == "__main__": main()