from PySide6.QtWidgets import QWidget, QApplication from PySide6.QtGui import QPainter, QPen, QBrush, QColor, QTransform from PySide6.QtCore import Qt, QPropertyAnimation, Property, QPointF import sys # 料斗夹爪部分,包括夹爪开合动画 class ClampWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self._angle = 0.0 # 开合角度(0-70度,单个夹具旋转角度为angle/2) self.setFixedSize(176, 127) # 绘图区域大小 240x150 # self.rot_center = QPointF(120, 30) # 旋转中心 # 重要,调整这里,设置夹爪绘制位置 self.rot_center = QPointF(88, 14) # 调整!!! 在主界面中位置需要调整这里 self.red_width = 37 # 红框宽度 self.red_height = 81 # 红框高度 def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # ---- 左红框及蓝色部分 ---- # 左红框原始顶点(严格对称,以旋转中心为轴) left_red_vertices = [ QPointF(self.rot_center.x() - self.red_width, self.rot_center.y()), # 顶部左 QPointF(self.rot_center.x(), self.rot_center.y()), # 顶部右(旋转中心) QPointF(self.rot_center.x(), self.rot_center.y() + self.red_height), # 底部右 QPointF(self.rot_center.x() - self.red_width, self.rot_center.y() + self.red_height) # 底部左 ] # 左红框旋转变换(顺时针转angle/2度) painter.save() transform = QTransform() transform.translate(self.rot_center.x(), self.rot_center.y()) transform.rotate(self._angle / 2) transform.translate(-self.rot_center.x(), -self.rot_center.y()) transformed_left_red = [transform.map(p) for p in left_red_vertices] # 绘制左红框辅助线 # painter.setPen(QPen(QColor(255, 0, 0), 1, Qt.DashLine)) # painter.setBrush(Qt.NoBrush) # painter.drawPolygon(transformed_left_red) painter.restore() # 绘制左蓝色部分 86, 180, 239 # painter.setPen(QPen(QColor(0, 120, 255), 2)) # painter.setBrush(QBrush(QColor(0, 170, 255, 180))) painter.setPen(QPen(QColor(66, 152, 215, 190), 2)) painter.setBrush(QBrush(QColor(86, 180, 239))) # 蓝色顶点:顶部中间17px直线 + 红框内边界 top_mid = QPointF( transformed_left_red[0].x() + self.red_width/2 - 10, transformed_left_red[0].y() ) top_end = QPointF(top_mid.x() + 17, top_mid.y()) bottom_left = transformed_left_red[3] # ============================== 特殊计算 bottom_right1 和 bottom_right2 ====== # 定义端点 A = transformed_left_red[1] # 左红框顶部右顶点 B = transformed_left_red[2] # 左红框底部右顶点 t = 0.7 # 70%位置 # 线性插值计算bottom_left2 bottom_right2_x = A.x() + t * (B.x() - A.x()) bottom_right2_y = A.y() + t * (B.y() - A.y()) bottom_right1 = QPointF(bottom_right2_x - 5, bottom_right2_y - 8) # 基于bottom_right2 计算坐标 bottom_right2 = QPointF(bottom_right2_x, bottom_right2_y) # =========================================================================== bottom_right3 = QPointF(transformed_left_red[2].x(), transformed_left_red[2].y()) left_blue_vertices = [ top_mid, top_end, bottom_right1, bottom_right2, bottom_right3, bottom_left, top_mid ] painter.drawPolygon(left_blue_vertices) # ---- 右红框及蓝色部分 ---- # 右红框原始顶点(与左红框完全对称) right_red_vertices = [ QPointF(self.rot_center.x(), self.rot_center.y()), # 顶部左(旋转中心) QPointF(self.rot_center.x() + self.red_width, self.rot_center.y()), # 顶部右 QPointF(self.rot_center.x() + self.red_width, self.rot_center.y() + self.red_height), # 底部右 QPointF(self.rot_center.x(), self.rot_center.y() + self.red_height) # 底部左 ] # 右红框旋转变换(逆时针转angle/2度) painter.save() transform = QTransform() transform.translate(self.rot_center.x(), self.rot_center.y()) transform.rotate(-self._angle / 2) transform.translate(-self.rot_center.x(), -self.rot_center.y()) transformed_right_red = [transform.map(p) for p in right_red_vertices] # 绘制右红框辅助线 # painter.setPen(QPen(QColor(255, 0, 0), 1, Qt.DashLine)) # painter.setBrush(Qt.NoBrush) # painter.drawPolygon(transformed_right_red) painter.restore() # 绘制右蓝色部分 83, 175, 234 66, 152, 215 painter.setPen(QPen(QColor(66, 152, 215, 190), 2)) painter.setBrush(QBrush(QColor(86, 180, 239))) # painter.setPen(QPen(QColor(0, 120, 255), 2)) # painter.setBrush(QBrush(QColor(0, 170, 255, 180))) # 蓝色顶点:顶部中间17px直线 + 红框内边界 top_mid = QPointF( transformed_right_red[1].x() - self.red_width/2 + 10, transformed_right_red[1].y() ) top_end = QPointF(top_mid.x() - 17, top_mid.y()) # ============================== 特殊计算 bottom_left1 和 bottom_left2 ======= # 定义端点 A = transformed_right_red[0] # 右红框顶部左顶点 B = transformed_right_red[3] # 右红框底部左顶点 t = 0.7 # 70%位置 # 线性插值计算bottom_left2 bottom_left2_x = A.x() + t * (B.x() - A.x()) bottom_left2_y = A.y() + t * (B.y() - A.y()) bottom_left1 = QPointF(bottom_left2_x + 5, bottom_left2_y - 8) # 基于bottom_left2 计算坐标 bottom_left2 = QPointF(bottom_left2_x, bottom_left2_y) # =========================================================================== bottom_left3 = transformed_right_red[3] bottom_right = transformed_right_red[2] right_blue_vertices = [ top_mid, top_end, bottom_left1, bottom_left2, bottom_left3, bottom_right, top_mid ] painter.drawPolygon(right_blue_vertices) # ---- 角度属性(用于动画)---- def get_angle(self): return self._angle def set_angle(self, angle): # 动画夹爪打开,限制范围为 0 到 70度 # 注意:为了动画显示效果,就算超过70度,也只按70度计算 self._angle = max(0.0, min(70.0, angle)) self.update() angle = Property(float, get_angle, set_angle) # 测试动画 def testAnimation(self, target_angle, duration=6): self.animation = QPropertyAnimation(self, b"angle") self.animation.setDuration(duration * 1000) # duration单位为秒 self.animation.setStartValue(self._angle) self.animation.setEndValue(target_angle) self.animation.start() if __name__ == "__main__": app = QApplication(sys.argv) widget = ClampWidget() widget.show() # widget.testAnimation(70) close_anim = QPropertyAnimation(widget, b"angle") close_anim.setDuration(6000) close_anim.setStartValue(0) close_anim.setEndValue(60) close_anim.start() # widget.set_angle(30) sys.exit(app.exec())