#!/usr/bin/env python # -*- coding: utf-8 -*- ''' Created on 2017年12月23日 @author: Irony."[讽刺] @site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447 @email: 892768447@qq.com @file: QChartToolTipTest @description: ''' import random import sys from PyQt5.QtChart import QChartView, QChart, QLineSeries from PyQt5.QtCore import Qt, QRectF, QPoint, QPointF from PyQt5.QtGui import QPainter, QCursor from PyQt5.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \ QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem __Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com" __Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]" __Version__ = "Version 1.0" m_listCount = 3 m_valueMax = 10 m_valueCount = 7 def generateRandomData(listCount, valueMax, valueCount): random.seed() dataTable = [] for i in range(listCount): dataList = [] yValue = 0.0 f_valueCount = float(valueCount) for j in range(valueCount): yValue += random.uniform(0, valueMax) / f_valueCount value = j + random.random() * m_valueMax / f_valueCount, yValue label = "Slice " + str(i) + ":" + str(j) dataList.append((value, label)) dataTable.append(dataList) return dataTable m_dataTable = generateRandomData(m_listCount, m_valueMax, m_valueCount) ''' class CircleWidget(QGraphicsProxyWidget): def __init__(self, color, *args, **kwargs): super(CircleWidget, self).__init__(*args, **kwargs) label = QLabel() label.setMinimumSize(12, 12) label.setMaximumSize(12, 12) label.setStyleSheet( "border:1px solid green;border-radius:6px;background: %s;" % color) self.setWidget(label) class TextWidget(QGraphicsProxyWidget): def __init__(self, text, *args, **kwargs): super(TextWidget, self).__init__(*args, **kwargs) self.setWidget(QLabel(text, styleSheet="color:white;")) class GraphicsWidget(QGraphicsWidget): def __init__(self, *args, **kwargs): super(GraphicsWidget, self).__init__(*args, **kwargs) # self.setFlags(self.ItemClipsChildrenToShape) self.setZValue(999) layout = QGraphicsGridLayout(self) for row in range(6): layout.addItem(CircleWidget("red"), row, 0) layout.addItem(TextWidget("red"), row, 1) self.hide() def show(self, pos): self.setGeometry(pos.x(), pos.y(), self.size().width(), self.size().height()) super(GraphicsWidget, self).show() ''' class ToolTipItem(QWidget): def __init__(self, color, text, parent=None): super(ToolTipItem, self).__init__(parent) layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) clabel = QLabel(self) clabel.setMinimumSize(12, 12) clabel.setMaximumSize(12, 12) clabel.setStyleSheet("border-radius:6px;background: rgba(%s,%s,%s,%s);" % ( color.red(), color.green(), color.blue(), color.alpha())) layout.addWidget(clabel) self.textLabel = QLabel(text, self, styleSheet="color:white;") layout.addWidget(self.textLabel) def setText(self, text): self.textLabel.setText(text) class ToolTipWidget(QWidget): Cache = {} def __init__(self, *args, **kwargs): super(ToolTipWidget, self).__init__(*args, **kwargs) self.setAttribute(Qt.WA_StyledBackground, True) self.setStyleSheet( "ToolTipWidget{background: rgba(50,50,50,70);}") layout = QVBoxLayout(self) self.titleLabel = QLabel(self, styleSheet="color:white;") layout.addWidget(self.titleLabel) def updateUi(self, title, points): self.titleLabel.setText(title) for serie, point in points: if serie not in self.Cache: item = ToolTipItem( serie.color(), (serie.name() or "-") + ":" + str(point.y()), self) self.layout().addWidget(item) self.Cache[serie] = item else: self.Cache[serie].setText( (serie.name() or "-") + ":" + str(point.y())) class GraphicsProxyWidget(QGraphicsProxyWidget): def __init__(self, *args, **kwargs): super(GraphicsProxyWidget, self).__init__(*args, **kwargs) self.setZValue(999) self.tipWidget = ToolTipWidget() self.setWidget(self.tipWidget) self.hide() def show(self, title, points, pos): self.setGeometry(QRectF(pos, self.size())) self.tipWidget.updateUi(title, points) super(GraphicsProxyWidget, self).show() class ChartView(QChartView): def __init__(self, *args, **kwargs): super(ChartView, self).__init__(*args, **kwargs) self.resize(800, 600) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self.initChart() self.toolTipWidget = GraphicsProxyWidget(self._chart) # line self.lineItem = QGraphicsLineItem(self._chart) self.lineItem.setZValue(998) self.lineItem.hide() def mouseMoveEvent(self, event): super(ChartView, self).mouseMoveEvent(event) # 获取x和y轴的最小最大值 axisX, axisY = self._chart.axisX(), self._chart.axisY() min_x, max_x = axisX.min(), axisX.max() min_y, max_y = axisY.min(), axisY.max() # 坐标系中左上角顶点 point_top = self._chart.mapToPosition(QPointF(min_x, max_y)) # 坐标原点坐标 point_bottom = self._chart.mapToPosition(QPointF(min_x, min_y)) step_x = (max_x - min_x) / (axisX.tickCount() - 1) step_y = (max_y - min_y) / (axisY.tickCount() - 1) # 把鼠标位置所在点转换为对应的xy值 x = self._chart.mapToValue(event.pos()).x() y = self._chart.mapToValue(event.pos()).y() # print(x, y, step_x, step_y, event.pos()) r_x = x / step_x r_y = y / step_y # @UnusedVariable index = round(r_x) - 1 # 四舍五入 # print(r_x, r_y, index, step_x, step_y) # 得到在坐标系中的所有series的类型和点 points = [(serie, serie.at(index)) for serie in self._chart.series() if min_x <= x <= max_x and min_y <= y <= max_y] if points: # 跟随鼠标的黑线条 self.lineItem.setLine(event.pos().x(), point_top.y(), event.pos().x(), point_bottom.y()) self.lineItem.show() self.toolTipWidget.show("", points, event.pos() + QPoint(20, 20)) else: self.toolTipWidget.hide() self.lineItem.hide() def onSeriesHoverd(self, point, state): if state: try: name = self.sender().name() except: name = "" QToolTip.showText(QCursor.pos(), "%s\nx: %s\ny: %s" % (name, point.x(), point.y())) def initChart(self): self._chart = QChart(title="Line Chart") self._chart.setAcceptHoverEvents(True) for i, data_list in enumerate(m_dataTable): series = QLineSeries(self._chart) for value, _ in data_list: series.append(*value) series.setName("Series " + str(i)) series.setPointsVisible(True) # 显示原点 series.hovered.connect(self.onSeriesHoverd) self._chart.addSeries(series) self._chart.createDefaultAxes() # 创建默认的轴 self.setChart(self._chart) if __name__ == "__main__": app = QApplication(sys.argv) app.setStyleSheet("""QToolTip { border: none; padding: 5px; color: white; background: rgb(50,50,50); opacity: 100; }""") view = ChartView() view.show() sys.exit(app.exec_())