在PC端绘制倒车的动画,达到实时显示车辆的倒车情况的目的。如车辆倒退时,动画中的车辆也倒退,车辆左转时,动画也实时变化。使用的工具为QT(python),做了一个如上形式的可视化界面。
选择的工具为:PyQt5,QGraphicsView,QGraphicsRectItem;
与以往的GUI设计不同,这次的GUI开发为场景和实体的运动,而不是控件的使用;前者更偏向游戏设计,场景移动和物体的碰撞检测,后者更像使用的软件,点击按钮输入密码等;
设置物体 首先创建车辆的实体,这里以方块代表车辆:
1 2 3 class RectItem (QGraphicsRectItem ): def __init__ (self, rect=QRectF( ) ): super (RectItem, self).__init__(rect)
而后创建场景,场景中包括车辆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class GraphicsView (QGraphicsView ): def __init__ (self ): super (GraphicsView, self).__init__() self.setFixedSize(460 , 560 ) self.scene = QGraphicsScene() self.scene.setSceneRect(0 , 0 , 450 , 550 ) self.rect = RectItem() self.rect.setRect(0 , 0 , 240 , 300 ) brush1 = QBrush(Qt.SolidPattern) brush1.setColor(QColor(124 , 214 , 175 )) self.rect.setBrush(brush1) self.scene.addItem(self.rect) self.setScene(self.scene)
之后,再将场景添加到窗口中即可,伪代码如下:
1 2 3 4 5 6 7 g = GraphicsView() self.car = g.rect self.scene = g.scene layout.addWidget(g)
因初始化位置为(0, 0),所以车辆在左上角,效果如下所示。完整代码,car_test.py
物体移动 最初的想法是:设定一组位置,通过time.sleep()设置移动速度来达到缓慢移动的效果,但这并不是可行的思路,错误的伪代码如下:
1 2 3 4 5 6 7 x, y = 100 , 100 i, j = 0 , 0 def move (self ): time.sleep(1 ) car.setpos(i, j) if i < x and j < y: i += 10 , j += 10
但实际是,物体会直接移动到(100, 100),并没有中间的过程。在Qt的开发中,一定不能使用time.sleep这种方法,因为它会阻塞事件的循环,导致窗口冻结,从而阻止了GUI的重新绘制。所以在Qt中,必须使用事件来执行操作,如:分为显示事件和工作事件,显示事件负责GUI的显示,工作事件负责刷新物体的位置。如:每次触发计时器的计时时,计算物体的位置,而后显示事件负责更新。
此时修改车辆实体的类,增加光滑移动的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class RectItem (QGraphicsRectItem ): def __init__ (self, rect=QRectF( ) ): super (RectItem, self).__init__(rect) self.setFlag(QGraphicsItem.ItemIsSelectable, True ) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True ) self._pos_animation = QVariantAnimation() self._pos_animation.valueChanged.connect(self.setPos) def move_smooth (self, end, duration ): if self._pos_animation.state() == QAbstractAnimation.Running: self._pos_animation.stop() self._pos_animation.setDuration(duration) self._pos_animation.setStartValue(self.pos()) self._pos_animation.setEndValue(end) self._pos_animation.start()
而后设置计时器及时触发即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 btn_start.clicked.connect(self.run) def move_pos (self, scene ): Left = [40 ] Center = [40 ] for it in scene.items(): self.item = it for left, center in zip (Left, Center): pos = QPointF(left, center) if hasattr (it, 'move_smooth' ): it.move_smooth(pos, 500 ) def run (self, distance ): wrapper = partial(self.move_pos, self.scene) timer = QTimer(interval=5000 , timeout=wrapper) timer.start() wrapper()
实现效果如下,此时可以完美的时间光滑移动。完整代码
物体旋转 我摸索了两种旋转方案,如下图所示:
第一种旋转方案是以左上角的顶点为中心,逆时针旋转
第二种旋转方案是以矩形的中心为旋转点,逆时针旋转
经仿真模拟,第二种方案在操作难度和展示效果两个方面均取得不错的效果。
那么,以此来设置旋转方案:
设立向左旋转的角度self.left_angle和向右旋转的角度self.right_angle两个参数,均初始化为5度
向左旋转时,self.left_angle += 5, self.right_angle -= 5
同理,向右旋转时,self.left_angle -= 5, self.right_angle += 5
PyQt5默认的旋转方式是逆时针,所以向左旋转时,角度为(360 - self.left_angle)
scenePos()方法可以捕获当前物体左上角的坐标,因此坐标加上车身宽度的一半就是旋转点的横坐标,加上车身长度的一半就是旋转点的纵坐标
更严谨一点可以加上旋转角度来计算旋转点坐标的位置,因为实际的车库倒车中不会频繁的左右转动,但我选择了忽略这些误差。
因此,不难得到旋转的代码:(旋转点的计算不够严谨)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 self.angle_left = 5 self.angle_right = 5 def rorate_right (self ): pos = self.car.scenePos() x = pos.x() + 120 y = pos.y() + 150 self.car.setTransformOriginPoint(QPointF(x, y)) self.car.setRotation(360 - self.angle_right) self.angle_right += 5 self.angle_left -= 5 def rorate_left (self ): pos = self.car.scenePos() x = pos.x() + 120 y = pos.y() + 150 self.car.setTransformOriginPoint(QPointF(x, y)) self.car.setRotation(self.angle_left) self.angle_left += 5 self.angle_right -= 5
实现的效果如下。完整代码
结语 而接下来的就是读取车辆倒车时与后方障碍物的距离,根据距离信息更新动画中车辆的位置。这涉及了多线程的东西,将在下文给出实现方案。
参考 站在巨人的肩膀上,我们能更好的前行:
https://stackoverflow.com/questions/55277098/move-qgraphicsitem-continuously-in-qgraphicsscene-and-check-collision
https://stackoverflow.com/questions/11147443/rotate-qgraphicspixmapitem-with-mouse