[0.1] Source code push
This commit is contained in:
327
main.py
Normal file
327
main.py
Normal file
@@ -0,0 +1,327 @@
|
||||
import customtkinter as ctk
|
||||
from tkinter import filedialog, messagebox
|
||||
|
||||
ctk.set_appearance_mode("System")
|
||||
ctk.set_default_color_theme("dark-blue")
|
||||
|
||||
class MainApp(ctk.CTk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.title("Liskod")
|
||||
self.geometry("400x403")
|
||||
self.resizable(False, False)
|
||||
|
||||
# --- Data State ---
|
||||
self.current_file_path = None
|
||||
self.list_in_memory = []
|
||||
self.set_in_memory = set()
|
||||
self.any_change = False
|
||||
|
||||
# --- Grid Configuration ---
|
||||
self.grid_rowconfigure(0, weight=0)
|
||||
self.grid_rowconfigure(1, weight=0)
|
||||
self.grid_rowconfigure(2, weight=0)
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# ============================================================
|
||||
# ROW 0: File Input & Mode Checkbox
|
||||
# ============================================================
|
||||
self.row1_frame = ctk.CTkFrame(self)
|
||||
self.row1_frame.grid(row=0, column=0, padx=20, pady=(15, 10), sticky="ew")
|
||||
self.row1_frame.grid_columnconfigure(0, weight=1)
|
||||
self.row1_frame.grid_columnconfigure(1, weight=0)
|
||||
|
||||
self.var_edit_mode = ctk.BooleanVar(value=False)
|
||||
self.chk_mode = ctk.CTkCheckBox(
|
||||
self.row1_frame,
|
||||
text="Edit Existing File",
|
||||
font=("Arial", 14),
|
||||
variable=self.var_edit_mode,
|
||||
command=self.toggle_confirm
|
||||
)
|
||||
self.chk_mode.grid(row=0, column=0, padx=10, pady=(15, 5), sticky="w")
|
||||
|
||||
self.sort_toggle = ctk.BooleanVar(value=False)
|
||||
self.chk_mode2 = ctk.CTkCheckBox(
|
||||
self.row1_frame,
|
||||
text="Sort Out",
|
||||
font=("Arial", 14),
|
||||
variable=self.sort_toggle,
|
||||
command=None
|
||||
)
|
||||
self.chk_mode2.grid(row=0, column=1, pady=(15, 5), sticky="e")
|
||||
|
||||
self.entry_filepath = ctk.CTkEntry(
|
||||
self.row1_frame,
|
||||
text_color="gray",
|
||||
font=("Arial", 14),
|
||||
placeholder_text="New File (Save As)",
|
||||
height=35
|
||||
)
|
||||
self.entry_filepath.grid(row=1, column=0, padx=(10, 0), pady=5, sticky="ew")
|
||||
self.entry_filepath.configure(state="disabled")
|
||||
|
||||
self.btn_browse = ctk.CTkButton(
|
||||
self.row1_frame,
|
||||
text="Browse",
|
||||
command=self.browse_file,
|
||||
width=80,
|
||||
height=35,
|
||||
font=("Arial", 14),
|
||||
)
|
||||
self.btn_browse.grid(row=1, column=1, padx=(10, 10), pady=10)
|
||||
|
||||
# ============================================================
|
||||
# ROW 1: Preview Window
|
||||
# ============================================================
|
||||
self.preview_frame = ctk.CTkFrame(self)
|
||||
self.preview_frame.grid(row=1, column=0, padx=20, pady=(5, 10), sticky="ew")
|
||||
self.preview_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self.lbl_preview = ctk.CTkLabel(self.preview_frame, text="Preview", font=("Arial", 14))
|
||||
self.lbl_preview.grid(row=0, column=0, padx=10, pady=(5, 0), sticky="w")
|
||||
|
||||
self.txt_preview = ctk.CTkTextbox(self.preview_frame, height=110)
|
||||
self.txt_preview.grid(row=1, column=0, padx=10, pady=(5, 10), sticky="ew")
|
||||
self.txt_preview.configure(state="disabled")
|
||||
|
||||
self.input_container = ctk.CTkFrame(self.preview_frame, fg_color="transparent")
|
||||
self.input_container.grid(row=2, column=0, padx=10, pady=(5, 10), sticky="ew")
|
||||
self.input_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self.entry_text = ctk.CTkEntry(self.input_container, placeholder_text="Type content here...", height=35, font=("Arial", 14))
|
||||
self.entry_text.grid(row=0, column=0, padx=(0, 10), sticky="ew")
|
||||
self.entry_text.bind("<Return>", lambda event: self.add_line_to_memory())
|
||||
|
||||
self.btn_add = ctk.CTkButton(
|
||||
self.input_container,
|
||||
text="Add",
|
||||
width=80,
|
||||
font=("Arial", 14),
|
||||
command=self.add_line_to_memory,
|
||||
height=35
|
||||
)
|
||||
self.btn_add.grid(row=0, column=1, sticky="e")
|
||||
|
||||
# ============================================================
|
||||
# ROW 2: Save Button
|
||||
# ============================================================
|
||||
self.row3_frame = ctk.CTkFrame(self, fg_color="transparent")
|
||||
self.row3_frame.grid(row=3, column=0, padx=20, pady=(5, 20), sticky="ew")
|
||||
self.row3_frame.grid_columnconfigure(0, weight=0)
|
||||
self.row3_frame.grid_columnconfigure(1, weight=0)
|
||||
|
||||
self.last_status = ctk.CTkLabel(
|
||||
self.row3_frame,
|
||||
height=35,
|
||||
width=290,
|
||||
fg_color="#282828",
|
||||
corner_radius=4,
|
||||
text_color="gray",
|
||||
text="Status",
|
||||
font=("Arial", 14),
|
||||
)
|
||||
self.last_status.grid(row=0, column=0, padx=(0, 10), sticky="ew")
|
||||
|
||||
self.btn_save = ctk.CTkButton(
|
||||
self.row3_frame,
|
||||
text="Save",
|
||||
height=35,
|
||||
width=60,
|
||||
font=("Arial", 14),
|
||||
fg_color="green",
|
||||
hover_color="darkgreen",
|
||||
command=self.save_action
|
||||
)
|
||||
self.btn_save.grid(row=0, column=1, sticky="e")
|
||||
self.toggle_mode()
|
||||
|
||||
self.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||
|
||||
# --- Logic Methods ---
|
||||
|
||||
def toggle_confirm(self):
|
||||
if len(self.list_in_memory) > 0:
|
||||
new_state = self.var_edit_mode.get()
|
||||
action = "enable" if new_state else "disable"
|
||||
|
||||
answer = messagebox.askyesno(
|
||||
title="Confirmation",
|
||||
message=f"Are you sure you want to {action} this setting?"
|
||||
)
|
||||
if answer:
|
||||
self.delete_memory()
|
||||
self.update_preview()
|
||||
self.toggle_mode()
|
||||
else:
|
||||
self.var_edit_mode.set(not new_state)
|
||||
else:
|
||||
self.toggle_mode()
|
||||
self.update_preview()
|
||||
|
||||
|
||||
def toggle_mode(self):
|
||||
new_state = self.var_edit_mode.get()
|
||||
|
||||
if new_state:
|
||||
# Checked: Edit Existing Mode
|
||||
self.btn_browse.configure(state="normal")
|
||||
self.entry_filepath.configure(state="normal")
|
||||
self.entry_filepath.delete(0, "end")
|
||||
if self.current_file_path:
|
||||
self.entry_filepath.insert(0, f"...{self.current_file_path[-30:]}")
|
||||
else:
|
||||
self.entry_filepath.insert(0, "No file loaded...")
|
||||
self.btn_add.configure(state="disabled")
|
||||
self.entry_filepath.configure(state="disabled")
|
||||
else:
|
||||
# Unchecked: New File Mode
|
||||
self.current_file_path = None
|
||||
self.btn_browse.configure(state="disabled")
|
||||
self.btn_add.configure(state="normal")
|
||||
self.entry_filepath.configure(state="normal")
|
||||
self.entry_filepath.delete(0, "end")
|
||||
self.entry_filepath.insert(0, "Create New File")
|
||||
self.entry_filepath.configure(state="disabled")
|
||||
|
||||
|
||||
def browse_file(self):
|
||||
filename = filedialog.askopenfilename(
|
||||
title="Select a Text File",
|
||||
filetypes=[("Text files", "*.txt")]
|
||||
)
|
||||
|
||||
if filename:
|
||||
if not filename.lower().endswith('.txt'):
|
||||
messagebox.showerror("Error", "Only .txt files are allowed!")
|
||||
return
|
||||
|
||||
self.current_file_path = filename
|
||||
|
||||
self.entry_filepath.configure(state="normal")
|
||||
self.entry_filepath.delete(0, "end")
|
||||
self.entry_filepath.insert(0, f"...{filename[-30:]}")
|
||||
self.entry_filepath.configure(state="disabled")
|
||||
|
||||
try:
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
self.list_in_memory = [line.strip() for line in f]
|
||||
|
||||
self.set_in_memory = set(self.list_in_memory)
|
||||
duplicates = len(self.list_in_memory) - len(self.set_in_memory)
|
||||
self.last_status.configure(
|
||||
text=f"File loaded, {duplicates} duplicate(s) removed",
|
||||
text_color="#9efc90"
|
||||
)
|
||||
self.list_in_memory = list(dict.fromkeys(self.list_in_memory))
|
||||
|
||||
self.btn_add.configure(state="normal")
|
||||
self.update_preview()
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error", f"Could not read file:\n{e}")
|
||||
self.last_status.configure(
|
||||
text=f"Could not read file:\n{e}",
|
||||
text_color="#ff9d9d"
|
||||
)
|
||||
|
||||
|
||||
def add_line_to_memory(self):
|
||||
text = self.entry_text.get()
|
||||
if not text:
|
||||
return
|
||||
|
||||
clean_text = text.replace('\n', '').replace('\r', '').strip()
|
||||
if clean_text in self.set_in_memory:
|
||||
self.entry_text.delete(0, "end")
|
||||
self.last_status.configure(
|
||||
text=f"Line \"{clean_text[:10]}\" already exists!",
|
||||
text_color="#ff9d9d"
|
||||
)
|
||||
else:
|
||||
if not len(clean_text) == 0:
|
||||
self.any_change = True
|
||||
self.list_in_memory.append(clean_text)
|
||||
self.set_in_memory.add(clean_text)
|
||||
self.entry_text.delete(0, "end")
|
||||
self.update_preview()
|
||||
self.last_status.configure(
|
||||
text=f"Line \"{clean_text[:10]}\" added!",
|
||||
text_color="#9efc90"
|
||||
)
|
||||
self.entry_text.delete(0, "end")
|
||||
|
||||
|
||||
def delete_memory(self):
|
||||
self.list_in_memory = []
|
||||
self.set_in_memory = set()
|
||||
|
||||
|
||||
def update_preview(self):
|
||||
self.txt_preview.configure(state="normal")
|
||||
self.txt_preview.delete("1.0", "end")
|
||||
|
||||
if not self.list_in_memory:
|
||||
display_text = ""
|
||||
elif len(self.list_in_memory) > 5:
|
||||
last_five = ["..."] + self.list_in_memory[-5:]
|
||||
display_text = "\n".join(last_five)
|
||||
else:
|
||||
last_five = self.list_in_memory[-5:]
|
||||
display_text = "\n".join(last_five)
|
||||
|
||||
self.txt_preview.insert("1.0", display_text)
|
||||
self.txt_preview.configure(state="disabled")
|
||||
|
||||
|
||||
def save_action(self):
|
||||
if len(self.list_in_memory) <= 0:
|
||||
return
|
||||
|
||||
target_path = filedialog.asksaveasfilename(
|
||||
title="Save As",
|
||||
defaultextension=".txt",
|
||||
filetypes=[("Text files", "*.txt")]
|
||||
)
|
||||
if not target_path:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(target_path, "w", encoding="utf-8") as f:
|
||||
to_be_saved = self.list_in_memory
|
||||
if self.sort_toggle.get():
|
||||
to_be_saved = sorted(self.list_in_memory)
|
||||
full_content = "\n".join(to_be_saved)
|
||||
f.write(full_content)
|
||||
|
||||
self.any_change = False
|
||||
self.last_status.configure(
|
||||
text="File saved successfully!",
|
||||
text_color="#9efc90"
|
||||
)
|
||||
self.var_edit_mode.set(True)
|
||||
self.current_file_path = target_path
|
||||
|
||||
self.entry_filepath.configure(state="normal")
|
||||
self.entry_filepath.delete(0, "end")
|
||||
self.entry_filepath.insert(0, f"...{self.current_file_path[-30:]}")
|
||||
self.entry_filepath.configure(state="disabled")
|
||||
|
||||
except Exception as e:
|
||||
self.last_status.configure(
|
||||
text=f"Could not save file:\n{e}",
|
||||
text_color="#ff9d9d"
|
||||
)
|
||||
|
||||
def on_closing(self):
|
||||
if self.any_change:
|
||||
if messagebox.askokcancel("Quit", "There are unsaved changes, are you sure you want to quit?"):
|
||||
self.destroy()
|
||||
return
|
||||
else:
|
||||
self.destroy()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = MainApp()
|
||||
app.mainloop()
|
||||
Reference in New Issue
Block a user