QGraphicsView manually draw scene items
-
I would like to know how to manually paint scene items in a QGraphicsView. My issues is if i add items with pixmaps they pixelate when zoomed in. If i could manually draw them then i could adjust the scaling appropriately. I have no issue doing this sort of thing in html canvases using javascript. I am just playing with PyQt to try and do the same thing in python. It seemed the QGraphicsView used to have a drawItems method but it no longer seems to exist so need to find out how to do it now. Following is a small demo that puts a pixmap into a label. The image i am using is 1085 x 1085 so plenty of resolution. As you can see when you zoom in the image pixelates making it look ugly. If i could manually draw it I should be able to rescale the image.
I am trying to do something similar to a mapping app with picture widgets on top the map. As i zoom in the detail on the pictures should get better not worse. Any help would be appreciated.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt class MyQGraphicsView(QtWidgets.QGraphicsView): def __init__(self, *args): super(MyQGraphicsView, self).__init__(*args) self.zoom = 1 pass def wheelEvent(self, event): delta = event.angleDelta().y() / 120 if delta > 0: self.zoom *= 1.05 elif delta < 0: self.zoom /= 1.05 self.transform() def transform(self): self.setTransform(QtGui.QTransform().scale(self.zoom, self.zoom)) def setZoom(self, newZoom): self.zoom = newZoom self.transform() class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(800, 600) self.centralwidget = QtWidgets.QWidget(MainWindow) self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.scene = QtWidgets.QGraphicsScene(self.centralwidget) self.graphicsView = MyQGraphicsView(self.scene) self.gridLayout.addWidget(self.graphicsView, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.graphicsView.setSceneRect(0, 0, 2000, 2000) self.graphicsView.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) pix = QtGui.QPixmap('./image/test.jpg') pix.scaled(50, 50, Qt.KeepAspectRatio) wig = QtWidgets.QLabel() wig.setGeometry(0,0,50,50) wig.setPixmap(pix) wig.setAlignment(QtCore.Qt.AlignCenter) wig.setScaledContents(True) wig.setMinimumSize(1, 1) proxy = QtWidgets.QGraphicsProxyWidget() proxy.setWidget(wig) self.scene.addItem(proxy) proxy.setPos(200, 200) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
-
I would like to know how to manually paint scene items in a QGraphicsView. My issues is if i add items with pixmaps they pixelate when zoomed in. If i could manually draw them then i could adjust the scaling appropriately. I have no issue doing this sort of thing in html canvases using javascript. I am just playing with PyQt to try and do the same thing in python. It seemed the QGraphicsView used to have a drawItems method but it no longer seems to exist so need to find out how to do it now. Following is a small demo that puts a pixmap into a label. The image i am using is 1085 x 1085 so plenty of resolution. As you can see when you zoom in the image pixelates making it look ugly. If i could manually draw it I should be able to rescale the image.
I am trying to do something similar to a mapping app with picture widgets on top the map. As i zoom in the detail on the pictures should get better not worse. Any help would be appreciated.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt class MyQGraphicsView(QtWidgets.QGraphicsView): def __init__(self, *args): super(MyQGraphicsView, self).__init__(*args) self.zoom = 1 pass def wheelEvent(self, event): delta = event.angleDelta().y() / 120 if delta > 0: self.zoom *= 1.05 elif delta < 0: self.zoom /= 1.05 self.transform() def transform(self): self.setTransform(QtGui.QTransform().scale(self.zoom, self.zoom)) def setZoom(self, newZoom): self.zoom = newZoom self.transform() class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(800, 600) self.centralwidget = QtWidgets.QWidget(MainWindow) self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.scene = QtWidgets.QGraphicsScene(self.centralwidget) self.graphicsView = MyQGraphicsView(self.scene) self.gridLayout.addWidget(self.graphicsView, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.graphicsView.setSceneRect(0, 0, 2000, 2000) self.graphicsView.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) pix = QtGui.QPixmap('./image/test.jpg') pix.scaled(50, 50, Qt.KeepAspectRatio) wig = QtWidgets.QLabel() wig.setGeometry(0,0,50,50) wig.setPixmap(pix) wig.setAlignment(QtCore.Qt.AlignCenter) wig.setScaledContents(True) wig.setMinimumSize(1, 1) proxy = QtWidgets.QGraphicsProxyWidget() proxy.setWidget(wig) self.scene.addItem(proxy) proxy.setPos(200, 200) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
@LeonLambert said in QGraphicsView manually draw scene items:
It seemed the QGraphicsView used to have a drawItems method but it no longer seems to exist
I don't have a comment on pixmap/scaling, but you can draw whatever you like via e.g. void QGraphicsScene::drawForeground(QPainter *painter, const QRectF &rect) (or
QGraphicsView
has this too). You override that and it gives you a QPainter which has all yourdraw...()
routines. -
The colliding mice example shows how to create custom items: https://doc.qt.io/qtforpython-6/examples/example_widgets_graphicsview_collidingmice.html
-
The colliding mice help by showing me how to use a QGraphicsItem. I made a few tweaks and came up with this code that seems to work. Thanks for your help.
import typing from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget class MyQGraphicsView(QtWidgets.QGraphicsView): def __init__(self, *args): super(MyQGraphicsView, self).__init__(*args) self.zoom = 1 self.gridSize = 50 pass def wheelEvent(self, event): delta = event.angleDelta().y() / 120 if delta > 0: self.zoom *= 1.05 elif delta < 0: self.zoom /= 1.05 self.transform() def transform(self): self.setTransform(QtGui.QTransform().scale(self.zoom, self.zoom)) def setZoom(self, newZoom): self.zoom = newZoom self.transform() def getScaledGridSize(self): return int(self.gridSize * self.zoom) class MyItem(QtWidgets.QGraphicsItem): def __init__(self, imagePath, view, *args): super(MyItem, self).__init__(*args) self.image = QtGui.QImage(imagePath) self.pixMap = None self.view = view self.scaledGridSize = 0 def boundingRect(self): return QtCore.QRectF(0, 0, 50, 50) def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionGraphicsItem', widget: typing.Optional[QWidget] = ...) -> None: gridSize = self.view.getScaledGridSize() if gridSize != self.scaledGridSize: self.scaledGridSize = gridSize pixMap = QtGui.QPixmap.fromImage(self.image) self.pixMap = pixMap.scaled(self.scaledGridSize, self.scaledGridSize, Qt.KeepAspectRatio, Qt.SmoothTransformation) painter.drawPixmap(0, 0, self.pixMap) class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(800, 600) self.centralwidget = QtWidgets.QWidget(MainWindow) self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.scene = QtWidgets.QGraphicsScene(self.centralwidget) self.graphicsView = MyQGraphicsView(self.scene) self.gridLayout.addWidget(self.graphicsView, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.graphicsView.setSceneRect(0, 0, 2000, 2000) self.graphicsView.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) wig = MyItem('../image/test.jpg', self.graphicsView) self.scene.addItem(wig) wig.setPos(200, 200) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
-