Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » PyQt5 Асинхронное обновление QListWidget [RSS Feed]

#1 Апрель 25, 2021 14:27:09

snakeous
Зарегистрирован: 2021-04-25
Сообщения: 2
Репутация: +  0  -
Профиль   Отправить e-mail  

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

Имеется код для демонстрации проблемы:

 #!/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.
Хотелось бы избежать и этого.

Офлайн

#2 Апрель 26, 2021 00:51:28

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 8017
Репутация: +  728  -
Профиль   Отправить e-mail  

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

snakeous
Вопрос: как обновлять QListWidget в отдельном потоке?
Здесь приводил пример создания потока в PyQt5.
Здесь обсуждали, как это делать без run().

Тема обширная, ты сначала почитай всё, разберись, инфа не для тупых. Иначе ничего работать у тебя не будет и будут только непонятные эффекты, природу которых не поймёшь.



Отредактировано py.user.next (Апрель 26, 2021 00:53:54)

Офлайн

#3 Апрель 26, 2021 09:35:53

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2177
Репутация: +  293  -
Профиль   Отправить e-mail  

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

snakeous по моему как раз для вас:
Use PyQt's QThread to Prevent Freezing GUIs
https://realpython.com/python-pyqt-qthread/#freezing-a-gui-with-long-running-tasks



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Онлайн

#4 Апрель 26, 2021 10:20:43

snakeous
Зарегистрирован: 2021-04-25
Сообщения: 2
Репутация: +  0  -
Профиль   Отправить e-mail  

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

Я пришел к выводу, что проще разделить всё по страницам - как-то так:

 #!/usr/bin/env python3
CHANNEL_COUNT = 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_())

Отредактировано snakeous (Апрель 26, 2021 10:21:14)

Офлайн

  • Начало
  • » GUI
  • » PyQt5 Асинхронное обновление QListWidget[RSS Feed]

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version