PyQt5 / PyQt

PyQt Examples(PyQt各种测试和例子) PyQt4 PyQt5
GNU Lesser General Public License v2.1
6.65k stars 1.97k forks source link

[qchart动态时间曲线坐标轴映射问题] 动态曲线的鼠标监听获取曲线值无法获取 #146

Closed lycfr closed 2 years ago

lycfr commented 2 years ago

viewchart.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setObjectName("widget")
        self.widget_layout = QtWidgets.QVBoxLayout(self.widget)
        self.widget_layout.setObjectName("widget_layout")
        self.verticalLayout.addWidget(self.widget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "开始"))
        self.pushButton_2.setText(_translate("MainWindow", "停止"))

ChartMouser.py

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtChart import *
from random import *
import sys
from viewchart import *
import random
import time

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:rgb(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.yMax = 10
        self.chart = QChart()
        self.add_series()
        self.setChart(self.chart)
        # self.initChart()
        self.toolTipWidget = GraphicsProxyWidget(self.chart)

        self.lineItem = QGraphicsLineItem(self.chart)
        self.lineItem.setZValue(998)
        self.lineItem.hide()

    def add_series(self):
        self.series = QLineSeries()
        self.series.setName('内存')
        self.chart.addSeries(self.series)
        self.chart.legend().setVisible(True)

        self.axis_x = QDateTimeAxis()
        self.axis_x.setTitleText('Time/s')
        self.axis_x.setMin(QDateTime.currentDateTime().addSecs(-10))
        self.axis_x.setMax(QDateTime.currentDateTime().addSecs(0))
        self.axis_x.setFormat('hh:mm:ss')
        self.axis_x.setTickCount(10)

        self.axis_y = QValueAxis()
        self.axis_y.setTitleText('内存/MB')
        self.axis_y.setMin(0)
        self.axis_y.setMax(self.yMax)
        self.axis_y.setTickCount(5)

        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)
        self.series.attachAxis(self.axis_x)
        self.series.attachAxis(self.axis_y)

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        super(ChartView, self).mouseMoveEvent(event)
        self.min_X, self.max_X = self.axis_x.min(), self.axis_x.max()
        self.min_Y, self.max_Y = self.axis_y.min(), self.axis_y.max()
        self.step_x = (self.min_X.secsTo(self.max_X)) / (self.axis_x.tickCount() - 1)
        self.point_top = self.chart.mapToPosition(QPointF(self.min_X.toTime_t(), self.max_Y))
        self.point_bottom = self.chart.mapToPosition(QPointF(self.min_X.toTime_t(), self.min_Y))
        print(self.point_top, self.point_bottom)

        x = self.chart.mapToValue(event.pos()).x()
        y = self.chart.mapToValue(event.pos()).y()
        timeArray = time.localtime(float(int(str(x).split('.')[0]) / 1000))
        index = round((time.mktime(timeArray) - self.min_X.toTime_t()) / self.step_x)
        # print('坐标:',time.strftime('%H:%M:%S',timeArray),y,index,self.step_x)
        points = [(serie, serie.at(index)) for serie in self.chart.series() if
                  self.min_X.toTime_t() <= time.mktime(timeArray) <= self.max_X.toTime_t() and self.min_Y <= y <= self.max_Y]
        # print('points',points)
        if points:
            pos_x = self.chart.mapToPosition(QPointF(index * self.step_x + self.min_X.toTime_t(), self.min_Y))
            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()))

class Window(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setupUi(self)
        self.yMax = 1

        self.chartView = ChartView(parent=self.widget)
        self.widget_layout.addWidget(self.chartView)

        self.pushButton_2.setEnabled(False)
        self.pushButton.clicked.connect(self.timer_init)
        self.pushButton_2.clicked.connect(self.stopTimer)

    def drawLine(self):
        time = QDateTime.currentDateTime()
        self.chartView.axis_x.setMax(QDateTime.currentDateTime().addSecs(1))
        yint = random.randint(0, 50)
        if yint > self.yMax:
            self.yMax = yint
            # print('Y轴最大值:', self.yMax)
            self.chartView.axis_y.setMin(0)
            self.chartView.axis_y.setMax(self.yMax + 5)
        print('x:',time.toMSecsSinceEpoch(),'y:',yint)
        self.chartView.series.append(time.toMSecsSinceEpoch(), yint)

    def timer_init(self):
        self.pushButton.setEnabled(False)
        self.pushButton_2.setEnabled(True)
        self.chartView.series.clear()
        self.timer = QTimer()
        self.timer.timeout.connect(self.drawLine)
        self.timer.start(1000)

    def stopTimer(self):
        self.pushButton.setEnabled(True)
        self.pushButton_2.setEnabled(False)
        self.timer.stop()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(App.exec_())

image 问题描述: 获取的值无法对应上或者无法更新,实在找不出问题的出处,初步怀疑应该是坐标映射的问题,时间坐标映射问题

lycfr commented 2 years ago

参考demo修改,https://github.com/PyQt5/PyQt/blob/master/QtChart/ToolTip.py

892768447 commented 2 years ago

你看下这个例子:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2021/5/13
@author: Irony
@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CpuLineChart
@description: 
"""

import sys

from psutil import cpu_percent
from PyQt5.QtChart import (QChart, QChartView, QDateTimeAxis, QLineSeries,
                           QSplineSeries, QValueAxis)
from PyQt5.QtCore import QDateTime, QPoint, QPointF, QRectF, Qt, QTimer
from PyQt5.QtGui import QColor, QCursor, QPainter, QPen
from PyQt5.QtWidgets import (QApplication, QGraphicsLineItem,
                             QGraphicsProxyWidget, QHBoxLayout, QLabel,
                             QToolTip, QVBoxLayout, QWidget)

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 CpuLineChart(QChart):

    def __init__(self, *args, **kwargs):
        super(CpuLineChart, self).__init__(*args, **kwargs)
        self.m_count = 10
        # 隐藏图例
        self.legend().hide()
        self.m_series = QSplineSeries(self)
        # 设置画笔
        self.m_series.setPen(
            QPen(QColor('#3B8CFF'), 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        self.addSeries(self.m_series)
        # x轴
        self.m_axisX = QDateTimeAxis(self)
        self.m_axisX.setTickCount(self.m_count + 1)  # 设置刻度数量
        self.m_axisX.setFormat('hh:mm:ss')  # 设置时间显示格式
        now = QDateTime.currentDateTime()  # 前10秒到现在
        self.m_axisX.setRange(now.addSecs(-self.m_count), now)
        self.addAxis(self.m_axisX, Qt.AlignBottom)
        self.m_series.attachAxis(self.m_axisX)
        # y轴
        self.m_axisY = QValueAxis(self)
        self.m_axisY.setLabelFormat('%d')  # 设置文本格式
        self.m_axisY.setMinorTickCount(4)  # 设置小刻度线的数目
        self.m_axisY.setTickCount(self.m_count + 1)
        self.m_axisY.setRange(0, 100)
        self.addAxis(self.m_axisY, Qt.AlignLeft)
        self.m_series.attachAxis(self.m_axisY)

        # 填充11个初始点,注意x轴 需要转为秒的时间戳
        self.m_series.append([
            QPointF(now.addSecs(-i).toMSecsSinceEpoch(), 0)
            for i in range(self.m_count, -1, -1)
        ])

        # 定时器获取数据
        self.m_timer = QTimer()
        self.m_timer.timeout.connect(self.update_data)
        self.m_timer.start(1000)

    def update_data(self):
        value = cpu_percent()
        now = QDateTime.currentDateTime()
        self.m_axisX.setRange(now.addSecs(-self.m_count), now)  # 重新调整x轴的时间范围
        # 获取原来的所有点,去掉第一个并追加新的一个
        points = self.m_series.pointsVector()
        points.pop(0)
        points.append(QPointF(now.toMSecsSinceEpoch(), value))
        # 替换法速度更快
        self.m_series.replace(points)

class ChartView(QChartView):

    def __init__(self, chart, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self._chart = chart
        self.setChart(chart)
        self.resize(800, 600)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿

        self.toolTipWidget = GraphicsProxyWidget(self._chart)
        # line
        self.lineItem = QGraphicsLineItem(self._chart)
        self.lineItem.setZValue(998)
        self.lineItem.hide()

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)

        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.min_x, self.max_x = axisX.min().toTime_t(), axisX.max().toTime_t()
        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)

        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(event.pos()).x() / 1000.0
        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
        ]
        print(x, y, index, points, self.min_x, self.max_x, self.min_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()))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    chart = CpuLineChart()
    chart.setTitle('cpu')
    # chart.setAnimationOptions(QChart.SeriesAnimations)

    view = ChartView(chart)
    view.show()
    sys.exit(app.exec_())
lycfr commented 2 years ago

ChartMouser.py

我按照你的demo改了一下,标签都可以正常显示了,但是这个弹窗的获取的points对不上我鼠标当前值,我采用的曲线不是用滚动的方式,是累积曲线,查看你demo是实时滚动的,这个累积曲线获取方式要怎么处理?有偿求解一下

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtChart import *
from random import *
import sys
from viewchart import *
import random
import time

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:rgb(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.yMax = 10
        self.chart = QChart()
        self.add_series()
        self.setChart(self.chart)
        # self.initChart()
        self.toolTipWidget = GraphicsProxyWidget(self.chart)

        self.lineItem = QGraphicsLineItem(self.chart)
        self.lineItem.setZValue(998)
        self.lineItem.hide()

    def add_series(self):
        self.series = QLineSeries()
        self.series.setName('内存')
        self.chart.addSeries(self.series)
        self.chart.legend().setVisible(True)

        self.axis_x = QDateTimeAxis()
        self.axis_x.setTitleText('Time/s')
        self.axis_x.setMin(QDateTime.currentDateTime().addSecs(-10))
        self.axis_x.setMax(QDateTime.currentDateTime().addSecs(0))
        self.axis_x.setFormat('hh:mm:ss')
        self.axis_x.setTickCount(10)

        self.axis_y = QValueAxis()
        self.axis_y.setTitleText('内存/MB')
        self.axis_y.setMin(0)
        self.axis_y.setMax(self.yMax)
        self.axis_y.setTickCount(5)

        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)
        self.series.attachAxis(self.axis_x)
        self.series.attachAxis(self.axis_y)

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        super(ChartView, self).mouseMoveEvent(event)
        self.min_X, self.max_X = self.axis_x.min().toTime_t(), self.axis_x.max().toTime_t()
        self.min_Y, self.max_Y = self.axis_y.min(), self.axis_y.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) / (self.axis_x.tickCount() - 1)

        x = self.chart.mapToValue(event.pos()).x() / 1000
        y = self.chart.mapToValue(event.pos()).y()
        index = round((x - self.min_X) / self.step_x)
        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]
        print(x, y, index, points, self.min_X, self.max_X, self.min_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()))

class Window(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setupUi(self)
        self.yMax = 1

        self.chartView = ChartView(parent=self.widget)
        self.widget_layout.addWidget(self.chartView)

        self.pushButton_2.setEnabled(False)
        self.pushButton.clicked.connect(self.timer_init)
        self.pushButton_2.clicked.connect(self.stopTimer)

    def drawLine(self):
        time = QDateTime.currentDateTime()
        self.chartView.axis_x.setMax(QDateTime.currentDateTime().addSecs(1))
        yint = random.randint(0, 50)
        if yint > self.yMax:
            self.yMax = yint
            # print('Y轴最大值:', self.yMax)
            self.chartView.axis_y.setMin(0)
            self.chartView.axis_y.setMax(self.yMax + 5)
        self.chartView.series.append(time.toMSecsSinceEpoch(), yint)

    def timer_init(self):
        self.pushButton.setEnabled(False)
        self.pushButton_2.setEnabled(True)
        self.chartView.series.clear()
        self.timer = QTimer()
        self.timer.timeout.connect(self.drawLine)
        self.timer.start(1000)

    def stopTimer(self):
        self.pushButton.setEnabled(True)
        self.pushButton_2.setEnabled(False)
        self.timer.stop()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(App.exec_())

image 有偿求解一下~

892768447 commented 2 years ago

你确定你要累积曲线?累积太多了也没有意义啊。前面的都看不到了 而且内存会一直增加

lycfr commented 2 years ago

因为我想看到整体的曲线趋势图,如果一直更新,前面趋势看不到,没法判断问题。内存增加这个可以接受,能搞个demo参考一下吗?有偿~

892768447 commented 2 years ago

因为我想看到整体的曲线趋势图,如果一直更新,前面趋势看不到,没法判断问题。内存增加这个可以接受,能搞个demo参考一下吗?有偿~

还是cpu那个。也就是下面时间在10秒内,坐标点一致增加?

lycfr commented 2 years ago

是的~

lycfr commented 2 years ago

可以在我demo上改改不~

lycfr commented 2 years ago

我想过把这个鼠标实时Y轴传入这个points里面去,但是得判断一下x是不是和当前曲线有交集了 image

892768447 commented 2 years ago

我想过把这个鼠标实时Y轴传入这个points里面去,但是得判断一下x是不是和当前曲线有交集了 image

就不应该通过点去取。发现问题了 serie.at(index+1) 这个是通过计算索引去取值的。但是你的serie是一直累加的。根本取不到后面的值

最好还是把竖线改为横线。取Y值来显示就行了比较简单

892768447 commented 2 years ago

大概是这个意思。你要弄个算法计算 series.at(索引) 这个索引。因为你是累加的。不是简单1-10之间 下面只是简单的测试。具体你自己调小吧。太累了。要等它走满一轮后


import sys
import time
from random import *

from PyQt5.QtChart import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

from viewchart import *

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:rgb(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.yMax = 10
        self.m_count = 10
        self.chart = QChart()
        self.add_series()
        self.setChart(self.chart)
        # self.initChart()
        self.toolTipWidget = GraphicsProxyWidget(self.chart)

        self.lineItem = QGraphicsLineItem(self.chart)
        self.lineItem.setZValue(998)
        self.lineItem.hide()

        self.clear()

    def add_series(self):
        self.series = QLineSeries()
        self.series.setName('内存')
        self.chart.addSeries(self.series)
        self.chart.legend().setVisible(True)

        # 创建默认xy轴
        self.chart.createDefaultAxes()
        # x轴
        self.axis_x = self.chart.axisX()
        self.axis_x.setTitleText('Time/s')
        self.axis_x.setTickCount(self.m_count)
        self.axis_x.setVisible(False)  # 隐藏x轴
        # y轴
        self.axis_y = self.chart.axisY()
        self.axis_y.setTitleText('内存/MB')
        self.axis_y.setRange(0, self.yMax)
        self.axis_y.setTickCount(5)

        # 自定义x轴显示时间
        self.axis_cx = QCategoryAxis(
            self.chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
        self.chart.addAxis(self.axis_cx, Qt.AlignBottom)
        self.series.attachAxis(self.axis_cx)  # 附加到series上

    def updateTimeLabel(self, now):
        # 10秒以前到现在
        f_now = now.addSecs(1 - self.m_count)
        # 设置x轴范围
        self.axis_x.setRange(f_now.toSecsSinceEpoch(),
                             now.toSecsSinceEpoch())  # 时间范围数字
        # 更新时间轴显示
        for label in self.axis_cx.categoriesLabels().copy():
            # 清空
            self.axis_cx.remove(label)
        for t in range(f_now.toSecsSinceEpoch(), now.toSecsSinceEpoch() + 1):
            # print(QDateTime.fromSecsSinceEpoch(t).toString("hh:mm:ss"), t)
            self.axis_cx.append(
                QDateTime.fromSecsSinceEpoch(t).toString("hh:mm:ss"), t)

    def clear(self):
        self.series.clear()
        now = QDateTime.currentDateTime()  # 前10秒到现在
        self.updateTimeLabel(now)
        # 填充初始点,注意x轴 需要转为秒的时间戳
        self.series.append([
            QPointF(now.addSecs(-i).toSecsSinceEpoch(), 0)
            for i in range(self.m_count - 1, -1, -1)
        ])

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        self.min_X, self.max_X = self.axis_x.min(), self.axis_x.max()
        self.min_Y, self.max_Y = self.axis_y.min(), self.axis_y.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) / (self.axis_x.tickCount() + 1)

        x = self.chart.mapToValue(event.pos()).x()
        y = self.chart.mapToValue(event.pos()).y()
        index = round((x - self.min_X) / self.step_x)

        points = [
            (self.series,
             self.series.at(self.series.count() - (self.m_count - index + 1)))
        ]
        print(x, y, index, points, self.min_X, self.max_X, self.min_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()

class Window(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super(Window, self).__init__()
        self.setupUi(self)
        self.yMax = 1

        self.widget_layout.setContentsMargins(0, 0, 0, 0)

        self.chartView = ChartView(parent=self.widget)
        self.widget_layout.addWidget(self.chartView)

        self.pushButton_2.setEnabled(False)
        self.pushButton.clicked.connect(self.timer_init)
        self.pushButton_2.clicked.connect(self.stopTimer)

    def drawLine(self):
        now = QDateTime.currentDateTime()
        # self.chartView.axis_x.setMax(QDateTime.currentDateTime().addSecs(1))
        self.chartView.updateTimeLabel(now)
        yint = randint(0, 50)
        if yint > self.yMax:
            self.yMax = yint
            # print('Y轴最大值:', self.yMax)
            self.chartView.axis_y.setRange(0, self.yMax + 5)
        self.chartView.series.append(now.toSecsSinceEpoch(), yint)

    def timer_init(self):
        self.pushButton.setEnabled(False)
        self.pushButton_2.setEnabled(True)
        self.chartView.clear()
        self.timer = QTimer()
        self.timer.timeout.connect(self.drawLine)
        self.timer.start(1000)

    def stopTimer(self):
        self.pushButton.setEnabled(True)
        self.pushButton_2.setEnabled(False)
        self.timer.stop()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(App.exec_())
892768447 commented 2 years ago

最简单还是横线显示 Y值 ,也不用去求交点。因为你x轴的点本来就很少

lycfr commented 2 years ago

感谢你的解答,我已经重新用其他框架替换实现了 image

QAnneQ commented 1 year ago

@lycfr 你这个我能参考下么, 使用PySide6 不显示线条

wisdom-2021 commented 11 months ago

感谢你的解答,我已经重新用其他框架替换实现了 image

请教下这个用的是什么框架呢