Jaime02
Jaime02

Reputation: 356

Widget used for infinitely resizable painting in librecad

Librecad uses a widget which can be infinitely resized, you can zoom in and out as much as you can. Which widget does it uses?

When I paint into a common widget, the painting is done at certain coordinates of the widget. However, I would like to draw at floating coordinates of the widget and use a line width which is fixed to certain pixels of the viewport.

Before resizing:

before resizing:

After resizing:

after resizing:

Which widget provides this functionality?

Upvotes: 1

Views: 116

Answers (1)

eyllanesc
eyllanesc

Reputation: 244301

You have to use QGraphicsView and QGraphicsScene(see Graphics View Framework):

from PyQt5 import QtCore, QtGui, QtWidgets


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(GraphicsView, self).__init__(parent)
        self.factor = 1.2

        self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
        self.setRenderHints(
            QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform
        )
        self.setMouseTracking(True)
        self.setScene(
            QtWidgets.QGraphicsScene(QtCore.QRectF(-400, -400, 800, 800), self)
        )

        QtWidgets.QShortcut(QtGui.QKeySequence.ZoomIn, self, activated=self.zoomIn) # Ctrl + +
        QtWidgets.QShortcut(QtGui.QKeySequence.ZoomOut, self, activated=self.zoomOut) # Ctrl + -

    @QtCore.pyqtSlot()
    def zoomIn(self):
        self.zoom(self.factor)

    @QtCore.pyqtSlot()
    def zoomOut(self):
        self.zoom(1 / self.factor)

    def zoom(self, f):
        self.scale(f, f)

    def drawForeground(self, painter, rect):
        super(GraphicsView, self).drawForeground(painter, rect)

        if not hasattr(self, "cursor_position"):
            return
        painter.save()
        painter.setTransform(QtGui.QTransform())
        pen = QtGui.QPen(QtGui.QColor("yellow"))
        pen.setWidth(4)
        painter.setPen(pen)

        r = self.mapFromScene(rect).boundingRect()

        linex = QtCore.QLine(
            r.left(), self.cursor_position.y(), r.right(), self.cursor_position.y(),
        )
        liney = QtCore.QLine(
            self.cursor_position.x(), r.top(), self.cursor_position.x(), r.bottom(),
        )
        for line in (linex, liney):
            painter.drawLine(line)

        painter.restore()

    def mouseMoveEvent(self, event):
        self.cursor_position = event.pos()
        self.scene().update()
        super(GraphicsView, self).mouseMoveEvent(event)

    def wheelEvent(self, event):
        angle = event.angleDelta().y()
        if angle < 0:
            self.zoomIn()
        else:
            self.zoomOut()
        super(GraphicsView, self).wheelEvent(event)


if __name__ == "__main__":
    import sys
    import random

    app = QtWidgets.QApplication(sys.argv)

    w = GraphicsView()

    for _ in range(4):
        r = QtCore.QLineF(
            *random.sample(range(-200, 200), 2), *random.sample(range(50, 150), 2)
        )
        it = w.scene().addLine(r)
        pen = QtGui.QPen(QtGui.QColor(*random.sample(range(255), 3)))
        pen.setWidthF(5.0)
        pen.setCosmetic(True)
        it.setPen(pen)

    w.resize(640, 480)
    w.show()

    sys.exit(app.exec_())

Upvotes: 3

Related Questions