Auto-clicker skrevet i Python med Gemini

Jeg elsker clicker- og idle-spil – spil der ofte involverer en masse klikkeri med musen. Og sådan noget kan automatiseres. Der findes allerede et hav af forskellige småprogrammer, der løser “problemet”, men jeg synes det kunne være sjovt at se om Gemini kunne spytte en autoclicker-app up.

Første prompt til Gemini 3 i Pro indstilling:

Jeg vil gerne lave en autoclicker-app til windows 11. Hjælp mig med at gøre det.

Det resulterede i lidt Python-kode, og med udviklerens vigtigste værktøj (CTRL-C + CTRL-V) fik jeg smidt koden i Visual Studio Code for en testkørsel. Allerede første version er faktisk ganske anstændig:

Meeeeen, den måde at indstille intervallet mellem klikkene er ikke lige min kop te, så det skal på plads:

Lad mig indstille intervallet mere nøjagtigt. Timer, minutter, sekunder og millisekunder mellem hvert klik.
Lav en "start" og "stop" knap.

Efter den rettelse ser app’en sådan ud:

Gemini foreslog at lave en “Pick Location”-funktion:

Would you like me to add the "Pick location" feature next, so you can tell the mouse exactly where to click on the screen?

Interessant og god idé, men så kan vi ligeså godt gøre det endnu sjovere:

Ja tak, jeg vil gerne have en pick location, men den skal være mere avanceret.
Det skal være muligt at tilføje flere locations, som så klikkes i rækkefølge i løkke med det valgte interval.

Det var faktisk nok til at lave en brugbar app, der gør lige præcis hvad jeg har brug for:

[autoclicker.py]
import tkinter as tk
from tkinter import ttk
import threading
import time
import pyautogui
import keyboard

class AutoClickerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Advanced Auto Clicker")
        self.root.geometry("600x350") # Gjort bredere for at gøre plads til listen
        self.root.resizable(False, False)

        # State variables
        self.running = False
        self.interval = 0.1
        self.coordinates = [] # Liste over (x, y) punkter
        self.coord_index = 0  # Hvilket punkt er vi nået til?
        
        # Variabel til Radiobuttons (0 = Current Location, 1 = Coordinate List)
        self.click_mode = tk.IntVar(value=0)

        # UI Setup
        self.setup_ui()
        
        # Start background thread
        self.click_thread = threading.Thread(target=self.clicker_logic)
        self.click_thread.daemon = True
        self.click_thread.start()

        # Hotkeys
        keyboard.add_hotkey('F6', self.toggle_clicking)
        keyboard.add_hotkey('F7', self.add_current_location) # Ny hotkey til at gemme punkt

    def setup_ui(self):
        # --- Left Side: Settings ---
        left_frame = ttk.Frame(self.root)
        left_frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)

        # 1. Click Interval
        interval_frame = ttk.LabelFrame(left_frame, text="Click interval")
        interval_frame.pack(fill="x", pady=5)
        
        input_container = ttk.Frame(interval_frame)
        input_container.pack(padx=5, pady=10)

        self.ent_hours = self.create_time_input(input_container, "hours", "0")
        self.ent_mins = self.create_time_input(input_container, "mins", "0")
        self.ent_secs = self.create_time_input(input_container, "secs", "0")
        self.ent_ms = self.create_time_input(input_container, "ms", "100")

        # 2. Click Options / Mode
        mode_frame = ttk.LabelFrame(left_frame, text="Cursor position")
        mode_frame.pack(fill="x", pady=10)

        rb_current = ttk.Radiobutton(mode_frame, text="Current location", variable=self.click_mode, value=0)
        rb_current.pack(anchor="w", padx=10, pady=2)

        rb_list = ttk.Radiobutton(mode_frame, text="Coordinate List (Sequence)", variable=self.click_mode, value=1)
        rb_list.pack(anchor="w", padx=10, pady=2)

        # 3. Status & Start/Stop
        control_frame = ttk.Frame(left_frame)
        control_frame.pack(pady=20)

        self.status_label = ttk.Label(control_frame, text="Status: STOPPED", foreground="red", font=("Segoe UI", 10, "bold"))
        self.status_label.pack(pady=5)

        btn_frame = ttk.Frame(left_frame)
        btn_frame.pack()

        self.btn_start = ttk.Button(btn_frame, text="Start (F6)", command=self.start_clicking)
        self.btn_start.pack(side="left", padx=5, ipadx=5)

        self.btn_stop = ttk.Button(btn_frame, text="Stop (F6)", command=self.stop_clicking)
        self.btn_stop.pack(side="left", padx=5, ipadx=5)
        self.btn_stop.state(['disabled'])

        # --- Right Side: Coordinate List ---
        right_frame = ttk.LabelFrame(self.root, text="Location List")
        right_frame.pack(side="right", fill="both", padx=10, pady=10, expand=True)

        # Listbox with Scrollbar
        list_scroll = ttk.Scrollbar(right_frame)
        list_scroll.pack(side="right", fill="y")

        self.lst_coords = tk.Listbox(right_frame, yscrollcommand=list_scroll.set, height=10)
        self.lst_coords.pack(side="left", fill="both", expand=True, padx=5, pady=5)
        list_scroll.config(command=self.lst_coords.yview)

        # List Controls
        lbl_instr = ttk.Label(right_frame, text="Hover & Press F7 to add", foreground="gray", font=("Segoe UI", 8))
        lbl_instr.pack(side="bottom", pady=2)

        btn_clear = ttk.Button(right_frame, text="Clear List", command=self.clear_locations)
        btn_clear.pack(side="bottom", fill="x", padx=5, pady=5)

    def create_time_input(self, parent, label_text, default_val):
        frame = ttk.Frame(parent)
        frame.pack(side="left", padx=2)
        entry = ttk.Entry(frame, width=4, justify='right')
        entry.insert(0, default_val)
        entry.pack(side="left")
        lbl = ttk.Label(frame, text=label_text)
        lbl.pack(side="left")
        return entry

    def get_total_seconds(self):
        try:
            h = int(self.ent_hours.get() or 0)
            m = int(self.ent_mins.get() or 0)
            s = int(self.ent_secs.get() or 0)
            ms = int(self.ent_ms.get() or 0)
            return (h * 3600) + (m * 60) + s + (ms / 1000.0)
        except ValueError:
            return 0.1

    def add_current_location(self):
        """Adds the current mouse x,y to the list"""
        x, y = pyautogui.position()
        self.coordinates.append((x, y))
        self.lst_coords.insert(tk.END, f"Point {len(self.coordinates)}: X={x}, Y={y}")
        # Auto-select the radio button for list mode if user starts adding points
        self.click_mode.set(1) 

    def clear_locations(self):
        self.coordinates = []
        self.lst_coords.delete(0, tk.END)
        self.coord_index = 0

    def start_clicking(self):
        if not self.running:
            self.interval = self.get_total_seconds()
            self.running = True
            self.status_label.config(text="Status: RUNNING", foreground="green")
            self.btn_start.state(['disabled'])
            self.btn_stop.state(['!disabled'])

    def stop_clicking(self):
        if self.running:
            self.running = False
            self.status_label.config(text="Status: STOPPED", foreground="red")
            self.btn_start.state(['!disabled'])
            self.btn_stop.state(['disabled'])

    def toggle_clicking(self):
        if self.running:
            self.stop_clicking()
        else:
            self.start_clicking()

    def clicker_logic(self):
        while True:
            if self.running:
                # Mode 1: Coordinate List
                if self.click_mode.get() == 1 and self.coordinates:
                    # Get current target
                    target_x, target_y = self.coordinates[self.coord_index]
                    
                    pyautogui.click(target_x, target_y)
                    
                    # Move to next point in list, loop back to 0 if at end
                    self.coord_index = (self.coord_index + 1) % len(self.coordinates)
                
                # Mode 0: Current Location (or List is empty)
                else:
                    pyautogui.click()
                
                time.sleep(self.interval)
            else:
                time.sleep(0.01)

if __name__ == "__main__":
    root = tk.Tk()
    try:
        from ctypes import windll
        windll.shcore.SetProcessDpiAwareness(1)
    except:
        pass
    app = AutoClickerApp(root)
    root.mainloop()
[### Sådan bruger du den nye "Pick Location" funktion:

Start scriptet.

Bevæg musen hen over det sted på skærmen, du vil klikke på.

Tryk på F7. Du vil se koordinaterne (f.eks. X=500, Y=300) dukke op i listen til højre.

Gentag for så mange punkter, du vil.

Sørg for, at "Coordinate List" er valgt (det sker automatisk, når du tilføjer punkter).

Tryk Start (F6).

Nu vil musen hoppe til Punkt 1, klikke, vente tiden ud, hoppe til Punkt 2, klikke, osv. Når den når bunden af listen, starter den forfra ved Punkt 1.]

Den “færdige” app (den kan jo stadig forbedres) skrevet på 5 minutter i Google Gemini 3 Pro. Og nu skal jeg lige finde et spil frem, og snyde som en gris!