attwad/python-osc

How to create an OSC server without blocking GUI operations

emoto-yasushi opened this issue · 0 comments

I created an OSC server with a GUI that sends OSC on the client and has "start" and "stop" buttons.

Messages are received without problems, but when I run serve_forever(), the GUI operation is blocked and I cannot press the stop() button.

OSC_Client.py

import argparse
import random
import time

from pythonosc import udp_client

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--ip", default="127.0.0.1",help="The ip of the OSC server")
    parser.add_argument("--port", type=int, default=5005,help="The port the OSC server is listening on")
    args = parser.parse_args()

    client = udp_client.SimpleUDPClient(args.ip, args.port)

    for x in range(10):
        client.send_message("/volume", str(x))
        time.sleep(1)

OSC_GUI_Server.py

import tkinter as tk
import threading
import time
import argparse
from pythonosc import dispatcher
from pythonosc import osc_server

class threading_and_sleepGUI():
    def __init__(self):
        self.stop_flag = False
        self.thread = None
        self.currentVolume = ""
        self.server = None
        self.osc_thread = None
        
    def worker(self):
        print(time.time())
        print(self.currentVolume)
        time.sleep(8)

    def scheduler(self,interval, f, wait = True):
        base_time = time.time()
        next_time = 0
        while not self.stop_flag:
            t = threading.Thread(target = f)
            t.start()
            if wait:
                t.join()
            next_time = ((base_time - time.time()) % interval) or interval
            time.sleep(next_time)

    def start(self):
        if not self.thread:
            self.thread = threading.Thread(target=self.scheduler, args=(1, self.worker, False))
            self.stop_flag=False
            self.thread.start()
            
            self.start_osc_server()

    def stop(self):
        if self.thread:
            self.stop_flag=True
            self.thread.join()
            self.thread=None
            self.server.stop()
            
    def print_volume_handler(self,unused_addr, args):
        self.currentVolume = args[0]

    def start_osc_server(self):
        parser = argparse.ArgumentParser()
        parser.add_argument("--ip", default="", help="The ip to listen on")
        parser.add_argument("--port",type=int, default=5005, help="The port to listen on")
        args = parser.parse_args()

        osc_dispatcher = dispatcher.Dispatcher()
        osc_dispatcher.map("/volume", self.print_volume_handler)

        self.server = osc_server.ThreadingOSCUDPServer((args.ip, args.port), osc_dispatcher)
        print("Serving on {}".format(self.server.server_address))
        self.server.serve_forever()
        
    def GUI_start(self):
        root=tk.Tk()
        Button001=tk.Button(root,text="Start",command=self.start)
        Button001.pack()
        Button002=tk.Button(root,text="Stop",command=self.stop)
        Button002.pack()
        root.mainloop()

        self.stop_flag=True
        self.thread.join()

t = threading_and_sleepGUI()
t.GUI_start()

I have also tried Concurrent Mode using the following code But this time I could not receive any messages. How can I create a server without blocking the GUI?

OSC_GUI_Concurrent_Server.py

import tkinter as tk
import threading
import time
import argparse
import pythonosc
from pythonosc.osc_server import AsyncIOOSCUDPServer
from pythonosc.dispatcher import Dispatcher
import asyncio

class threading_and_sleepGUI():
	def __init__(self):
		self.stop_flag = False
		self.thread = None
		self.currentVolume = ""
		self.transport = None
		
	def worker(self):
		print(time.time())
		print(self.currentVolume)
		time.sleep(8)

	def scheduler(self,interval, f, wait = True):
		base_time = time.time()
		next_time = 0
		while not self.stop_flag:
			t = threading.Thread(target = f)
			t.start()
			if wait:
				t.join()
			next_time = ((base_time - time.time()) % interval) or interval
			time.sleep(next_time)

	def start(self):
		if not self.thread:
			self.thread = threading.Thread(target=self.scheduler, args=(1, self.worker, False))
			self.stop_flag=False
			self.thread.start()
			
			asyncio.run(self.start_osc_server())

	def stop(self):
		if self.thread:
			self.stop_flag=True
			self.thread.join()
			self.thread=None
			self.transport.close()
			
	def print_volume_handler(self,unused_addr, args):
		self.currentVolume = args[0]

	async def start_osc_server(self):
		dispatcher = Dispatcher()
		dispatcher.map("/volume", self.print_volume_handler)
		
		ip = ""
		port = 5005
		
		server = AsyncIOOSCUDPServer((ip, port), dispatcher, asyncio.get_event_loop())
		self.transport, protocol = await server.create_serve_endpoint()  # Create datagram endpoint and start serving

	def GUI_start(self):
		root=tk.Tk()
		Button001=tk.Button(root,text="Start",command=self.start)
		Button001.pack()
		Button002=tk.Button(root,text="Stop",command=self.stop)
		Button002.pack()
		root.mainloop()

		self.stop_flag=True
		self.thread.join()

t = threading_and_sleepGUI()
t.GUI_start()