Динамическое отображение данных в PyQt

С помощью библиотеки Pybit я получаю данные криптовалют в реальном времени. Решил усложнить себе задачу и создать для удобства интерфейс на PyQt6, но столкнулся с проблемой.

При запуске программы я получаю одновременно информацию по двум объектам из списка в одном окне. Эта информация динамически изменяется, показывая то один объект то другой (См. Рис. 1, Рис. 2).

(Рис. 1) Рис. 1

(Рис. 2) Рис. 2

Как добавить полученную информацию c новой строки по каждому объекту списка, взависимости от их колличества?

Я ожидаю при запуске программы такую картину: (См.Рис.3)

(Рис.3) Рис.3

Вот код интерфейса:

from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1500,200)
        MainWindow.setStyleSheet("background-color: #282828")
   
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
    
        self.search = QtWidgets.QLineEdit(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(32)
        self.search.setFont(font)
        self.search.setToolTipDuration(-3)
        self.search.setStyleSheet("background-color: #161a1e; color: white; border: 3px solid #161a1e")
        self.search.setObjectName("search")
        self.gridLayout.addWidget(self.search, 0, 0, 1, 4)
   
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(32)
        self.pushButton.setFont(font)
        self.pushButton.setStyleSheet("background-color: #161a1e; color: white;")
        self.pushButton.setObjectName("pushButton")
        self.gridLayout.addWidget(self.pushButton, 0, 4, 1, 1)
    
        self.line_1 = QtWidgets.QFrame(self.centralwidget)
        self.line_1.setStyleSheet("")
        self.line_1.setObjectName("line_1")
        self.line_1.setFrameShape(QtWidgets.QFrame.Shape.HLine)
        self.line_1.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.gridLayout.addWidget(self.line_1, 1, 0, 1, 5)
    
        self.marketLabel_1 = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(24)
        self.marketLabel_1.setFont(font)
        self.marketLabel_1.setStyleSheet("color: white")
        self.marketLabel_1.setFixedHeight(40)
        self.marketLabel_1.setObjectName("marketLabel_1")
        self.gridLayout.addWidget(self.marketLabel_1, 2, 0, 1, 1)
    
        self.marketLabel_2 = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(24)
        self.marketLabel_2.setFont(font)
        self.marketLabel_2.setStyleSheet("color: white")
        self.marketLabel_2.setFixedHeight(40)
        self.marketLabel_2.setObjectName("marketLabel_2")
        self.gridLayout.addWidget(self.marketLabel_2, 2, 1, 1, 1)

        self.marketLabel_3 = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(24)
        self.marketLabel_3.setFont(font)
        self.marketLabel_3.setStyleSheet("color: white")       
        self.marketLabel_3.setFixedHeight(40)
        self.marketLabel_3.setObjectName("marketLabel_3")
        self.gridLayout.addWidget(self.marketLabel_3, 2, 2, 1, 1)
    
        self.marketLabel_4 = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(24)
        self.marketLabel_4.setFont(font)
        self.marketLabel_4.setStyleSheet("color: white") 
        self.marketLabel_4.setFixedHeight(40)
        self.marketLabel_4.setObjectName("marketLabel_4")
        self.gridLayout.addWidget(self.marketLabel_4, 2, 3, 1, 1)

        self.line_2 = QtWidgets.QFrame(self.centralwidget)
        self.line_2.setStyleSheet("")
        self.line_2.setObjectName("line_2")
        self.line_2.setFrameShape(QtWidgets.QFrame.Shape.HLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.gridLayout.addWidget(self.line_2, 3, 0, 1, 5)

        self.tradingPairs = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.tradingPairs.setFont(font)
        self.tradingPairs.setStyleSheet("color: white;")
        self.tradingPairs.setFixedHeight(40)
        self.tradingPairs.setObjectName("tradingPairs")
        self.gridLayout.addWidget(self.tradingPairs, 4, 0, 1, 1)
      
        self.lastTradedPrice = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.lastTradedPrice.setFont(font)
        self.lastTradedPrice.setStyleSheet("color: white;")
        self.lastTradedPrice.setObjectName("lastTradedPrice")
        self.gridLayout.addWidget(self.lastTradedPrice, 4, 1, 1, 1)

        self.percentageChange = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.percentageChange.setFont(font)
        self.percentageChange.setStyleSheet("color: white;")
        self.percentageChange.setObjectName("percentageChange")
        self.gridLayout.addWidget(self.percentageChange, 4, 2, 1, 1)

        self.turnover = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.turnover.setFont(font)
        self.turnover.setStyleSheet("color: white;")        
        self.turnover.setObjectName("turnover")
        self.gridLayout.addWidget(self.turnover, 4, 3, 1, 1)  

        self.line_3 = QtWidgets.QFrame(self.centralwidget)
        self.line_3.setStyleSheet("")
        self.line_3.setObjectName("line_3")
        self.line_3.setFrameShape(QtWidgets.QFrame.Shape.HLine)
        self.line_3.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.gridLayout.addWidget(self.line_3, 1000, 0, 1, 4)

Вот программы:

import sys
import time
from datetime import datetime

from plyer import notification
from pybit import usdt_perpetual
from PyQt6 import QtCore, QtGui, QtWidgets

from programWindow import Ui_MainWindow

class HandleMessage(QtWidgets.QMainWindow):    
    def __init__(self, *args, **kwds):
        super(HandleMessage, self).__init__()
        self.programWindow = Ui_MainWindow()
        self.programWindow.setupUi(self) 
        self.display()
        self.connect()
        self.addNewLine()
    
    def display(self):
        self.programWindow.search.setPlaceholderText('Пример: btc, eth, doge')
        self.programWindow.pushButton.clicked.connect(self.addNewLine)

    #Подключение
    def connect(self): 
        symbol_list = ["BTCUSDT", "DOGEUSDT"]

    ws_linear = usdt_perpetual.WebSocket(
        test=False,
        ping_interval=3000,
        ping_timeout=2000,
        domain="bybit"
        )
    
    ws_linear.kline_stream(
        callback=self.handleMessage, 
        symbol=symbol_list, 
        interval="1"
        )
    #Получение книги заказов   
    def handleMessage(self, message):
        data = message['data'][0]
        open = data["open"]
        symbol = message["topic"][9:]
        close = data['close']
        turnover = '{0:,}'.format(int(float(data['turnover']))).replace(',', '.')
        priceChange = round((close/open-1)*100, 2)
          
        self.programWindow.lastTradedPrice.setText(str(close))
        self.programWindow.tradingPairs.setText(symbol)
        self.programWindow.percentageChange.setText(str(priceChange))
        self.programWindow.turnover.setText(str(turnover))

        if close > open: 
            self.programWindow.lastTradedPrice.setStyleSheet("color: yellow;\nborder: 0")
            self.programWindow.percentageChange.setStyleSheet("color: yellow;\nborder: 0")
        elif close < open:   
            self.programWindow.lastTradedPrice.setStyleSheet("color: red;\nborder: 0")
            self.programWindow.percentageChange.setStyleSheet("color: red;\nborder: 0")
        else:
            self.programWindow.lastTradedPrice.setStyleSheet("color: white;\nborder: 0")
            self.programWindow.percentageChange.setStyleSheet("color: white;\nborder: 0")

    # Создание новых виджетов (QLineEdit) при нажатии на кнопуку pushButton 
    def addNewLine(self):

        self.tradingPairs = QtWidgets.QLineEdit(self.programWindow.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.tradingPairs.setFont(font)
        self.tradingPairs.setStyleSheet("color: white;")            
        self.tradingPairs.setFixedHeight(40)
        self.tradingPairs.setObjectName("tradingPairs")
    
        self.lastTradedPrice = QtWidgets.QLineEdit(self.programWindow.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.lastTradedPrice.setFont(font)
        self.lastTradedPrice.setStyleSheet("color: white;")        
        self.lastTradedPrice.setObjectName("lastTradedPrice")
    
        self.percentageChange = QtWidgets.QLineEdit(self.programWindow.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.percentageChange.setFont(font)
        self.percentageChange.setStyleSheet("color: white;")        
        self.percentageChange.setObjectName("percentageChange")
    
        self.turnover = QtWidgets.QLineEdit(self.programWindow.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(26)
        self.turnover.setFont(font)
        self.turnover.setStyleSheet("color: white;")
        self.turnover.setObjectName("turnover")
     
        row = self.programWindow.gridLayout.count()-8

        self.programWindow.gridLayout.addWidget(self.tradingPairs, row, 0, 1, 1)
        self.programWindow.gridLayout.addWidget(self.lastTradedPrice, row, 1, 1, 1)
        self.programWindow.gridLayout.addWidget(self.percentageChange, row, 2, 1, 1)
        self.programWindow.gridLayout.addWidget(self.turnover, row, 3, 1, 1)
                     
app = QtWidgets.QApplication([])
application = HandleMessage()
application.show()
sys.exit(app.exec())

Я много что уже испробовал, половину не помню уже, но самое близкое к желаемому результату было изменение кода в функции handleMessage, убрав обращение к родительскому классу через .programWindow.

Но в итоге происодит добавление одновременно информации по двум объектам из списка в каждый вновь созданный виджет при помощи функции addNewLine, при этом динамически меняется только в новь созданном виджете! (См. Рис.4)

Код функции:

def handleMessage(self, message):
        data = message['data'][0]
        open = data["open"]
        symbol = message["topic"][9:]
        close = data['close']
        turnover = '{0:,}'.format(int(float(data['turnover']))).replace(',', '.')
        priceChange = round((close/open-1)*100, 2)

        #Изменения этого участка кода  
        self.programWindow.lastTradedPrice.setText(str(close))
        self.programWindow.tradingPairs.setText(symbol)
        self.programWindow.percentageChange.setText(str(priceChange))
        self.programWindow.turnover.setText(str(turnover))
        ########################################################
        if close > open: 
            self.programWindow.lastTradedPrice.setStyleSheet("color: yellow;\nborder: 0")
            self.programWindow.percentageChange.setStyleSheet("color: yellow;\nborder: 0")
        elif close < open:   
            self.programWindow.lastTradedPrice.setStyleSheet("color: red;\nborder: 0")
            self.programWindow.percentageChange.setStyleSheet("color: red;\nborder: 0")
        else:
            self.programWindow.lastTradedPrice.setStyleSheet("color: white;\nborder: 0")
            self.programWindow.percentageChange.setStyleSheet("color: white;\nborder: 0")

(Рис. 4) Рис. 4


Ответы (1 шт):

Автор решения: S. Nick

Я не могу проверить ваше приложение.

Я попробовал что-то сделать для вас, имитируя какие-то случайные данные.

Обратите внимание:

  • я убрал некоторые виджеты, которые мне не понятны;
  • я заменил ваши QLabelы на QTableView с моделью QStandardItemModel;
  • данные криптовалют вы получаете в дополнительном потоке;
  • с колонку 'Изменения, %' посчитаете сами, если вы ее не получаете готовую.

import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *


class Thread(QThread):
    updateSignal = QtCore.pyqtSignal(object)

    def __init__(self, symbol_list):
        super().__init__()
        self.symbol_list = symbol_list

    def run(self):
        self.msleep(1000)
        while True:    
        
            # !!! 
            # !!! тут вы получаю данные криптовалют в реальном времени
            # !!! 
        
            symbol_ = random.choice(self.symbol_list)
            price = round(random.uniform(0.1, 77777.99), 2) 
            amount = round(random.uniform(0.1, 77777.99), 3) 

            # !!! 
            # !!! и передаете в основной поток для отображения
            # !!! 
            
            result = [symbol_, price, amount]
            self.updateSignal.emit(result)                              # ---->
            
            self.msleep(1000 * 2)  # пауза 2sec между запросами на получение новых данных


class HandleMessage(QtWidgets.QMainWindow): 
    def __init__(self):
        super().__init__()
        self.centralwidget = QtWidgets.QWidget()
        self.centralwidget.setObjectName("centralwidget")
        self.setCentralWidget(self.centralwidget)
        
        self.row = 2
        self.column = 4
        self.symbol_list = ["BTCUSDT", "DOGEUSDT"]
        self.thread = None

        self.view = QTableView(self)
        self.view.setSelectionBehavior(QTableWidget.SelectRows)

        self.view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) 
        self.view.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
        
        self.header = ['Торговая пара', 'Цена', 'Изменения, %', 'Объем']
       
        self.model = QStandardItemModel(self.row, self.column)
        self.model.setHorizontalHeaderLabels(self.header)
        self.view.setModel(self.model)
      
        for row in range(self.model.rowCount()):
            item = QStandardItem(self.symbol_list[row])
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.model.setItem(row, 0, item)
            
        self.pushButton = QtWidgets.QPushButton('Start', self.centralwidget)
        self.pushButton.clicked.connect(self.run_th)
        self.pushButton.setObjectName("pushButton")
        
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.addWidget(self.view, 0, 0, 1, 4)
        self.gridLayout.addWidget(self.pushButton, 1, 3, 1, 1)

    def run_th(self):
        pass
        if self.thread is None:
            self.thread = Thread(self.symbol_list)
            self.thread.updateSignal.connect(self.update_thread)
            self.thread.start()
            self.pushButton.setText("Stop")
        else:
            reply = QMessageBox.question(
                self, 
                'ВНИМАНИЕ !',
                "Вы уверены, что хотите прервать обновление данных?",
                 QMessageBox.Yes, QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.thread.terminate() 
                self.thread = None
                self.pushButton.setText("Start")         

    def update_thread(self, obj):                                       # <---- 
        #print(f'\nobj = {obj}') #
        find = self.model.findItems(obj[0]) 
        if not self.model.findItems(obj[0]):
            msg = QMessageBoxQMessageBox.information(self, 
                'Внимание', 'Что-то пошло не так.')
            return
        
        rows = self.model.rowCount()
        for row in range(rows):
            item = self.model.item(row, 0)
            if item.text() == obj[0]:
                item = QStandardItem(f'{obj[1]:>15.5f}')
                self.model.setItem(row, 1, item)
                item = QStandardItem(f'{obj[2]:>15.3f}')
                self.model.setItem(row, 3, item)
                break

        self.model.sort(3, Qt.AscendingOrder)               
        
    def closeEvent(self, event):
        reply = QMessageBox.question(
            self, 
            'Информация',
            "Вы уверены, что хотите закрыть приложение?",
             QMessageBox.Yes, QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            if self.thread:
                self.thread.quit()
            del self.thread
            super(HandleMessage, self).closeEvent(event)
        else:
            event.ignore()
    

if __name__ == "__main__":                     
    app = QtWidgets.QApplication(sys.argv)
    app.setFont(QtGui.QFont("Consolas", 18, QtGui.QFont.Bold))
    w = HandleMessage()
    w.resize(1200, 235)
    w.show()
    sys.exit(app.exec())

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка