pyqt5用QPainter在扇形上面描绘数据点,并可点击提示

  1. 绘画底层图形
  2. 在图形内描绘数据点
  3. 点击数据点时悬浮提示内容

数据有距离和角度,来绘画数据点,实时检测数据插入,可以用多进程,通过共用队列来传递数据
在这里插入图片描述

需要自动更新绘图时,调用self.update(),此代码仅保留思路

class Alarm_widget(QWidget):
    message_signal = pyqtSignal(object)  # 自定义信号,发送给子窗口的消息
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.init_width = 1100
        self.init_high = 800
        # self.setGeometry(100, 100, self.init_width, self.init_high)
        self.setWindowTitle('PyQt5 扇形和点绘制')
        # 创建一个定时器,用于在达到一定时间后关闭tooltip
        self.toolTip_time = QTimer(self)
        self.toolTip_time.timeout.connect(self.hide_tool_tip)
        self.time_hide_tip = 10000  # 设置Tip多长时间的显示,毫秒
        #插入测试数据
        self.data_points['data1'] = {'distance': 5000, 'angle': 12, 'color': QColor(255, 165, 0)}
        
    def paintEvent(self, event):
        qp = QPainter(self)
        qp.setRenderHint(QPainter.Antialiasing, True)  # 使用抗锯齿
        qp.begin(self)
        self.draw_pie_and_points(qp, self.data_points)
        qp.end()


    def draw_pie_and_points(self, qp, data_points:dict):
        """
        画扇形和打点
        :param qp: QPainter
        :param data_points: 数据点位字典
        :return:
        """
        pie_num = 4  # 扇形的数量
        widget_size = (int(self.width() / 2), int(self.height() / 2))  # 图形占窗口的比例
        self.center = QPointF(widget_size[0], widget_size[1] + 20)   # 圆心位置
        self.start_angle = 45 * 16 # 开始角度是从3点钟方向逆时针开始, 转换为16进制表示的角度
        self.span_angle = 90 * 16  # 结束角度
        if self.center.x() < self.center.y():  # 取尺寸最小的,防止超出屏幕
            radius_step = int(widget_size[0] / pie_num)  # 每多少像素代表10km
        else:
            radius_step = int(widget_size[1] / pie_num)
        self.point_radius = 5  # 告警点的半径大小

        for i in range(0, 4):
            radius = (4-i) * radius_step
            # 绘制扇形
            qp.setBrush(QBrush(QColor(213, 226, 247)))
            # qp.setPen(QPen(Qt.black, 1, Qt.DashLine)) # 线宽1,线型为虚线
            qp.drawPie(self.center.x() - radius, self.center.y() - radius, 2 * radius, 2 * radius, self.start_angle, self.span_angle)

        # 中线
        radius = 4 * radius_step
        qp.setPen(QPen(Qt.black, 1, Qt.DashLine)) # 黑色,像素宽,虚线样式
        qp.drawLine(self.center.x(), self.center.y(), self.center.x(), (self.center.y() - radius))

        num = 0  # 计数多少数据
        # 绘制短线和旁边的名称
        line_height = 40  # 每行数据之间的垂直间隔
        text_offset = 20  # 文本与线条之间的水平间隔
        font = QFont()
        font.setPointSize(12)  # 设置字体大小为12点
        # 角度转弧度,计算数据详情列表的左侧位置
        angle_rad = math.radians(180-(self.start_angle+self.span_angle)/16)
        # 计算余弦值
        cos_value = math.cos(angle_rad) * 4 * radius_step
        result_list_posx = self.center.x() - cos_value

        for data, meta in data_points.items():

            distance = meta['distance']
            color_value = meta['color']
            # 设置点的颜色和大小
            qp.setPen(QPen(color_value, 8))
            # 距离 转换 像素距离
            pix_distance = distance * radius_step / 10000
            if 'angle' in meta:  # 存在角度值,则画点
                angle = meta['angle']
                # 将极坐标转换为直角坐标
                x = self.center.x() + pix_distance * math.cos(math.radians(90 - angle))
                y = self.center.y() - pix_distance * math.sin(math.radians(90 - angle))
                self.points[data] = (x, y)
                # 绘制点(使用 drawEllipse 方法绘制很小的圆来近似点)
                qp.drawEllipse(QPointF(x, y), self.point_radius - 1, self.point_radius - 1)

                # 在下面列表展示详情
                qp.setFont(font)
                qp.setBrush(color_value)  # 设置填充颜色
                qp.drawEllipse(result_list_posx + 3, self.center.y() + 20 + line_height * num, 14, 14)  # 绘制圆点
                qp.setPen(Qt.black)  # 设置文本颜色为黑色
                text = "提示内容1"
                qp.drawText(result_list_posx + 20 + text_offset, self.center.y() + 33 + line_height * num, text)
            

            num += 1



    def mousePressEvent(self, event):
        # 检测鼠标是否在点的附近
        pos = event.pos()
        check_radius = self.point_radius + 2  # 检测鼠标点击半径,比告警点宽一点点
        # self.lastClickedPoint = None
        for data, position in self.points.items():
            x = position[0]
            y = position[1]
            # 检查鼠标是否在点的矩形区域内(使用半径扩展)
            rect = QRect(x - check_radius, y - check_radius, 2 * check_radius, 2 * check_radius)
            if rect.contains(pos):
                # self.lastClickedPoint = QPoint(x, y)
                tip = data[-4:] + ': ' + self.data_points[data]['collect_time'][5:-3]
                # 显示提示,并设置定时器
                self.show_tool_tip(pos, tip)
                self.toolTip_time.start(self.time_hide_tip)  # 到时隐藏提示
                break
        else:
            for imsi, r in self.arcs_radius.items():
                if self.is_point_on_arc(r, self.start_angle, self.span_angle, pos, self.point_radius):
                    # print("点击点在圆弧上")
                    tip = imsi[-4:] + ': ' + self.data_points[imsi]['collect_time'][5:-3]
                    # 显示提示,并设置定时器
                    self.show_tool_tip(pos, tip)
                    self.toolTip_time.start(self.time_hide_tip)  # 到时隐藏提示
                    break
            else:
                # 如果没有在任何点附近,隐藏工具提示
                self.hide_tool_tip()

    def resizeEvent(self, event):
        # 当窗口大小改变时,触发重绘
        self.update()

    def show_tool_tip(self, pos, text:str):
        """
        在指定坐标显示提示
        :param pos: 信息提示的坐标
        :param text: 提示内容
        :return:
        """
        self.toolTipLabel.setText(f"{text}")
        # 将label移动到鼠标位置附近,但可能需要根据需要进行调整以避免遮挡
        label_pos = pos + QPoint(20, -self.toolTipLabel.height() - 20)

        # 显示CustomToolTip在widget的指定位置附近
        screen = QApplication.desktop().screenGeometry()
        # widget_rect = self.geometry()
        # widget_top_left = self.mapToGlobal(widget_rect.topLeft())
        # # 计算位置,确保ToolTip不会超出屏幕
        # x = widget_top_left.x() + pos.x()
        # y = widget_top_left.y() + pos.y()
        x = label_pos.x()
        y = label_pos.y()
        if x + self.toolTipLabel.width() > screen.width():
            x = screen.width() - self.toolTipLabel.width()
        if y + self.toolTipLabel.height() > screen.height():
            y = screen.height() - self.toolTipLabel.height()

        self.toolTipLabel.move(x, y)
        self.toolTipLabel.show()


    def hide_tool_tip(self):
        self.toolTipLabel.hide()
        self.toolTip_time.stop()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值