Python Forum
What to do to stop all these threads?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
What to do to stop all these threads?
#1
Hi guys, sorry for my noobness again:

I noticed when I was closing my app the task icon on visual code studio was still running. So my app was not terminating accordingly:

I decided printing the processes were running after closing the window and noticed this list:

MainThread
Thread-3 (listen)
Thread-4 (process)
Thread-5 (create_popup)
Thread-6 (<lambda>)
Dummy-41

So, it has to do with threads and not correctly stopping them. I read some articles, tried sending stop signals, but I cant get it to work. I am having difficulties dealing probrably with pywebivew loop (that starts with webview.start) and the tkinter popup loop. I am sucessfully stopping the keyboard listener in my Keylistener class, however there is still a listener thread that I am now able to stop. And I dont know where these "lambda" and "Dummy41" threads come from.

Can someone please give me a starting point? I began writing the code without a solid base, and then I am realizing that I need to come back to build something more robust to have less problems in the future. Thanks.



class Api:
    def __init__(self):
        

        self.is_maximized = False
        self.events = []

    def create_and_position_window(self):
        monitor = get_monitors()[0]
        screen_width = monitor.width
        screen_height = monitor.height
        pos_x = (screen_width - WINDOW_WIDTH) // 2
        pos_y = (screen_height - WINDOW_HEIGHT) // 2

        self.window = webview.create_window(
            title=WINDOW_TITLE,
            url="index.html",
            frameless=False,
            resizable=True,
            js_api=self,
            min_size=(screen_width // 2, WINDOW_HEIGHT),
        )

        time.sleep(1)

        window = get_window()

        if window:
            window.moveTo(pos_x, pos_y)

        threading.Thread(target=self.call_load_handler_after_delay).start()

    def call_load_handler_after_delay(self):
        time.sleep(0.5)
        load_handler(self.window)



    def create_popup(tk_queue, key_listener_instance):
    
         print("Entered create_popup")  # Debugging
    
         while True:
         queue_data = tk_queue.get()
         msg, data = queue_data[:2]  # Only take the first two values

         current_window = getActiveWindow()
         current_win_title = current_window.title if current_window else "Unknown Window"
        
        if msg == "create_popup":
            print("About to stop listener and create popup")  # Debugging
            key_listener_instance.stop_listener()

            windows = gw.getWindowsWithTitle(current_win_title)
            if windows:
                main_win = windows[0]
                pyautogui.click(main_win.left + 10, main_win.top + 10)
            else:
                print(f"No window with title '{current_win_title}' found.")

            popup = ctk.CTk()  # Use ctk instead of tk
            popup.title("Select Expansion")

            for i, option in enumerate(key_listener_instance.expansions_list):
                raw_button_text = option['expansion'] if 'expansion' in option else "Undefined"
                button_text = truncate_text(raw_button_text, 60)
                button = ctk.CTkButton(
                    popup,
                    text=button_text,
                    command=partial(key_listener_instance.make_selection, i, popup),
                    font=("Work Sans", 12),
                    anchor="w"
                )
                button.pack(fill=ctk.X, padx=10, pady=5)

            # Update idle tasks to get updated dimensions
            popup.update_idletasks()

            # Get the content width and height
            content_width = 400
            content_height = popup.winfo_height()

              # Get the caret's screen coordinates
            caret_x, caret_y = get_caret_position()

                # Set the popup window's position to be 20 pixels below the y-coordinate of the caret
            popup.geometry(f"{content_width}x{content_height}+{caret_x}+{caret_y + 120}")

          
         
            def on_closing():
                try:
                    print("Trying to restart the listener...")  # Debugging
                    key_listener_instance.start_listener()  # Start the listener
                except Exception as e:
                    print(f"Failed to restart listener. Exception: {e}")
                finally:
                    popup.destroy()

            popup.protocol("WM_DELETE_WINDOW", on_closing)
            print("Setting WM_DELETE_WINDOW protocol")  # Debugging

            popup.attributes("-topmost", True)
            popup.focus_force()

            print("Entering Tkinter mainloop")  # Debugging
            popup.mainloop()




##############################################################
def start_app(tk_queue):
    global api  # Existing line
    api = Api()  # Existing line
    api.create_and_position_window()  # Existing line

    key_listener_instance = KeyListener(api, tk_queue)  # Moved inside start_app
    key_listener_thread = threading.Thread(target=key_listener_instance.start_listener)
    key_listener_thread.start()
    key_listener_thread.join() 

    # Pass the key_listener_instance to create_popup
    threading.Thread(target=create_popup, args=(tk_queue, key_listener_instance)).start()

    print("Starting Listener from Main.py")  # Existing line
    
    try:
        webview.start(http_server=True)
   
    finally:
        print('Cleanup function called.')
        key_listener_instance.stop_listener()
      
        
        
        time.sleep(2)
        for thread in threading.enumerate():
           print(thread.name)
             

def main():
    
    def on_close():
        # This function will be executed when the main window is closed
        root.quit()

    tk_queue = queue.Queue()  # Make sure to import the 'queue' module

    # Initialize Tkinter root window
    root = tk.Tk()
    root.withdraw()  # Hide the main Tkinter window

    root.protocol("WM_DELETE_WINDOW", on_close)  # Bind the close event to on_close function

    # Call start_app
    start_app(tk_queue)

    root.mainloop()


# Start the application
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"An error occurred: {e}")
Reply
#2
You do something in all your posts that is really annoying. You never post the imports. Posting the imports lets other know what packages you are using.

Why do you do this:
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"An error occurred: {e}")
with the try/except I get one error message when there is a exception. Without it I get the same error message, additional information, and a traceback. In this case the default exception handler is better than yours.

Now back to your question:

Your code is a mess. Start by cleaning up the code, and I think the issue of closing threads will disappear.

Why are you creating two main windows? You create one in main() that is never used. You create another in create_popup(). tkinter does not like it when you create root windows outside the main thread. Create the popup window in main(). deiconify the window in create_popup(). One thread down.

Does KeyListener need to run in a thread? I thought you were using the keyboard package. The keyboard package is non-blocking. You don't post the code so I cannot see if there is anything else being done in KeyListener that requires it runs in its own thread. Probably not. Two threads down.

So now we are down to these threads:
MainThread
Thread-4 (process)
Thread-6 (<lambda>)
Dummy-41

I don't see where Thread-4, Thrads-6 or Dummy-41 are created. I cannot comment on code that I have not seen.
Reply
#3
Sorry bro. I know that everything must be not as correct as a code made by someone with more experience.. As I said, I am starting. So I will stop bothering you. Guees is forum for more experienced poeple. Thanks
Reply
#4
If you stop your whining I'll stop blowing off steam. You've gotten a lot of help from this forum and you know it.

When you signed up with the forum you signed up to follow the rules of the forum. Along side the no profanity and be courteous rules (I occasionally slip up here) are suggestions about how to post questions to the forums. Did you read those? They are good suggestions. If you want to review, they are here:

https://python-forum.io/misc.php?action=help

In particular take a look at "How to ask Smart Questions" and "What to include in a post". Do your posts follow these suggestions? I don't think you've ever posted a runnable example, let alone a small runnable example. If you ask better questions, as described in the forum help, I guarantee you will get better answers.

Back to your question:

I really think that your shutdown problem is caused by your code being a mess. As a beginner, don't know what you are doing, and you took on a substantial task. I am impressed that it works at all. I don't think I could have made that my first python program and got it to work. But the code has grown organically and it has become a mess. It's full of history and unfollowed paths, and it is time to take a step back and review what you have. What parts are needed, and which can be removed. How can the code be restructured to be more understandable?

Why is the KeyListener being run in a thread? Is it because you used to use pynput and that had a listener that blocked? If that was the reason, it is no longer a reason now that you are using keyboard. Or is there some other reason for the thread?

Last week I showed you a way to do the popup window with 1 tkinter root window. The window is withdrawn or deiconified to make it disappear/appear. I also mentioned that tkinter does not like running mainloop outside the main thread. You are making mulitple root windows and some of them outside the main thread. If you clean that up it will get rid of another thread.

The other threads are new to me. Not sure what process does, and I am really curious to see what a lambda thread does. Why are these threads? What do they do?

So, do you want to continue?
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  stop multiple threads jeuvrey 5 3,400 Nov-15-2018, 01:34 PM
Last Post: jeuvrey

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020