如何创建可控制Python-OSC Server并显示数据的Python GUI应用
解决Python GUI与OSC Server协作的两个问题
问题1:GUI窗口被OSC Server阻塞
你的代码里有两个导致GUI阻塞的问题:
- 按钮绑定错误:
command=tombol_click()会在创建按钮时立即执行函数,而非点击按钮时触发,应改为command=tombol_click(去掉括号)。 asyncio.run()会阻塞主线程,而tkinter主循环也在主线程,导致GUI无法响应,必须把OSC Server的运行放到单独线程中,避免占用主线程。
问题2:全局变量修改后GUI不更新
- 修改全局变量
koneksi后,tkinter的Label不会自动同步值,必须手动调用config()方法更新显示。 - OSC回调函数运行在asyncio事件循环线程中,不能直接操作tkinter GUI元素,需用
window.after()将GUI更新操作切换到tkinter主线程执行。 - 原代码的
loop()只运行2次就关闭服务器,无法持续接收OSC数据,需调整逻辑让服务器持续运行,直到用户主动停止。
修改后的完整代码
import tkinter as tk from tkinter import ttk from pythonosc.osc_server import AsyncIOOSCUDPServer from pythonosc.dispatcher import Dispatcher import asyncio import threading ip = "0.0.0.0" port = 5000 koneksi = None server_transport = None loop = None def filter_handler(address, *args): for arg in args: status(arg) def status(stat): global koneksi # 更新全局变量 koneksi = "Connected" if stat == 1 else "Disconnected" # 用after将GUI更新操作切回主线程 window.after(0, lambda: labelStatus.config(text=koneksi)) dispatcher = Dispatcher() dispatcher.map("/muse/elements/touching_forehead", filter_handler) async def run_server(): global server_transport server = AsyncIOOSCUDPServer((ip, port), dispatcher, asyncio.get_event_loop()) server_transport, protocol = await server.create_serve_endpoint() # 让服务器持续运行,直到手动关闭 await asyncio.Event().wait() def start_server(): global loop # 创建新线程运行asyncio事件循环 def run_loop(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(run_server()) threading.Thread(target=run_loop, daemon=True).start() tombol_sapa.config(text="stop", command=stop_server) def stop_server(): global server_transport, loop if server_transport: server_transport.close() if loop: loop.call_soon_threadsafe(loop.stop) tombol_sapa.config(text="start", command=start_server) 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=start_server) tombol_sapa.grid(row=1, column=0, padx=10, pady=10) frame.pack() window.mainloop()
关键修改点说明
- 按钮绑定修正:把
command=tombol_click()改为command=start_server,确保点击按钮时才触发服务器启动。 - 线程分离:用
threading.Thread创建新线程运行asyncio事件循环,避免阻塞tkinter主线程,GUI可正常响应操作。 - GUI安全更新:在
status函数中用window.after(0, ...)将Label的更新操作放到tkinter主线程执行,避免跨线程操作GUI的问题。 - 服务器启停控制:保存
server_transport和事件循环对象,实现点击按钮切换启停状态,服务器默认持续运行直到手动停止。 - 全局变量同步:修改
koneksi后立即更新Label的显示,确保GUI和变量值一致。
内容的提问来源于stack exchange,提问作者Safrizal
相关产品推荐
相关产品推荐

