Search code examples
python-3.xpyqt5qpainter

how rotate a line about the origin?


I want to rotate and translate a line about the origin by reading text file values and it working but its giving me the last and final output in the last . I am not able to see this as animation . there is some problem with self.update() may be. Filename is log file of drone flying reading.

import math
from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsScene
from PyQt5.QtGui import QPainter, QColor, QBrush, QPen
from PyQt5.QtCore import Qt
import sys
import xlrd 
import time

class Heads_ups(QWidget):
    lineno=0
    pitch,roll=[],[]
    def __init__(self):
        super().__init__()
        self.initUI()


    def initUI(self):

        self.setGeometry(300,300,480,360)
        self.setWindowTitle('Colours')
        self.show()



    def read_file(self,lineno):
        withopen('text file having rows and columns','r',) as f :
        lines = f.readlines()[4:]
        for s in lines:
            a = s.split()
            #print (len(a))
            if(len(a)!=14):
                print("point reached")
                lineno += 1
                continue
            self.pitch.append(float(a[4]))
            self.roll.append(float(a[6]))
            lineno += 1        
       # print(lineno)
         return lineno

    def counterrotate(self,origin,point1,roll):
        ox,oy = origin
        px,py = point1
        angle=math.radians(roll)
    #counterclockwise
        qx= ox+ math.cos(angle)*(px-ox)-math.sin(angle)*(py-oy)
        qy= oy+ math.sin(angle)*(px-ox)+math.cos(angle)*(py-oy)
        return qx,qy

    def rotate(self,origin,point,roll):
        ox,oy = origin
        px,py = point
        angle=math.radians(roll)
    #clockwise
        qx= ox+ math.cos(angle)*(px-ox)+math.sin(angle)*(py-oy)
        qy= oy+ math.sin(angle)*(px-ox)+math.cos(angle)*(py-oy)
        return qx,qy



    def paintEvent(self,e):
        # print(filename)
        qp= QPainter(self)
        qp.begin(self)
        self.drawLine(qp)
        self.update()
        self.move_line(qp)
        qp.end()


    def drawLine(self,qp):
        pen= QPen(Qt.black,2,Qt.SolidLine)
        qp.setPen(pen)
        qp.drawLine(0,180,480,180)
        self.update()
        QApplication.processEvents()
        time.sleep(0.2)


    def move_line(self,qp):
        pen=QPen(Qt.green,2,Qt.SolidLine)
        qp.setPen(pen)
        qp.drawLine(0,180+50,480,180+50)
        # self.scene=QGraphicsScene(self)
        lineno = self.read_file(self.lineno)
        for m in range(0,lineno,1):
            x0=-400
            x1=880
            y0=180
            y1=180
            xc=240
            yc=180
            point=(x0,y0)
            point1= (x1,y1)
            origin= (xc,yc)
            x0,y0=self.rotate(origin,point,self.roll[m]*10)
            y0=y0-(self.pitch[m]*60)
            # canvas.move(point,x0,y0)
            x1,y1=self.counterrotate(origin,point1,self.roll[m]*10)
            y1=y1-(self.pitch[m]*60)
            dlt=qp.drawLine(x0,y0,x1,y1)
            self.update()
            QApplication.processEvents()
            time.sleep(0.5)
            # self.scene.removeItem(dlt)



if __name__ == '__main__': 
    app=QApplication(sys.argv)
    hp=Heads_ups()
    filename=open('textfile having rows and columns','r')
    sys.exit(app.exec_())

Thanks in advance.


Solution

  • You must avoid using time.sleep() in a GUI since it blocks the event-loop and therefore generates that it does not update correctly. I have not taken the time to review what is wrong but to propose a correct implementation.

    My solution proposes to have a class that provides the data every certain interval of time through signals, to generate the intervals of time I have used a QTimer.

    The widget obtains the data and performs the calculations as you wish.

    import sys
    
    import math
    
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    
    
    def counterrotate(origin,point1,roll):
        ox,oy = origin
        px,py = point1
        angle=math.radians(roll)
        #counterclockwise
        qx= ox+ math.cos(angle)*(px-ox)-math.sin(angle)*(py-oy)
        qy= oy+ math.sin(angle)*(px-ox)+math.cos(angle)*(py-oy)
        return qx,qy
    
    def rotate(origin,point,roll):
        ox,oy = origin
        px,py = point
        angle=math.radians(roll)
        #clockwise
        qx= ox+ math.cos(angle)*(px-ox)+math.sin(angle)*(py-oy)
        qy= oy+ math.sin(angle)*(px-ox)+math.cos(angle)*(py-oy)
        return qx,qy
    
    
    class Manager(QObject):
        changedValue = pyqtSignal(tuple)
        def __init__(self):
            QObject.__init__(self)
            filename = "Attitude_data_Manual_1232018_1158.txt"
            res = self.read_content(filename)
            self.results = zip(*res)
    
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.update_value)
            self.timer.start(100)
    
        @pyqtSlot()
        def update_value(self):
            try:
                self.changedValue.emit(next(self.results))
            except StopIteration:
                self.timer.stop()
                #QCoreApplication.instance().quit()
    
        def read_content(self, filename):
            pitch = []
            roll = []
            with open(filename,'r') as f :
                lines = f.readlines()[4:]
                for s in lines:
                    a = s.split()
                    if len(a) !=14:
                        print("point reached")
                        continue
                    pitch.append(float(a[4]))
                    roll.append(float(a[6]))
            return pitch, roll
    
    class Widget(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent)
            self.v = None
    
        @pyqtSlot(tuple)
        def update_value(self, v):
            self.v = v
            self.update()
    
        def paintEvent(self, event):
            QWidget.paintEvent(self, event)
            painter = QPainter(self)
            r = self.rect()
    
            if self.v:
                x0, x1, y0, y1, xc, yc = -400, 880, 180, 180, 240, 180
                point1 = (x1,y1)
                point = (x0,y0)
                origin = (xc,yc)
    
                pitch, roll = self.v
    
                x0,y0 = rotate(origin, point, roll*10)
                y0 -= pitch*60
                x1, y1 = counterrotate(origin,point1, roll*10)
                y1 -= pitch*60
    
                posx0 = QPoint(x0, y0)
                posx1 = QPoint(x1, y1)
    
                upper_polygon = QPolygonF([r.topLeft(), posx0, posx1, r.topRight()])
                bottom_polygon = QPolygonF([posx0, posx1, r.bottomRight(), r.bottomLeft()])
    
    
                painter.setBrush(QColor("skyblue"))
                painter.drawPolygon(upper_polygon)
    
                painter.setBrush(QColor("green"))
                painter.drawPolygon(bottom_polygon)
    
            painter.drawLine(r.left(), r.center().y(), r.right(), r.center().y())
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        manager = Manager()
        w = Widget()
        manager.changedValue.connect(w.update_value)
        w.show()
        sys.exit(app.exec_())