pythonmultithreadingtkintermultiprocessingosc

How to create python GUI apps that control and displaying data from Python-OSC Server?


I am beginner in python. I want to create Python GUI application that control OSC Server (start and stop) and displaying the data into window. The problem is, when I run OSC Server the window did't show up until PSC Server finishes itself. And the function that supposed to change the data within global variable failed to changed.

import tkinter  as tk
from tkinter import ttk

from pythonosc.osc_server import AsyncIOOSCUDPServer
from pythonosc.dispatcher import Dispatcher
import asyncio

ip = "0.0.0.0"
port = 5000
koneksi=None
def filter_handler(address, *args):
    # print(f"{address}: {args}")
    for arg in args:
        status(arg)
        
def status(stat):
    global koneksi
    if stat==1:
        koneksi="Connected"
    else:
        koneksi="Disconnected"
    # print(koneksi)
    
    

dispatcher = Dispatcher()
dispatcher.map("/muse/elements/touching_forehead", filter_handler)



async def loop():
    """Example main loop that only runs for 10 iterations before finishing"""
    for i in range(2):
        print(f"Loop {i}")
        await asyncio.sleep(1)


async def init_main():
    server = AsyncIOOSCUDPServer((ip, port), dispatcher, asyncio.get_event_loop())
    transport, protocol = await server.create_serve_endpoint()  # Create datagram endpoint and start serving
    await loop()  # Enter main loop of program
    transport.close()  # Clean up serve endpoint
        
def tombol_click():
    asyncio.run(init_main())

window=tk.Tk()
window.geometry("600x400")
window.resizable(False,False)
frame=ttk.Frame(window)
labelDevice=ttk.Label(frame,text="Device :")
labelDevice.grid(row=0,column=0, padx=10,pady=10)

labelStatus=ttk.Label(frame,text=koneksi)
labelStatus.grid(row=0,column=1, padx=10,pady=10)
tombol_sapa = ttk.Button(frame,text="start",command=tombol_click())
tombol_sapa.grid(row=1,column=0,padx=10, pady=10)



frame.pack()

window.mainloop()

I'm trying to search similar problem but I can't find the solution.


Solution

  • Using threading. First, we launch a single-threaded tkinter and from it we launch a thread to work with the OSC server.

    import time
    import threading
    import tkinter as tk
    
    
    # Create a window with a button and a label.
    class App(tk.Tk):
        def __init__(self):
            super().__init__()
            self.button = tk.Button(self, command=self.start_action, text="start")
            self.button.pack(padx=100, pady=50)
            self.lable = tk.Label(self, text="initial value")
            self.lable.pack(padx=100, pady=50)
    
    # We start an additional thread for the PSC Server.
        def start_action(self):
            self.button.config(state=tk.DISABLED)
            thread = threading.Thread(target=self.init_main)
            thread.start()
            self.check_thread(thread)
    
    # We make the button active only after the flow is completed.
        def check_thread(self, thread):
            if thread.is_alive():
                self.check = self.after(500, lambda: self.check_thread(thread))
            else:
                self.button.config(state=tk.NORMAL)
    
        def init_main(self):
            # start dispatcher,server
            self.loop()  # Enter main loop of program
            # Clean up serve endpoint
    
        def loop(self):
            """Example main loop that only runs for 10 iterations before finishing"""
            for i in range(2):
                print(f"Loop {i}")
                self.lable.config(text=f"Loop {i}")
                time.sleep(1)
    
    
    
    if __name__ == "__main__":
        app = App()
        app.mainloop()
    

    I took the basis for the program from here.