Не отображаются объекты на сцене 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 шт):
В основном потоке работает графическая часть, поэтому в дополнительных потоках не следует изменять напрямую, это нужно сделать, отправив сигнал в основной поток и именно в его слоте обновляется графическая часть.
В следующем примере создается сигнал,
который выдается каждые 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_())
