0

Hello to all and thanks for coming to my topic.

I am trying to learn on how to create applications using python, I have run into an issue where I am trying to add a background image, fav icon and button images to an auto-clicker application that I am doing (for fun).

Currently I am running into some issues which are frustrating as hell to be honest, one of the issue is that the images do not load and the second is because the images do not load the application itself does not start.

Below you can see the code clean:

import os
import sys
import time
import threading
import tkinter as tk
from pynput.mouse import Button, Controller
from pynput.keyboard import Key, Listener
import logging
from pynput import keyboard

# Define the base path of the script
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

class AutoClickerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Auto Clicker")
        self.root.geometry("761x335")  # Set a specific window size for my desired background image.

        # Set application icon
        self.root.iconbitmap(os.path.join(BASE_DIR, "images/app_icon.ico"))

        # Added logging.
        self.init_logging()

        # Initialize variables.
        self.click_speed = tk.DoubleVar(value=5)
        self.auto_clicking_active = False
        self.auto_click_timer = None

        # Try to load and display background image.
        self.background_image = self.load_background_image()
        self.background_label = tk.Label(root, image=self.background_image)
        self.background_label.place(x=0, y=0, relwidth=1, relheight=1)

        # Creating the UI elements.
        self.label = tk.Label(root, text="Auto Clicker", font=("Verdana", 16))
        self.label.pack()

        # Commented out the welcome label
        self.welcome_label = tk.Label(root, text="Welcome to Auto Clicker!", font=("Verdana", 14))
        self.welcome_label.pack()

        self.speed_label = tk.Label(root, text="Clicks per second:", font=("Verdana", 12))
        self.speed_label.pack()

        self.speed_slider = tk.Scale(root, from_=5, to=999, orient="horizontal", variable=self.click_speed)
        self.speed_slider.pack()

        # Load button images
        self.start_button_image = tk.PhotoImage(file=os.path.join(BASE_DIR, "images/start_button.png"))
        self.stop_button_image = tk.PhotoImage(file=os.path.join(BASE_DIR, "images/stop_button.png"))

        # The buttons to start and stop auto-clicking process.
        self.start_button = tk.Button(root, image=self.start_button_image, command=self.toggle_auto_click)
        self.start_button.pack()

        self.stop_button = tk.Button(root, image=self.stop_button_image, command=self.stop_auto_click)
        self.stop_button.pack()

        # Mouse controller and keyboard listener
        self.mouse = Controller()
        self.keyboard_listener = None

    def init_logging(self):
        # Starting the logging for recording events
        self.logger = logging.getLogger("AutoClicker")
        self.logger.setLevel(logging.DEBUG)

        log_format = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")

        file_handler = logging.FileHandler("autoclicker.log.txt")
        file_handler.setFormatter(log_format)
        self.logger.addHandler(file_handler)

    def load_background_image(self):
        # Hopefully load the background image for the UI
        image_path = os.path.join(BASE_DIR, "images/background_image.png")
        try:
            return tk.PhotoImage(file=image_path)
        except tk.TclError:
            self.logger.error("Background image not found: %s", image_path)
            return None

    def on_press(self, key):
        # Event handler for key press
        if key == Key.f1:
            if not self.auto_clicking_active:
                self.logger.info("Auto Clicker Started")
                self.toggle_auto_click()

    def on_release(self, key):
        # Event handler for key release
        if key == Key.f2:
            self.logger.info("Auto Clicker Stopped")
            self.stop_auto_click()

    def auto_click(self):
        # Auto-clicking logic
        start_time = time.time()
        while self.auto_clicking_active and time.time() - start_time < 30:
            self.mouse.click(Button.left)
            time.sleep(1 / self.click_speed.get())

        self.auto_clicking_active = False
        if self.keyboard_listener:
            self.keyboard_listener.stop()

    def toggle_auto_click(self):
        # Toggle auto-clicking on/off
        if not self.auto_clicking_active:
            self.auto_clicking_active = True
            self.keyboard_listener = Listener(on_press=self.on_press, on_release=self.on_release)
            self.keyboard_listener.start()
            self.auto_click_thread = threading.Thread(target=self.auto_click)
            self.auto_click_thread.start()
        else:
            self.stop_auto_click()

    def stop_auto_click(self):
        # Stop auto-clicking and cleanup
        self.auto_clicking_active = False
        if self.auto_click_thread and self.auto_click_thread.is_alive():
            self.auto_click_thread.join()
        if self.keyboard_listener:
            self.keyboard_listener.stop()

if __name__ == "__main__":
    root = tk.Tk()
    app = AutoClickerApp(root)
    root.mainloop()

As I want to make the application for Windows 10. I am running the following command (example with the background only) to try and add the background image:

pyinstaller --onefile --add-data "background_image.png;." --add-data "images/background_image.png;." auto-clicker.py

But when I start the application it tries to find the background in the temporary files instead of the same directory as the python script.

File structure before running the pyinstaller is as follow -> Folder/auto-clicker.py , Folder/background_image.png , Folder/images/background_image.png .

Upon running the pyinstaller command it creates "Folder/build" folder and "Folder/dist" where the exe is located and the "Folder/auto-clicker.spec" file that contains the following:

block_cipher = None


a = Analysis(
    ['auto-clicker.py'],
    pathex=[],
    binaries=[],
    datas=[('background_image.png', '.'), ('images/background_image.png', '.')],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='auto-clicker',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

And two additional issues I encounter while meddling with the code, which are:

  • When pressing F1 the clicker does not start (if I press it with the mouse it works including the F2 to stop it);
  • When stopping it with F2 I cannot start it again giving "RuntimeError: threads can only be started once".

Tried few different approaches to resolve it with the 'base_dir' defining but the application would load without a background image and giving the following error in the log file:

ERROR - Background image not found: C:\Users\SomeName\AppData\Local\Temp\_MEI464682\images/background_image.png

For the other issues I have tried adding "threading.Thread", which upon starting the application freezes (not responding), tried to change the "pynput" and to add "time.sleep" but does not resolve it.

ZFPuhi
  • 1
  • 1
  • Use `sys._MEIPASS` instead of `BASE_DIR` to load the background image. Have a look at: [What is sys._MEIPASS in Python](https://stackoverflow.com/questions/22472124/what-is-sys-meipass-in-python) – Maurice Meyer Aug 21 '23 at 14:35
  • @MauriceMeyer honestly not sure how exactly to implement that in my case :think: – ZFPuhi Aug 21 '23 at 18:18
  • Have a look at: https://stackoverflow.com/questions/70063838/html-file-to-pdf-in-python-without-wkhtmltopdf/70064940#70064940 – Maurice Meyer Aug 21 '23 at 19:08
  • @MauriceMeyer in the suggested article I can see that the answer to which you are referring is using absolute paths: `htmlPath = 'C:/temp/test.html' pdfPath = 'C:/temp/test_through_exe.pdf else: wkPath = 'C:/.../Downloads/wkhtmltopdf.exe''` In my case can't we have a folder 'images/' located in the python directory that will be included in the process of converting it to an 'exe' file and reading it from that directory itself without having to post a full absolute path? – ZFPuhi Aug 22 '23 at 13:12
  • There is just a hard-coded path for the case, you are running the Python script **not** the exe. You just need this line adjusted to your images: `os.path.join(sys._MEIPASS, "wkhtmltopdf.exe")` – Maurice Meyer Aug 22 '23 at 13:42
  • @MauriceMeyer , ye in my case I want to run it from the 'exe' file. Could you provide me with a little inside on what should I change over the code before export it as an exe? :think: – ZFPuhi Aug 22 '23 at 20:24
  • Read my first comment again. – Maurice Meyer Aug 22 '23 at 20:29

0 Answers0