QChart
|
@ -8,6 +8,7 @@ encoding//QTableView/CopyContent/CopyContent.py=utf-8
|
|||
encoding//QTableView/CopyContent/__main__.py=utf-8
|
||||
encoding//QTableWidget/SqlQuery/SqlQuery.py=utf-8
|
||||
encoding//QTableWidget/SqlQuery/__main__.py=utf-8
|
||||
encoding//QTreeWidget/ParsingJson.py=utf-8
|
||||
encoding//\u5176\u5B83/C\u548CC++\u6269\u5C55/py\u8F6Cpyd/pydmod.py=utf-8
|
||||
encoding//\u5176\u5B83/QRC\u8D44\u6E90\u6587\u4EF6\u4F7F\u7528/qrctest1.py=utf-8
|
||||
encoding//\u5176\u5B83/QRC\u8D44\u6E90\u6587\u4EF6\u4F7F\u7528/qrctest2.py=utf-8
|
||||
|
@ -55,14 +56,6 @@ encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248
|
|||
encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C1/testNinePatch.py=utf-8
|
||||
encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C2/QtNinePatch.py=utf-8
|
||||
encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C2/testQtNinePatch.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/ChartView/ChartView.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/ChartView/ChatWidget.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/charts/bar/BarStack.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/charts/line/LineStack.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/demo/LineChart.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/demo/LineChart\u81EA\u5B9A\u4E49xy\u8F74.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/demo/ToolTip.py=utf-8
|
||||
encoding//\u56FE\u8868/PyQtChart/demo/ToolTip2.py=utf-8
|
||||
encoding//\u591A\u7EBF\u7A0B/moveToThread.py=utf-8
|
||||
encoding//\u591A\u7EBF\u7A0B/\u7EBF\u7A0B\u4F11\u7720\u5524\u9192.py=utf-8
|
||||
encoding//\u591A\u7EBF\u7A0B/\u7EBF\u7A0B\u6302\u8D77\u6062\u590D.py=utf-8
|
||||
|
@ -70,7 +63,6 @@ encoding//\u591A\u7EBF\u7A0B/\u7EE7\u627FQThread.py=utf-8
|
|||
encoding//\u591A\u9875\u9762/QScrollArea/\u4EFFQQ\u8BBE\u7F6E\u9762\u677F/SettingUi.py=utf-8
|
||||
encoding//\u591A\u9875\u9762/QScrollArea/\u4EFFQQ\u8BBE\u7F6E\u9762\u677F/\u4EFFQQ\u8BBE\u7F6E\u9762\u677F.py=utf-8
|
||||
encoding//\u591A\u9875\u9762/QStackedWidget/\u5DE6\u4FA7\u9009\u9879\u5361/\u5DE6\u4FA7\u9009\u9879\u5361.py=utf-8
|
||||
encoding//\u6811\u7ED3\u6784/QTreeWidget/Json\u751F\u6210QTreeWidget/Json\u751F\u6210\u6811\u5F62\u7ED3\u6784.py=utf-8
|
||||
encoding//\u6D4F\u89C8\u5668/QWebView/\u68A6\u5E7B\u6811/\u68A6\u5E7B\u6811.py=utf-8
|
||||
encoding//\u6ED1\u52A8\u6761/\u6ED1\u52A8\u6761\u70B9\u51FB\u5B9A\u4F4D.py=utf-8
|
||||
encoding//\u7A97\u53E3/\u5206\u5272\u7A97\u53E3\u7684\u5206\u5272\u6761\u91CD\u5199.py=utf-8
|
||||
|
|
|
@ -1,256 +1,256 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月28日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: charts.bar.BarStack
|
||||
@description: like http://echarts.baidu.com/demo.html#bar-stack
|
||||
'''
|
||||
|
||||
from random import randint
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QChart, QBarSeries, QBarSet, QBarCategoryAxis
|
||||
from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
|
||||
from PyQt5.QtGui import QPainter, QPen
|
||||
from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
|
||||
QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
|
||||
|
||||
|
||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||
__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
|
||||
__Version__ = "Version 1.0"
|
||||
|
||||
|
||||
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, 100);}")
|
||||
layout = QVBoxLayout(self)
|
||||
self.titleLabel = QLabel(self, styleSheet="color:white;")
|
||||
layout.addWidget(self.titleLabel)
|
||||
|
||||
def updateUi(self, title, bars):
|
||||
self.titleLabel.setText(title)
|
||||
for bar, value in bars:
|
||||
if bar not in self.Cache:
|
||||
item = ToolTipItem(
|
||||
bar.color(),
|
||||
(bar.label() or "-") + ":" + str(value), self)
|
||||
self.layout().addWidget(item)
|
||||
self.Cache[bar] = item
|
||||
else:
|
||||
self.Cache[bar].setText(
|
||||
(bar.label() or "-") + ":" + str(value))
|
||||
brush = bar.brush()
|
||||
color = brush.color()
|
||||
self.Cache[bar].setVisible(color.alphaF() == 1.0) # 隐藏那些不可用的项
|
||||
self.adjustSize() # 调整大小
|
||||
|
||||
|
||||
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 width(self):
|
||||
return self.size().width()
|
||||
|
||||
def height(self):
|
||||
return self.size().height()
|
||||
|
||||
def show(self, title, bars, pos):
|
||||
self.setGeometry(QRectF(pos, self.size()))
|
||||
self.tipWidget.updateUi(title, bars)
|
||||
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()
|
||||
|
||||
# 提示widget
|
||||
self.toolTipWidget = GraphicsProxyWidget(self._chart)
|
||||
|
||||
# line 宽度需要调整
|
||||
self.lineItem = QGraphicsLineItem(self._chart)
|
||||
pen = QPen(Qt.gray)
|
||||
self.lineItem.setPen(pen)
|
||||
self.lineItem.setZValue(998)
|
||||
self.lineItem.hide()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.category_len = len(axisX.categories())
|
||||
self.min_x, self.max_x = -0.5, self.category_len - 0.5
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
pos = event.pos()
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(pos).x()
|
||||
y = self._chart.mapToValue(pos).y()
|
||||
index = round(x)
|
||||
# 得到在坐标系中的所有bar的类型和点
|
||||
serie = self._chart.series()[0]
|
||||
bars = [(bar, bar.at(index))
|
||||
for bar in serie.barSets() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
|
||||
# print(bars)
|
||||
if bars:
|
||||
right_top = self._chart.mapToPosition(
|
||||
QPointF(self.max_x, self.max_y))
|
||||
# 等分距离比例
|
||||
step_x = round(
|
||||
(right_top.x() - self.point_top.x()) / self.category_len)
|
||||
posx = self._chart.mapToPosition(QPointF(x, self.min_y))
|
||||
self.lineItem.setLine(posx.x(), self.point_top.y(),
|
||||
posx.x(), posx.y())
|
||||
self.lineItem.show()
|
||||
try:
|
||||
title = self.categories[index]
|
||||
except:
|
||||
title = ""
|
||||
t_width = self.toolTipWidget.width()
|
||||
t_height = self.toolTipWidget.height()
|
||||
# 如果鼠标位置离右侧的距离小于tip宽度
|
||||
x = pos.x() - t_width if self.width() - \
|
||||
pos.x() - 20 < t_width else pos.x()
|
||||
# 如果鼠标位置离底部的高度小于tip高度
|
||||
y = pos.y() - t_height if self.height() - \
|
||||
pos.y() - 20 < t_height else pos.y()
|
||||
self.toolTipWidget.show(
|
||||
title, bars, QPoint(x, y))
|
||||
else:
|
||||
self.toolTipWidget.hide()
|
||||
self.lineItem.hide()
|
||||
|
||||
def handleMarkerClicked(self):
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
bar = marker.barset()
|
||||
if not bar:
|
||||
return
|
||||
# bar透明度
|
||||
brush = bar.brush()
|
||||
color = brush.color()
|
||||
alpha = 0.0 if color.alphaF() == 1.0 else 1.0
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
bar.setBrush(brush)
|
||||
# marker
|
||||
brush = marker.labelBrush()
|
||||
color = brush.color()
|
||||
alpha = 0.4 if color.alphaF() == 1.0 else 1.0
|
||||
# 设置label的透明度
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setLabelBrush(brush)
|
||||
# 设置marker的透明度
|
||||
brush = marker.brush()
|
||||
color = brush.color()
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setBrush(brush)
|
||||
|
||||
def handleMarkerHovered(self, status):
|
||||
# 设置bar的画笔宽度
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
bar = marker.barset()
|
||||
if not bar:
|
||||
return
|
||||
pen = bar.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if status else -1))
|
||||
bar.setPen(pen)
|
||||
|
||||
def handleBarHoverd(self, status, index):
|
||||
# 设置bar的画笔宽度
|
||||
bar = self.sender() # 信号发送者
|
||||
pen = bar.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if status else -1))
|
||||
bar.setPen(pen)
|
||||
|
||||
def initChart(self):
|
||||
self._chart = QChart(title="柱状图堆叠")
|
||||
self._chart.setAcceptHoverEvents(True)
|
||||
# Series动画
|
||||
self._chart.setAnimationOptions(QChart.SeriesAnimations)
|
||||
self.categories = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
names = ["邮件营销", "联盟广告", "视频广告", "直接访问", "搜索引擎"]
|
||||
series = QBarSeries(self._chart)
|
||||
for name in names:
|
||||
bar = QBarSet(name)
|
||||
# 随机数据
|
||||
for _ in range(7):
|
||||
bar.append(randint(0, 10))
|
||||
series.append(bar)
|
||||
bar.hovered.connect(self.handleBarHoverd) # 鼠标悬停
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
# x轴
|
||||
axis_x = QBarCategoryAxis(self._chart)
|
||||
axis_x.append(self.categories)
|
||||
self._chart.setAxisX(axis_x, series)
|
||||
# chart的图例
|
||||
legend = self._chart.legend()
|
||||
legend.setVisible(True)
|
||||
# 遍历图例上的标记并绑定信号
|
||||
for marker in legend.markers():
|
||||
# 点击事件
|
||||
marker.clicked.connect(self.handleMarkerClicked)
|
||||
# 鼠标悬停事件
|
||||
marker.hovered.connect(self.handleMarkerHovered)
|
||||
self.setChart(self._chart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
view = ChartView()
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月28日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: charts.bar.BarStack
|
||||
@description: like http://echarts.baidu.com/demo.html#bar-stack
|
||||
'''
|
||||
|
||||
from random import randint
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QChart, QBarSeries, QBarSet, QBarCategoryAxis
|
||||
from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
|
||||
from PyQt5.QtGui import QPainter, QPen
|
||||
from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
|
||||
QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
|
||||
|
||||
|
||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||
__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
|
||||
__Version__ = "Version 1.0"
|
||||
|
||||
|
||||
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, 100);}")
|
||||
layout = QVBoxLayout(self)
|
||||
self.titleLabel = QLabel(self, styleSheet="color:white;")
|
||||
layout.addWidget(self.titleLabel)
|
||||
|
||||
def updateUi(self, title, bars):
|
||||
self.titleLabel.setText(title)
|
||||
for bar, value in bars:
|
||||
if bar not in self.Cache:
|
||||
item = ToolTipItem(
|
||||
bar.color(),
|
||||
(bar.label() or "-") + ":" + str(value), self)
|
||||
self.layout().addWidget(item)
|
||||
self.Cache[bar] = item
|
||||
else:
|
||||
self.Cache[bar].setText(
|
||||
(bar.label() or "-") + ":" + str(value))
|
||||
brush = bar.brush()
|
||||
color = brush.color()
|
||||
self.Cache[bar].setVisible(color.alphaF() == 1.0) # 隐藏那些不可用的项
|
||||
self.adjustSize() # 调整大小
|
||||
|
||||
|
||||
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 width(self):
|
||||
return self.size().width()
|
||||
|
||||
def height(self):
|
||||
return self.size().height()
|
||||
|
||||
def show(self, title, bars, pos):
|
||||
self.setGeometry(QRectF(pos, self.size()))
|
||||
self.tipWidget.updateUi(title, bars)
|
||||
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()
|
||||
|
||||
# 提示widget
|
||||
self.toolTipWidget = GraphicsProxyWidget(self._chart)
|
||||
|
||||
# line 宽度需要调整
|
||||
self.lineItem = QGraphicsLineItem(self._chart)
|
||||
pen = QPen(Qt.gray)
|
||||
self.lineItem.setPen(pen)
|
||||
self.lineItem.setZValue(998)
|
||||
self.lineItem.hide()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.category_len = len(axisX.categories())
|
||||
self.min_x, self.max_x = -0.5, self.category_len - 0.5
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
pos = event.pos()
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(pos).x()
|
||||
y = self._chart.mapToValue(pos).y()
|
||||
index = round(x)
|
||||
# 得到在坐标系中的所有bar的类型和点
|
||||
serie = self._chart.series()[0]
|
||||
bars = [(bar, bar.at(index))
|
||||
for bar in serie.barSets() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
|
||||
# print(bars)
|
||||
if bars:
|
||||
right_top = self._chart.mapToPosition(
|
||||
QPointF(self.max_x, self.max_y))
|
||||
# 等分距离比例
|
||||
step_x = round(
|
||||
(right_top.x() - self.point_top.x()) / self.category_len)
|
||||
posx = self._chart.mapToPosition(QPointF(x, self.min_y))
|
||||
self.lineItem.setLine(posx.x(), self.point_top.y(),
|
||||
posx.x(), posx.y())
|
||||
self.lineItem.show()
|
||||
try:
|
||||
title = self.categories[index]
|
||||
except:
|
||||
title = ""
|
||||
t_width = self.toolTipWidget.width()
|
||||
t_height = self.toolTipWidget.height()
|
||||
# 如果鼠标位置离右侧的距离小于tip宽度
|
||||
x = pos.x() - t_width if self.width() - \
|
||||
pos.x() - 20 < t_width else pos.x()
|
||||
# 如果鼠标位置离底部的高度小于tip高度
|
||||
y = pos.y() - t_height if self.height() - \
|
||||
pos.y() - 20 < t_height else pos.y()
|
||||
self.toolTipWidget.show(
|
||||
title, bars, QPoint(x, y))
|
||||
else:
|
||||
self.toolTipWidget.hide()
|
||||
self.lineItem.hide()
|
||||
|
||||
def handleMarkerClicked(self):
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
bar = marker.barset()
|
||||
if not bar:
|
||||
return
|
||||
# bar透明度
|
||||
brush = bar.brush()
|
||||
color = brush.color()
|
||||
alpha = 0.0 if color.alphaF() == 1.0 else 1.0
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
bar.setBrush(brush)
|
||||
# marker
|
||||
brush = marker.labelBrush()
|
||||
color = brush.color()
|
||||
alpha = 0.4 if color.alphaF() == 1.0 else 1.0
|
||||
# 设置label的透明度
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setLabelBrush(brush)
|
||||
# 设置marker的透明度
|
||||
brush = marker.brush()
|
||||
color = brush.color()
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setBrush(brush)
|
||||
|
||||
def handleMarkerHovered(self, status):
|
||||
# 设置bar的画笔宽度
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
bar = marker.barset()
|
||||
if not bar:
|
||||
return
|
||||
pen = bar.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if status else -1))
|
||||
bar.setPen(pen)
|
||||
|
||||
def handleBarHoverd(self, status, index):
|
||||
# 设置bar的画笔宽度
|
||||
bar = self.sender() # 信号发送者
|
||||
pen = bar.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if status else -1))
|
||||
bar.setPen(pen)
|
||||
|
||||
def initChart(self):
|
||||
self._chart = QChart(title="柱状图堆叠")
|
||||
self._chart.setAcceptHoverEvents(True)
|
||||
# Series动画
|
||||
self._chart.setAnimationOptions(QChart.SeriesAnimations)
|
||||
self.categories = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
names = ["邮件营销", "联盟广告", "视频广告", "直接访问", "搜索引擎"]
|
||||
series = QBarSeries(self._chart)
|
||||
for name in names:
|
||||
bar = QBarSet(name)
|
||||
# 随机数据
|
||||
for _ in range(7):
|
||||
bar.append(randint(0, 10))
|
||||
series.append(bar)
|
||||
bar.hovered.connect(self.handleBarHoverd) # 鼠标悬停
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
# x轴
|
||||
axis_x = QBarCategoryAxis(self._chart)
|
||||
axis_x.append(self.categories)
|
||||
self._chart.setAxisX(axis_x, series)
|
||||
# chart的图例
|
||||
legend = self._chart.legend()
|
||||
legend.setVisible(True)
|
||||
# 遍历图例上的标记并绑定信号
|
||||
for marker in legend.markers():
|
||||
# 点击事件
|
||||
marker.clicked.connect(self.handleMarkerClicked)
|
||||
# 鼠标悬停事件
|
||||
marker.hovered.connect(self.handleMarkerHovered)
|
||||
self.setChart(self._chart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
view = ChartView()
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
|
@ -1,147 +1,146 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月19日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: LineChart自定义xy轴
|
||||
@description:
|
||||
'''
|
||||
import random
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QLineSeries, QChart, QCategoryAxis
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
|
||||
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def getChart(title):
|
||||
chart = QChart(title=title)
|
||||
for i, data_list in enumerate(m_dataTable):
|
||||
series = QLineSeries(chart)
|
||||
for value, _ in data_list:
|
||||
series.append(*value)
|
||||
series.setName("Series " + str(i))
|
||||
chart.addSeries(series)
|
||||
chart.createDefaultAxes() # 创建默认的轴
|
||||
return chart
|
||||
|
||||
|
||||
def customAxisX(chart):
|
||||
# 自定义x轴(均分)
|
||||
series = chart.series()
|
||||
if not series:
|
||||
return
|
||||
axisx = QCategoryAxis(
|
||||
chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
minx = chart.axisX().min()
|
||||
maxx = chart.axisX().max()
|
||||
tickc = chart.axisX().tickCount()
|
||||
if tickc < 2:
|
||||
axisx.append("lable0", minx)
|
||||
else:
|
||||
step = (maxx - minx) / (tickc - 1) # tickc>=2
|
||||
for i in range(0, tickc):
|
||||
axisx.append("lable%s" % i, minx + i * step)
|
||||
chart.setAxisX(axisx, series[-1])
|
||||
|
||||
|
||||
def customTopAxisX(chart):
|
||||
# 自定义top x轴
|
||||
series = chart.series()
|
||||
if not series:
|
||||
return
|
||||
category = ["%d月" % i for i in range(1, 9)] # 1-8月
|
||||
axisx = QCategoryAxis(
|
||||
chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
axisx.setGridLineVisible(False) # 隐藏网格线条
|
||||
axisx.setTickCount(len(category)) # 设置刻度个数
|
||||
chart.axisX().setTickCount(len(category)) # 强制修改x轴的刻度个数一致
|
||||
minx = chart.axisX().min()
|
||||
maxx = chart.axisX().max()
|
||||
tickc = chart.axisX().tickCount()
|
||||
step = (maxx - minx) / (tickc - 1) # tickc>=2
|
||||
for i in range(0, tickc):
|
||||
axisx.append(category[i], minx + i * step)
|
||||
chart.addAxis(axisx, Qt.AlignTop) # 添加到右侧
|
||||
series[-1].attachAxis(axisx) # 附加到series上
|
||||
|
||||
|
||||
def customAxisY(chart):
|
||||
# 自定义y轴(不等分)
|
||||
series = chart.series()
|
||||
if not series:
|
||||
return
|
||||
category = ["周一", "周二", "周三", "周四",
|
||||
"周五", "周六", "周日"]
|
||||
axisy = QCategoryAxis(
|
||||
chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
axisy.setGridLineVisible(False) # 隐藏网格线条
|
||||
axisy.setTickCount(len(category)) # 设置刻度个数
|
||||
miny = chart.axisY().min()
|
||||
maxy = chart.axisY().max()
|
||||
tickc = axisy.tickCount()
|
||||
if tickc < 2:
|
||||
axisy.append(category[0])
|
||||
else:
|
||||
step = (maxy - miny) / (tickc - 1) # tickc>=2
|
||||
for i in range(0, tickc):
|
||||
axisy.append(category[i], miny + i * step)
|
||||
chart.addAxis(axisy, Qt.AlignRight) # 添加到右侧
|
||||
series[-1].attachAxis(axisy) # 附加到series上
|
||||
|
||||
|
||||
class Window(QWidget):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Window, self).__init__(*args, **kwargs)
|
||||
layout = QHBoxLayout(self)
|
||||
|
||||
# 自定义x轴(和原来的x轴值对应等分)
|
||||
chart = getChart("自定义x轴(和原来的x轴值对应等分)")
|
||||
customAxisX(chart)
|
||||
layout.addWidget(QChartView(chart, self))
|
||||
# 自定义添加右侧y轴(等分,与左侧不对应)
|
||||
chart = getChart("自定义添加右侧y轴(等分,与左侧不对应)")
|
||||
customAxisY(chart)
|
||||
layout.addWidget(QChartView(chart, self))
|
||||
# 自定义top x轴(按现有新的x轴划分)
|
||||
chart = getChart("自定义top x轴(按现有新的x轴划分)")
|
||||
customTopAxisX(chart)
|
||||
layout.addWidget(QChartView(chart, self))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
view = Window()
|
||||
view.resize(800, 600)
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月19日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: CustomXYaxis
|
||||
@description:
|
||||
'''
|
||||
import random
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QLineSeries, QChart, QCategoryAxis
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def getChart(title):
|
||||
chart = QChart(title=title)
|
||||
for i, data_list in enumerate(m_dataTable):
|
||||
series = QLineSeries(chart)
|
||||
for value, _ in data_list:
|
||||
series.append(*value)
|
||||
series.setName("Series " + str(i))
|
||||
chart.addSeries(series)
|
||||
chart.createDefaultAxes() # 创建默认的轴
|
||||
return chart
|
||||
|
||||
|
||||
def customAxisX(chart):
|
||||
# 自定义x轴(均分)
|
||||
series = chart.series()
|
||||
if not series:
|
||||
return
|
||||
axisx = QCategoryAxis(
|
||||
chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
minx = chart.axisX().min()
|
||||
maxx = chart.axisX().max()
|
||||
tickc = chart.axisX().tickCount()
|
||||
if tickc < 2:
|
||||
axisx.append("lable0", minx)
|
||||
else:
|
||||
step = (maxx - minx) / (tickc - 1) # tickc>=2
|
||||
for i in range(0, tickc):
|
||||
axisx.append("lable%s" % i, minx + i * step)
|
||||
chart.setAxisX(axisx, series[-1])
|
||||
|
||||
|
||||
def customTopAxisX(chart):
|
||||
# 自定义top x轴
|
||||
series = chart.series()
|
||||
if not series:
|
||||
return
|
||||
category = ["%d月" % i for i in range(1, 9)] # 1-8月
|
||||
axisx = QCategoryAxis(
|
||||
chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
axisx.setGridLineVisible(False) # 隐藏网格线条
|
||||
axisx.setTickCount(len(category)) # 设置刻度个数
|
||||
chart.axisX().setTickCount(len(category)) # 强制修改x轴的刻度个数一致
|
||||
minx = chart.axisX().min()
|
||||
maxx = chart.axisX().max()
|
||||
tickc = chart.axisX().tickCount()
|
||||
step = (maxx - minx) / (tickc - 1) # tickc>=2
|
||||
for i in range(0, tickc):
|
||||
axisx.append(category[i], minx + i * step)
|
||||
chart.addAxis(axisx, Qt.AlignTop) # 添加到右侧
|
||||
series[-1].attachAxis(axisx) # 附加到series上
|
||||
|
||||
|
||||
def customAxisY(chart):
|
||||
# 自定义y轴(不等分)
|
||||
series = chart.series()
|
||||
if not series:
|
||||
return
|
||||
category = ["周一", "周二", "周三", "周四",
|
||||
"周五", "周六", "周日"]
|
||||
axisy = QCategoryAxis(
|
||||
chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
axisy.setGridLineVisible(False) # 隐藏网格线条
|
||||
axisy.setTickCount(len(category)) # 设置刻度个数
|
||||
miny = chart.axisY().min()
|
||||
maxy = chart.axisY().max()
|
||||
tickc = axisy.tickCount()
|
||||
if tickc < 2:
|
||||
axisy.append(category[0])
|
||||
else:
|
||||
step = (maxy - miny) / (tickc - 1) # tickc>=2
|
||||
for i in range(0, tickc):
|
||||
axisy.append(category[i], miny + i * step)
|
||||
chart.addAxis(axisy, Qt.AlignRight) # 添加到右侧
|
||||
series[-1].attachAxis(axisy) # 附加到series上
|
||||
|
||||
|
||||
class Window(QWidget):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Window, self).__init__(*args, **kwargs)
|
||||
layout = QHBoxLayout(self)
|
||||
|
||||
# 自定义x轴(和原来的x轴值对应等分)
|
||||
chart = getChart("自定义x轴(和原来的x轴值对应等分)")
|
||||
customAxisX(chart)
|
||||
layout.addWidget(QChartView(chart, self))
|
||||
# 自定义添加右侧y轴(等分,与左侧不对应)
|
||||
chart = getChart("自定义添加右侧y轴(等分,与左侧不对应)")
|
||||
customAxisY(chart)
|
||||
layout.addWidget(QChartView(chart, self))
|
||||
# 自定义top x轴(按现有新的x轴划分)
|
||||
chart = getChart("自定义top x轴(按现有新的x轴划分)")
|
||||
customTopAxisX(chart)
|
||||
layout.addWidget(QChartView(chart, self))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
view = Window()
|
||||
view.resize(800, 600)
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
|
@ -1,36 +1,36 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月19日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: LineChart
|
||||
@description:
|
||||
'''
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QLineSeries, QChart
|
||||
from PyQt5.QtGui import QPainter
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
chart = QChart()
|
||||
chart.setTitle("Line Chart 1")
|
||||
series = QLineSeries(chart)
|
||||
series.append(0, 6)
|
||||
series.append(2, 4)
|
||||
chart.addSeries(series)
|
||||
chart.createDefaultAxes() # 创建默认轴
|
||||
|
||||
view = QChartView(chart)
|
||||
view.setRenderHint(QPainter.Antialiasing) # 抗锯齿
|
||||
view.resize(800, 600)
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月19日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: LineChart
|
||||
@description:
|
||||
'''
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QLineSeries, QChart
|
||||
from PyQt5.QtGui import QPainter
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
chart = QChart()
|
||||
chart.setTitle("Line Chart 1")
|
||||
series = QLineSeries(chart)
|
||||
series.append(0, 6)
|
||||
series.append(2, 4)
|
||||
chart.addSeries(series)
|
||||
chart.createDefaultAxes() # 创建默认轴
|
||||
|
||||
view = QChartView(chart)
|
||||
view.setRenderHint(QPainter.Antialiasing) # 抗锯齿
|
||||
view.resize(800, 600)
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
|
@ -1,279 +1,279 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月28日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: charts.line.LineStack
|
||||
@description: like http://echarts.baidu.com/demo.html#line-stack
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QChart, QLineSeries, QLegend,\
|
||||
QCategoryAxis
|
||||
from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
|
||||
from PyQt5.QtGui import QPainter, QPen
|
||||
from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
|
||||
QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
|
||||
|
||||
|
||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||
__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
|
||||
__Version__ = "Version 1.0"
|
||||
|
||||
|
||||
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, 100);}")
|
||||
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()))
|
||||
self.Cache[serie].setVisible(serie.isVisible()) # 隐藏那些不可用的项
|
||||
self.adjustSize() # 调整大小
|
||||
|
||||
|
||||
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 width(self):
|
||||
return self.size().width()
|
||||
|
||||
def height(self):
|
||||
return self.size().height()
|
||||
|
||||
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) # 抗锯齿
|
||||
# 自定义x轴label
|
||||
self.category = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
self.initChart()
|
||||
|
||||
# 提示widget
|
||||
self.toolTipWidget = GraphicsProxyWidget(self._chart)
|
||||
|
||||
# line
|
||||
self.lineItem = QGraphicsLineItem(self._chart)
|
||||
pen = QPen(Qt.gray)
|
||||
pen.setWidth(1)
|
||||
self.lineItem.setPen(pen)
|
||||
self.lineItem.setZValue(998)
|
||||
self.lineItem.hide()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.min_x, self.max_x = axisX.min(), axisX.max()
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(ChartView, self).resizeEvent(event)
|
||||
# 当窗口大小改变时需要重新计算
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
# 坐标原点坐标
|
||||
self.point_bottom = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.min_y))
|
||||
self.step_x = (self.max_x - self.min_x) / \
|
||||
(self._chart.axisX().tickCount() - 1)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
pos = event.pos()
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(pos).x()
|
||||
y = self._chart.mapToValue(pos).y()
|
||||
index = round((x - self.min_x) / self.step_x)
|
||||
# 得到在坐标系中的所有正常显示的series的类型和点
|
||||
points = [(serie, serie.at(index))
|
||||
for serie in self._chart.series()
|
||||
if self.min_x <= x <= self.max_x and
|
||||
self.min_y <= y <= self.max_y]
|
||||
if points:
|
||||
pos_x = self._chart.mapToPosition(
|
||||
QPointF(index * self.step_x + self.min_x, self.min_y))
|
||||
self.lineItem.setLine(pos_x.x(), self.point_top.y(),
|
||||
pos_x.x(), self.point_bottom.y())
|
||||
self.lineItem.show()
|
||||
try:
|
||||
title = self.category[index]
|
||||
except:
|
||||
title = ""
|
||||
t_width = self.toolTipWidget.width()
|
||||
t_height = self.toolTipWidget.height()
|
||||
# 如果鼠标位置离右侧的距离小于tip宽度
|
||||
x = pos.x() - t_width if self.width() - \
|
||||
pos.x() - 20 < t_width else pos.x()
|
||||
# 如果鼠标位置离底部的高度小于tip高度
|
||||
y = pos.y() - t_height if self.height() - \
|
||||
pos.y() - 20 < t_height else pos.y()
|
||||
self.toolTipWidget.show(
|
||||
title, points, QPoint(x, y))
|
||||
else:
|
||||
self.toolTipWidget.hide()
|
||||
self.lineItem.hide()
|
||||
|
||||
def handleMarkerClicked(self):
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
visible = not marker.series().isVisible()
|
||||
# # 隐藏或显示series
|
||||
marker.series().setVisible(visible)
|
||||
marker.setVisible(True) # 要保证marker一直显示
|
||||
# 透明度
|
||||
alpha = 1.0 if visible else 0.4
|
||||
# 设置label的透明度
|
||||
brush = marker.labelBrush()
|
||||
color = brush.color()
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setLabelBrush(brush)
|
||||
# 设置marker的透明度
|
||||
brush = marker.brush()
|
||||
color = brush.color()
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setBrush(brush)
|
||||
# 设置画笔透明度
|
||||
pen = marker.pen()
|
||||
color = pen.color()
|
||||
color.setAlphaF(alpha)
|
||||
pen.setColor(color)
|
||||
marker.setPen(pen)
|
||||
|
||||
def handleMarkerHovered(self, status):
|
||||
# 设置series的画笔宽度
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
series = marker.series()
|
||||
if not series:
|
||||
return
|
||||
pen = series.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if status else -1))
|
||||
series.setPen(pen)
|
||||
|
||||
def handleSeriesHoverd(self, point, state):
|
||||
# 设置series的画笔宽度
|
||||
series = self.sender() # 信号发送者
|
||||
pen = series.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if state else -1))
|
||||
series.setPen(pen)
|
||||
|
||||
def initChart(self):
|
||||
self._chart = QChart(title="折线图堆叠")
|
||||
self._chart.setAcceptHoverEvents(True)
|
||||
# Series动画
|
||||
self._chart.setAnimationOptions(QChart.SeriesAnimations)
|
||||
dataTable = [
|
||||
["邮件营销", [120, 132, 101, 134, 90, 230, 210]],
|
||||
["联盟广告", [220, 182, 191, 234, 290, 330, 310]],
|
||||
["视频广告", [150, 232, 201, 154, 190, 330, 410]],
|
||||
["直接访问", [320, 332, 301, 334, 390, 330, 320]],
|
||||
["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]]
|
||||
]
|
||||
for series_name, data_list in dataTable:
|
||||
series = QLineSeries(self._chart)
|
||||
for j, v in enumerate(data_list):
|
||||
series.append(j, v)
|
||||
series.setName(series_name)
|
||||
series.setPointsVisible(True) # 显示圆点
|
||||
series.hovered.connect(self.handleSeriesHoverd) # 鼠标悬停
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
axisX = self._chart.axisX() # x轴
|
||||
axisX.setTickCount(7) # x轴设置7个刻度
|
||||
axisX.setGridLineVisible(False) # 隐藏从x轴往上的线条
|
||||
axisY = self._chart.axisY()
|
||||
axisY.setTickCount(7) # y轴设置7个刻度
|
||||
axisY.setRange(0, 1500) # 设置y轴范围
|
||||
# 自定义x轴
|
||||
axis_x = QCategoryAxis(
|
||||
self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
axis_x.setTickCount(7)
|
||||
axis_x.setGridLineVisible(False)
|
||||
min_x = axisX.min()
|
||||
max_x = axisX.max()
|
||||
step = (max_x - min_x) / (7 - 1) # 7个tick
|
||||
for i in range(0, 7):
|
||||
axis_x.append(self.category[i], min_x + i * step)
|
||||
self._chart.setAxisX(axis_x, self._chart.series()[-1])
|
||||
# chart的图例
|
||||
legend = self._chart.legend()
|
||||
# 设置图例由Series来决定样式
|
||||
legend.setMarkerShape(QLegend.MarkerShapeFromSeries)
|
||||
# 遍历图例上的标记并绑定信号
|
||||
for marker in legend.markers():
|
||||
# 点击事件
|
||||
marker.clicked.connect(self.handleMarkerClicked)
|
||||
# 鼠标悬停事件
|
||||
marker.hovered.connect(self.handleMarkerHovered)
|
||||
self.setChart(self._chart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
view = ChartView()
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Created on 2017年12月28日
|
||||
@author: Irony."[讽刺]
|
||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file: charts.line.LineStack
|
||||
@description: like http://echarts.baidu.com/demo.html#line-stack
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
from PyQt5.QtChart import QChartView, QChart, QLineSeries, QLegend,\
|
||||
QCategoryAxis
|
||||
from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
|
||||
from PyQt5.QtGui import QPainter, QPen
|
||||
from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
|
||||
QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
|
||||
|
||||
|
||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||
__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
|
||||
__Version__ = "Version 1.0"
|
||||
|
||||
|
||||
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, 100);}")
|
||||
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()))
|
||||
self.Cache[serie].setVisible(serie.isVisible()) # 隐藏那些不可用的项
|
||||
self.adjustSize() # 调整大小
|
||||
|
||||
|
||||
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 width(self):
|
||||
return self.size().width()
|
||||
|
||||
def height(self):
|
||||
return self.size().height()
|
||||
|
||||
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) # 抗锯齿
|
||||
# 自定义x轴label
|
||||
self.category = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
self.initChart()
|
||||
|
||||
# 提示widget
|
||||
self.toolTipWidget = GraphicsProxyWidget(self._chart)
|
||||
|
||||
# line
|
||||
self.lineItem = QGraphicsLineItem(self._chart)
|
||||
pen = QPen(Qt.gray)
|
||||
pen.setWidth(1)
|
||||
self.lineItem.setPen(pen)
|
||||
self.lineItem.setZValue(998)
|
||||
self.lineItem.hide()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.min_x, self.max_x = axisX.min(), axisX.max()
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(ChartView, self).resizeEvent(event)
|
||||
# 当窗口大小改变时需要重新计算
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
# 坐标原点坐标
|
||||
self.point_bottom = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.min_y))
|
||||
self.step_x = (self.max_x - self.min_x) / \
|
||||
(self._chart.axisX().tickCount() - 1)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
pos = event.pos()
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(pos).x()
|
||||
y = self._chart.mapToValue(pos).y()
|
||||
index = round((x - self.min_x) / self.step_x)
|
||||
# 得到在坐标系中的所有正常显示的series的类型和点
|
||||
points = [(serie, serie.at(index))
|
||||
for serie in self._chart.series()
|
||||
if self.min_x <= x <= self.max_x and
|
||||
self.min_y <= y <= self.max_y]
|
||||
if points:
|
||||
pos_x = self._chart.mapToPosition(
|
||||
QPointF(index * self.step_x + self.min_x, self.min_y))
|
||||
self.lineItem.setLine(pos_x.x(), self.point_top.y(),
|
||||
pos_x.x(), self.point_bottom.y())
|
||||
self.lineItem.show()
|
||||
try:
|
||||
title = self.category[index]
|
||||
except:
|
||||
title = ""
|
||||
t_width = self.toolTipWidget.width()
|
||||
t_height = self.toolTipWidget.height()
|
||||
# 如果鼠标位置离右侧的距离小于tip宽度
|
||||
x = pos.x() - t_width if self.width() - \
|
||||
pos.x() - 20 < t_width else pos.x()
|
||||
# 如果鼠标位置离底部的高度小于tip高度
|
||||
y = pos.y() - t_height if self.height() - \
|
||||
pos.y() - 20 < t_height else pos.y()
|
||||
self.toolTipWidget.show(
|
||||
title, points, QPoint(x, y))
|
||||
else:
|
||||
self.toolTipWidget.hide()
|
||||
self.lineItem.hide()
|
||||
|
||||
def handleMarkerClicked(self):
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
visible = not marker.series().isVisible()
|
||||
# # 隐藏或显示series
|
||||
marker.series().setVisible(visible)
|
||||
marker.setVisible(True) # 要保证marker一直显示
|
||||
# 透明度
|
||||
alpha = 1.0 if visible else 0.4
|
||||
# 设置label的透明度
|
||||
brush = marker.labelBrush()
|
||||
color = brush.color()
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setLabelBrush(brush)
|
||||
# 设置marker的透明度
|
||||
brush = marker.brush()
|
||||
color = brush.color()
|
||||
color.setAlphaF(alpha)
|
||||
brush.setColor(color)
|
||||
marker.setBrush(brush)
|
||||
# 设置画笔透明度
|
||||
pen = marker.pen()
|
||||
color = pen.color()
|
||||
color.setAlphaF(alpha)
|
||||
pen.setColor(color)
|
||||
marker.setPen(pen)
|
||||
|
||||
def handleMarkerHovered(self, status):
|
||||
# 设置series的画笔宽度
|
||||
marker = self.sender() # 信号发送者
|
||||
if not marker:
|
||||
return
|
||||
series = marker.series()
|
||||
if not series:
|
||||
return
|
||||
pen = series.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if status else -1))
|
||||
series.setPen(pen)
|
||||
|
||||
def handleSeriesHoverd(self, point, state):
|
||||
# 设置series的画笔宽度
|
||||
series = self.sender() # 信号发送者
|
||||
pen = series.pen()
|
||||
if not pen:
|
||||
return
|
||||
pen.setWidth(pen.width() + (1 if state else -1))
|
||||
series.setPen(pen)
|
||||
|
||||
def initChart(self):
|
||||
self._chart = QChart(title="折线图堆叠")
|
||||
self._chart.setAcceptHoverEvents(True)
|
||||
# Series动画
|
||||
self._chart.setAnimationOptions(QChart.SeriesAnimations)
|
||||
dataTable = [
|
||||
["邮件营销", [120, 132, 101, 134, 90, 230, 210]],
|
||||
["联盟广告", [220, 182, 191, 234, 290, 330, 310]],
|
||||
["视频广告", [150, 232, 201, 154, 190, 330, 410]],
|
||||
["直接访问", [320, 332, 301, 334, 390, 330, 320]],
|
||||
["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]]
|
||||
]
|
||||
for series_name, data_list in dataTable:
|
||||
series = QLineSeries(self._chart)
|
||||
for j, v in enumerate(data_list):
|
||||
series.append(j, v)
|
||||
series.setName(series_name)
|
||||
series.setPointsVisible(True) # 显示圆点
|
||||
series.hovered.connect(self.handleSeriesHoverd) # 鼠标悬停
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
axisX = self._chart.axisX() # x轴
|
||||
axisX.setTickCount(7) # x轴设置7个刻度
|
||||
axisX.setGridLineVisible(False) # 隐藏从x轴往上的线条
|
||||
axisY = self._chart.axisY()
|
||||
axisY.setTickCount(7) # y轴设置7个刻度
|
||||
axisY.setRange(0, 1500) # 设置y轴范围
|
||||
# 自定义x轴
|
||||
axis_x = QCategoryAxis(
|
||||
self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
|
||||
axis_x.setTickCount(7)
|
||||
axis_x.setGridLineVisible(False)
|
||||
min_x = axisX.min()
|
||||
max_x = axisX.max()
|
||||
step = (max_x - min_x) / (7 - 1) # 7个tick
|
||||
for i in range(0, 7):
|
||||
axis_x.append(self.category[i], min_x + i * step)
|
||||
self._chart.setAxisX(axis_x, self._chart.series()[-1])
|
||||
# chart的图例
|
||||
legend = self._chart.legend()
|
||||
# 设置图例由Series来决定样式
|
||||
legend.setMarkerShape(QLegend.MarkerShapeFromSeries)
|
||||
# 遍历图例上的标记并绑定信号
|
||||
for marker in legend.markers():
|
||||
# 点击事件
|
||||
marker.clicked.connect(self.handleMarkerClicked)
|
||||
# 鼠标悬停事件
|
||||
marker.hovered.connect(self.handleMarkerHovered)
|
||||
self.setChart(self._chart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
view = ChartView()
|
||||
view.show()
|
||||
sys.exit(app.exec_())
|
30
QChart/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# QChart
|
||||
|
||||
## 1、折线图
|
||||
[运行 LineChart.py](LineChart.py)
|
||||
|
||||
![LineChart](ScreenShot/LineChart.png)
|
||||
|
||||
## 2、折线堆叠图
|
||||
[运行 LineStack.py](LineStack.py)
|
||||
|
||||
仿照 [line-stack](http://echarts.baidu.com/demo.html#line-stack)
|
||||
|
||||
![LineStack](ScreenShot/LineStack.gif)
|
||||
|
||||
## 3、柱状堆叠图
|
||||
[运行 BarStack.py](BarStack.py)
|
||||
|
||||
仿照 [bar-stack](http://echarts.baidu.com/demo.html#bar-stack)
|
||||
|
||||
![BarStack](ScreenShot/BarStack.gif)
|
||||
|
||||
## 4、LineChart自定义xy轴
|
||||
[运行 CustomXYaxis.py](CustomXYaxis.py)
|
||||
|
||||
![CustomXYaxis](ScreenShot/CustomXYaxis.png)
|
||||
|
||||
## 5、ToolTip提示
|
||||
[运行 ToolTip.py](ToolTip.py) | [运行 ToolTip2.py](ToolTip2.py)
|
||||
|
||||
![ToolTip](ScreenShot/ToolTip.gif) ![ToolTip2](ScreenShot/ToolTip2.gif)
|
Before Width: | Height: | Size: 325 KiB After Width: | Height: | Size: 325 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 327 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 204 KiB |
|
@ -1,220 +1,220 @@
|
|||
#!/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: ToolTip
|
||||
@description:
|
||||
'''
|
||||
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"
|
||||
|
||||
|
||||
'''
|
||||
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()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.min_x, self.max_x = axisX.min(), axisX.max()
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(QPointF(self.min_x, self.max_y))
|
||||
# 坐标原点坐标
|
||||
self.point_bottom = self._chart.mapToPosition(QPointF(self.min_x, self.min_y))
|
||||
self.step_x = (self.max_x - self.min_x) / (axisX.tickCount() - 1)
|
||||
# self.step_y = (self.max_y - self.min_y) / (axisY.tickCount() - 1)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(event.pos()).x()
|
||||
y = self._chart.mapToValue(event.pos()).y()
|
||||
index = round((x - self.min_x) / self.step_x)
|
||||
pos_x = self._chart.mapToPosition(
|
||||
QPointF(index * self.step_x + self.min_x, self.min_y))
|
||||
# print(x, pos_x, index, index * self.step_x + self.min_x)
|
||||
# 得到在坐标系中的所有series的类型和点
|
||||
points = [(serie, serie.at(index))
|
||||
for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
|
||||
if points:
|
||||
# 永远在轴上的黑线条
|
||||
self.lineItem.setLine(pos_x.x(), self.point_top.y(),
|
||||
pos_x.x(), self.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)
|
||||
dataTable = [
|
||||
[120, 132, 101, 134, 90, 230, 210],
|
||||
[220, 182, 191, 234, 290, 330, 310],
|
||||
[150, 232, 201, 154, 190, 330, 410],
|
||||
[320, 332, 301, 334, 390, 330, 320],
|
||||
[820, 932, 901, 934, 1290, 1330, 1320]
|
||||
]
|
||||
for i, data_list in enumerate(dataTable):
|
||||
series = QLineSeries(self._chart)
|
||||
for j, v in enumerate(data_list):
|
||||
series.append(j, v)
|
||||
series.setName("Series " + str(i))
|
||||
series.setPointsVisible(True) # 显示原点
|
||||
series.hovered.connect(self.onSeriesHoverd)
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
self._chart.axisX().setTickCount(7) # x轴设置7个刻度
|
||||
self._chart.axisY().setTickCount(7) # y轴设置7个刻度
|
||||
self._chart.axisY().setRange(0, 1500) # 设置y轴范围
|
||||
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_())
|
||||
#!/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: ToolTip
|
||||
@description:
|
||||
'''
|
||||
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"
|
||||
|
||||
'''
|
||||
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()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.min_x, self.max_x = axisX.min(), axisX.max()
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
# 坐标原点坐标
|
||||
self.point_bottom = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.min_y))
|
||||
self.step_x = (self.max_x - self.min_x) / (axisX.tickCount() - 1)
|
||||
|
||||
# self.step_y = (self.max_y - self.min_y) / (axisY.tickCount() - 1)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(event.pos()).x()
|
||||
y = self._chart.mapToValue(event.pos()).y()
|
||||
index = round((x - self.min_x) / self.step_x)
|
||||
pos_x = self._chart.mapToPosition(
|
||||
QPointF(index * self.step_x + self.min_x, self.min_y))
|
||||
# print(x, pos_x, index, index * self.step_x + self.min_x)
|
||||
# 得到在坐标系中的所有series的类型和点
|
||||
points = [(serie, serie.at(index))
|
||||
for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
|
||||
if points:
|
||||
# 永远在轴上的黑线条
|
||||
self.lineItem.setLine(pos_x.x(), self.point_top.y(),
|
||||
pos_x.x(), self.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)
|
||||
dataTable = [
|
||||
[120, 132, 101, 134, 90, 230, 210],
|
||||
[220, 182, 191, 234, 290, 330, 310],
|
||||
[150, 232, 201, 154, 190, 330, 410],
|
||||
[320, 332, 301, 334, 390, 330, 320],
|
||||
[820, 932, 901, 934, 1290, 1330, 1320]
|
||||
]
|
||||
for i, data_list in enumerate(dataTable):
|
||||
series = QLineSeries(self._chart)
|
||||
for j, v in enumerate(data_list):
|
||||
series.append(j, v)
|
||||
series.setName("Series " + str(i))
|
||||
series.setPointsVisible(True) # 显示原点
|
||||
series.hovered.connect(self.onSeriesHoverd)
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
self._chart.axisX().setTickCount(7) # x轴设置7个刻度
|
||||
self._chart.axisY().setTickCount(7) # y轴设置7个刻度
|
||||
self._chart.axisY().setRange(0, 1500) # 设置y轴范围
|
||||
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_())
|
|
@ -1,220 +1,218 @@
|
|||
#!/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: ToolTip2
|
||||
@description:
|
||||
'''
|
||||
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"
|
||||
|
||||
|
||||
'''
|
||||
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()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.min_x, self.max_x = axisX.min(), axisX.max()
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
# 坐标原点坐标
|
||||
self.point_bottom = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.min_y))
|
||||
self.step_x = (self.max_x - self.min_x) / (axisX.tickCount() - 1)
|
||||
# self.step_y = (self.max_y - self.min_y) / (axisY.tickCount() - 1)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(event.pos()).x()
|
||||
y = self._chart.mapToValue(event.pos()).y()
|
||||
index = round((x - self.min_x) / self.step_x)
|
||||
# print(x, pos_x, index, index * self.step_x + self.min_x)
|
||||
# 得到在坐标系中的所有series的类型和点
|
||||
points = [(serie, serie.at(index))
|
||||
for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
|
||||
if points:
|
||||
# 跟随鼠标的黑线条
|
||||
self.lineItem.setLine(event.pos().x(), self.point_top.y(),
|
||||
event.pos().x(), self.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)
|
||||
dataTable = [
|
||||
[120, 132, 101, 134, 90, 230, 210],
|
||||
[220, 182, 191, 234, 290, 330, 310],
|
||||
[150, 232, 201, 154, 190, 330, 410],
|
||||
[320, 332, 301, 334, 390, 330, 320],
|
||||
[820, 932, 901, 934, 1290, 1330, 1320]
|
||||
]
|
||||
for i, data_list in enumerate(dataTable):
|
||||
series = QLineSeries(self._chart)
|
||||
for j, v in enumerate(data_list):
|
||||
series.append(j, v)
|
||||
series.setName("Series " + str(i))
|
||||
series.setPointsVisible(True) # 显示原点
|
||||
series.hovered.connect(self.onSeriesHoverd)
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
self._chart.axisX().setTickCount(7) # x轴设置7个刻度
|
||||
self._chart.axisY().setTickCount(7) # y轴设置7个刻度
|
||||
self._chart.axisY().setRange(0, 1500) # 设置y轴范围
|
||||
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_())
|
||||
#!/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: ToolTip2
|
||||
@description:
|
||||
'''
|
||||
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"
|
||||
|
||||
'''
|
||||
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()
|
||||
|
||||
# 一些固定计算,减少mouseMoveEvent中的计算量
|
||||
# 获取x和y轴的最小最大值
|
||||
axisX, axisY = self._chart.axisX(), self._chart.axisY()
|
||||
self.min_x, self.max_x = axisX.min(), axisX.max()
|
||||
self.min_y, self.max_y = axisY.min(), axisY.max()
|
||||
# 坐标系中左上角顶点
|
||||
self.point_top = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.max_y))
|
||||
# 坐标原点坐标
|
||||
self.point_bottom = self._chart.mapToPosition(
|
||||
QPointF(self.min_x, self.min_y))
|
||||
self.step_x = (self.max_x - self.min_x) / (axisX.tickCount() - 1)
|
||||
|
||||
# self.step_y = (self.max_y - self.min_y) / (axisY.tickCount() - 1)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
super(ChartView, self).mouseMoveEvent(event)
|
||||
# 把鼠标位置所在点转换为对应的xy值
|
||||
x = self._chart.mapToValue(event.pos()).x()
|
||||
y = self._chart.mapToValue(event.pos()).y()
|
||||
index = round((x - self.min_x) / self.step_x)
|
||||
# print(x, pos_x, index, index * self.step_x + self.min_x)
|
||||
# 得到在坐标系中的所有series的类型和点
|
||||
points = [(serie, serie.at(index))
|
||||
for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
|
||||
if points:
|
||||
# 跟随鼠标的黑线条
|
||||
self.lineItem.setLine(event.pos().x(), self.point_top.y(),
|
||||
event.pos().x(), self.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)
|
||||
dataTable = [
|
||||
[120, 132, 101, 134, 90, 230, 210],
|
||||
[220, 182, 191, 234, 290, 330, 310],
|
||||
[150, 232, 201, 154, 190, 330, 410],
|
||||
[320, 332, 301, 334, 390, 330, 320],
|
||||
[820, 932, 901, 934, 1290, 1330, 1320]
|
||||
]
|
||||
for i, data_list in enumerate(dataTable):
|
||||
series = QLineSeries(self._chart)
|
||||
for j, v in enumerate(data_list):
|
||||
series.append(j, v)
|
||||
series.setName("Series " + str(i))
|
||||
series.setPointsVisible(True) # 显示原点
|
||||
series.hovered.connect(self.onSeriesHoverd)
|
||||
self._chart.addSeries(series)
|
||||
self._chart.createDefaultAxes() # 创建默认的轴
|
||||
self._chart.axisX().setTickCount(7) # x轴设置7个刻度
|
||||
self._chart.axisY().setTickCount(7) # y轴设置7个刻度
|
||||
self._chart.axisY().setRange(0, 1500) # 设置y轴范围
|
||||
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_())
|
|
@ -1,56 +1,56 @@
|
|||
[
|
||||
{
|
||||
"name": "仪表盘",
|
||||
"icon": "xxx.png"
|
||||
},
|
||||
{
|
||||
"name": "微信管理",
|
||||
"icon": "weixin.png",
|
||||
"badge": [
|
||||
"火爆",
|
||||
"#F8AC54"
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"name": "爬虫项目管理",
|
||||
"icon": "xxx.png",
|
||||
"badge": [
|
||||
"推荐",
|
||||
"#23C6C9"
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"name": "腾讯课堂"
|
||||
},
|
||||
{
|
||||
"name": "淘宝"
|
||||
},
|
||||
{
|
||||
"name": "京东"
|
||||
},
|
||||
{
|
||||
"name": "天猫"
|
||||
},
|
||||
{
|
||||
"name": "拉钩"
|
||||
},
|
||||
{
|
||||
"name": "BOSS直聘"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "爬虫部署管理",
|
||||
"icon": "xxx.png"
|
||||
},
|
||||
{
|
||||
"name": "爬虫监控管理",
|
||||
"icon": "xxx.png"
|
||||
},
|
||||
{
|
||||
"name": "PyQt",
|
||||
"url": "https://github.com/892768447/PyQt"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"name": "仪表盘",
|
||||
"icon": "xxx.png"
|
||||
},
|
||||
{
|
||||
"name": "微信管理",
|
||||
"icon": "Data/weixin.png",
|
||||
"badge": [
|
||||
"火爆",
|
||||
"#F8AC54"
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"name": "爬虫项目管理",
|
||||
"icon": "xxx.png",
|
||||
"badge": [
|
||||
"推荐",
|
||||
"#23C6C9"
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"name": "腾讯课堂"
|
||||
},
|
||||
{
|
||||
"name": "淘宝"
|
||||
},
|
||||
{
|
||||
"name": "京东"
|
||||
},
|
||||
{
|
||||
"name": "天猫"
|
||||
},
|
||||
{
|
||||
"name": "拉钩"
|
||||
},
|
||||
{
|
||||
"name": "BOSS直聘"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "爬虫部署管理",
|
||||
"icon": "xxx.png"
|
||||
},
|
||||
{
|
||||
"name": "爬虫监控管理",
|
||||
"icon": "xxx.png"
|
||||
},
|
||||
{
|
||||
"name": "PyQt",
|
||||
"url": "https://github.com/892768447/PyQt"
|
||||
}
|
||||
]
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -6,7 +6,7 @@ Created on 2018年4月8日
|
|||
@author: Irony
|
||||
@site: https://pyqt5.com, https://github.com/892768447
|
||||
@email: 892768447@qq.com
|
||||
@file:
|
||||
@file: ParsingJson
|
||||
@description:
|
||||
"""
|
||||
import json
|
||||
|
@ -148,5 +148,5 @@ QTreeView:branch:selected {
|
|||
""")
|
||||
w = JsonTreeWidget()
|
||||
w.show()
|
||||
w.loadData('data.json')
|
||||
w.loadData('Data/data.json')
|
||||
sys.exit(app.exec_())
|
|
@ -0,0 +1,8 @@
|
|||
# QTreeWidget
|
||||
|
||||
## 1、通过json数据生成树形结构
|
||||
[运行 ParsingJson.py](ParsingJson.py)
|
||||
|
||||
解析每一层json数据中的list
|
||||
|
||||
![ParsingJson](ScreenShot/ParsingJson.png)
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
22
图表/README.md
|
@ -1,22 +0,0 @@
|
|||
# 图表
|
||||
|
||||
## [1、PyQtChart](PyQtChart/)
|
||||
|
||||
### 1. [折线图](PyQtChart/charts/line/LineStack.py)
|
||||
仿照 [line-stack](http://echarts.baidu.com/demo.html#line-stack)
|
||||
|
||||
![截图](PyQtChart/charts/line/ScreenShot/LineStack.gif)
|
||||
|
||||
### 2. [柱状图](PyQtChart/charts/bar/BarStack.py)
|
||||
仿照 [bar-stack](http://echarts.baidu.com/demo.html#bar-stack)
|
||||
|
||||
![截图](PyQtChart/charts/bar/ScreenShot/BarStack.gif)
|
||||
|
||||
### 3. [LineChart](PyQtChart/demo/LineChart.py)
|
||||
![截图](PyQtChart/demo/ScreenShot/LineChart.png)
|
||||
|
||||
### 4. [LineChart自定义xy轴](PyQtChart/demo/LineChart自定义xy轴.py)
|
||||
![截图](PyQtChart/demo/ScreenShot/LineChart自定义xy轴.png)
|
||||
|
||||
### 5. [ToolTip提示](PyQtChart/demo/ToolTip.py)
|
||||
![截图](PyQtChart/demo/ScreenShot/ToolTip.gif)
|
|
@ -1,8 +0,0 @@
|
|||
# 树结构
|
||||
|
||||
## [1、QTreeWidget](QTreeWidget/)
|
||||
|
||||
### 1.通过json数据生成树形结构
|
||||
emmmmmm,原理就是那样,,解析每一层json数据中的list,美化就不想弄了.
|
||||
|
||||
![截图](QTreeWidget/Json生成QTreeWidget/ScreenShot/Json生成树形结构.png)
|