Не отображаются объекты на сцене QGraphicsScene

Задача была следующая - создаем сцену и добавляем на нее много объектов в другом потоке, пока объекты добавляются, сама сцена не виснет, т.е. я могу передвигаться скроллами по сцене без фризов, да еще и видеть постепенно добавляющиеся объекты. Рассмотрим реализацию:

Создаем стандартную сцену и задаем ей размер (50000 х 1000), а так же поток, в котором будем добавлять на сцену объекты:

class Widget_GraphSurface(QWidget):

    ggv_main: QGraphicsView

    def __init__(self):
        super().__init__()

        # init settings
        self.setMinimumSize(2000, 1200)
        self.show()
        # initial main layout
        self.lay_main = QVBoxLayout()
        self.setLayout(self.lay_main)
        # init lay graph
        self.lay_graph = QHBoxLayout()
        # add control panel and graph lays to main lay
        self.lay_main.addLayout(self.lay_graph)
        # main scene graph
        self.mainScene = QGraphicsScene()
        self.mainScene.setBackgroundBrush(QBrush(QColor('#ff333333')))
        # graphics view of main scene
        self.ggv_main = QGraphicsView(self.mainScene)
 self.ggv_main.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
        self.ggv_main.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
        self.ggv_main.setFrameShape(QFrame.Shape.NoFrame)
        self.lay_graph.addWidget(self.ggv_main)
        self.ggv_main.scene().setSceneRect(0, 0, 50000, 1000)
        self.moveThread = MoveThread(self.mainScene)
        self.moveThread.start()

Поток:

class MoveThread(QThread):

    def __init__(self, scene: QGraphicsScene):
        super().__init__()

        self.scene = scene

    def run(self):
        try:
            i = 0
            while i < 50000:
                item = QGraphicsRectItem(i, 50, 45, 45)
                self.scene.addItem(item)
                i += 50
                time.sleep(0.001)
        except Exception as e:
            print(e)

Все отлично работает, квадратики

QGraphicsRectItem(i, 50, 45, 45)

добавляются на сцену, сцена не виснет, пока они добавляются. Но, самих квадратов не видно(кроме первых двух), но они есть на сцене! Ни метод update, ни метод show не помогают. Почему видно только первые два квадратика - я не могу понять. Квадратики отображаются только в двух случаях:

  • Если не задавать заранее сцене размеры, то квадратики будут добавляться и автоматически расширять сцену в ширину и в таком случае они видны
  • Если добавлять все объекты через основной main поток, но мне это не нужно
  • Самая дурость в том, что если после добавления всех квадратиков, вызвать scene.setSceneRect и задать значение высоты отличное от заданного ранее на 1 пиксель, все квадратики отобразятся!!! Как это работает? Почему такие проблемы с отображением объектов, если их добавили из другого потока?

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

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

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

В следующем примере создается сигнал, который выдается каждые 10 ms в дополнительном потоке, он подключается к слоту add_item() и именно там добавляем объект.

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


class MoveThread(QThread):
# +++
    signal = pyqtSignal(object)                                      # +++
    
    def __init__(self):                 #-, scene: QGraphicsScene):
        super().__init__()
#-       self.scene = scene

    def run(self):
#?        try:
        i = 0
        while i < 50000:                             
            item = QGraphicsRectItem(i, 50, 45, 45)
#-           self.scene.addItem(item)
            self.signal.emit(item)
            i += 50
                
#-                time.sleep(0.001)
            self.msleep(10)
#?        except Exception as e:
#?            print(e)


class Widget_GraphSurface(QWidget):
    ggv_main: QGraphicsView

    def __init__(self):
        super().__init__()
        self.setMinimumSize(2000, 1200)
#        self.show()
        
        # initial main layout
        self.lay_main = QVBoxLayout()
        self.setLayout(self.lay_main)
        
        # init lay graph
        self.lay_graph = QHBoxLayout()
        # add control panel and graph lays to main lay
        self.lay_main.addLayout(self.lay_graph)
        
        # main scene graph
        self.mainScene = QGraphicsScene()
        self.mainScene.setBackgroundBrush(QBrush(QColor('#ff333333')))
        
        # graphics view of main scene
        self.ggv_main = QGraphicsView(self.mainScene)
        self.ggv_main.setTransformationAnchor(
            QGraphicsView.ViewportAnchor.AnchorUnderMouse)
        self.ggv_main.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
        self.ggv_main.setFrameShape(QFrame.Shape.NoFrame)
        self.ggv_main.scene().setSceneRect(0, 0, 50000, 1000)
        self.lay_graph.addWidget(self.ggv_main)
        
        self.moveThread = MoveThread()                #- self.mainScene
# +++
        self.moveThread.signal.connect(self.add_item)                  # +++
        self.moveThread.start()
        
        self.i = 1                                                     # +

# +++        
    def add_item(self, item):                                          # +++
        self.mainScene.addItem(item)                                   # +++
        
        x = item.rect().x()
        y = item.rect().y()

        self.text = QGraphicsTextItem()
        self.text.setHtml(f'<center>{self.i}</center>')
        self.text.setTextWidth(item.boundingRect().width())
        self.text.setDefaultTextColor(QColor('#fff')) 
        self.text.setPos(x, y+10)
        self.mainScene.addItem(self.text)
        self.i += 1
        
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Widget_GraphSurface()
    w.show()                                                         # +
    sys.exit(app.exec_())

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

→ Ссылка