- Апр
- 23
- 3
Пользователь
Спустя 4 месяцев изучения python (я самоучка) решил попробовать что-то написать, скрывать не буду несколько раз прибегал к помощи вайб кодинга, но основную часть писал сам так как было интересно чему я научился прошу оценить, проект написан на tkinter, ибо он идет в комплекте с питоном и простой в освоении, дизайн конечно не блещет красотой, но я попытался выжать максимум из него (насколько я способен)
Буду рад наставлениям и поправкам
Буду рад наставлениям и поправкам
AkoBook:
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import time
class AdvancedNotepad:
def __init__(self, root):
self.root = root
self.root.title("AkoBook")
self.root.geometry("1000x700")
self.root.configure(bg="#2c2c2c")
self.current_file = None
self.auto_save_interval = 30
self.last_save_time = time.time()
self.highlight_delay = None
self.setup_style()
self.create_widgets()
self.setup_layout()
self.bind_events()
def setup_style(self):
style = ttk.Style()
style.theme_use("clam")
style.configure("TButton", padding=5, font=("Arial", 9))
style.configure("TLabel", background="#1e1e1e", foreground="#ffffff")
style.map("TButton",
background=[("active", "#404040")],
foreground=[("active", "#ffffff")])
def create_widgets(self):
self.main_frame = tk.Frame(self.root, bg="#2c2c2c")
self.sidebar = tk.Frame(self.main_frame, width=220, bg="#1e1e1e", relief="sunken", bd=1)
self.content_frame = tk.Frame(self.main_frame, bg="#2c2c2c")
self.line_numbers = tk.Text(
self.content_frame,
width=4,
padx=3,
takefocus=0,
border=0,
background="#252525",
foreground="#808080",
font=("Consolas", 11),
state="disabled"
)
self.text_area = tk.Text(
self.content_frame,
wrap="word",
font=("Consolas", 11),
bg="#1e1e1e",
fg="#e0e0e0",
insertbackground="#ffffff",
selectbackground="#404040",
selectforeground="#ffffff",
padx=10,
pady=10,
undo=True
)
self.scrollbar = tk.Scrollbar(self.content_frame, command=self.text_area.yview)
self.text_area.config(yscrollcommand=self.scrollbar.set)
self.status_bar = tk.Label(
self.root,
text="Готов",
bd=1,
relief=tk.SUNKEN,
anchor=tk.W,
bg="#1e1e1e",
fg="#a0a0a0",
font=("TkDefaultFont", 9)
)
self.setup_sidebar_widgets()
self.setup_tags()
def setup_tags(self):
self.text_area.tag_configure("search", background="#ffcc00", foreground="#000000")
self.text_area.tag_configure("keyword", foreground="#ff9900", font=("Consolas", 11, "bold"))
self.text_area.tag_configure("current_line", background="#2a2a2a")
def setup_sidebar_widgets(self):
tk.Label(
self.sidebar,
text="Панель инструментов",
bg="#1e1e1e",
fg="#ffffff",
font=("Arial", 10, "bold"),
pady=10
).pack(pady=(10, 5))
self.char_count_label = tk.Label(
self.sidebar,
text="Символов: 0 | Слов: 0 | Строк: 1",
bg="#1e1e1e",
fg="#aaaaaa",
font=("Arial", 9),
anchor="w",
padx=10
)
self.char_count_label.pack(fill="x", padx=10, pady=5)
tk.Label(self.sidebar, text="Поиск:", bg="#1e1e1e", fg="#ffffff", font=("Arial", 9)).pack(anchor="w", padx=10)
self.search_entry = tk.Entry(self.sidebar, font=("Arial", 9), bg="#333333", fg="#ffffff", insertbackground="#ffffff")
self.search_entry.pack(fill="x", padx=10, pady=(0, 5))
tk.Label(self.sidebar, text="Заменить на:", bg="#1e1e1e", fg="#ffffff", font=("Arial", 9)).pack(anchor="w", padx=10)
self.replace_entry = tk.Entry(self.sidebar, font=("Arial", 9), bg="#333333", fg="#ffffff", insertbackground="#ffffff")
self.replace_entry.pack(fill="x", padx=10, pady=(0, 5))
ttk.Button(self.sidebar, text="Найти", command=self.find_text).pack(fill="x", padx=10, pady=(0, 5))
ttk.Button(self.sidebar, text="Заменить", command=self.replace_text).pack(fill="x", padx=10, pady=(0, 5))
ttk.Button(self.sidebar, text="Заменить всё", command=self.replace_all_text).pack(fill="x", padx=10, pady=(0, 10))
tk.Label(self.sidebar, text="Тема:", bg="#1e1e1e", fg="#ffffff", font=("Arial", 9)).pack(anchor="w", padx=10)
self.theme_var = tk.StringVar(value="Тёмная")
themes = ["Тёмная", "Светлая", "Синий", "Зелёный"]
for theme in themes:
tk.Radiobutton(
self.sidebar,
text=theme,
variable=self.theme_var,
value=theme,
bg="#1e1e1e",
fg="#ffffff",
selectcolor="#404040",
activebackground="#1e1e1e",
font=("Arial", 9),
command=self.change_theme
).pack(anchor="w", padx=20, pady=2)
ttk.Button(self.sidebar, text="Очистить всё", command=self.clear_text).pack(fill="x", padx=10, pady=(20, 0))
def setup_layout(self):
self.main_frame.pack(fill="both", expand=True, padx=5, pady=5)
self.sidebar.pack(side="left", fill="y")
self.content_frame.pack(side="right", fill="both", expand=True)
self.line_numbers.pack(side="left", fill="y")
self.text_area.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
self.status_bar.pack(side="bottom", fill="x")
def bind_events(self):
self.text_area.bind("<KeyRelease>", self.on_key_release)
self.text_area.bind("<Button-3>", self.show_context_menu)
self.text_area.bind("<MouseWheel>", self.on_mousewheel)
self.search_entry.bind("<KeyRelease>", lambda e: self.find_text())
self.root.bind("<F11>", self.toggle_fullscreen)
self.root.bind("<Control-plus>", lambda e: self.zoom_in())
self.root.bind("<Control-minus>", lambda e: self.zoom_out())
self.root.after(100, self.update_line_numbers)
self.root.after(1000, self.auto_save_check)
def on_key_release(self, event=None):
self.update_char_count()
self.schedule_highlight()
def schedule_highlight(self):
if self.highlight_delay:
self.text_area.after_cancel(self.highlight_delay)
self.highlight_delay = self.text_area.after(200, self.highlight_syntax)
def highlight_syntax(self):
keywords = ["def", "class", "if", "else", "elif", "for", "while", "import", "from", "return", "try", "except", "with"]
self.text_area.tag_remove("keyword", "1.0", "end")
for word in keywords:
start = "1.0"
while True:
start = self.text_area.search(r'\b' + word + r'\b', start, stopindex="end", regexp=True)
if not start:
break
end = f"{start}+{len(word)}c"
self.text_area.tag_add("keyword", start, end)
start = end
def update_line_numbers(self):
self.line_numbers.config(state="normal")
self.line_numbers.delete("1.0", "end")
lines = self.text_area.get("1.0", "end-1c").split("\n")
line_numbers = "\n".join(str(i) for i in range(1, len(lines) + 1))
self.line_numbers.insert("1.0", line_numbers)
self.line_numbers.config(state="disabled")
self.root.after(100, self.update_line_numbers)
def update_char_count(self):
text = self.text_area.get("1.0", "end-1c")
chars = len(text)
words = len(text.split())
lines = int(self.text_area.index('end-1c').split('.')[0])
self.char_count_label.config(text=f"Символов: {chars} | Слов: {words} | Строк: {lines}")
def show_context_menu(self, event):
menu = tk.Menu(self.root, tearoff=0)
menu.add_command(label="Вырезать", command=self.cut_text)
menu.add_command(label="Копировать", command=self.copy_text)
menu.add_command(label="Вставить", command=self.paste_text)
menu.add_separator()
menu.add_command(label="Выделить всё", command=self.select_all)
menu.tk_popup(event.x_root, event.y_root)
def find_text(self):
term = self.search_entry.get().strip()
if not term:
return
self.text_area.tag_remove("search", "1.0", "end")
start = "1.0"
count = tk.IntVar()
found = 0
while True:
start = self.text_area.search(term, start, stopindex="end", count=count)
if not start:
break
end = f"{start}+{count.get()}c"
self.text_area.tag_add("search", start, end)
start = end
found += 1
self.status_bar.config(text=f"Найдено: {found} вхождений" if found else "Не найдено")
def replace_text(self):
find = self.search_entry.get().strip()
replace = self.replace_entry.get().strip()
if not find or not self.text_area.tag_ranges("sel"):
return
self.text_area.insert(tk.INSERT, replace)
self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
def replace_all_text(self):
find = self.search_entry.get().strip()
replace = self.replace_entry.get().strip()
if not find:
return
content = self.text_area.get("1.0", "end-1c")
new_content = content.replace(find, replace)
self.text_area.delete("1.0", "end")
self.text_area.insert("1.0", new_content)
self.status_bar.config(text=f"Заменено все вхождения '{find}' на '{replace}'")
def change_theme(self):
theme = self.theme_var.get()
themes = {
"Тёмная": {"bg": "#1e1e1e", "fg": "#e0e0e0", "insert": "#ffffff", "select": "#404040"},
"Светлая": {"bg": "#ffffff", "fg": "#000000", "insert": "#000000", "select": "#c0c0c0"},
"Синий": {"bg": "#001f3f", "fg": "#ffffff", "insert": "#ffffff", "select": "#003366"},
"Зелёный": {"bg": "#003300", "fg": "#00ff00", "insert": "#00ff00", "select": "#006600"},
}
cfg = themes.get(theme, themes["Тёмная"])
self.text_area.config(bg=cfg["bg"], fg=cfg["fg"], insertbackground=cfg["insert"], selectbackground=cfg["select"])
def clear_text(self):
if messagebox.askyesno("Подтверждение", "Очистить весь текст?"):
self.text_area.delete("1.0", "end")
def auto_save_check(self):
if self.current_file and time.time() - self.last_save_time > self.auto_save_interval:
self.save_file()
self.status_bar.config(text="Автосохранение...")
self.root.after(1000, self.auto_save_check)
def toggle_fullscreen(self, event=None):
state = self.root.attributes("-fullscreen")
self.root.attributes("-fullscreen", not state)
def zoom_in(self):
font = self.text_area.cget("font")
family, size = font.split()[0], int(font.split()[1])
if size < 24:
self.text_area.config(font=(family, size + 1))
self.line_numbers.config(font=(family, size + 1))
def zoom_out(self):
font = self.text_area.cget("font")
family, size = font.split()[0], int(font.split()[1])
if size > 8:
self.text_area.config(font=(family, size - 1))
self.line_numbers.config(font=(family, size - 1))
def on_mousewheel(self, event):
if event.state & 0x04: # Ctrl + колесо
if event.delta > 0:
self.zoom_in()
else:
self.zoom_out()
return "break"
def cut_text(self):
self.text_area.event_generate("<<Cut>>")
def copy_text(self):
self.text_area.event_generate("<<Copy>>")
def paste_text(self):
self.text_area.event_generate("<<Paste>>")
def select_all(self):
self.text_area.tag_add("sel", "1.0", "end")
def setup_menu(self):
menu = tk.Menu(self.root)
self.root.config(menu=menu)
file_menu = tk.Menu(menu, tearoff=0)
menu.add_cascade(label="Файл", menu=file_menu)
file_menu.add_command(label="Новый", command=self.new_file, accelerator="Ctrl+N")
file_menu.add_command(label="Открыть...", command=self.open_file, accelerator="Ctrl+O")
file_menu.add_command(label="Сохранить", command=self.save_file, accelerator="Ctrl+S")
file_menu.add_command(label="Сохранить как...", command=self.save_file_as, accelerator="Ctrl+Shift+S")
file_menu.add_separator()
file_menu.add_command(label="Выход", command=self.exit_app)
edit_menu = tk.Menu(menu, tearoff=0)
menu.add_cascade(label="Правка", menu=edit_menu)
edit_menu.add_command(label="Отменить", command=self.undo_text, accelerator="Ctrl+Z")
edit_menu.add_command(label="Повторить", command=self.redo_text, accelerator="Ctrl+Y")
edit_menu.add_separator()
edit_menu.add_command(label="Вырезать", command=self.cut_text, accelerator="Ctrl+X")
edit_menu.add_command(label="Копировать", command=self.copy_text, accelerator="Ctrl+C")
edit_menu.add_command(label="Вставить", command=self.paste_text, accelerator="Ctrl+V")
edit_menu.add_command(label="Удалить", command=self.delete_text, accelerator="Del")
edit_menu.add_separator()
edit_menu.add_command(label="Выделить всё", command=self.select_all, accelerator="Ctrl+A")
def new_file(self):
if messagebox.askyesno("Новый файл", "Сохранить текущий?"):
self.save_file()
self.text_area.delete("1.0", "end")
self.current_file = None
def open_file(self):
path = filedialog.askopenfilename(
filetypes=[("Текстовые файлы", "*.txt"), ("Python", "*.py"), ("HTML", "*.html"), ("Все файлы", "*.*")]
)
if path:
try:
with open(path, "r", encoding="utf-8") as f:
content = f.read()
self.text_area.delete("1.0", "end")
self.text_area.insert("1.0", content)
self.current_file = path
self.update_char_count()
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось открыть файл:\n{e}")
def save_file(self):
if self.current_file:
try:
content = self.text_area.get("1.0", "end-1c")
with open(self.current_file, "w", encoding="utf-8") as f:
f.write(content)
self.last_save_time = time.time()
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить:\n{e}")
else:
self.save_file_as()
def save_file_as(self):
path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Текстовые файлы", "*.txt"), ("Python", "*.py"), ("HTML", "*.html"), ("Все файлы", "*.*")]
)
if path:
try:
content = self.text_area.get("1.0", "end-1c")
with open(path, "w", encoding="utf-8") as f:
f.write(content)
self.current_file = path
self.last_save_time = time.time()
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить:\n{e}")
def exit_app(self):
if messagebox.askokcancel("Выход", "Закрыть приложение?"):
self.root.destroy()
def delete_text(self):
self.text_area.delete("sel.first", "sel.last")
def undo_text(self):
try:
self.text_area.edit_undo()
except:
pass
def redo_text(self):
try:
self.text_area.edit_redo()
except:
pass
if __name__ == "__main__":
root = tk.Tk()
app = AdvancedNotepad(root)
app.setup_menu()
root.mainloop()