Hi, so I'm having quite the difficulty here. Let me start by explaining the architecture of my scripts. My "" is a script that uses asyncore in order to listen for connections and receive commands from them. If it receives the text "play" from a connection, it calls another script, "," that uses the tkSnack library to play a sound file (which requires root.mainloop() to be called). So, I have that working well. receives a connection, receives the text "play," and calls "" to play a sound file.

Now, here is where the trouble begins. What good is playing music if you can't pause/stop/restart/etc. it? If I don't change anything about my scripts, the mainloop of Tkinter prevents from receiving any more connections/data.

Immediately, I thought to myself, "Oh, no problem! I can just give it's own thread and then I can continue receiving connections/data!" Sadly, this didn't work. It gives me the following error from Tkinter: RuntimeError: main thread is not in main loop . I guess Tkinter doesn't want its main loop inside a separate thread.

So...what can I do about this? If I were to put into another thread, I would have the same problem...asyncore requires that you call its mainloop method, and I would be willing to bet it would give me the same error as Tkinter did.

Thanks so, so much in advance.

Dani AI

Generated

Quick, practical guidance for and anyone mixing a Tk-based UI with a separate socket/event loop.

Keep the GUI on the main thread and do not call Tk APIs from worker threads; otherwise you get intermittent, hard-to-debug failures because the Tcl/Tk internals expect the GUI to own the main event thread. (github.com)

A very reliable pattern is: run blocking I/O in a background worker (thread or process), push events onto a thread-safe queue, and have the GUI poll that queue on a short interval with Tk’s scheduling (so all widget work stays in the main thread). This cleanly separates concerns and makes shutdown predictable. Example sketch:

import threading, queue, tkinter as tk

q = queue.Queue()
stop = threading.Event()

def worker():
    while not stop.is_set():
        item = blocking_read_from_network()   # replace with your listener loop
        q.put(item)

t = threading.Thread(target=worker, daemon=True)
t.start()

root = tk.Tk()

def poll_queue():
    try:
        while True:
            item = q.get_nowait()
            # update widgets here (main thread)
    except queue.Empty:
        pass
    root.after(100, poll_queue)   # reschedule

poll_queue()
root.mainloop()
stop.set()
t.join()

The queue module is specifically designed for safe handoff between threads; Tk’s after() is the standard way to schedule GUI work without blocking the event loop. (docs.python.org)

Other options depending on platform and future maintenance:

  • Integrate non-blocking I/O into the Tk mainloop (or use Tk’s file-handler hooks on Unix), but note the file-handler API isn’t available on Windows. (docs.python.org)
  • Move away from legacy asyncore (it was deprecated and later removed); consider asyncio for modern async networking and use thread-safe scheduling primitives (e.g., loop.call_soon_threadsafe) if you mix threads and an asyncio loop. (docs.python.org)
  • Run the player or blocking subsystems in a separate process and control them via subprocess/IPC when isolation is desirable. (docs.python.org)

If a working hack is already in place, it may be fine short-term; for robustness and cross-platform maintainability, prefer one of the patterns above and make sure threads/processes are cleanly stopped before destroying the GUI.

Okay, I've done a little more work and hopefully somebody will be able to help me from here. I have found a way to run asyncore's loop after Tkinter's main loop (the after method), but when asyncore does its loop, Tkinter stops doing its main loop, mucking everything up. Is there a way I can run asyncore's loop to change this?

Thanks again.

I found a solution! I am still using Tkinter's after method to run asyncore's loop function, but instead of using asyncore.loop(), I use asyncore.poll(0). If I call that function every 100ms or so, it no longer has an effect on Tkinter's main loop.

Victory.

commented: congrats +10
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.