PyQt5 Асинхронное обновление QListWidget

amakeloan

Новичок
Пользователь
Апр 25, 2021
6
1
0
1
Linux
Python 3.7.3
python-pyqt5 5.11.3+dfsg-1+b3

Имеется код для демонстрации проблемы:
Python:
#!/usr/bin/env python3
import sys
import time
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QPushButton, QLabel
def redraw():
    list.clear()
    for i in range(1000000):
        test = QLabel(str(i))
        list.addItem(str(i))
def show_time():
    clock.setText(time.strftime('%d.%m.%y %H:%M:%S', time.localtime()))
app = QApplication(sys.argv)
root = QWidget()
root.resize(600, 600)
list = QListWidget(root)
list.resize(600, 500)
for i in range(5):
    list.addItem(str(i))
clock = QLabel(root)
clock.move(300, 520)
clock.resize(300, 30)
show_time()
btn = QPushButton('Redraw', root)
btn.clicked.connect(redraw)
btn.move(300, 570)
timer = QTimer()
timer.timeout.connect(show_time)
timer.start(1000)
root.show()
sys.exit(app.exec_())
При нажатии кнопки Redraw часы останавливаются (оно и понятно, всё в одном потоке)
Вопрос: как обновлять QListWidget в отдельном потоке?

p.s. Этого нет в демонстрационном коде, но в реальном приложении (где используется ещё и setItemWidget) при попытке ввести многопоточность происходит следующее:
Код:
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
Хотелось бы избежать и этого.
 

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 581
457
83
название поста меня заинтересовало, но потом разочаровался...

как вы делали многопоточность? для Qt используйте Qthread...
вот тема на форуме qt, где описано, почему так происходит...
 

amakeloan

Новичок
Пользователь
Апр 25, 2021
6
1
0
1
Я пришел к выводу, что проще разделить всё по страницам - как-то так:
Python:
#!/usr/bin/env python3
CHANNEL_COUNT = 1200 # допустим, у нас 1200 каналов.
import sys
import time
import random
from PyQt5.QtCore import QTimer
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QPushButton, QLabel, QSpinBox
class QCustomQWidget(QtWidgets.QWidget): # pylint: disable=too-many-instance-attributes
    def __init__(self, parent=None):
        super(QCustomQWidget, self).__init__(parent)
        self.textQVBoxLayout = QtWidgets.QVBoxLayout()      # QtWidgets
        self.textUpQLabel = QtWidgets.QLabel()         # QtWidgets
        myFont = QtGui.QFont()
        myFont.setBold(True)
        self.textUpQLabel.setFont(myFont)
        self.textDownQLabel = QtWidgets.QLabel()         # QtWidgets
        self.textQVBoxLayout.addWidget(self.textUpQLabel)
        self.textQVBoxLayout.addWidget(self.textDownQLabel)
        self.allQHBoxLayout = QtWidgets.QGridLayout()      # QtWidgets
        self.iconQLabel = QtWidgets.QLabel()         # QtWidgets
        self.progressLabel = QtWidgets.QLabel()
        self.progressBar = QtWidgets.QProgressBar()
        self.endLabel = QtWidgets.QLabel()
        self.op = QtWidgets.QGraphicsOpacityEffect()
        self.op.setOpacity(100)
        self.allQHBoxLayout.addWidget(self.iconQLabel, 0, 0)
        self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 0, 1)
        self.allQHBoxLayout.addWidget(self.progressLabel, 3, 0)
        self.allQHBoxLayout.addWidget(self.progressBar, 3, 1)
        self.allQHBoxLayout.addWidget(self.endLabel, 3, 2)
        self.allQHBoxLayout.setSpacing(10)
        self.setLayout(self.allQHBoxLayout)
        self.progressBar.setStyleSheet('''
        background-color: #C0C6CA;
        border: 0px;
        padding: 0px;
        height: 5px;
        ''')
        self.setStyleSheet('''
        QProgressBar::chunk {
        background: #7D94B0;
        width:5px
        }
        ''')
    def setTextUp(self, text):
        self.textUpQLabel.setText(text)
    def setTextDown(self, text):
        self.textDownQLabel.setText(text)
    def setTextProgress(self, text):
        self.progressLabel.setText(text)
    def setTextEnd(self, text):
        self.endLabel.setText(text)
    def setIcon(self, image):
        self.iconQLabel.setPixmap(image.pixmap(QtCore.QSize(32, 32)))
    def setProgress(self, progress_val):
        self.op.setOpacity(100)
        self.progressBar.setGraphicsEffect(self.op)
        self.progressBar.setFormat('')
        self.progressBar.setValue(progress_val)
    def hideProgress(self):
        self.op.setOpacity(0)
        self.progressBar.setGraphicsEffect(self.op)
data = {}
def redraw():
    row0 = list.currentRow()
    scroll_value = list.verticalScrollBar().value()
    list.clear()
    idx = (box.value() - 1) * 100
    for i in range(CHANNEL_COUNT)[idx:idx+100]:
        test_widget = QtWidgets.QListWidgetItem()
        item_widget = QCustomQWidget()
        item_widget.setTextUp(data[i][0])
        item_widget.setTextDown(data[i][1])
        item_widget.setProgress(data[i][2])
        test_widget.setSizeHint(item_widget.sizeHint())
        list.addItem(test_widget)
        list.setItemWidget(test_widget, item_widget)
    list.setCurrentRow(row0)
    list.verticalScrollBar().setValue(scroll_value)
def update_data():
    global data
    clock.setText(time.strftime('%d.%m.%y %H:%M:%S', time.localtime()))
    for i in range(CHANNEL_COUNT):
        data[i] = ["Channel " + str(i), "Demo TV Guide " + str(time.time()), random.randint(0, 100)]
    redraw()
app = QApplication(sys.argv)
root = QWidget()
root.resize(600, 560)
list = QListWidget(root)
list.resize(600, 500)
clock = QLabel(root)
clock.move(250, 500)
clock.resize(300, 30)
box = QSpinBox(root)
box.move(250, 530)
box.setMinimum(1)
box.setMaximum(round(CHANNEL_COUNT / 100) + 1)
def change_page():
    list.verticalScrollBar().setValue(0)
    redraw()
box.valueChanged.connect(change_page)
update_data()
redraw()
timer = QTimer()
timer.timeout.connect(update_data)
timer.start(1000)
root.show()
sys.exit(app.exec_())
 

Форум IT Специалистов