keypresser/view/view.py

158 lines
8.2 KiB
Python
Raw Normal View History

2025-06-19 18:38:55 +00:00
import tkinter as tk
from tkinter import scrolledtext, Label, Button, Entry, StringVar, ttk, messagebox
import datetime
import queue
class KeyPressView(tk.Frame):
def __init__(self, master, controller):
super().__init__(master)
self.master = master
self.controller = controller # Store reference to controller
self.master.title("Jarvis Key Press Utility (AHK Integrated)")
self.master.geometry("600x600")
self.pack(fill=tk.BOTH, expand=True)
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
self.grid_rowconfigure(0, weight=0)
self.grid_rowconfigure(1, weight=0)
self.grid_rowconfigure(2, weight=1)
# --- Configuration Section ---
self.config_frame = tk.LabelFrame(self, text="Configuration", padx=10, pady=10, font=('Inter', 10, 'bold'))
self.config_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="ew")
self.config_frame.grid_columnconfigure(1, weight=1)
Label(self.config_frame, text="Key to Press:", font=('Inter', 10)).grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.key_to_press_var = StringVar(value="+")
self.key_input_entry = Entry(self.config_frame, textvariable=self.key_to_press_var, width=20, font=('Inter', 10))
self.key_input_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
Label(self.config_frame, text="(e.g., '+', 'a', 'enter', 'space', 'f5')", font=('Inter', 8), fg="#586e75").grid(row=1, column=1, padx=5, pady=0, sticky="nw")
self.key_validation_label = Label(self.config_frame, text="Please enter a key to press.", font=('Inter', 8, 'bold'), fg="red")
self.key_validation_label.grid(row=0, column=2, padx=5, pady=5, sticky="w")
self.key_validation_label.grid_remove()
Label(self.config_frame, text="Target Window:", font=('Inter', 10)).grid(row=2, column=0, padx=5, pady=5, sticky="w")
self.target_window_title_var = StringVar(value="--- Select a Window ---")
self.window_combobox = ttk.Combobox(self.config_frame, textvariable=self.target_window_title_var,
state="readonly", font=('Inter', 10))
self.window_combobox.grid(row=2, column=1, padx=5, pady=5, sticky="ew")
self.window_combobox.bind("<<ComboboxSelected>>", self.controller.on_window_selected) # Bind to controller directly
self.refresh_button = Button(self.config_frame, text="Refresh Windows", command=self.controller.refresh_windows, font=('Inter', 9))
self.refresh_button.grid(row=2, column=2, padx=5, pady=5, sticky="ew")
self.target_window_validation_label = Label(self.config_frame, text="Please select a target window.", font=('Inter', 8, 'bold'), fg="red")
self.target_window_validation_label.grid(row=3, column=1, padx=5, pady=0, sticky="nw")
self.target_window_validation_label.grid_remove()
# --- Control Buttons Section ---
self.button_frame = tk.Frame(self)
self.button_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="ew")
self.button_frame.grid_columnconfigure(0, weight=1)
self.button_frame.grid_columnconfigure(1, weight=1)
self.button_frame.grid_columnconfigure(2, weight=1) # For Clear Log Button
self.start_button = Button(self.button_frame, text="Start Holding Key", command=self.controller.start_key_press, bg="#2aa198", fg="white", font=('Inter', 12, 'bold'))
self.start_button.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
self.stop_button = Button(self.button_frame, text="Stop Key Press (or F6)", command=self.controller.stop_key_press, bg="#dc322f", fg="white", font=('Inter', 12, 'bold'))
self.stop_button.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
self.stop_button.config(state=tk.DISABLED)
self.clear_log_button = Button(self.button_frame, text="Clear Log", command=self._clear_log, font=('Inter', 9))
self.clear_log_button.grid(row=0, column=2, padx=5, pady=5, sticky="ew")
# --- Log Display Area ---
self.log_frame = tk.LabelFrame(self, text="Activity Log (Jarvis's Observations)", padx=10, pady=10, font=('Consolas', 10, 'bold'))
self.log_frame.grid(row=2, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")
self.log_frame.grid_rowconfigure(0, weight=1)
self.log_frame.grid_columnconfigure(0, weight=1)
self.log_text_area = scrolledtext.ScrolledText(self.log_frame, state='disabled', wrap='word', width=60, height=15, bg="#eee8d5", fg="#586e75", font=('Consolas', 10))
self.log_text_area.grid(row=0, column=0, sticky="nsew")
# Initial population of window list is now triggered by controller's initial_setup_after_view_is_ready()
self.master.after(100, self._poll_message_queue)
# --- View Update Methods (Called by Controller) ---
def update_window_list(self, window_titles, current_selected_title):
"""Updates the combobox with new window titles."""
self.window_combobox['values'] = window_titles
self.target_window_title_var.set(current_selected_title)
def get_key_input(self):
"""Returns the current value of the key input entry."""
return self.key_to_press_var.get().strip()
def get_selected_window_title(self):
"""Returns the currently selected window title from the combobox."""
return self.target_window_title_var.get()
def set_ui_state_running(self):
"""Sets UI elements to a 'running' state."""
self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
self.key_input_entry.config(state=tk.DISABLED)
self.window_combobox.config(state=tk.DISABLED)
self.refresh_button.config(state=tk.DISABLED)
# Validation labels are hidden by UIController when changing state
def set_ui_state_idle(self):
"""Sets UI elements to an 'idle' state."""
self.start_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
self.key_input_entry.config(state=tk.NORMAL)
self.window_combobox.config(state="readonly")
self.refresh_button.config(state=tk.NORMAL)
# Validation labels visibility will be managed by UIController's _check_start_button_state_ui()
def show_input_error(self, message):
"""Displays a messagebox error for input validation."""
messagebox.showerror("Input Error", message)
def set_key_validation_visibility(self, visible):
"""Controls visibility of the key validation label."""
if visible:
self.key_validation_label.grid()
else:
self.key_validation_label.grid_remove()
def set_window_validation_visibility(self, visible):
"""Controls visibility of the window validation label."""
if visible:
self.target_window_validation_label.grid()
else:
self.target_window_validation_label.grid_remove()
def _on_window_selected_event(self, event):
"""Internal handler for combobox selection event, calls controller."""
# This now directly calls the main controller, which will then use the UIController
self.controller.on_window_selected()
def _poll_message_queue(self):
"""Periodically checks the message queue for log updates."""
message_queue = self.controller.get_message_queue()
while True:
try:
record = message_queue.get(block=False)
self.log_text_area.configure(state='normal')
# Add current timestamp to log entries for better tracking
timestamp_str = datetime.datetime.now().strftime('%H:%M:%S')
self.log_text_area.insert(tk.END, f"{timestamp_str} - {record}\n")
self.log_text_area.see(tk.END)
self.log_text_area.configure(state='disabled')
except queue.Empty:
break
self.master.after(100, self._poll_message_queue)
def _clear_log(self):
"""Clears the content of the log text area."""
self.log_text_area.configure(state='normal')
self.log_text_area.delete(1.0, tk.END)
self.log_text_area.configure(state='disabled')