updating mp-server.py
This commit is contained in:
		
							
								
								
									
										397
									
								
								mp-server.py
									
									
									
									
									
								
							
							
						
						
									
										397
									
								
								mp-server.py
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| import tkinter as tk | import tkinter as tk | ||||||
| from tkinter import filedialog, simpledialog | from tkinter import filedialog, simpledialog, ttk | ||||||
| import json | import json | ||||||
| import socket | import socket | ||||||
| import threading | import threading | ||||||
| @@ -10,13 +10,35 @@ import subprocess | |||||||
| from PIL import Image, ImageTk | from PIL import Image, ImageTk | ||||||
| import pystray  # Add this import for system tray functionality | import pystray  # Add this import for system tray functionality | ||||||
| import io | import io | ||||||
|  | import time | ||||||
|  |  | ||||||
| class MacroServer: | class MacroPadServer: | ||||||
|     def __init__(self, root): |     def __init__(self, root): | ||||||
|         self.root = root |         self.root = root | ||||||
|         self.root.title("MacroPad Server") |         self.root.title("MacroPad Server") | ||||||
|         self.root.geometry("800x600") |         self.root.geometry("800x600") | ||||||
|  |  | ||||||
|  |         # Set dark theme colors | ||||||
|  |         self.bg_color = "#2E2E2E" | ||||||
|  |         self.fg_color = "#FFFFFF" | ||||||
|  |         self.highlight_color = "#3D3D3D" | ||||||
|  |         self.accent_color = "#007BFF" | ||||||
|  |         self.button_bg = "#444444" | ||||||
|  |         self.button_fg = "#FFFFFF" | ||||||
|  |  | ||||||
|  |         # Apply theme | ||||||
|  |         self.root.configure(bg=self.bg_color) | ||||||
|  |         self.style = ttk.Style() | ||||||
|  |         self.style.theme_use('clam') | ||||||
|  |  | ||||||
|  |         # Configure styles | ||||||
|  |         self.style.configure('TFrame', background=self.bg_color) | ||||||
|  |         self.style.configure('TLabel', background=self.bg_color, foreground=self.fg_color) | ||||||
|  |         self.style.configure('TButton', background=self.button_bg, foreground=self.button_fg, borderwidth=0) | ||||||
|  |         self.style.map('TButton', | ||||||
|  |                    background=[('active', self.accent_color), ('disabled', self.highlight_color)], | ||||||
|  |                    foreground=[('active', self.button_fg), ('disabled', '#999999')]) | ||||||
|  |         self.style.configure('TRadiobutton', background=self.bg_color, foreground=self.fg_color) | ||||||
|         self.macros = {} |         self.macros = {} | ||||||
|         self.load_macros() |         self.load_macros() | ||||||
|  |  | ||||||
| @@ -36,9 +58,10 @@ class MacroServer: | |||||||
|         self.root.protocol("WM_DELETE_WINDOW", self.on_closing) |         self.root.protocol("WM_DELETE_WINDOW", self.on_closing) | ||||||
|         # Capture the window minimize event |         # Capture the window minimize event | ||||||
|         self.root.bind("<Unmap>", lambda e: self.minimize_to_tray() if self.root.state() == 'iconic' else None) |         self.root.bind("<Unmap>", lambda e: self.minimize_to_tray() if self.root.state() == 'iconic' else None) | ||||||
|  |  | ||||||
|     def setup_tray_icon(self): |     def setup_tray_icon(self): | ||||||
|         # Create a simple icon for the tray |         # Create a simple icon for the tray | ||||||
|         icon_image = Image.new("RGB", (64, 64), color="blue") |         icon_image = Image.new("RGB", (64, 64), color=self.accent_color) | ||||||
|  |  | ||||||
|         # Menu for the tray icon |         # Menu for the tray icon | ||||||
|         menu = ( |         menu = ( | ||||||
| @@ -72,22 +95,26 @@ class MacroServer: | |||||||
|  |  | ||||||
|     def create_widgets(self): |     def create_widgets(self): | ||||||
|         # Toolbar |         # Toolbar | ||||||
|         toolbar = tk.Frame(self.root) |         toolbar = tk.Frame(self.root, bg=self.highlight_color) | ||||||
|         toolbar.pack(side=tk.TOP, fill=tk.X) |         toolbar.pack(side=tk.TOP, fill=tk.X) | ||||||
|  |  | ||||||
|         tk.Button(toolbar, text="Add Macro", command=self.add_macro).pack(side=tk.LEFT, padx=5, pady=5) |         # Custom button style | ||||||
|         tk.Button(toolbar, text="Edit Macro", command=self.edit_macro).pack(side=tk.LEFT, padx=5, pady=5) |         button_style = {'bg': self.button_bg, 'fg': self.button_fg, 'padx': 10, | ||||||
|         tk.Button(toolbar, text="Delete Macro", command=self.delete_macro).pack(side=tk.LEFT, padx=5, pady=5) |                         'pady': 5, 'bd': 0, 'activebackground': self.accent_color, | ||||||
|         tk.Button(toolbar, text="Minimize to Tray", command=self.minimize_to_tray).pack(side=tk.LEFT, padx=5, pady=5) |                         'activeforeground': self.button_fg, 'relief': tk.FLAT} | ||||||
|  |         tk.Button(toolbar, text="Add Macro", command=self.add_macro, **button_style).pack(side=tk.LEFT, padx=5, pady=5) | ||||||
|  |         tk.Button(toolbar, text="Edit Macro", command=self.edit_macro, **button_style).pack(side=tk.LEFT, padx=5, pady=5) | ||||||
|  |         tk.Button(toolbar, text="Delete Macro", command=self.delete_macro, **button_style).pack(side=tk.LEFT, padx=5, pady=5) | ||||||
|  |         tk.Button(toolbar, text="Minimize to Tray", command=self.minimize_to_tray, **button_style).pack(side=tk.LEFT, padx=5, pady=5) | ||||||
|  |  | ||||||
|         # Macro list frame |         # Macro list frame | ||||||
|         list_frame = tk.Frame(self.root) |         list_frame = tk.Frame(self.root, bg=self.bg_color) | ||||||
|         list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) |         list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) | ||||||
|  |  | ||||||
|         # Scrollable canvas for macros |         # Scrollable canvas for macros | ||||||
|         self.canvas = tk.Canvas(list_frame) |         self.canvas = tk.Canvas(list_frame, bg=self.bg_color, highlightthickness=0) | ||||||
|         scrollbar = tk.Scrollbar(list_frame, orient="vertical", command=self.canvas.yview) |         scrollbar = tk.Scrollbar(list_frame, orient="vertical", command=self.canvas.yview) | ||||||
|         self.scrollable_frame = tk.Frame(self.canvas) |         self.scrollable_frame = tk.Frame(self.canvas, bg=self.bg_color) | ||||||
|  |  | ||||||
|         self.scrollable_frame.bind( |         self.scrollable_frame.bind( | ||||||
|             "<Configure>", |             "<Configure>", | ||||||
| @@ -105,7 +132,8 @@ class MacroServer: | |||||||
|         # Status bar |         # Status bar | ||||||
|         self.status_var = tk.StringVar() |         self.status_var = tk.StringVar() | ||||||
|         self.status_var.set("Server ready. Waiting for connections...") |         self.status_var.set("Server ready. Waiting for connections...") | ||||||
|         status_bar = tk.Label(self.root, textvariable=self.status_var, bd=1, relief=tk.SUNKEN, anchor=tk.W) |         status_bar = tk.Label(self.root, textvariable=self.status_var, bd=1, relief=tk.SUNKEN, | ||||||
|  |                              anchor=tk.W, bg=self.highlight_color, fg=self.fg_color) | ||||||
|         status_bar.pack(side=tk.BOTTOM, fill=tk.X) |         status_bar.pack(side=tk.BOTTOM, fill=tk.X) | ||||||
|  |  | ||||||
|     def load_macros(self): |     def load_macros(self): | ||||||
| @@ -134,7 +162,8 @@ class MacroServer: | |||||||
|         max_cols = 3 |         max_cols = 3 | ||||||
|  |  | ||||||
|         for macro_id, macro in self.macros.items(): |         for macro_id, macro in self.macros.items(): | ||||||
|             frame = tk.Frame(self.scrollable_frame, bd=2, relief=tk.RAISED, padx=5, pady=5) |             frame = tk.Frame(self.scrollable_frame, bd=2, relief=tk.RAISED, padx=5, pady=5, | ||||||
|  |                             bg=self.highlight_color) | ||||||
|             frame.grid(row=row, column=col, padx=10, pady=10, sticky="nsew") |             frame.grid(row=row, column=col, padx=10, pady=10, sticky="nsew") | ||||||
|  |  | ||||||
|             # Load image if exists |             # Load image if exists | ||||||
| @@ -144,16 +173,16 @@ class MacroServer: | |||||||
|                     image = Image.open(io.BytesIO(image_data)) |                     image = Image.open(io.BytesIO(image_data)) | ||||||
|                     image = image.resize((64, 64), Image.LANCZOS) |                     image = image.resize((64, 64), Image.LANCZOS) | ||||||
|                     photo = ImageTk.PhotoImage(image) |                     photo = ImageTk.PhotoImage(image) | ||||||
|                     label = tk.Label(frame, image=photo) |                     label = tk.Label(frame, image=photo, bg=self.highlight_color) | ||||||
|                     label.image = photo  # Keep a reference |                     label.image = photo  # Keep a reference | ||||||
|                     label.pack() |                     label.pack() | ||||||
|                 except Exception as e: |                 except Exception as e: | ||||||
|                     print(f"Error displaying image: {e}") |                     print(f"Error displaying image: {e}") | ||||||
|                     tk.Label(frame, text="[No Image]", width=8, height=4).pack() |                     tk.Label(frame, text="[No Image]", width=8, height=4, bg=self.highlight_color, fg=self.fg_color).pack() | ||||||
|             else: |             else: | ||||||
|                 tk.Label(frame, text="[No Image]", width=8, height=4).pack() |                 tk.Label(frame, text="[No Image]", width=8, height=4, bg=self.highlight_color, fg=self.fg_color).pack() | ||||||
|  |  | ||||||
|             tk.Label(frame, text=macro["name"]).pack() |             tk.Label(frame, text=macro["name"], bg=self.highlight_color, fg=self.fg_color).pack() | ||||||
|  |  | ||||||
|             col += 1 |             col += 1 | ||||||
|             if col >= max_cols: |             if col >= max_cols: | ||||||
| @@ -164,27 +193,55 @@ class MacroServer: | |||||||
|         # Create a dialog to add a new macro |         # Create a dialog to add a new macro | ||||||
|         dialog = tk.Toplevel(self.root) |         dialog = tk.Toplevel(self.root) | ||||||
|         dialog.title("Add Macro") |         dialog.title("Add Macro") | ||||||
|         dialog.geometry("400x300") |         dialog.geometry("450x350")  # Increased height for additional options | ||||||
|         dialog.transient(self.root) |         dialog.transient(self.root) | ||||||
|  |         dialog.configure(bg=self.bg_color) | ||||||
|  |  | ||||||
|         tk.Label(dialog, text="Macro Name:").grid(row=0, column=0, padx=5, pady=5, sticky="w") |         # Apply dark theme to dialog | ||||||
|         name_entry = tk.Entry(dialog, width=30) |         tk.Label(dialog, text="Macro Name:", bg=self.bg_color, fg=self.fg_color).grid(row=0, column=0, padx=5, pady=5, sticky="w") | ||||||
|  |         name_entry = tk.Entry(dialog, width=30, bg=self.highlight_color, fg=self.fg_color, insertbackground=self.fg_color) | ||||||
|         name_entry.grid(row=0, column=1, padx=5, pady=5) |         name_entry.grid(row=0, column=1, padx=5, pady=5) | ||||||
|  |  | ||||||
|         tk.Label(dialog, text="Type:").grid(row=1, column=0, padx=5, pady=5, sticky="w") |         tk.Label(dialog, text="Type:", bg=self.bg_color, fg=self.fg_color).grid(row=1, column=0, padx=5, pady=5, sticky="w") | ||||||
|         type_var = tk.StringVar(value="text") |         type_var = tk.StringVar(value="text") | ||||||
|         tk.Radiobutton(dialog, text="Text", variable=type_var, value="text").grid(row=1, column=1, sticky="w") |  | ||||||
|         tk.Radiobutton(dialog, text="Application", variable=type_var, value="app").grid(row=2, column=1, sticky="w") |  | ||||||
|  |  | ||||||
|         tk.Label(dialog, text="Command/Text:").grid(row=3, column=0, padx=5, pady=5, sticky="w") |         radio_style = {'bg': self.bg_color, 'fg': self.fg_color, 'selectcolor': self.accent_color} | ||||||
|         command_text = tk.Text(dialog, width=30, height=5) |         tk.Radiobutton(dialog, text="Text", variable=type_var, value="text", **radio_style).grid(row=1, column=1, sticky="w") | ||||||
|  |         tk.Radiobutton(dialog, text="Application", variable=type_var, value="app", **radio_style).grid(row=2, column=1, sticky="w") | ||||||
|  |  | ||||||
|  |         tk.Label(dialog, text="Command/Text:", bg=self.bg_color, fg=self.fg_color).grid(row=3, column=0, padx=5, pady=5, sticky="w") | ||||||
|  |         command_text = tk.Text(dialog, width=30, height=5, bg=self.highlight_color, fg=self.fg_color, insertbackground=self.fg_color) | ||||||
|         command_text.grid(row=3, column=1, padx=5, pady=5) |         command_text.grid(row=3, column=1, padx=5, pady=5) | ||||||
|  |  | ||||||
|  |         # Key modifiers frame | ||||||
|  |         mod_frame = tk.Frame(dialog, bg=self.bg_color) | ||||||
|  |         mod_frame.grid(row=4, column=0, columnspan=2, sticky="w", padx=5, pady=5) | ||||||
|  |  | ||||||
|  |         tk.Label(mod_frame, text="Key Modifiers:", bg=self.bg_color, fg=self.fg_color).pack(side=tk.LEFT, padx=5) | ||||||
|  |  | ||||||
|  |         # Add checkboxes for modifiers | ||||||
|  |         ctrl_var = tk.BooleanVar(value=False) | ||||||
|  |         alt_var = tk.BooleanVar(value=False) | ||||||
|  |         shift_var = tk.BooleanVar(value=False) | ||||||
|  |         enter_var = tk.BooleanVar(value=False) | ||||||
|  |  | ||||||
|  |         checkbox_style = {'bg': self.bg_color, 'fg': self.fg_color, 'selectcolor': self.accent_color, | ||||||
|  |                         'activebackground': self.bg_color, 'activeforeground': self.fg_color} | ||||||
|  |  | ||||||
|  |         tk.Checkbutton(mod_frame, text="Ctrl", variable=ctrl_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |         tk.Checkbutton(mod_frame, text="Alt", variable=alt_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |         tk.Checkbutton(mod_frame, text="Shift", variable=shift_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |         tk.Checkbutton(mod_frame, text="Add Enter", variable=enter_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |  | ||||||
|         image_path = tk.StringVar() |         image_path = tk.StringVar() | ||||||
|         tk.Label(dialog, text="Image:").grid(row=4, column=0, padx=5, pady=5, sticky="w") |         tk.Label(dialog, text="Image:", bg=self.bg_color, fg=self.fg_color).grid(row=5, column=0, padx=5, pady=5, sticky="w") | ||||||
|         tk.Entry(dialog, textvariable=image_path, width=30).grid(row=4, column=1, padx=5, pady=5) |         tk.Entry(dialog, textvariable=image_path, width=30, bg=self.highlight_color, fg=self.fg_color, insertbackground=self.fg_color).grid(row=5, column=1, padx=5, pady=5) | ||||||
|  |  | ||||||
|  |         button_style = {'bg': self.button_bg, 'fg': self.button_fg, 'activebackground': self.accent_color, | ||||||
|  |                         'activeforeground': self.button_fg, 'bd': 0, 'relief': tk.FLAT} | ||||||
|  |  | ||||||
|         tk.Button(dialog, text="Browse...", command=lambda: image_path.set(filedialog.askopenfilename( |         tk.Button(dialog, text="Browse...", command=lambda: image_path.set(filedialog.askopenfilename( | ||||||
|             filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif *.bmp")]))).grid(row=4, column=2) |             filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif *.bmp")])), **button_style).grid(row=5, column=2) | ||||||
|  |  | ||||||
|         def save_macro(): |         def save_macro(): | ||||||
|             name = name_entry.get().strip() |             name = name_entry.get().strip() | ||||||
| @@ -208,28 +265,214 @@ class MacroServer: | |||||||
|                 except Exception as e: |                 except Exception as e: | ||||||
|                     print(f"Error processing image: {e}") |                     print(f"Error processing image: {e}") | ||||||
|  |  | ||||||
|             # Create macro |             # Create macro with modifier keys | ||||||
|             self.macros[macro_id] = { |             self.macros[macro_id] = { | ||||||
|                 "name": name, |                 "name": name, | ||||||
|                 "type": macro_type, |                 "type": macro_type, | ||||||
|                 "command": command, |                 "command": command, | ||||||
|                 "image_data": image_data |                 "image_data": image_data, | ||||||
|  |                 "modifiers": { | ||||||
|  |                     "ctrl": ctrl_var.get(), | ||||||
|  |                     "alt": alt_var.get(), | ||||||
|  |                     "shift": shift_var.get(), | ||||||
|  |                     "enter": enter_var.get() | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             self.save_macros() |             self.save_macros() | ||||||
|             self.display_macros() |             self.display_macros() | ||||||
|             dialog.destroy() |             dialog.destroy() | ||||||
|  |  | ||||||
|         tk.Button(dialog, text="Save", command=save_macro).grid(row=5, column=0, padx=5, pady=20) |         tk.Button(dialog, text="Save", command=save_macro, **button_style).grid(row=6, column=0, padx=5, pady=20) | ||||||
|         tk.Button(dialog, text="Cancel", command=dialog.destroy).grid(row=5, column=1, padx=5, pady=20) |         tk.Button(dialog, text="Cancel", command=dialog.destroy, **button_style).grid(row=6, column=1, padx=5, pady=20) | ||||||
|  |  | ||||||
|     def edit_macro(self): |     def edit_macro(self): | ||||||
|         # To be implemented - similar to add_macro but pre-fills fields with existing data |         # First, show a dialog to select which macro to edit | ||||||
|         pass |         if not self.macros: | ||||||
|  |             tk.messagebox.showinfo("No Macros", "There are no macros to edit.") | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         dialog = tk.Toplevel(self.root) | ||||||
|  |         dialog.title("Select Macro to Edit") | ||||||
|  |         dialog.geometry("200x340") | ||||||
|  |         dialog.transient(self.root) | ||||||
|  |         dialog.configure(bg=self.bg_color) | ||||||
|  |  | ||||||
|  |         # Create a listbox to show available macros | ||||||
|  |         tk.Label(dialog, text="Select a macro to edit:", bg=self.bg_color, fg=self.fg_color).pack(pady=5) | ||||||
|  |  | ||||||
|  |         listbox = tk.Listbox(dialog, bg=self.highlight_color, fg=self.fg_color, selectbackground=self.accent_color) | ||||||
|  |         listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | ||||||
|  |  | ||||||
|  |         # Populate the listbox with macro names | ||||||
|  |         macro_ids = [] | ||||||
|  |         for macro_id, macro in self.macros.items(): | ||||||
|  |             listbox.insert(tk.END, macro["name"]) | ||||||
|  |             macro_ids.append(macro_id) | ||||||
|  |  | ||||||
|  |         def on_select(): | ||||||
|  |             if not listbox.curselection(): | ||||||
|  |                 tk.messagebox.showwarning("No Selection", "Please select a macro to edit.") | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             idx = listbox.curselection()[0] | ||||||
|  |             selected_macro_id = macro_ids[idx] | ||||||
|  |             dialog.destroy() | ||||||
|  |             self.open_edit_dialog(selected_macro_id) | ||||||
|  |  | ||||||
|  |         button_style = {'bg': self.button_bg, 'fg': self.button_fg, 'activebackground': self.accent_color, | ||||||
|  |                         'activeforeground': self.button_fg, 'bd': 0, 'relief': tk.FLAT} | ||||||
|  |  | ||||||
|  |         tk.Button(dialog, text="Edit Selected", command=on_select, **button_style).pack(pady=10) | ||||||
|  |         tk.Button(dialog, text="Cancel", command=dialog.destroy, **button_style).pack(pady=5) | ||||||
|  |  | ||||||
|  |     def open_edit_dialog(self, macro_id): | ||||||
|  |         # Open dialog to edit the selected macro | ||||||
|  |         macro = self.macros[macro_id] | ||||||
|  |  | ||||||
|  |         dialog = tk.Toplevel(self.root) | ||||||
|  |         dialog.title("Edit Macro") | ||||||
|  |         dialog.geometry("450x350")  # Increased height for additional options | ||||||
|  |         dialog.transient(self.root) | ||||||
|  |         dialog.configure(bg=self.bg_color) | ||||||
|  |  | ||||||
|  |         # Apply dark theme to dialog | ||||||
|  |         tk.Label(dialog, text="Macro Name:", bg=self.bg_color, fg=self.fg_color).grid(row=0, column=0, padx=5, pady=5, sticky="w") | ||||||
|  |         name_entry = tk.Entry(dialog, width=30, bg=self.highlight_color, fg=self.fg_color, insertbackground=self.fg_color) | ||||||
|  |         name_entry.grid(row=0, column=1, padx=5, pady=5) | ||||||
|  |         name_entry.insert(0, macro["name"]) | ||||||
|  |  | ||||||
|  |         tk.Label(dialog, text="Type:", bg=self.bg_color, fg=self.fg_color).grid(row=1, column=0, padx=5, pady=5, sticky="w") | ||||||
|  |         type_var = tk.StringVar(value=macro["type"]) | ||||||
|  |  | ||||||
|  |         radio_style = {'bg': self.bg_color, 'fg': self.fg_color, 'selectcolor': self.accent_color} | ||||||
|  |         tk.Radiobutton(dialog, text="Text", variable=type_var, value="text", **radio_style).grid(row=1, column=1, sticky="w") | ||||||
|  |         tk.Radiobutton(dialog, text="Application", variable=type_var, value="app", **radio_style).grid(row=2, column=1, sticky="w") | ||||||
|  |  | ||||||
|  |         tk.Label(dialog, text="Command/Text:", bg=self.bg_color, fg=self.fg_color).grid(row=3, column=0, padx=5, pady=5, sticky="w") | ||||||
|  |         command_text = tk.Text(dialog, width=30, height=5, bg=self.highlight_color, fg=self.fg_color, insertbackground=self.fg_color) | ||||||
|  |         command_text.grid(row=3, column=1, padx=5, pady=5) | ||||||
|  |         command_text.insert("1.0", macro["command"]) | ||||||
|  |  | ||||||
|  |         # Modifiers frame | ||||||
|  |         mod_frame = tk.Frame(dialog, bg=self.bg_color) | ||||||
|  |         mod_frame.grid(row=4, column=0, columnspan=2, sticky="w", padx=5, pady=5) | ||||||
|  |  | ||||||
|  |         tk.Label(mod_frame, text="Key Modifiers:", bg=self.bg_color, fg=self.fg_color).pack(side=tk.LEFT, padx=5) | ||||||
|  |  | ||||||
|  |         # Get existing modifiers or set defaults | ||||||
|  |         modifiers = macro.get("modifiers", {}) | ||||||
|  |  | ||||||
|  |         # Add checkboxes for modifiers | ||||||
|  |         ctrl_var = tk.BooleanVar(value=modifiers.get("ctrl", False)) | ||||||
|  |         alt_var = tk.BooleanVar(value=modifiers.get("alt", False)) | ||||||
|  |         shift_var = tk.BooleanVar(value=modifiers.get("shift", False)) | ||||||
|  |         enter_var = tk.BooleanVar(value=modifiers.get("enter", False)) | ||||||
|  |  | ||||||
|  |         checkbox_style = {'bg': self.bg_color, 'fg': self.fg_color, 'selectcolor': self.accent_color, | ||||||
|  |                         'activebackground': self.bg_color, 'activeforeground': self.fg_color} | ||||||
|  |  | ||||||
|  |         tk.Checkbutton(mod_frame, text="Ctrl", variable=ctrl_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |         tk.Checkbutton(mod_frame, text="Alt", variable=alt_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |         tk.Checkbutton(mod_frame, text="Shift", variable=shift_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |         tk.Checkbutton(mod_frame, text="Add Enter", variable=enter_var, **checkbox_style).pack(side=tk.LEFT, padx=5) | ||||||
|  |  | ||||||
|  |         image_path = tk.StringVar() | ||||||
|  |         tk.Label(dialog, text="Image:", bg=self.bg_color, fg=self.fg_color).grid(row=5, column=0, padx=5, pady=5, sticky="w") | ||||||
|  |         image_entry = tk.Entry(dialog, textvariable=image_path, width=30, bg=self.highlight_color, fg=self.fg_color, insertbackground=self.fg_color) | ||||||
|  |         image_entry.grid(row=5, column=1, padx=5, pady=5) | ||||||
|  |  | ||||||
|  |         button_style = {'bg': self.button_bg, 'fg': self.button_fg, 'activebackground': self.accent_color, | ||||||
|  |                         'activeforeground': self.button_fg, 'bd': 0, 'relief': tk.FLAT} | ||||||
|  |  | ||||||
|  |         tk.Button(dialog, text="Browse...", command=lambda: image_path.set(filedialog.askopenfilename( | ||||||
|  |             filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif *.bmp")])), **button_style).grid(row=5, column=2) | ||||||
|  |  | ||||||
|  |         def save_edited_macro(): | ||||||
|  |             name = name_entry.get().strip() | ||||||
|  |             if not name: | ||||||
|  |                 tk.messagebox.showerror("Error", "Macro name is required") | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             new_type = type_var.get() | ||||||
|  |             command = command_text.get("1.0", tk.END).strip() | ||||||
|  |  | ||||||
|  |             # Keep the old image or update with new one | ||||||
|  |             image_data = macro.get("image_data", "") | ||||||
|  |             img_path = image_path.get() | ||||||
|  |             if img_path: | ||||||
|  |                 try: | ||||||
|  |                     with open(img_path, "rb") as img_file: | ||||||
|  |                         image_data = base64.b64encode(img_file.read()).decode('utf-8') | ||||||
|  |                 except Exception as e: | ||||||
|  |                     print(f"Error processing image: {e}") | ||||||
|  |  | ||||||
|  |             # Update macro with modifiers | ||||||
|  |             self.macros[macro_id] = { | ||||||
|  |                 "name": name, | ||||||
|  |                 "type": new_type, | ||||||
|  |                 "command": command, | ||||||
|  |                 "image_data": image_data, | ||||||
|  |                 "modifiers": { | ||||||
|  |                     "ctrl": ctrl_var.get(), | ||||||
|  |                     "alt": alt_var.get(), | ||||||
|  |                     "shift": shift_var.get(), | ||||||
|  |                     "enter": enter_var.get() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             self.save_macros() | ||||||
|  |             self.display_macros() | ||||||
|  |             dialog.destroy() | ||||||
|  |  | ||||||
|  |         tk.Button(dialog, text="Save", command=save_edited_macro, **button_style).grid(row=6, column=0, padx=5, pady=20) | ||||||
|  |         tk.Button(dialog, text="Cancel", command=dialog.destroy, **button_style).grid(row=6, column=1, padx=5, pady=20) | ||||||
|  |  | ||||||
|     def delete_macro(self): |     def delete_macro(self): | ||||||
|         # To be implemented - offers a selection and deletes the chosen macro |         # Show a dialog to select which macro to delete | ||||||
|         pass |         if not self.macros: | ||||||
|  |             tk.messagebox.showinfo("No Macros", "There are no macros to delete.") | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         dialog = tk.Toplevel(self.root) | ||||||
|  |         dialog.title("Delete Macro") | ||||||
|  |         dialog.geometry("200x340") | ||||||
|  |         dialog.transient(self.root) | ||||||
|  |         dialog.configure(bg=self.bg_color) | ||||||
|  |  | ||||||
|  |         # Create a listbox to show available macros | ||||||
|  |         tk.Label(dialog, text="Select a macro to delete:", bg=self.bg_color, fg=self.fg_color).pack(pady=5) | ||||||
|  |  | ||||||
|  |         listbox = tk.Listbox(dialog, bg=self.highlight_color, fg=self.fg_color, selectbackground=self.accent_color) | ||||||
|  |         listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | ||||||
|  |  | ||||||
|  |         # Populate the listbox with macro names | ||||||
|  |         macro_ids = [] | ||||||
|  |         for macro_id, macro in self.macros.items(): | ||||||
|  |             listbox.insert(tk.END, macro["name"]) | ||||||
|  |             macro_ids.append(macro_id) | ||||||
|  |  | ||||||
|  |         def on_delete(): | ||||||
|  |             if not listbox.curselection(): | ||||||
|  |                 tk.messagebox.showwarning("No Selection", "Please select a macro to delete.") | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             idx = listbox.curselection()[0] | ||||||
|  |             selected_macro_id = macro_ids[idx] | ||||||
|  |             selected_name = self.macros[selected_macro_id]["name"] | ||||||
|  |  | ||||||
|  |             # Confirm deletion | ||||||
|  |             if tk.messagebox.askyesno("Confirm Deletion", f"Are you sure you want to delete macro '{selected_name}'?"): | ||||||
|  |                 del self.macros[selected_macro_id] | ||||||
|  |                 self.save_macros() | ||||||
|  |                 self.display_macros() | ||||||
|  |                 dialog.destroy() | ||||||
|  |  | ||||||
|  |         button_style = {'bg': self.button_bg, 'fg': self.button_fg, 'activebackground': self.accent_color, | ||||||
|  |                         'activeforeground': self.button_fg, 'bd': 0, 'relief': tk.FLAT} | ||||||
|  |  | ||||||
|  |         tk.Button(dialog, text="Delete Selected", command=on_delete, **button_style).pack(pady=10) | ||||||
|  |         tk.Button(dialog, text="Cancel", command=dialog.destroy, **button_style).pack(pady=5) | ||||||
|  |  | ||||||
|     def execute_macro(self, macro_id): |     def execute_macro(self, macro_id): | ||||||
|         if macro_id not in self.macros: |         if macro_id not in self.macros: | ||||||
| @@ -238,7 +481,39 @@ class MacroServer: | |||||||
|         macro = self.macros[macro_id] |         macro = self.macros[macro_id] | ||||||
|         try: |         try: | ||||||
|             if macro["type"] == "text": |             if macro["type"] == "text": | ||||||
|                 pyautogui.typewrite(macro["command"]) |                 # Handle key modifiers | ||||||
|  |                 modifiers = macro.get("modifiers", {}) | ||||||
|  |                 keys_to_press = [] | ||||||
|  |  | ||||||
|  |                 # Add modifier keys if enabled | ||||||
|  |                 if modifiers.get("ctrl", False): | ||||||
|  |                     keys_to_press.append('ctrl') | ||||||
|  |                 if modifiers.get("alt", False): | ||||||
|  |                     keys_to_press.append('alt') | ||||||
|  |                 if modifiers.get("shift", False): | ||||||
|  |                     keys_to_press.append('shift') | ||||||
|  |  | ||||||
|  |                 # If there are modifier keys, use hotkey functionality | ||||||
|  |                 if keys_to_press: | ||||||
|  |                     # For single characters with modifiers, use hotkey | ||||||
|  |                     if len(macro["command"]) == 1: | ||||||
|  |                         keys_to_press.append(macro["command"], interval=0.02) | ||||||
|  |                         pyautogui.hotkey(*keys_to_press) | ||||||
|  |                     else: | ||||||
|  |                         # For longer text, press modifiers, type text, then release | ||||||
|  |                         for key in keys_to_press: | ||||||
|  |                             pyautogui.keyDown(key) | ||||||
|  |                         pyautogui.typewrite(macro["command"], interval=0.02) | ||||||
|  |                         for key in reversed(keys_to_press): | ||||||
|  |                             pyautogui.keyUp(key) | ||||||
|  |                 else: | ||||||
|  |                     # No modifiers, just type the text | ||||||
|  |                     pyautogui.typewrite(macro["command"], interval=0.02) | ||||||
|  |  | ||||||
|  |                 # Add Enter/Return if requested | ||||||
|  |                 if modifiers.get("enter", False): | ||||||
|  |                     pyautogui.press('enter') | ||||||
|  |  | ||||||
|             elif macro["type"] == "app": |             elif macro["type"] == "app": | ||||||
|                 subprocess.Popen(macro["command"], shell=True) |                 subprocess.Popen(macro["command"], shell=True) | ||||||
|             return True |             return True | ||||||
| @@ -270,38 +545,54 @@ class MacroServer: | |||||||
|     def handle_client(self, client_socket): |     def handle_client(self, client_socket): | ||||||
|         try: |         try: | ||||||
|             while self.server_running: |             while self.server_running: | ||||||
|                 data = client_socket.recv(1024).decode('utf-8') |                 data = b"" | ||||||
|  |                 while True: | ||||||
|  |                     chunk = client_socket.recv(4096) | ||||||
|  |                     if not chunk: | ||||||
|  |                         break | ||||||
|  |                     data += chunk | ||||||
|  |                     try: | ||||||
|  |                         # Try to see if we've received a complete JSON object | ||||||
|  |                         json_data = json.loads(data.decode('utf-8')) | ||||||
|  |                         break  # If successful, break out of the inner loop | ||||||
|  |                     except json.JSONDecodeError: | ||||||
|  |                         # If it's not a complete JSON object yet, keep reading | ||||||
|  |                         continue | ||||||
|  |  | ||||||
|                 if not data: |                 if not data: | ||||||
|                     break |                     break | ||||||
|  |  | ||||||
|                 try: |                 try: | ||||||
|                     request = json.loads(data) |                     request = json_data | ||||||
|                     if request['action'] == 'get_macros': |                     if request['action'] == 'get_macros': | ||||||
|                         # Send all macros to client |                         # Send all macros to client | ||||||
|                         client_socket.send(json.dumps(self.macros).encode('utf-8')) |                         client_socket.send(json.dumps(self.macros).encode('utf-8')) | ||||||
|                     elif request['action'] == 'execute': |                     elif request['action'] == 'execute': | ||||||
|                         # Execute the specified macro |                         # Execute the specified macro | ||||||
|                         success = self.execute_macro(request['macro_id']) |                         success = self.execute_macro(request['macro_id']) | ||||||
|                         client_socket.send(json.dumps({'success': success}).encode('utf-8')) |                         response = {'success': success} | ||||||
|                 except json.JSONDecodeError: |                         client_socket.send(json.dumps(response).encode('utf-8')) | ||||||
|                     print("Invalid JSON received") |                     else: | ||||||
|  |                         client_socket.send(json.dumps({'error': 'Unknown action'}).encode('utf-8')) | ||||||
|  |                 except (json.JSONDecodeError, KeyError, TypeError) as e: | ||||||
|  |                     error_msg = {'error': f'Invalid request: {str(e)}'} | ||||||
|  |                     client_socket.send(json.dumps(error_msg).encode('utf-8')) | ||||||
|  |                     print(f"JSON processing error: {e}, Data received: {data[:100]}") | ||||||
|  |  | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print(f"Error handling client: {e}") |             print(f"Client handling error: {e}") | ||||||
|         finally: |         finally: | ||||||
|             client_socket.close() |             client_socket.close() | ||||||
|  |             self.status_var.set("Client disconnected. Waiting for connections...") | ||||||
|  |  | ||||||
|     def on_closing(self): |     def on_closing(self): | ||||||
|         # Instead of directly closing, ask if user wants to minimize to tray |         # When the window is closed, minimize to tray instead of closing | ||||||
|         if tk.messagebox.askyesno("Minimize to Tray", "Do you want to minimize to the system tray?"): |         self.minimize_to_tray() | ||||||
|             self.minimize_to_tray() |  | ||||||
|         else: |  | ||||||
|             self.exit_app() |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     import io  # Added for BytesIO |  | ||||||
|     import tkinter.messagebox  # Add this for message boxes |  | ||||||
|  |  | ||||||
|     root = tk.Tk() |     root = tk.Tk() | ||||||
|     app = MacroServer(root) |     app = MacroPadServer(root) | ||||||
|     root.mainloop() |     root.mainloop() | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user