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()
