How to Pan Images in Qgraphicsview

how to pan images in QGraphicsView

QGraphicsView has build-in mouse-panning support. Set correct DragMode and it will handle the rest. You do need the enable scroll bars for that to work.

How to enable Pan and Zoom in a QGraphicsView

This is not too difficult to do using the built in capabilities of QGraphicsView.

The demo script below has left-button panning and wheel zoom (including anchoring to the current cursor position). The fitInView method has been reimplemented because the built in version adds a weird fixed margin that can't be removed.

PyQt4 version:

from PyQt4 import QtCore, QtGui

class PhotoViewer(QtGui.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)

def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
self._empty = True
self._scene = QtGui.QGraphicsScene(self)
self._photo = QtGui.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtGui.QFrame.NoFrame)

def hasPhoto(self):
return not self._empty

def fitInView(self, scale=True):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0

def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
else:
self._empty = True
self.setDragMode(QtGui.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
self.fitInView()

def wheelEvent(self, event):
if self.hasPhoto():
if event.delta() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0

def toggleDragMode(self):
if self.dragMode() == QtGui.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtGui.QGraphicsView.NoDrag)
elif not self._photo.pixmap().isNull():
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)

def mousePressEvent(self, event):
if self._photo.isUnderMouse():
self.photoClicked.emit(self.mapToScene(event.pos()).toPoint())
super(PhotoViewer, self).mousePressEvent(event)

class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = PhotoViewer(self)
# 'Load image' button
self.btnLoad = QtGui.QToolButton(self)
self.btnLoad.setText('Load image')
self.btnLoad.clicked.connect(self.loadImage)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtGui.QToolButton(self)
self.btnPixInfo.setText('Enter pixel info mode')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtGui.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtGui.QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QtGui.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout)

def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap('image.jpg'))

def pixInfo(self):
self.viewer.toggleDragMode()

def photoClicked(self, pos):
if self.viewer.dragMode() == QtGui.QGraphicsView.NoDrag:
self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))

if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())

PyQt5 version:

from PyQt5 import QtCore, QtGui, QtWidgets

class PhotoViewer(QtWidgets.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)

def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
self._empty = True
self._scene = QtWidgets.QGraphicsScene(self)
self._photo = QtWidgets.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtWidgets.QFrame.NoFrame)

def hasPhoto(self):
return not self._empty

def fitInView(self, scale=True):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0

def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
else:
self._empty = True
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
self.fitInView()

def wheelEvent(self, event):
if self.hasPhoto():
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0

def toggleDragMode(self):
if self.dragMode() == QtWidgets.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
elif not self._photo.pixmap().isNull():
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)

def mousePressEvent(self, event):
if self._photo.isUnderMouse():
self.photoClicked.emit(self.mapToScene(event.pos()).toPoint())
super(PhotoViewer, self).mousePressEvent(event)

class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = PhotoViewer(self)
# 'Load image' button
self.btnLoad = QtWidgets.QToolButton(self)
self.btnLoad.setText('Load image')
self.btnLoad.clicked.connect(self.loadImage)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtWidgets.QToolButton(self)
self.btnPixInfo.setText('Enter pixel info mode')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtWidgets.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtWidgets.QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QtWidgets.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout)

def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap('image.jpg'))

def pixInfo(self):
self.viewer.toggleDragMode()

def photoClicked(self, pos):
if self.viewer.dragMode() == QtWidgets.QGraphicsView.NoDrag:
self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))

if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())

How to pan beyond the scrollbar range in a QGraphicsview?

If you take a look at this video, at the 3 minute mark you'll see the demonstration panning the screen. The application here is one I developed and although it doesn't show it, the real estate of the board appears limitless when panning.

What I did for this was to create a QGraphicsScene of 32000 x 32000 and start the application with the view at the centre of the QGraphicsScene. The test team spent ages trying to pan to the edge of the graphics scene and everyone gave up before getting there - perhaps the scene could have been smaller!

The scroll bar policies are set to off and translation is done by moving the QGraphicsView via its translate function, passing in the delta of either touch, or mouse movement that is applied in the mouseMoveEvent.

Done this way, you need not worry about exceeding the scroll bar range and there was no problem creating a very large QGraphicsScene as it's just a coordinate space.

Zooming and panning an image in a QScrollArea

Given that the paintEvent implementation for a custom zoomable and pannable pixmap viewer is 5 lines long, one might as well implement it from scratch:

// https://github.com/KubaO/stackoverflown/tree/master/questions/image-panzoom-40683840
#include <QtWidgets>
#include <QtNetwork>

class ImageViewer : public QWidget {
QPixmap m_pixmap;
QRectF m_rect;
QPointF m_reference;
QPointF m_delta;
qreal m_scale = 1.0;
void paintEvent(QPaintEvent *) override {
QPainter p{this};
p.translate(rect().center());
p.scale(m_scale, m_scale);
p.translate(m_delta);
p.drawPixmap(m_rect.topLeft(), m_pixmap);
}
void mousePressEvent(QMouseEvent *event) override {
m_reference = event->pos();
qApp->setOverrideCursor(Qt::ClosedHandCursor);
setMouseTracking(true);
}
void mouseMoveEvent(QMouseEvent *event) override {
m_delta += (event->pos() - m_reference) * 1.0/m_scale;
m_reference = event->pos();
update();
}
void mouseReleaseEvent(QMouseEvent *) override {
qApp->restoreOverrideCursor();
setMouseTracking(false);
}
public:
void setPixmap(const QPixmap &pix) {
m_pixmap = pix;
m_rect = m_pixmap.rect();
m_rect.translate(-m_rect.center());
update();
}
void scale(qreal s) {
m_scale *= s;
update();
}
QSize sizeHint() const override { return {400, 400}; }
};

A comparable QGraphicsView-based widget would be only slightly shorter, and would have a bit more overhead if the pixmap was very small. For large pixmaps, the time spent rendering the pixmap vastly overshadows any overhead due to the QGraphicsScene/QGraphicsView machinery. After all, the scene itself is static, and that is the ideal operating point for performance of QGraphicsView.

class SceneImageViewer : public QGraphicsView {
QGraphicsScene m_scene;
QGraphicsPixmapItem m_item;
public:
SceneImageViewer() {
setScene(&m_scene);
m_scene.addItem(&m_item);
setDragMode(QGraphicsView::ScrollHandDrag);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setResizeAnchor(QGraphicsView::AnchorViewCenter);
}
void setPixmap(const QPixmap &pixmap) {
m_item.setPixmap(pixmap);
auto offset = -QRectF(pixmap.rect()).center();
m_item.setOffset(offset);
setSceneRect(offset.x()*4, offset.y()*4, -offset.x()*8, -offset.y()*8);
translate(1, 1);
}
void scale(qreal s) { QGraphicsView::scale(s, s); }
QSize sizeHint() const override { return {400, 400}; }
};

And a test harness:

int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QWidget ui;
QGridLayout layout{&ui};
ImageViewer viewer1;
SceneImageViewer viewer2;
QPushButton zoomOut{"Zoom Out"}, zoomIn{"Zoom In"};
layout.addWidget(&viewer1, 0, 0);
layout.addWidget(&viewer2, 0, 1);
layout.addWidget(&zoomOut, 1, 0, 1, 1, Qt::AlignLeft);
layout.addWidget(&zoomIn, 1, 1, 1, 1, Qt::AlignRight);

QNetworkAccessManager mgr;
QScopedPointer<QNetworkReply> rsp(
mgr.get(QNetworkRequest({"http://i.imgur.com/ikwUmUV.jpg"})));
QObject::connect(rsp.data(), &QNetworkReply::finished, [&]{
if (rsp->error() == QNetworkReply::NoError) {
QPixmap pixmap;
pixmap.loadFromData(rsp->readAll());
viewer1.setPixmap(pixmap);
viewer2.setPixmap(pixmap);
}
rsp.reset();
});
QObject::connect(&zoomIn, &QPushButton::clicked, [&]{
viewer1.scale(1.1); viewer2.scale(1.1);
});
QObject::connect(&zoomOut, &QPushButton::clicked, [&]{
viewer1.scale(1.0/1.1); viewer2.scale(1.0/1.1);
});
ui.show();
return a.exec();
}

Synchronize two QGraphicsView with different images

You only need to syncronize scrollbar values of two QGraphicsViews

def bindScrollBars(scrollBar1, scrollBar2):

# syncronizing scrollbars syncrnonously somehow breaks zooming and doesn't work
# scrollBar1.valueChanged.connect(lambda value: scrollBar2.setValue(value))
# scrollBar2.valueChanged.connect(lambda value: scrollBar1.setValue(value))

# syncronizing scrollbars asyncronously works ok
scrollBar1.valueChanged.connect(lambda _: QtCore.QTimer.singleShot(0, lambda: scrollBar2.setValue(scrollBar1.value())))
scrollBar2.valueChanged.connect(lambda _: QtCore.QTimer.singleShot(0, lambda: scrollBar1.setValue(scrollBar2.value())))

class Window(QtWidgets.QWidget):
def __init__(self):
...
bindScrollBars(self.viewer.horizontalScrollBar(), self.viewerSecondImage.horizontalScrollBar())
bindScrollBars(self.viewer.verticalScrollBar(), self.viewerSecondImage.verticalScrollBar())

Also wheelEvent can be simplified

    def wheelEvent(self, event):
if self.hasPhoto():
factor = 1.25
if event.angleDelta().y() > 0:
self.scale(factor, factor)
else:
self.scale(1/factor, 1/factor)
self.viewUpdated.emit(self.transform())

Maintaining view/scroll position in QGraphicsView when swapping images

I found a solution that works better than the code I first posted. It's still not perfect, but is much improved. Just in case anyone else is trying to solve a similar problem...

When setting the low quality image I call this method added to my QGraphicsView:

def get_scroll_state(self):
"""
Returns a tuple of scene extents percentages.
"""
centerPoint = self.mapToScene(self.viewport().width()/2,
self.viewport().height()/2)
sceneRect = self.sceneRect()
centerWidth = centerPoint.x() - sceneRect.left()
centerHeight = centerPoint.y() - sceneRect.top()
sceneWidth = sceneRect.width()
sceneHeight = sceneRect.height()

sceneWidthPercent = centerWidth / sceneWidth if sceneWidth != 0 else 0
sceneHeightPercent = centerHeight / sceneHeight if sceneHeight != 0 else 0
return sceneWidthPercent, sceneHeightPercent

This gets stored in self.scroll_state. When setting the high quality image I call another function to restore the percentages used in the previous function.

def set_scroll_state(self, scroll_state):
sceneWidthPercent, sceneHeightPercent = scroll_state
x = (sceneWidthPercent * self.sceneRect().width() +
self.sceneRect().left())
y = (sceneHeightPercent * self.sceneRect().height() +
self.sceneRect().top())
self.centerOn(x, y)

This sets the center position to the same location (percentage-wise) as I was at before swapping the image.

How to pass images on QGraphicsView to QTableView programmatically using QPushButton

I wanted to answer to this question hoping that could also be useful to others. As suggested by Jeremy Friesner, the best (and fast in comparison to a QItemDelegate) way to send images into a QTableView using a QPushButton is to modify the void MainWindow::addData() function by using a QImage and pass it to a setData(QVariant(QPixmap::fromImage), Qt::DecorationRole) so that the entire function can be written as follows:

FIRST OPTION:

void MainWindow::on_sendBtn_clicked()
{
addData();
}

void MainWindow::addData()
{
QStandardItem *pathAItem = new QStandardItem(ui->pathLineEdit_A->text());
QStandardItem *pathBItem = new QStandardItem(ui->pathLineEdit_B->text());

QImage image1(ui->graphicsViewLeft->grab().toImage());
QStandardItem *item1 = new QStandardItem();
item1->setData(QVariant(QPixmap::fromImage(image1.scaled(42,42, Qt::KeepAspectRatio,Qt::SmoothTransformation))), Qt::DecorationRole);
ui->bookMarkTableView->setModel(model);

QImage image2(ui->graphicsViewRight->grab().toImage());
QStandardItem *item2 = new QStandardItem();
item2->setData(QVariant(QPixmap::fromImage(image2.scaled(42,42, Qt::KeepAspectRatio,Qt::SmoothTransformation))), Qt::DecorationRole);
ui->bookMarkTableView->setModel(model);

QList<QStandardItem*> row;
row << pathAItem << pathBItem << item1 << item2;
model->appendRow(row);
}

SECOND OPTION

If it is necessary to use a QItemDelgate I am posting that part of the code too (it is working as I already tried it):

In the imagedelegate.h is necessary to provide a QSize as follows:

class ImageDelegate : public QStyledItemDelegate
{
public:
ImageDelegate(QObject * parent = nullptr);
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const;

After that on your imagedelegate.cpp the implementation is:

#include "imagedelegate.h"
ImageDelegate::ImageDelegate(QObject * parent) : QStyledItemDelegate(parent)
{}

QSize ImageDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
return QSize(32,32);
Q_UNUSED(option);
Q_UNUSED(index);
}

void ImageDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
qDebug() << (index.model()->headerData(index.column(), Qt::Horizontal).toString());
QString colName = index.model()->headerData(index.column(), Qt::Horizontal).toString();

if(colName == "image1" || colName == "image2")
{
QPixmap iconPix;
if(!iconPix.loadFromData(index.model()->data(index).toByteArray())) {
}
iconPix = iconPix.scaledToHeight(32);
painter->drawPixmap(option.rect.x(),option.rect.y(),iconPix);
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}

In my case I had two columns in which I needed to save the images, so you can expand it for how many columns as you like and I also set a QSize of (32,32) but this is up to the developer.

I hope this will save your programming time and this is the final result! :)

Qt5 C++ QGraphicsView: Images don't fit view frame

Solution for my question is showEvent() for Dialog. This means that you can't call fitInView() before the form is showed, so you have to create showEvent() for dialog and the picture will be fitted into QGraphics View's frame.

And example code which you have to add into dialog's code:

void YourClass::showEvent(QShowEvent *) {
ui->graphicsView->fitInView(scn->sceneRect(),Qt::KeepAspectRatio);
}

Pyqt5 QgraphicsView pan past scroll bar limits

Update2

Please try this Code. Is this appropriate answer for you?
If so, I want to add new explanation.

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Line(QGraphicsLineItem):
def __init__(self, x1, y1, x2, y2):
super(Line, self).__init__()
pen = self.pen()
pen.setWidth(10)
pen.setColor(Qt.gray)
pen.setStyle(Qt.SolidLine)

self.setPen(pen)
self.origin = self.pos()
self.setZValue(1)
self.setLine(QLineF(x1, y1, x2, y2))
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self.origin = QPointF(self.pos())
self._brush = QBrush(Qt.black)
self.setZValue(2)
def setBrush(self, brush):
self._brush = brush
self.update()

def boundingRect(self):
return self.rectF

def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)

def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)

class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)

def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self.mid_panning = False

self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self._scene.setBackgroundBrush(Qt.white)
self.white_board = QGraphicsRectItem()
self.white_board.setZValue(1)
self.white_board.setBrush(Qt.white)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self._old_x = QCursor.pos().x()
self._old_y = QCursor.pos().y()

QTimer.singleShot(0, self.fitInView) # This is done so that it can fit into view on load

def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)

self.area = QRectF(minX , minY , distance , distance )
self.white_board.setRect(QRectF(minX , minY , distance , distance ))
self._scene.addItem(self.white_board)
line1 = Line(minX, minY, minX+distance, minY+distance)
line2 = Line(minX+distance, minY, minX, minY+distance)
self._scene.addItem(line1)
self._scene.addItem(line2)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)

self.setScene(self._scene)

def make_area2(self, area):
x = area.x()
y = area.y()
width = area.width()
height = area.height()
x -= 2*x
y -= 2*y
width = width*2
height = height*2
area = QRectF(x, y, width, height)
return area
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
print(unity.width(), unity.height())
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
print(scenerect.width(), scenerect.height())
self.scale(factor, factor)

self._zoom = 0

def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(self.ScrollHandDrag)
# self.fitInView()

def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0

def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:

self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.origin = event.pos()
self.original_event = event
self.mid_panning = True
self.scene_origin = self.mapToScene(event.pos())
self._old_x = QCursor.pos().x()
self._old_y = QCursor.pos().y()
for i in self._scene.items():

i.origin = i.pos()
# I recommend that you get the all item position.
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)

super(Viewer, self).mousePressEvent(event)

def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
self.mid_panning = False
# here you set the original point.
for i in self._scene.items():
i.setPos(i.origin)
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def calc_offset(self, x, y):
offset_x = x - int(self.viewport().width()/2)
offset_y = y - int(self.viewport().height()/2)
return offset_x, offset_y

def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
elif self.mid_panning:

new_x = event.x()
new_y = event.y()
offset_x, offset_y = self.calc_offset(new_x, new_y)
for item in self._scene.items():
item.setPos(QPointF(item.pos().x() - (new_x - self._old_x)*10, item.pos().y() - (new_y - self._old_y)*10))

self._old_x = new_x
self._old_y = new_y
return

super(Viewer, self).mouseMoveEvent(event)

class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Fit Into View')
self.btnLoad.clicked.connect(self.fitPoints)


Related Topics



Leave a reply



Submit