QChart 移动 缩放 加速
qchart 和 qchartview 的运用的例子
qchart 存在一些问题
一般用在2000个点以下的场景,点多了,就会卡。 解决的办法就是 开启opengl加速。
但这时,对qchartview 进行transform 时, 绘制的点,并不能同时变换。原因是QT把opengl的图案是单独绘制的,要找到这个单独的view 进行变换才行。 但找不到。。。
这就不能使用的底层的函数了,只能用 qchart的setrange,变换的事情,交给qt 自己去实现。
这个例子,实现了 拖动 滚轮缩放 ,加了一个取值的限定框。
python 3.10 pyside6
import sys
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QVBoxLayout,
QGraphicsSceneWheelEvent,
QGraphicsSceneMouseEvent,
QLabel,
QHBoxLayout,
QFormLayout,
QPushButton,
QSlider,
)
from PySide6.QtCharts import QChartView, QChart, QScatterSeries, QValueAxis, QLineSeries
from PySide6.QtGui import QMouseEvent
from PySide6.QtCore import Qt, QPointF, Signal, QRectF, QTimer, QPoint
import random
import math
import threading
class zn_chart(QChart):
sg_mouse_db_click = Signal(QPointF)
def __init__(self):
super().__init__()
self._left_pos = None
# data series
self.se = QScatterSeries()
self.ax_x = QValueAxis()
self.ax_y = QValueAxis()
self.addSeries(self.se)
self.addAxis(self.ax_x, Qt.AlignmentFlag.AlignBottom)
self.addAxis(self.ax_y, Qt.AlignmentFlag.AlignLeft)
self.se.attachAxis(self.ax_x)
self.se.attachAxis(self.ax_y)
self.se.setBorderColor(self.se.color())
self.se.setMarkerSize(2.0)
self.ax_x.setRange(-51, 51)
self.ax_y.setRange(-51, 51)
self.setTitle("Line Chart")
self.se.setUseOpenGL(True)
# data
self._data: list[QPointF] = []
self._x_min: float = None
self._x_max: float = None
self._y_min: float = None
self._y_max: float = None
# limit line data
self.lm_x_min: float = None
self.lm_x_max: float = None
self.lm_se = QLineSeries()
self.addSeries(self.lm_se)
self.lm_se.attachAxis(self.ax_x)
self.lm_se.attachAxis(self.ax_y)
self.lm_se.setUseOpenGL(True)
self.lm_pts: list[QPointF] = []
self.lm_se.setMarkerSize(2.0)
self.lm_se.setVisible(True)
# signal
self.drag_start_pos: QPointF = None
self.drag_start_range: QRectF = None
self.on_drag: bool = False
# tm
self.tm: QTimer = None
self._tmp_data: list[QPointF] = []
self._tm_lock = threading.Lock()
@property
def range(self) -> QRectF:
return QRectF(
self.ax_x.min(),
self.ax_y.min(),
self.ax_x.max() - self.ax_x.min(),
self.ax_y.max() - self.ax_y.min(),
)
def set_range_x(self, p1: float, p2: float):
self.ax_x.setRange(p1, p2)
def set_range_y(self, p1: float, p2: float):
self.ax_y.setRange(p1, p2)
def setrange(self, r: QRectF):
self.ax_x.setRange(r.x(), r.x() + r.width())
self.ax_y.setRange(r.y(), r.y() + r.height())
def wheelEvent(self, event: QGraphicsSceneWheelEvent) -> None:
y = event.delta()
factor = 1
if y > 0:
factor = 1.1
elif y < 0:
factor = 1 / 1.1
if factor != 1:
f = 1 / factor
r = self.range
r_center_x = r.x() + r.width() * 0.5
r_center_y = r.y() + r.height() * 0.5
p_center = self.mapToValue(event.pos())
r_c_x2 = p_center.x() + f * (r_center_x - p_center.x())
r_c_y2 = p_center.y() + f * (r_center_y - p_center.y())
w2 = r.width() * f
h2 = r.height() * f
r2 = QRectF(r_c_x2 - w2 * 0.5, r_c_y2 - h2 * 0.5, w2, h2)
self.setrange(r2)
def on_drag_enter(self, p: QPointF) -> None:
self.drag_start_pos = p
self.drag_start_range = self.range
self.on_drag = True
def on_drag_exit(self) -> None:
self.on_drag = False
self.drag_start_pos = None
self.drag_start_range = None
def on_drag_move(self, p: QPointF) -> None:
if self.on_drag:
vp = self.plotArea()
now_p = p
dx = -(
(now_p.x() - self.drag_start_pos.x())
/ vp.width()
* self.drag_start_range.width()
)
dy = (
(now_p.y() - self.drag_start_pos.y())
/ vp.height()
* self.drag_start_range.height()
)
r2 = QRectF(
self.drag_start_range.x() + dx,
self.drag_start_range.y() + dy,
self.drag_start_range.width(),
self.drag_start_range.height(),
)
self.setrange(r2)
def mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent) -> None:
p = self.mapToValue(event.pos())
self.sg_mouse_db_click.emit(p)
print(p)
return super().mouseDoubleClickEvent(event)
# data op
@property
def data(self) -> list[QPointF]:
return self._data
def data_add_xy(self, x: float, y: float):
self._data.append(QPointF(x, y))
self.se.append(x, y)
if self._x_min is None or x < self._x_min:
self._x_min = x
if self._x_max is None or x > self._x_max:
self._x_max = x
if self._y_min is None or y < self._y_min:
self._y_min = y
if self._y_max is None or y > self._y_max:
self._y_max = y
def data_add_p(self, d: QPointF | QPoint):
self.data_add_xy(d.x(), d.y())
def data_add_list_point(self, lp: list[QPointF | QPointF]):
for i in lp:
self.data_add_p(i)
self.se.append(lp)
def data_clear(self):
self._data.clear()
self.se.clear()
self._x_min = self._x_max = self._y_min = self._y_max = None
def data_load_more(self, sp: list[QPointF]):
with self._tm_lock:
if self.tm is not None:
self.tm.stop()
self.tm = None
self.data_clear()
self._tmp_data.clear()
self._tmp_data += sp[:]
self.tm = QTimer()
self.tm.setInterval(15)
self.tm.timeout.connect(self._do_add)
self.tm.start()
def _do_add(self):
with self._tm_lock:
if self.tm is None:
self._tmp_data.clear()
elif len(self._tmp_data) == 0:
self.tm.stop()
self.tm = None
print("over")
else:
if len(self._tmp_data) > 1000:
to_add = self._tmp_data[:1000]
self._tmp_data = self._tmp_data[1000:]
else:
to_add = self._tmp_data[:]
self._tmp_data.clear()
self.data_add_list_point(to_add)
# limit setting
def lm_refresh(self):
if (
self.lm_x_min is not None
and self.lm_x_max is not None
and self._y_max is not None
and self._y_min is not None
):
if self._y_max > self._y_min:
x1 = float(self.lm_x_min)
x2 = float(self.lm_x_max)
d = self._y_max - self._y_min
y1 = self._y_min - 0.2 * d
y2 = self._y_max + 0.2 * d
self.lm_pts.clear()
for i in [[x1, y1], [x1, y2], [x2, y2], [x2, y1], [x1, y1]]:
self.lm_pts.append(QPointF(*i))
self.lm_se.clear()
self.lm_se.append(self.lm_pts)
def lm_set_x_min(self, x: float):
xx = float(x)
if self.lm_x_max is None or xx <= self.lm_x_max:
self.lm_x_min = xx
self.lm_refresh()
def lm_set_x_max(self, x: float):
xx = float(x)
if self.lm_x_min is None or xx >= self.lm_x_min:
self.lm_x_max = xx
self.lm_refresh()
def lm_set_x_min_percent(self, v: int):
f = float(v / 100)
if self._x_min is not None and self._x_max is not None:
dd = self._x_max - self._x_min
self.lm_set_x_min(dd * f + self._x_min)
def lm_set_x_max_percent(self, v: int):
f = float(v / 100)
if self._x_min is not None and self._x_max is not None:
dd = self._x_max - self._x_min
self.lm_set_x_max(dd * f + self._x_min)
def lm_show(self):
self.lm_se.setVisible(True)
def lm_hide(self):
self.lm_se.setVisible(False)
def lm_new_from_data(self):
self.lm_set_x_min(self._x_min)
self.lm_set_x_max(self._x_max)
def lm_clear(self):
self.lm_se.clear()
self.lm_x_min = None
self.lm_x_max = None
self.lm_pts = []
class my_view(QChartView):
sg_drag_enter = Signal(QPointF)
sg_drag_exit = Signal()
sg_drag_move = Signal(QPointF)
on_drag: bool = False
def mousePressEvent(self, event: QMouseEvent) -> None:
if event.button() == Qt.MouseButton.LeftButton:
self.sg_drag_enter.emit(event.position())
self.on_drag = True
return super().mousePressEvent(event)
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
self.sg_drag_exit.emit()
self.on_drag = False
return super().mouseReleaseEvent(event)
def mouseMoveEvent(self, event: QMouseEvent) -> None:
if self.on_drag:
self.sg_drag_move.emit(event.position())
return super().mouseMoveEvent(event)
def setChart(self, chart: QChart) -> None:
if isinstance(chart, zn_chart):
self.sg_drag_enter.connect(chart.on_drag_enter)
self.sg_drag_exit.connect(chart.on_drag_exit)
self.sg_drag_move.connect(chart.on_drag_move)
return super().setChart(chart)
class zn_slider(QSlider):
def __init__(self):
super().__init__(Qt.Orientation.Horizontal)
self.setMinimum(0)
self.setMaximum(100)
self.setSingleStep(1)
self.setValue(0)
self.setTickPosition(self.TickPosition.TicksBelow)
if __name__ == "__main__":
class test_wg(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PySide6 QChart Example")
self.setGeometry(50, 50, 800, 600)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.main_layout = QVBoxLayout(self.central_widget)
self.chart_view = my_view()
self.main_layout.addWidget(self.chart_view)
self.chart = zn_chart()
self.chart_view.setChart(self.chart)
##
self.c_ly = QHBoxLayout()
self.main_layout.addLayout(self.c_ly)
self.b1 = QPushButton("add data")
self.b1.clicked.connect(self.adddata)
self.c_ly.addWidget(self.b1)
self.b2 = QPushButton("init limit")
self.b2.clicked.connect(self.lm_init)
self.c_ly.addWidget(self.b2)
self.b3 = QPushButton("show limit")
self.b3.clicked.connect(self.chart.lm_show)
self.c_ly.addWidget(self.b3)
self.b4 = QPushButton("hide limit")
self.b4.clicked.connect(self.chart.lm_hide)
self.c_ly.addWidget(self.b4)
# slider
self.f_ly = QFormLayout()
self.s1 = zn_slider()
self.s1.valueChanged.connect(self.on_s1_change)
self.s1_lb = QLabel("下限")
self.s1_lb.setFixedWidth(120)
self.f_ly.addRow(self.s1_lb, self.s1)
self.s2 = zn_slider()
self.s2.setValue(100)
self.s2.valueChanged.connect(self.on_s2_change)
self.s2_lb = QLabel("上限")
self.s2_lb.setFixedWidth(120)
self.f_ly.addRow(self.s2_lb, self.s2)
self.main_layout.addLayout(self.f_ly)
# show
self.show()
def adddata(self):
self.chart.data_clear()
dt = []
for i in range(10000):
x = random.randint(0, 10000) / 100 - 50
xx = math.pi * 2 * x / 100
y = math.sin(xx) * 50
dt.append(QPointF(x, y))
self.chart.data_load_more(dt)
def lm_init(self):
self.chart.lm_set_x_min_percent(0)
self.chart.lm_set_x_max_percent(100)
self.chart.lm_show()
def on_s1_change(self, v: int):
self.chart.lm_set_x_min_percent(v)
if self.chart.lm_x_min is None:
self.s1_lb.setText(f"下限")
else:
self.s1_lb.setText(f"下限 {self.chart.lm_x_min:<.3f}")
def on_s2_change(self, v: int):
self.chart.lm_set_x_max_percent(v)
if self.chart.lm_x_max is None:
self.s2_lb.setText(f"上限")
else:
self.s2_lb.setText(f"上限 {self.chart.lm_x_max:<.3f}")
app = QApplication(sys.argv)
window = test_wg()
app.exec()

QChart 移动 缩放 加速的更多相关文章
- [C1] 线性回归(Linear Regression)
线性回归(Linear Regression with One / Multiple Variable) 定义符号(Symbol Definition) m = 数据集中训练样本的数量 n = 特征的 ...
- qt qchart缩放后坐标轴间隔取整
使用qt的qchart显示数据曲线,坐标轴QValueAxis可以设置刻度间隔数量,但每个刻度的数值是根据坐标的极值除以间隔数量得到的,不一定是整数,导致曲线控件的显示刻度不适合观察. 如图: 纵坐标 ...
- 项目实战:流水线图像显示控件(列刷新、1ms一次、缩放、拽拖、拽拖预览、性能优化、支持OpenGL GPU加速)
需求 流水线图像扫描采集控件(带模拟数据测试)性能需求 1.需至少满足可1ms接收一次列数据,而不丢包(接收后可不必立马显示) 2.图片刷新率可达30HZ:限制需求 1.图片高度最小只能 ...
- 开启gpu加速的高性能移动端相框组件!
通过设置新的css3新属性translateX来代替传统的绝对定位改变left值的动画原理,新属性translateX会开启浏览器自带的gpu硬件加速动画性能,提高流畅度从而提高用户体验, 代码有很详 ...
- Minimit Anima – 硬件加速的 CSS3 动画插件
Minimit Anima 是一个实现 CSS3 Transforms 和 Transitions 动画的 jQuery 插件.基于硬件加速的 CSS3 动画执行更快,而且它有一个类似于 jQuery ...
- lecture6-mini批量梯度训练及三个加速的方法
Hinton的第6课,这一课中最后的那个rmsprop,关于它的资料,相对较少,差不多除了Hinton提出,没论文的样子,各位大大可以在这上面研究研究啊. 一.mini-批量梯度下降概述 这部分将介绍 ...
- Android动画及图片的缩放和旋转
Android动画有2种,一种是Tween Animation,另一种是Frame Animation,先说说Tween动画吧. Tween动画是对视图对象中的内容进行一系列简单的转换,比如位置的移动 ...
- HDU 4087 三维上的平移缩放旋转矩阵变化
题目大意: 就是根据它给的程序的要求,不断平移,缩放,旋转三维的点,最后计算出点的位置 这里主要是要列出三种转换方式的齐次矩阵描述 平移translate tx ty tz1 0 0 00 1 0 0 ...
- jQuery支持移动Mobile的DOM元素移动和缩放插件
jQuery Panzoom是一款很有用的HTML DOM元素平移和缩放jQuery和CSS3插件. Panzoom利用CSS transforms 和 matrix函数来为浏览器进行硬件(GPU)加 ...
- TensorFlow之DNN(二):全连接神经网络的加速技巧(Xavier初始化、Adam、Batch Norm、学习率衰减与梯度截断)
在上一篇博客<TensorFlow之DNN(一):构建“裸机版”全连接神经网络>中,我整理了一个用TensorFlow实现的简单全连接神经网络模型,没有运用加速技巧(小批量梯度下降不算哦) ...
随机推荐
- ITIL是标准吗?
ITIL不是标准 OGC:是一个推荐的管理框架,一个模版,可根据运维实践自由裁量落地 itil诞生环境:欧美思维.欧美文化.欧美制度.欧美人文习惯.... 对欧美来说可能是最佳实践,但是对中国特色文化 ...
- PyTorch的安装与使用
技术背景 PyTorch是一个非常常用的AI框架,主要归功于其简单易用的特点,深受广大科研人员的喜爱.在前面的一篇文章中我们介绍过制作PyTorch的Singularity镜像的方法,这里我们单独抽出 ...
- Swift中Tuple的比较
Swift中Tuple的比较遵循如下规则: 1 被比较的Tuple中包含的元素个数必须一样,并且对应元素的类型也必须一样: 2 比较的结果由整个Tuple的比较结果来决定.比如,如果是相等比较,那么必 ...
- 10分钟了解Flink SQL使用
Flink 是一个流处理和批处理统一的大数据框架,专门为高吞吐量和低延迟而设计.开发者可以使用SQL进行流批统一处理,大大简化了数据处理的复杂性.本文将介绍Flink SQL的基本原理.使用方法.流批 ...
- vue ali-oss 上传
vue项目使用阿里云oss上传图片或下载图片 https://www.cnblogs.com/zoo-x/articles/11778010.html#4565291Vue Element UI + ...
- HTML——文本域
在 HTML 中,使用 <textarea> 标签来表示多行文本框,又叫做文本域.与其它 <input> 标签不同,<textarea> 标签是单闭合标签,它包含起 ...
- 3分钟部署 我的世界(Minecraft) 联机服务
游戏简介 我的世界(Minecraft)是一款沙盒类电子游戏,该游戏以玩家在一个充满着方块的三维空间中自由地创造和破坏不同种类的方块为主题.玩家在游戏中可以在单人或多人模式中通过摧毁或创造精妙绝伦的建 ...
- RTMP 直播 H265 推流适配总结
1.在iOS11的系统之上,苹果逐渐放开H265硬编硬解的能力,硬解码的能力只要升级到iOS11之后,iPhone6+以上的机型就支持了(印象中): H265硬编码的能力对设备要求较高,不仅要求系统版 ...
- uniapp 组件使用
组件使用情况:页面出现多个相似的页面这个时候我们就可以把公共的页面进行封装,避免冗余的代码 1. compoents 目录下新建组件,名称随意[案例就叫 newsList]2. 开始封装需要多次使用的 ...
- Javascript高级程序设计第七章 | ch7 | 阅读笔记
迭代器与生成器 在软件开发领域,"迭代"的意思是按照顺序反复多次执行一段程序 理解迭代 在JavaScript中,计数循环就是最简单的迭代 但是这种迭代有点问题: 1. 迭代之前需 ...