Search code examples
pythonpython-3.xpyqtpyqt5qpainter

QPaint draw lines with offset


I would like to draw some lines with QPainter, then move the start (x1,y1) coordinates of lines to centerpoint and some other lines which want to offset from center. Those lines should behave programmatically based on value of ellipse or other values. I have tried myself different ways to come around it but does not work.

To QRect can utilize codes such as

moveCenter, moveTopLeft, etc...

But for Qline there are not such methods. According to PyQt doc, a line can be drawn by this:

QLine(int x1, int y1, int x2, int y2)
QLine(const QPoint &p1, const QPoint &p2)

Maybe this line should be used in order to offset it. But no knowlegde to do it.

translated(const QPoint &offset)

On another hand would like to draw some texts and offset them in similar way as Qline.

Look at figures below to see what is that I exactly want to do?

Visualisation

What I have achieved so far.

enter image description here

What I want to achieve.

enter image description here

The code:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Foo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setGeometry(QtCore.QRect(200, 100, 700, 600))        
        self.paint = Paint()
        self.sizeHint()
        self.lay = QtWidgets.QVBoxLayout()
        self.lay.addWidget(self.paint)
        self.setLayout(self.lay)

class Paint(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Paint, self).__init__(parent)
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)

        xl = self.rect().center().x()
        yl = self.rect().center().y()

        self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
        self.l = QtCore.QLine(QtCore.QPoint(xl, yl) , QtCore.QPoint(self.width(), self.height()/2))



    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
        painter = QtGui.QPainter(self)
        painter.setBrush(brush)
        painter.setPen(pen)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        self.e.moveCenter(self.rect().center())

        painter.drawEllipse(self.e)
        painter.drawLine(self.l)
#        painter.drawText('D')

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Foo()
    w.show()
    sys.exit(app.exec_())

The Update issue:

I have updated the code, I manage to draw what I want. But having another challenge. You could see on figure below.

When code runs and displays

enter image description here

When Widget minimizes or maximizes, some lines disappears?

enter image description here

The Update code:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Foo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setGeometry(QtCore.QRect(200, 100, 700, 600))        
        self.paint = Paint()
        self.sizeHint()
        self.lay = QtWidgets.QVBoxLayout()
        self.lay.addWidget(self.paint)
        self.setLayout(self.lay)

class Paint(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Paint, self).__init__(parent)
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)


        self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
        self.l1 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(200, 0))
        self.l2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -200))

        self.vl = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -125))
        delta = QtCore.QPoint(20, 0)
        self.hl = QtCore.QLine(-delta, self.e.topRight() + delta)
        self.al = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(-20, -20))
        self.a2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(-20, 20))
        self.a3 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(20, 20))
        self.a4 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(-20, 20))
        self._factor = 1.0

    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
        painter = QtGui.QPainter(self)
        painter.setBrush(brush)
        painter.setPen(pen)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        self.e.moveCenter(self.rect().center())
        self.l1.translate(self.rect().center() - self.l1.p1())
        self.l2.translate(self.rect().center() - self.l2.p1())

        self.al.translate(self.l1.p2())
        self.a2.translate(self.l1.p2())

        painter.translate(self.rect().center())
        painter.scale(self._factor, self._factor)
        painter.translate(-self.rect().center())

        painter.drawEllipse(self.e)
        painter.drawLine(self.l1)
        painter.drawLine(self.l2)

        fnt = painter.font() 
        fnt.setPointSize(20) 
        painter.setFont(fnt) 
        h = QtGui.QFontMetrics(painter.font()).height() 
        p = QtCore.QPoint(self.rect().center().x(), self.e.top() - 3*h) 
        u = QtCore.QPoint(self.e.right() + 3*h, self.rect().center().y()) 
        painter.drawText(p, "y")
        painter.drawText(u, "x")

        r = QtCore.QRect(QtCore.QPoint(self.e.x(), self.e.bottom()), QtCore.QSize(self.e.width(), h)) 
        painter.drawText(r, QtCore.Qt.AlignCenter, "D = 350mm")
        offset = QtCore.QPoint(0, 1.5*h)

        self.vl.translate(self.e.bottomLeft() - self.vl.p1() + offset)
        painter.drawLine(self.vl)
        self.vl.translate(self.e.width(), 0)
        painter.drawLine(self.vl)
        self.hl.translate(self.e.bottomLeft() +  offset - QtCore.QPoint(0, 0.4*h))

        self.a3.translate(self.l2.p2())
        self.a4.translate(self.l2.p2())

        painter.drawLine(self.hl)
        painter.drawLine(self.al)
        painter.drawLine(self.a2)
        painter.drawLine(self.a3)
        painter.drawLine(self.a4)       


    def wheelEvent(self, event):
        self._factor *= 1.01**(event.angleDelta().y()/15.0)
        self.update()
        super(Paint, self).wheelEvent(event)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Foo()
    w.show()
    sys.exit(app.exec_())

I do not know why it behaves like this? any comment or help I appreciate much.


Solution

  • Qt does not know what size the widget will have at the beginning so self.rect() will have any size, for example in my test self.rect() in __init__ returns PyQt5.QtCore.QRect(0, 0, 640, 480) but in paintEvent() it returns PyQt5.QtCore.QRect(0, 0, 678, 578) so that's why the calculation is incorrect.

    the solution is to move the line with translate()(do not use translated() because this creates a new QLine and that's what I do not want)

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Foo(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Foo, self).__init__(parent)
            self.setGeometry(QtCore.QRect(200, 100, 700, 600))        
            self.paint = Paint()
            self.sizeHint()
            self.lay = QtWidgets.QVBoxLayout()
            self.lay.addWidget(self.paint)
            self.setLayout(self.lay)
    
    class Paint(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Paint, self).__init__(parent)
            self.setBackgroundRole(QtGui.QPalette.Base)     
            self.setAutoFillBackground(True)
    
            self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
            self.l1 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(200, 0))
            self.l2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -200))
    
        def paintEvent(self, event):
            pen = QtGui.QPen()
            brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
            painter = QtGui.QPainter(self)
            painter.setBrush(brush)
            painter.setPen(pen)
            painter.setRenderHint(QtGui.QPainter.Antialiasing)
    
            self.e.moveCenter(self.rect().center())
            self.l1.translate(self.rect().center() - self.l1.p1())
            self.l2.translate(self.rect().center() - self.l2.p1())
            painter.drawEllipse(self.e)
            painter.drawLine(self.l1)
            painter.drawLine(self.l2)
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        w = Foo()
        w.show()
        sys.exit(app.exec_())
    

    enter image description here

    Update:

    class Paint(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Paint, self).__init__(parent)
            self.setBackgroundRole(QtGui.QPalette.Base)     
            self.setAutoFillBackground(True)
    
            self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
            self.l1 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(200, 0))
            self.l2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -200))
    
            self.vl = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -125))
            delta = QtCore.QPoint(20, 0)
            self.hl = QtCore.QLine(-delta, self.e.topRight() + delta)
    
        def paintEvent(self, event):
            pen = QtGui.QPen()
            brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
            painter = QtGui.QPainter(self)
            painter.setBrush(brush)
            painter.setPen(pen)
            painter.setRenderHint(QtGui.QPainter.Antialiasing)
    
            self.e.moveCenter(self.rect().center())
            self.l1.translate(self.rect().center() - self.l1.p1())
            self.l2.translate(self.rect().center() - self.l2.p1())
    
            painter.drawEllipse(self.e)
            painter.drawLine(self.l1)
            painter.drawLine(self.l2)
    
            fnt = painter.font() 
            fnt.setPointSize(25) 
            painter.setFont(fnt) 
            h = QtGui.QFontMetrics(painter.font()).height() 
            p = QtCore.QPoint(self.rect().center().x(), self.e.bottom() + 0.8*h)
    
            r = QtCore.QRect(QtCore.QPoint(self.e.x(), self.e.bottom()), QtCore.QSize(self.e.width(), h)) 
            painter.drawText(r, QtCore.Qt.AlignCenter, "D")
            offset = QtCore.QPoint(0, 1.5*h)
    
            self.vl.translate(self.e.bottomLeft() - self.vl.p1() + offset)
            painter.drawLine(self.vl)
            self.vl.translate(self.e.width(), 0)
            painter.drawLine(self.vl)
            self.hl.translate(self.e.bottomLeft() +  offset - QtCore.QPoint(0, 20))
            painter.drawLine(self.hl)
    

    enter image description here