You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建可控制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()

关键修改点说明

  1. 按钮绑定修正:把command=tombol_click()改为command=start_server,确保点击按钮时才触发服务器启动。
  2. 线程分离:用threading.Thread创建新线程运行asyncio事件循环,避免阻塞tkinter主线程,GUI可正常响应操作。
  3. GUI安全更新:在status函数中用window.after(0, ...)将Label的更新操作放到tkinter主线程执行,避免跨线程操作GUI的问题。
  4. 服务器启停控制:保存server_transport和事件循环对象,实现点击按钮切换启停状态,服务器默认持续运行直到手动停止。
  5. 全局变量同步:修改koneksi后立即更新Label的显示,确保GUI和变量值一致。

内容的提问来源于stack exchange,提问作者Safrizal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 19:30:35