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

Python中WebSocket的on_message函数无法更新TableWidget数据

问题分析

你的代码存在两个核心问题导致表格无法更新:

  1. 主线程被阻塞:ws.run_forever()会阻塞调用它的主线程(按钮点击事件在主线程执行),导致Qt的UI事件循环无法正常运转,界面卡死,无法刷新表格内容。
  2. 跨线程操作UI:WebSocket的on_message回调在WebSocket工作线程中执行,而PySide要求所有UI操作必须在主线程中进行,跨线程直接修改UI组件会导致数据无法显示或界面异常。
解决方案

通过子线程运行WebSocket+信号槽机制传递数据解决上述问题:

  • 将WebSocket的初始化和运行逻辑移到QThread子类中,避免阻塞主线程。
  • 定义自定义信号,在on_message回调中发射信号传递行情数据。
  • 在主线程中连接信号到槽函数,安全更新TableWidget。
修改后的完整代码
from PySide6.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem
from PySide6.QtCore import QThread, Signal
# Important:
# You need to run the following command to generate the ui_form.py file
#     pyside6-uic form.ui -o ui_form.py, or
#     pyside2-uic form.ui -o ui_form.py
from ui_form import Ui_Widget
import websocket
import json
import sys

class WebSocketWorker(QThread):
    # 自定义信号:传递资产名称与对应价格
    price_update = Signal(str, str)

    def __init__(self):
        super().__init__()
        self.ws = None

    def run(self):
        # 在子线程中初始化并启动WebSocket
        self.ws = websocket.WebSocketApp(
            "wss://stream.binance.com:9443/stream?streams=adausdt@ticker/dotusdt@ticker/xrpusdt@ticker/roseusdt@ticker/runeusdt@ticker/avaxusdt@ticker/xlmusdt@ticker/hbarusdt@ticker/fluxusdt@ticker/qntusdt@ticker/linkusdt@ticker",
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self.on_close
        )
        self.ws.run_forever()

    def on_message(self, ws, message):
        data = json.loads(message)
        current_asset = data['data']['s']
        current_price = data['data']['c']
        print(current_asset, current_price)
        # 发射信号,将数据传递到主线程
        self.price_update.emit(current_asset, current_price)

    def on_error(self, ws, error):
        print(error)

    def on_close(self, ws):
        print("WebSocket closed")

class Widget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_Widget()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(self.start_websocket)
        
        # 初始化WebSocket工作线程
        self.ws_worker = WebSocketWorker()
        # 连接信号到UI更新槽函数
        self.ws_worker.price_update.connect(self.update_table)
        
        # 确保表格有足够的行和列(根据你的需求调整)
        self.ui.tableWidget.setRowCount(2)
        self.ui.tableWidget.setColumnCount(2)
        # 可以设置表头
        self.ui.tableWidget.setHorizontalHeaderLabels(["资产", "价格"])

    def start_websocket(self):
        # 启动子线程(避免重复启动)
        if not self.ws_worker.isRunning():
            self.ws_worker.start()

    def update_table(self, asset, price):
        # 主线程中安全更新表格
        if asset == 'ADAUSDT':
            self.ui.tableWidget.setItem(0, 0, QTableWidgetItem(asset))
            self.ui.tableWidget.setItem(0, 1, QTableWidgetItem(price))
        elif asset == 'DOTUSDT':
            self.ui.tableWidget.setItem(1, 0, QTableWidgetItem(asset))
            self.ui.tableWidget.setItem(1, 1, QTableWidgetItem(price))

    def closeEvent(self, event):
        # 窗口关闭时清理WebSocket线程
        if self.ws_worker.isRunning():
            self.ws_worker.ws.close()
            self.ws_worker.quit()
            self.ws_worker.wait()
        event.accept()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = Widget()
    widget.show()
    sys.exit(app.exec())
关键修改说明
  • 子线程隔离WebSocket:将WebSocket逻辑放到QThread子类中,调用start()启动子线程,避免阻塞主线程的UI事件循环。
  • 信号传递数据:通过自定义信号将行情数据从WebSocket线程传递到主线程,确保UI操作在主线程执行,符合PySide的线程安全要求。
  • 表格初始化:添加表格行、列初始化代码,避免因表格无足够行列导致setItem失效。
  • 资源清理:重写closeEvent,在窗口关闭时关闭WebSocket并停止子线程,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 22:57:48