一、项目背景:

看了matplotlib for python developers这本书,基本掌握了在pyqt中显示曲线的做法,于是自己写一个。

二、需求描述:

1)X轴显示时间点,显示长度为1分钟,每一秒钟绘制一个点,X轴长度超过1分钟,则左移1秒刻度,实现动态效果

2)Y轴显示随机变化的数值,1-100

三、准备工作

1环境:python3.3,eric5,pyqt4

四、开始动手 :

使用Eric创建新项目:

在设计编码前期主要用到Eric的两个窗口:源码和窗体浏览器,类似delphi。

在窗体浏览器中,右键,new Form,窗体类型选择Main Window,如下:

保存时,取名为MplMainWindow。

在界面上放两个PushButton,水平布局,然后放一个Widget,修改名称、水平及垂直策略。

界面设计如下:

最后执行网格布局。

为了嵌入Matplotlib在mplCanvas中,需要将mplCanvas升级,右键执行Promote,输入类名称为MplCanvasWrapper,这个类就是编写matplotlib代码的,文件名称为mplCanvasWrapper。

点击添加,然后点击提升。

保存当前设计的窗体。

到此就完成了界面设计,qt的界面保存的内容是xml的,需要转换成python代码,有两种方式:

方法1:使用Eric自带的功能:在窗体浏览器中,右键窗体ui文件,执行compile form命令,此时会在当前ui文件目录中生成Ui_MplMainWindow.py文件。

方法2:在cmd中执行命令【pyuic 4 –o 目的文件名 原文件名】,如下:

此时在项目文件夹中生成了一个MplMainWindow.py文件。

在此文档中使用方式1,默认生成的文件名称是Ui_MplMainWindow.py。

打开这个文件,做两件事情:

1)在最后一行会有这么一句:“from mplCodeWrapper import MplCodeWrapper”,与提升时输入的类名文件名完全一致,把这句话剪切到文件顶部,要不会报错的。

2)将窗体的继承由object改为QtGui.QMainWindow

然后我们要创建文件mplCodeWrapper.py

在Eric的源码浏览器中,新建文件,保存为mplCodeWrapper.py,写上两句空代码:

from PyQt4 import QtCore

from PyQt4 import QtGui

from Ui_MplMainWindow import Ui_MainWindow

class Code_MainWindow(Ui_MainWindow):#修改为从Ui_MainWindow继承

def __init__(self, parent = None):

super(Code_MainWindow, self).__init__(parent)

pass

到此为止,整个框架搭起来了,界面文件和绘图文件都有了。

下面为窗体添加事件处理。

本着界面和代码分离的原则,我们新建一个py文件,用于编写界面代码

在当前目录中新建文件Code_MplMainWindow.py,主要用来绑定按钮事件及中间逻辑。

上面说了一堆,可能不是很明白为什么要这么改,在此画出类图如下:

PyQt生成的文件Ui_MplMainWindow属于纯界面文件,类似于C#的designer文件,Code_MplMainWindow文件类似于C#的cs文件,而绘图的逻辑放在MplCanvasWrapper中,这样界面和实现就分离了。

如何让X轴显示时间并动起来呢?

1)  关于X轴显示时间,matplotlib提供了plot_date方法

2)  设计一个线程,用于产生数据和绘图,根据功能单一原则,我们需要将产生数据和绘图分成两类来实现,一个数据处理类,一个画板类。完善后的类图如下:

注意几点:

1)  窗体关闭时,要有关闭确认提示,通过重写closeEvent实现

2)  线程要有退出信号

完整代码如下:

 Ui_MplMainWindow.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'MplMainWindow.ui'

#

# Created: Mon Aug 11 14:18:31 2014

#      by: PyQt4 UI code generator 4.10.3

#

# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

from mplCanvasWrapper import MplCanvasWrapper

try:

_fromUtf8 = QtCore.QString.fromUtf8

except AttributeError:

def _fromUtf8(s):

return s

try:

_encoding = QtGui.QApplication.UnicodeUTF8

def _translate(context, text, disambig):

return QtGui.QApplication.translate(context, text, disambig, _encoding)

except AttributeError:

def _translate(context, text, disambig):

return QtGui.QApplication.translate(context, text, disambig)

#inheritent from QtGui.QMainWindow

class Ui_MainWindow(QtGui.QMainWindow):

def setupUi(self, MainWindow):

MainWindow.setObjectName(_fromUtf8("MainWindow"))

MainWindow.resize(690, 427)

self.centralWidget = QtGui.QWidget(MainWindow)

self.centralWidget.setObjectName(_fromUtf8("centralWidget"))

self.gridLayout = QtGui.QGridLayout(self.centralWidget)

self.gridLayout.setObjectName(_fromUtf8("gridLayout"))

self.horizontalLayout = QtGui.QHBoxLayout()

self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))

self.btnStart = QtGui.QPushButton(self.centralWidget)

self.btnStart.setObjectName(_fromUtf8("btnStart"))

self.horizontalLayout.addWidget(self.btnStart)

self.btnPause = QtGui.QPushButton(self.centralWidget)

self.btnPause.setObjectName(_fromUtf8("btnPause"))

self.horizontalLayout.addWidget(self.btnPause)

spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)

self.horizontalLayout.addItem(spacerItem)

self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)

self.mplCanvas = MplCanvasWrapper(self.centralWidget)

sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)

sizePolicy.setHorizontalStretch(0)

sizePolicy.setVerticalStretch(0)

sizePolicy.setHeightForWidth(self.mplCanvas.sizePolicy().hasHeightForWidth())

self.mplCanvas.setSizePolicy(sizePolicy)

self.mplCanvas.setObjectName(_fromUtf8("mplCanvas"))

self.gridLayout.addWidget(self.mplCanvas, 1, 0, 1, 1)

MainWindow.setCentralWidget(self.centralWidget)

self.retranslateUi(MainWindow)

QtCore.QMetaObject.connectSlotsByName(MainWindow)

def retranslateUi(self, MainWindow):

MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))

self.btnStart.setText(_translate("MainWindow", "开始", None))

self.btnPause.setText(_translate("MainWindow","暂停",None))2)Code_MplMainWindow.py

fromPyQt4importQtGui,QtCorefromUi_MplMainWindowimportUi_MainWindowclassCode_MainWindow(Ui_MainWindow):def __init__(self, parent =None):

super(Code_MainWindow, self).__init__(parent)

self.setupUi(self)

self.btnStart.clicked.connect(self.startPlot)

self.btnPause.clicked.connect(self.pausePlot)def startPlot(self):''' begin to plot'''

self.mplCanvas.startPlot()passdef pausePlot(self):''' pause plot '''

self.mplCanvas.pausePlot()passdef releasePlot(self):''' stop and release thread'''

self.mplCanvas.releasePlot()def closeEvent(self,event):

result =QtGui.QMessageBox.question(self,"Confirm Exit...","Are you sure you want to exit ?",QtGui.QMessageBox.Yes|QtGui.QMessageBox.No)

event.ignore()if result ==QtGui.QMessageBox.Yes:

self.releasePlot()#release thread's resouce

event.accept()

if __name__ == "__main__":

import sys

app = QtGui.QApplication(sys.argv)

ui = Code_MainWindow()

ui.show()

sys.exit(app.exec_())

3)mplCanvasWrapper.py

from PyQt4 import  QtGui

from matplotlib.backends.backend_qt4agg import  FigureCanvasQTAgg as FigureCanvas

from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar

from matplotlib.figure import Figure

import numpy as np

from array import array

import time

import random

import threading

from datetime import datetime

from matplotlib.dates import  date2num, MinuteLocator, SecondLocator, DateFormatter

X_MINUTES = 1

Y_MAX = 100

Y_MIN = 1

INTERVAL = 1

MAXCOUNTER = int(X_MINUTES * 60/ INTERVAL)

class MplCanvas(FigureCanvas):

def __init__(self):

self.fig = Figure()

self.ax = self.fig.add_subplot(111)

FigureCanvas.__init__(self, self.fig)

FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)

FigureCanvas.updateGeometry(self)

self.ax.set_xlabel("time of data generator")

self.ax.set_ylabel('random data value')

self.ax.legend()

self.ax.set_ylim(Y_MIN,Y_MAX)

self.ax.xaxis.set_major_locator(MinuteLocator())  # every minute is a major locator

self.ax.xaxis.set_minor_locator(SecondLocator([10,20,30,40,50])) # every 10 second is a minor locator

self.ax.xaxis.set_major_formatter( DateFormatter('%H:%M:%S') ) #tick label formatter

self.curveObj = None # draw object

def plot(self, datax, datay):

if self.curveObj is None:

#create draw object once

self.curveObj, = self.ax.plot_date(np.array(datax), np.array(datay),'bo-')

else:

#update data of draw object

self.curveObj.set_data(np.array(datax), np.array(datay))

#update limit of X axis,to make sure it can move

self.ax.set_xlim(datax[0],datax[-1])

ticklabels = self.ax.xaxis.get_ticklabels()

for tick in ticklabels:

tick.set_rotation(25)

self.draw()

class  MplCanvasWrapper(QtGui.QWidget):

def __init__(self , parent =None):

QtGui.QWidget.__init__(self, parent)

self.canvas = MplCanvas()

self.vbl = QtGui.QVBoxLayout()

self.ntb = NavigationToolbar(self.canvas, parent)

self.vbl.addWidget(self.ntb)

self.vbl.addWidget(self.canvas)

self.setLayout(self.vbl)

self.dataX= []

self.dataY= []

self.initDataGenerator()

def startPlot(self):

self.__generating = True

def pausePlot(self):

self.__generating = False

pass

def initDataGenerator(self):

self.__generating=False

self.__exit = False

self.tData = threading.Thread(name = "dataGenerator",target = self.generateData)

self.tData.start()

def releasePlot(self):

self.__exit  = True

self.tData.join()

def generateData(self):

counter=0

while(True):

if self.__exit:

break

if self.__generating:

newData = random.randint(Y_MIN, Y_MAX)

newTime= date2num(datetime.now())

self.dataX.append(newTime)

self.dataY.append(newData)

self.canvas.plot(self.dataX, self.dataY)

if counter >= MAXCOUNTER:

self.dataX.pop(0)

self.dataY.pop(0)

else:

counter+=1

time.sleep(INTERVAL)

效果图:

总结:

通过该程序,主要熟悉了以下几点:

1)  Eric、QtDesigner使用,界面与逻辑分离

2)  重写窗体事件

3)  绑定信号和槽

4)  使用线程

5)  面向对象的matplotlib的使用

http://www.seanyxie.com/pyqt%E4%B8%AD%E4%BD%BF%E7%94%A8matplotlib%E7%BB%98%E5%88%B6%E5%8A%A8%E6%80%81%E6%9B%B2%E7%BA%BF-pythonic/

pyqt中使用matplotlib绘制动态曲线 – pythonic的更多相关文章

  1. pyqt中使用matplotlib绘制动态曲线

    一.项目背景: 看了matplotlib for python developers这本书,基本掌握了在pyqt中显示曲线的做法,于是自己写一个. 二.需求描述: 1)X轴显示时间点,显示长度为1分钟 ...

  2. [转载] MFC绘制动态曲线,用双缓冲绘图技术防闪烁

    转载的原文地址 先上效果图 随着时间的推移,曲线向右平移,同时X轴的时间坐标跟着更新. 一.如何绘制动态曲线 所谓动画,都是一帧一帧的图像连续呈现在用户面前形成的.所以如果你掌握了如何绘制静态曲线,那 ...

  3. 【MFC】MFC绘制动态曲线,用双缓冲绘图技术防闪烁

    摘自:http://zhy1987819.blog.163.com/blog/static/841427882011614103454335/ MFC绘制动态曲线,用双缓冲绘图技术防闪烁   2011 ...

  4. Matlab | 绘制动态曲线(使用 animatedline 对象)

    效果如下: 示例代码: figure('Color','w'); h1 = animatedline; h1.Color = 'r'; h1.LineWidth = 1.0; h1.LineStyle ...

  5. Python使用matplotlib绘制三维曲线

    本文主要演示如何使用matplotlib绘制三维图形 代码如下: # -*- coding: UTF-8 -*- import matplotlib as mpl from mpl_toolkits. ...

  6. C++第四十四篇 -- MFC使用ChartCtrl绘制动态曲线

    前言 目的:使用控制台程序带MFC类库画一个动态曲线图 参考链接: https://blog.csdn.net/sinat_29890433/article/details/105360032 htt ...

  7. 使用Python matplotlib做动态曲线

    今天看到“Python实时监控CPU使用率”的教程: https://www.w3cschool.cn/python3/python3-ja3d2z2g.html 自己也学习如何使用Python ma ...

  8. 在PyQt5中显示matplotlib绘制的图形

    import sys from PyQt5.QtCore import Qt from PyQt5.QtWidgets import * from plot_pyqt import PlotCanva ...

  9. Qt绘制动态曲线

    首先*.pro文件中加一句 QT += charts 然后 mainwindow.cpp文件如下: #include "mainwindow.h" #include "u ...

随机推荐

  1. C++自定义命名空间

    关于C++自定义命名空间,今天验证了一下命名空间如何使用,和嵌套命名空间以及出现的bug. 如何自定义命名空间,实例如下: insertion_sort.h和insertion_sort.cpp #p ...

  2. split 函数自己实现

    要求自己实现split函数 def mySplit(str,delimiter): result = [] start = 0 pos = str.find(delimiter, start) whi ...

  3. CGI version1.1-第一章 介绍 (译)

    CGI version1.1-第一章 介绍 1.简介 1.1 用途 CGI 是为 HTTP服务器 与 CGI脚本 在 响应客户端请求分配职责, 客户请求由url,方法与关于传输协议的附属信息, CGI ...

  4. 终于解决“Git Windows客户端保存用户名与密码”的问题

    这就是正确答案,我们已经验证过了,下面详细描述一下解决方法: 1. 在Windows中添加一个HOME环境变量,值为%USERPROFILE%,如下图: 2. 在“开始>运行”中打开%Home% ...

  5. poj 2274 The Race 最小堆

    题目链接 题目大意: 给n个小车, 每个小车有一个初始位置x和初始速度v, 保证x1 < x2..... <xn. 0<v<100. 然后问你一共会发生多少次超车, 以及前10 ...

  6. python显示当前时间

    import time time.strftime("%Y%m%d %X", time.localtime()) #当前时区 time.strftime("%Y%m%d ...

  7. smtp cmd 测试 smtp发送邮件 cmd发送邮件

    无账户MAIL FROM:<test@rmvbdy.com>RCPT TO:2850965781@qq.comDATASubject: test messagetest body . 有账 ...

  8. C语言入门(11)——switch分支语句

    C语言提供了一种用于多分支选择的switch语句, 其一般形式为: switch(表达式) { case 常量表达式1:语句1; break; case 常量表达式2:语句2; break; .... ...

  9. 桂林电子科技大学出校流量控制器Android版1.0.0

    每次玩游戏的时候,总是要开着电脑挂着出校控制器,真是浪费国家资源啊,,, 突然想起学校有个开放流量的网页,无奈UC等浏览器真是尝试优化js脚本啊,挂在后台,不到几分钟就掉线了,悲剧啊~~~ 还好And ...

  10. C++编程技术之 异常处理(上)

    增强错误恢复能力是提高代码健壮性的最有力途径之一 之所以平时编写代码的时候不愿意去写错误处理,主要是由于这项工作及其无聊并可能导致代码膨胀,导致的结果就是本来就比较复杂的程序变得更加复杂.当然了,前面 ...