Pyqt QCustomPlot 简介、安装与实用代码示例(二)

所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nixgnauhcuy’s blog

如需转载,请标明出处!

完整代码我已经上传到 Github 上了,可前往 https://github.com/nixgnauhcuy/QCustomPlot_Pyqt_Study 获取。
完整文章路径:

前言

继上文,继续补充官方示例 demo 实现~

实用代码示例

彩色图演示

A 2D color map with color scale. Color scales can be dragged and zoomed just like axes

import sys, math

from PyQt5.QtWidgets import QApplication, QGridLayout, QWidget
from QCustomPlot_PyQt5 import QCustomPlot, QCPAxis, QCPColorScale, QCPColorMap
from QCustomPlot_PyQt5 import QCPColorGradient, QCPMarginGroup, QCP, QCPRange

class MainForm(QWidget):

    def __init__(self) -> None:
        super().__init__()

        self.setWindowTitle("彩色地图演示")
        self.resize(600,400)

        self.customPlot = QCustomPlot(self)
        self.gridLayout = QGridLayout(self).addWidget(self.customPlot)

        # configure axis rect:
        self.customPlot.setInteractions(QCP.Interactions(QCP.iRangeDrag | QCP.iRangeZoom)) # this will also allow rescaling the color scale by dragging/zooming
        self.customPlot.axisRect().setupFullAxesBox(True)
        self.customPlot.xAxis.setLabel("x")
        self.customPlot.yAxis.setLabel("y")

        # set up the QCPColorMap:
        self.colorMap = QCPColorMap(self.customPlot.xAxis, self.customPlot.yAxis)
        nx = 200
        ny = 200
        self.colorMap.data().setSize(nx, ny) # we want the color map to have nx * ny data points
        self.colorMap.data().setRange(QCPRange(-4, 4), QCPRange(-4, 4)) # and span the coordinate range -4..4 in both key (x) and value (y) dimensions
        # now we assign some data, by accessing the QCPColorMapData instance of the color map:
        x, y, z = 0, 0, 0
        for xIndex in range(nx):
            for yIndex in range(ny):
                x, y =self.colorMap.data().cellToCoord(xIndex, yIndex)
                r = 3*math.sqrt(x*x+y*y)+1e-2
                z = 2*x*(math.cos(r+2)/r-math.sin(r+2)/r) # the B field strength of dipole radiation (modulo physical constants)
                self.colorMap.data().setCell(xIndex, yIndex, z)
        # add a color scale:
        self.colorScale = QCPColorScale(self.customPlot)
        self.customPlot.plotLayout().addElement(0, 1, self.colorScale) # add it to the right of the main axis rect
        self.colorScale.setType(QCPAxis.atRight) # scale shall be vertical bar with tick/axis labels right (actually atRight is already the default)
        self.colorMap.setColorScale(self.colorScale) # associate the color map with the color scale
        self.colorScale.axis().setLabel("Magnetic Field Strength")

        # set the color gradient of the color map to one of the presets:
        self.colorMap.setGradient(QCPColorGradient(QCPColorGradient.gpPolar))
        # we could have also created a QCPColorGradient instance and added own colors to
        # the gradient, see the documentation of QCPColorGradient for what's possible.

        self.colorMap.rescaleDataRange() 

        # make sure the axis rect and color scale synchronize their bottom and top margins (so they line up):
        marginGroup = QCPMarginGroup(self.customPlot)
        self.customPlot.axisRect().setMarginGroup(QCP.msBottom|QCP.msTop, marginGroup)
        self.colorScale.setMarginGroup(QCP.msBottom|QCP.msTop, marginGroup)

        # rescale the key (x) and value (y) axes so the whole color map is visible:
        self.customPlot.rescaleAxes()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainForm = MainForm()
    mainForm.show()
    sys.exit(app.exec())

在这里插入图片描述

散点像素图演示

Pixmap scatter points and a multi-lined axis label, as well as a plot title at the top

import sys

from PyQt5.QtWidgets import QApplication, QGridLayout, QWidget
from PyQt5.QtGui import QPen, QColor, QFont, QBrush, QPixmap
from PyQt5.QtCore import Qt

from QCustomPlot_PyQt5 import QCustomPlot, QCPTextElement, QCPScatterStyle, QCPGraph

class MainForm(QWidget):

    def __init__(self) -> None:
        super().__init__()

        self.setWindowTitle("散点像素图演示")
        self.resize(600,400)

        self.customPlot = QCustomPlot(self)
        self.gridLayout = QGridLayout(self).addWidget(self.customPlot)

        self.customPlot.axisRect().setBackground(QColor(0, 0, 0))
        self.customPlot.addGraph()
        self.customPlot.graph().setLineStyle(QCPGraph.lsLine)
        pen = QPen(QColor(255, 200, 20, 200))
        pen.setStyle(Qt.DashLine)
        pen.setWidthF(2.5)
        self.customPlot.graph().setPen(pen)
        self.customPlot.graph().setBrush(QBrush(QColor(255,200,20,70)))
        self.customPlot.graph().setScatterStyle(QCPScatterStyle(QPixmap("./tmp.png")))

        # set graph name, will show up in legend next to icon:
        self.customPlot.graph().setName("Data from Photovoltaic\nenergy barometer 2011")
        # set data:
        year = [2005, 2006, 2007, 2008, 2009, 2010, 2011]
        value = [2.17, 3.42, 4.94, 10.38, 15.86, 29.33, 52.1]
        self.customPlot.graph().setData(year, value)

        # set title of plot:
        self.customPlot.plotLayout().insertRow(0)
        self.customPlot.plotLayout().addElement(0, 0, QCPTextElement(self.customPlot, "Regenerative Energies", QFont("sans", 12, QFont.Bold)))
        # axis configurations:
        self.customPlot.xAxis.setLabel("Year")
        self.customPlot.yAxis.setLabel("Installed Gigawatts of\nphotovoltaic in the European Union")
        self.customPlot.xAxis2.setVisible(True)
        self.customPlot.yAxis2.setVisible(True)
        self.customPlot.xAxis2.setTickLabels(False)
        self.customPlot.yAxis2.setTickLabels(False)
        self.customPlot.xAxis2.setTicks(False)
        self.customPlot.yAxis2.setTicks(False)
        self.customPlot.xAxis2.setSubTicks(False)
        self.customPlot.yAxis2.setSubTicks(False)
        self.customPlot.xAxis.setRange(2004.5, 2011.5)
        self.customPlot.yAxis.setRange(0, 52)
        # setup legend:
        self.customPlot.legend.setFont(QFont(self.font().family(), 7))
        self.customPlot.legend.setIconSize(50, 20)
        self.customPlot.legend.setVisible(True)
        self.customPlot.axisRect().insetLayout().setInsetAlignment(0, Qt.AlignLeft | Qt.AlignTop)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainForm = MainForm()
    mainForm.show()
    sys.exit(app.exec())

在这里插入图片描述

官方 demo 的背景图我没有,随便用黑色底了,太阳 logo 也没有,随便找了一个,效果一样就行~

实时数据演示

Real time generated data and time bottom axis

import sys, math, random

from PyQt5.QtWidgets import QApplication, QGridLayout, QWidget
from PyQt5.QtGui import QPen, QColor
from PyQt5.QtCore import Qt, QTime, QTimer

from QCustomPlot_PyQt5 import QCustomPlot, QCPAxisTickerTime

class MainForm(QWidget):

    def __init__(self) -> None:
        super().__init__()

        self.setWindowTitle("实时数据演示")
        self.resize(600,400)

        self.lastPointKey = 0

        self.customPlot = QCustomPlot(self)
        self.gridLayout = QGridLayout(self).addWidget(self.customPlot)

        self.customPlot.addGraph()
        self.customPlot.graph(0).setPen(QPen(QColor(40, 110, 255)))
        self.customPlot.addGraph()
        self.customPlot.graph(1).setPen(QPen(QColor(255, 110, 40)))

        timeTicker = QCPAxisTickerTime()
        timeTicker.setTimeFormat("%h:%m:%s")
        self.customPlot.xAxis.setTicker(timeTicker)
        self.customPlot.axisRect().setupFullAxesBox()
        self.customPlot.yAxis.setRange(-1.2, 1.2)

        # make left and bottom axes transfer their ranges to right and top axes:
        self.customPlot.xAxis.rangeChanged.connect(self.customPlot.xAxis2.setRange)
        self.customPlot.yAxis.rangeChanged.connect(self.customPlot.yAxis2.setRange)

        # setup a timer that repeatedly calls MainWindow::realtimeDataSlot:
        self.curTime = QTime.currentTime()
        self.dataTimer = QTimer(self)
        self.dataTimer.timeout.connect(self.realtimeDataSlot)
        self.dataTimer.start(0) # Interval 0 means to refresh as fast as possible

    def realtimeDataSlot(self) -> None:
        # calculate two new data points:
        key = self.curTime.msecsTo(QTime.currentTime())/1000.0
        
        if key-self.lastPointKey > 0.002: # at most add point every 2 ms
            # add data to lines:
            self.customPlot.graph(0).addData(key, math.sin(key)+random.random()*1*math.sin(key/0.3843))
            self.customPlot.graph(1).addData(key, math.cos(key)+random.random()*0.5*math.sin(key/0.4364))
            # rescale value (vertical) axis to fit the current data:
            # self.customPlot.graph(0).rescaleValueAxis()
            # self.customPlot.graph(1).rescaleValueAxis(True)
            self.lastPointKey = key

        # make key axis range scroll with the data (at a constant range size of 8):
        self.customPlot.xAxis.setRange(key, 8, Qt.AlignRight)
        self.customPlot.replot()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainForm = MainForm()
    mainForm.show()
    sys.exit(app.exec())

多轴演示

Multiple plot styles with different key/value axes and pi tick labeling at top axis

import sys, math, random

from PyQt5.QtWidgets import QApplication, QGridLayout, QWidget
from PyQt5.QtGui import QPen, QColor, QFont, QBrush
from PyQt5.QtCore import Qt, QLocale
from QCustomPlot_PyQt5 import QCustomPlot, QCPGraph, QCPScatterStyle, QCPTextElement, QCPAxisTickerPi, QCPErrorBars

class MainForm(QWidget):

    def __init__(self) -> None:
        super().__init__()

        self.setWindowTitle("多轴演示")
        self.resize(600,400)

        self.customPlot = QCustomPlot(self)
        self.gridLayout = QGridLayout(self).addWidget(self.customPlot)

        self.customPlot.setLocale(QLocale(QLocale.English, QLocale.UnitedKingdom)) # period as decimal separator and comma as thousand separator
        self.customPlot.legend.setVisible(True)

        legendFont = self.font()  # start out with MainWindow's font..
        legendFont.setPointSize(9) # and make a bit smaller for legend
        self.customPlot.legend.setFont(legendFont)
        self.customPlot.legend.setBrush(QBrush(QColor(255,255,255,230)))
        # by default, the legend is in the inset layout of the main axis rect. So this is how we access it to change legend placement:
        self.customPlot.axisRect().insetLayout().setInsetAlignment(0, Qt.AlignBottom|Qt.AlignRight)

        # setup for graph 0: key axis left, value axis bottom
        # will contain left maxwell-like function
        self.customPlot.addGraph(self.customPlot.yAxis, self.customPlot.xAxis)
        self.customPlot.graph(0).setPen(QPen(QColor(255, 100, 0)))
        self.customPlot.graph(0).setLineStyle(QCPGraph.lsLine)
        self.customPlot.graph(0).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssDisc, 5))
        self.customPlot.graph(0).setName("Left maxwell function")

        # setup for graph 1: key axis bottom, value axis left (those are the default axes)
        # will contain bottom maxwell-like function with error bars
        self.customPlot.addGraph()
        self.customPlot.graph(1).setPen(QPen(Qt.red))
        self.customPlot.graph(1).setLineStyle(QCPGraph.lsStepCenter)
        self.customPlot.graph(1).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, Qt.red, Qt.white, 7))
        self.customPlot.graph(1).setName("Bottom maxwell function")
        errorBars = QCPErrorBars(self.customPlot.xAxis, self.customPlot.yAxis)
        errorBars.removeFromLegend()
        errorBars.setDataPlottable(self.customPlot.graph(1))

        # setup for graph 2: key axis top, value axis right
        # will contain high frequency sine with low frequency beating:
        self.customPlot.addGraph(self.customPlot.xAxis2, self.customPlot.yAxis2)
        self.customPlot.graph(2).setPen(QPen(Qt.blue))
        self.customPlot.graph(2).setName("High frequency sine")

        # setup for graph 3: same axes as graph 2
        # will contain low frequency beating envelope of graph 2
        self.customPlot.addGraph(self.customPlot.xAxis2, self.customPlot.yAxis2)
        blueDotPen = QPen(QColor(30, 40, 255, 150))
        blueDotPen.setStyle(Qt.DotLine)
        blueDotPen.setWidthF(4)
        self.customPlot.graph(3).setPen(blueDotPen)
        self.customPlot.graph(3).setName("Sine envelope")

        # setup for graph 4: key axis right, value axis top
        # will contain parabolically distributed data points with some random perturbance
        self.customPlot.addGraph(self.customPlot.yAxis2, self.customPlot.xAxis2)
        self.customPlot.graph(4).setPen(QPen(QColor(50, 50, 50, 255)))
        self.customPlot.graph(4).setLineStyle(QCPGraph.lsNone)
        self.customPlot.graph(4).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, 4))
        self.customPlot.graph(4).setName("Some random data around\na quadratic function")

        # generate data, just playing with numbers, not much to learn here:
        x0 = [3*i/25.0 for i in range(25)]
        y0 = [math.exp(-x*x*0.8)*(x*x+x) for x in x0]
        self.customPlot.graph(0).setData(x0, y0)

        x1 = [3*i/15.0 for i in range(15)]
        y1 = [math.exp(-x*x)*(x*x)*2.6 for x in x1]
        y1err = [y*0.25 for y in y1]
        self.customPlot.graph(1).setData(x1, y1)
        errorBars.setData(y1err, y1err)

        x2 = [i/250.0*3*math.pi for i in range(250)]
        y2 = [math.sin(x*12)*math.cos(x)*10 for x in x2]
        self.customPlot.graph(2).setData(x2, y2)

        x3 = x2
        y3 = [math.cos(x)*10 for x in x3]
        self.customPlot.graph(3).setData(x3, y3)

        x4 = [i/250.0*100-50 for i in range(250)]
        y4 = [0.01*x*x + 1.5*(random.random()-0.5) + 1.5*math.pi for x in x4]
        self.customPlot.graph(4).setData(x4, y4)

        # activate top and right axes, which are invisible by default:
        self.customPlot.xAxis2.setVisible(True)
        self.customPlot.yAxis2.setVisible(True)

        # set ranges appropriate to show data:
        self.customPlot.xAxis.setRange(0, 2.7)
        self.customPlot.yAxis.setRange(0, 2.6)
        self.customPlot.xAxis2.setRange(0, 3.0*math.pi)
        self.customPlot.yAxis2.setRange(-70, 35)

        # set pi ticks on top axis:
        self.customPlot.xAxis2.setTicker(QCPAxisTickerPi())

        # add title layout element:
        self.customPlot.plotLayout().insertRow(0)
        self.customPlot.plotLayout().addElement(0, 0, QCPTextElement(self.customPlot, "Way too many graphs in one plot", QFont("sans", 12, QFont.Bold)))

        # set labels:
        self.customPlot.xAxis.setLabel("Bottom axis with outward ticks")
        self.customPlot.yAxis.setLabel("Left axis label")
        self.customPlot.xAxis2.setLabel("Top axis label")
        self.customPlot.yAxis2.setLabel("Right axis label")

        # make ticks on bottom axis go outward:
        self.customPlot.xAxis.setTickLength(0, 5)
        self.customPlot.xAxis.setSubTickLength(0, 3)

        # make ticks on right axis go inward and outward:
        self.customPlot.yAxis2.setTickLength(3, 3)
        self.customPlot.yAxis2.setSubTickLength(1, 1)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainForm = MainForm()
    mainForm.show()
    sys.exit(app.exec())

在这里插入图片描述

对数轴演示

Logarithmic axis scaling. Note correct display of the sine function crossing zero in negative infinity

import sys, math

from PyQt5.QtWidgets import QApplication, QGridLayout, QWidget
from PyQt5.QtGui import QPen, QColor, QBrush
from PyQt5.QtCore import Qt
from QCustomPlot_PyQt5 import QCustomPlot, QCPGraph, QCPGraphData, QCP, QCPAxis, QCPAxisTickerLog

class MainForm(QWidget):

    def __init__(self) -> None:
        super().__init__()

        self.setWindowTitle("对数轴演示")
        self.resize(600,400)

        self.customPlot = QCustomPlot(self)
        self.gridLayout = QGridLayout(self).addWidget(self.customPlot)

        self.customPlot.setNoAntialiasingOnDrag(True) # more performance/responsiveness during dragging
        self.customPlot.addGraph()
        pen = QPen(QColor(255,170,100))
        pen.setWidth(2)
        pen.setStyle(Qt.DotLine)
        self.customPlot.graph(0).setPen(pen)
        self.customPlot.graph(0).setName("x")

        self.customPlot.addGraph()
        self.customPlot.graph(1).setPen(QPen(Qt.red))
        self.customPlot.graph(1).setBrush(QBrush(QColor(255, 0, 0, 20)))
        self.customPlot.graph(1).setName("-sin(x)exp(x)")

        self.customPlot.addGraph()
        self.customPlot.graph(2).setPen(QPen(Qt.blue))
        self.customPlot.graph(2).setBrush(QBrush(QColor(0, 0, 255, 20)))
        self.customPlot.graph(2).setName(" sin(x)exp(x)")

        self.customPlot.addGraph ()
        pen = QPen(QColor(0,0,0))
        pen.setWidth(1)
        pen.setStyle(Qt.DashLine)
        self.customPlot.graph(3).setPen(pen)
        self.customPlot.graph(3).setBrush(QBrush(QColor(0,0,0,15)))
        self.customPlot.graph(3).setLineStyle(QCPGraph.lsStepCenter)
        self.customPlot.graph(3).setName("x!")

        dataCount = 200
        dataFactorialCount = 21
        dataLinear = [QCPGraphData() for i in range(dataCount)]
        dataMinusSinExp = [QCPGraphData() for i in range(dataCount)]
        dataPlusSinExp = [QCPGraphData() for i in range(dataCount)]
        dataFactorial = [QCPGraphData() for i in range(dataFactorialCount)]
        for i in range(dataCount):
            dataLinear[i].key = i/10.0
            dataLinear[i].value = dataLinear[i].key
            dataMinusSinExp[i].key = i/10.0
            dataMinusSinExp[i].value = -math.sin(dataMinusSinExp[i].key)*math.exp(dataMinusSinExp[i].key)
            dataPlusSinExp[i].key = i/10.0
            dataPlusSinExp[i].value = math.sin(dataPlusSinExp[i].key)*math.exp(dataPlusSinExp[i].key)
        for i in range(dataFactorialCount):
            dataFactorial[i].key = i
            dataFactorial[i].value = 1.0
            for k in range(1, i+1):
                dataFactorial[i].value *= k

        self.customPlot.graph(0).data().set(dataLinear)
        self.customPlot.graph(1).data().set(dataMinusSinExp)
        self.customPlot.graph(2).data().set(dataPlusSinExp)
        self.customPlot.graph(3).data().set(dataFactorial)
        
        self.customPlot.xAxis.grid().setSubGridVisible(True)
        self.customPlot.yAxis.grid().setSubGridVisible(True)
        self.customPlot.yAxis.setScaleType(QCPAxis.stLogarithmic)
        self.customPlot.yAxis2.setScaleType(QCPAxis.stLogarithmic)
        logTicker = QCPAxisTickerLog()
        self.customPlot.yAxis.setTicker(logTicker)
        self.customPlot.yAxis2.setTicker(logTicker)
        self.customPlot.yAxis.setNumberFormat("eb") # e = exponential, b = beautiful decimal powers
        self.customPlot.yAxis.setNumberPrecision(0) # makes sure "1*10^4" is displayed only as "10^4"
        self.customPlot.xAxis.setRange(0, 19.9)
        self.customPlot.yAxis.setRange(1e-2, 1e10)
        # make range draggable and zoomable:
        self.customPlot.setInteractions(QCP.Interactions(QCP.iRangeDrag | QCP.iRangeZoom))
        # make top right axes clones of bottom left axes:
        self.customPlot.axisRect().setupFullAxesBox()
        # connect signals so top and right axes move in sync with bottom and left axes:
        self.customPlot.xAxis.rangeChanged.connect(self.customPlot.xAxis2.setRange)
        self.customPlot.yAxis.rangeChanged.connect(self.customPlot.yAxis2.setRange)

        self.customPlot.legend.setVisible(True)
        self.customPlot.legend.setBrush(QBrush(QColor(255,255,255,150)))
        self.customPlot.axisRect().insetLayout().setInsetAlignment(0, Qt.AlignLeft|Qt.AlignTop) # make legend align in top left corner or axis rect

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainForm = MainForm()
    mainForm.show()
    sys.exit(app.exec())

在这里插入图片描述

结语

还剩下一些示例,待续篇更新!~

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QCustomPlot is a C++ library for creating 2D plots and graphs. PyQt is a Python binding for the Qt application framework, which includes the Qt GUI library that QCustomPlot is built on. To use QCustomPlot with PyQt, you need to install PyQt and the QCustomPlot library. Once you have done that, you can create a QCustomPlot widget in your PyQt application and use its methods to create and customize plots. Here is an example of how to create a simple plot using QCustomPlot and PyQt: ``` python import sys from PyQt5.QtWidgets import QApplication, QMainWindow from qcustomplot import QCustomPlot, QCPGraphData, QCPScatterStyle class MainWindow(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle("QCustomPlot Example") self.setGeometry(100, 100, 800, 600) self.plot = QCustomPlot(self) self.plot.setGeometry(50, 50, 700, 500) self.plot.addGraph() self.plot.graph(0).setLineStyle(QCPGraphData.lsNone) self.plot.graph(0).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, 5)) self.plot.graph(0).setData([0, 1, 2, 3, 4], [1, 2, 3, 2, 1]) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` In this example, we create a MainWindow class that inherits from QMainWindow. We create a QCustomPlot widget and add it to the main window. We then add a graph to the plot and set its line style to none and its scatter style to a circle with a size of 5. We set the data for the graph using two arrays of x and y values. When we run the application, we see a simple plot with five points. We can customize the plot further by using more of QCustomPlot's methods and properties.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值