Zooming in/out QGraphicsView
-
Hello, i have an application for drawing shapes, I used QGraphicsView with QGraphicsScene, And then I added two different QPixmap QGraphicsItem to the scene.
- the first one is a canvas for drawing and printing the shapes.
- the second one is only for displaying a "ruler" to show you that 'x' of canvas boxes represents a number of pixels or whatever unit (this one should stay on the left bottom corner).
And I implemented the zooming feature successfully, BUT my problem is that whenever I zoom in both items get zoomed so I can not see the "ruler" anymore because it is out of the scope after zooming (it only appears after zooming if you used pam and go to the bottom left corner), And that's right because I applied that feature to the whole view.
I want to keep the second pixmap out of zooming functionality. Only apply that feature on the first item the "canvas"
Here is a snippet of my code:
class Canvas(QGraphicsView): def __init__(self, scene: QGraphicsScene): super().__init__(scene) self.scene = scene self.setFrameShape(QFrame.NoFrame) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) background_color = QColor("#333") self.pixmap_item: QGraphicsItem = self.scene.addPixmap(QPixmap(780, 580)) self.pixmap_item.setTransformationMode(Qt.FastTransformation) self.pixmap_item.pixmap().fill(background_color) # legend mean the ruler self.legend = QPixmap(780, 580) self.legend.fill(QColor("#00ffffff")) p = QtGui.QPainter(self.legend) p.setPen(QPen(QColor("#0000FF"), 4)) p.drawLine(35 * 5.1, self.legend.height() - 60, 35 * 8.9, self.legend.height() - 60) p.setPen(QColor("#c9c9c9")) p.setFont(QFont('Century Gothic', 14)) p.drawText(35 * 5.5, self.legend.height() - 35, f'this text is from the other pixmap (legend)') p.end() self.scene.addPixmap(self.legend) self.zoom_times = 0 def wheelEvent(self, event): # this function is working fine, but what I want is to only apply zooming on the self.pixmapitem, and keep the self.legend visable. zoom_in_factor = 1.25 zoom_out_factor = 1 / zoom_in_factor # Save the scene pos old_pos = self.mapToScene(event.pos()) # Zoom if event.angleDelta().y() > 0: if self.zoom_times == 6: return zoom_factor = zoom_in_factor self.zoom_times += 1 else: if self.zoom_times == 0: return zoom_factor = zoom_out_factor self.zoom_times -= 1 self.scale(zoom_factor, zoom_factor) # Get the new position new_pos = self.mapToScene(event.pos()) # Move scene to old position delta = new_pos - old_pos self.translate(delta.x(), delta.y())
Here are some images that explain what I am saying:
-
@Pythonic-person said in Zooming in/out QGraphicsView:
the second one is only for displaying a "ruler" to show you that 'x' of canvas boxes represents a number of pixels or whatever unit (this one should stay on the left bottom corner).
Anything like this would be best implemented by overriding your
QGraphicsView.drawForeground()
, rather than via adding aQGraphicsItem
. That should have two advantages:- No scaling from zoom.
- Can be placed always in same place, e.g. bottom-left of a view, regardless of where the view is positioned.
Having said that, if your "legend" is actually a "ruler" what is it measuring? If it is the "scale" for the map/scene, you usually would want that to change with the zoom factor!
Also unless you want really want your pixmap for the background you might draw the black background + grid lines in an override of
QGraphicsScene.drawBackground()
. -
@JonB said in Zooming in/out QGraphicsView:
Anything like this would be best implemented by overriding your QGraphicsView.drawForeground(), rather than via adding a QGraphicsItem. That should have two advantages:
No scaling from zoom.
Can be placed always in same place, e.g. bottom-left of a view, regardless of where the view is positioned.It was really really helpful I was searching for this for a long time, Thanks.
And that won't affect the performance of the app or any other effect?here is my implementation (it worked well just needs some calculation and taking care of the zooming factor):
Old code PLUS this function:
def drawForeground(self, painter, rectF): super(Canvas, self).drawForeground(painter, rectF) painter.setPen(QPen(QColor("#0000FF"), 4)) painter.setFont(QFont('Century Gothic', 14)) painter.drawLine(rectF.bottomLeft().x(), rectF.bottomLeft().y(),rectF.bottomLeft().x(), rectF.bottomLeft().y()) painter.setPen(QColor("#c9c9c9")) painter.drawText(rectF.bottomLeft().x(),rectF.bottomLeft().y(), f'this text is from the other pixmap (legend)')
-
@Pythonic-person said in Zooming in/out QGraphicsView:
And that won't affect the performance of the app or any other effect?
LOL no. If anything I think it might be a microsecond faster. For all the
QGraphicsItem
s on your scene it has to see where they are, who's on top of whom, whether the item is hidden, how to draw it etc. The foreground is just a simple layer it puts on top of any items (just as the background is below any items). There are probably lots of calculations it can skip, at a guess. If you want you can optimize the foreground/background redrawing with clip areas.Using the background/foreground of the scene or view for things which are static and not the actual objects you are playing with on the scene seems to me to keep things cleaner. E.g. if you go looking for one of your items on the scene, why do you want to have to encounter and skip a ruler or the background grid pattern or whatever?