Use matplotlib to draw dynamic curves in pyqt, and use pyqtmatplotlib
I. project background:
After reading the matplotlib for python developers book, I basically learned how to display curves in pyqt, so I wrote one myself.
Ii. Requirement Description:
1) the X axis shows the time point. The length is 1 minute, and a point is drawn every second. If the length of the X axis exceeds 1 minute, the scale is shifted to 1 second to achieve dynamic effect.
2) the Y axis shows a random value, ranging from 1 to 100.
Iii. Preparations
1 environment: python3.3, eric5, pyqt4
4. Start:
Use Eric to create a new project:
At the early stage of design coding, Eric's two windows are mainly used: source code and form browser, similar to delphi.
In the Form browser, right-click, new Form, and select Main Window as the Form type, as shown below:
The name is MplMainWindow.
Put two PushButton on the interface, horizontally layout, and then put a Widget to modify the name, horizontal and vertical policies.
The interface is designed as follows:
Finally, execute the grid layout.
To embed Matplotlib into mplCanvas, You need to upgrade mplCanvas, right-click and execute Promote, and enter the class name as MplCanvasWrapper. This class is used to compile matplotlib code. The file name is mplCanvasWrapper.
Click Add and then click upgrade.
Save the current design form.
This completes the interface design. The qt interface stores xml content and needs to be converted to python code in two ways:
Method 1: Use the built-in functions of Eric: In the form browser, right-click the form ui file and execute the compile form command. The ui_mplmain1_1_py file is generated in the current ui file directory.
Method 2: run the command [original file name of pyuic 4-o destination file] in cmd, as follows:
A MplMainWindow. py file is generated in the project folder.
Use method 1 in this document. The default file name is ui_mplmain1_1_py.
Open this file and do two things:
1) In the last line, there will be the following sentence: "from mplCodeWrapper import MplCodeWrapper", which is exactly the same as the class name file name entered during the upgrade. Cut this sentence to the top of the file, yes.
2) Change the inheritance of the form from object to QtGui. QMainWindow.
Then we will create the file mplCodeWrapper. py
In Eric's source code browser, create a new file, save it as mplCodeWrapper. py, and write two blank codes:
From PyQt4 import QtCore
From PyQt4 import QtGui
From Ui_MplMainWindow import Ui_MainWindow
Class Code_MainWindow (Ui_MainWindow): # modify to inherit from Ui_MainWindow
Def _ init _ (self, parent = None ):
Super (Code_MainWindow, self). _ init _ (parent)
Pass
So far, the entire framework has been set up, and both the interface file and the drawing file have been set up.
Add event processing for the form below.
In line with the principle of separation of interface and code, we create a new py file for writing interface code
Create the file code_mplmain1_0000py in the current directory. It is mainly used to bind button events and intermediate logic.
I have mentioned a bunch of things above, but I may not quite understand why I want to change it. Here I will draw a class diagram as follows:
The file Ui_MplMainWindow generated by PyQt is a pure interface file, similar to the designer file of C #. The Code_MplMainWindow file is similar to the cs file of C #, and the drawing logic is placed in MplCanvasWrapper, in this way, the interface and implementation are separated.
How can I display and change the time on the X axis?
1) matplotlib provides the plot_date Method for displaying the time on the X axis.
2) Design a thread for generating data and drawing. Based on the single function principle, we need to divide the generated data and drawing into two types for implementation, one for data processing and one for drawing board. The improved class diagram is as follows:
Notes:
1) when the form is closed, a close confirmation prompt should be provided, which is implemented by rewriting closeEvent.
2) The thread must have an exit signal.
The complete code is as follows:
1) ui_mplmain1_1_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
T AttributeError:
Def _ fromUtf8 (s ):
Return s
Try:
_ Encoding = QtGui. QApplication. UnicodeUTF8
Def _ translate (context, text, disambig ):
Return QtGui. QApplication. translate (context, text, disambig, _ encoding)
T 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", "Start", None ))
Self. btnPause. setText (_ translate ("MainWindow", "Suspend", None ))
2) code_mplmain1_1_py
From PyQt4 import QtGui, QtCore
From Ui_MplMainWindow import Ui_MainWindow
Class Code_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 ):
'''Beginto plot '''
Self. mplCanvas. startPlot ()
Pass
Def pausePlot (self ):
'''Pause plot '''
Self. mplCanvas. pausePlot ()
Pass
Def 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.exe c _())
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]) # 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)
:
Summary:
Through this program, I am familiar with the following points:
1) Eric and QtDesigner are used, and the interface and logic are separated.
2) rewrite a form event
3) bind signals and slots
4) use threads
5) Use of object-oriented matplotlib
How to dynamically display the interval between a column and a column as needed when using matplotlib in python to draw a Histogram
Static or dynamic,
It must be triggered at a specific time,
Or write it in the program, click or ..
Either accept the keyboard input or pass a parameter.
Hope to help you!
How to Use the matplotlib module of python to draw a cumulative distribution chart
The following program draws the cumulative distribution function of random variable X and the cumulative result of array p.
>>> Pl. plot (t, X. cdf (t ))
>>> Pl. plot (t2, np. add. accumulate (p) * (t2 [1]-t2 [0])