K.M*_*ier 6 python animation pyqt matplotlib pyqt4
我正在用Python编写软件.我需要将Matplotlib时间动画嵌入到自制的GUI中.以下是有关它们的更多详细信息:
GUI也是用Python编写的,使用PyQt4库.我的GUI与您在网上可以找到的常见GUI没有太大区别.我只是继承QtGui.QMainWindow并添加一些按钮,布局,......
Matplotlib动画基于animation.TimedAnimation类.这是动画的代码:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import matplotlib.animation as animation
class CustomGraph(animation.TimedAnimation):
def __init__(self):
self.n = np.linspace(0, 1000, 1001)
self.y = 1.5 + np.sin(self.n/20)
#self.y = np.zeros(self.n.size)
# The window
self.fig = plt.figure()
ax1 = self.fig.add_subplot(1, 2, 1)
self.mngr = plt.get_current_fig_manager()
self.mngr.window.setGeometry(50,100,2000, 800)
# ax1 settings
ax1.set_xlabel('time')
ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
ax1.add_line(self.line1)
ax1.set_xlim(0, 1000)
ax1.set_ylim(0, 4)
animation.TimedAnimation.__init__(self, self.fig, interval=20, blit=True)
def _draw_frame(self, framedata):
i = framedata
print(i)
self.line1.set_data(self.n[ 0 : i ], self.y[ 0 : i ])
self._drawn_artists = [self.line1]
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1]
for l in lines:
l.set_data([], [])
def showMyAnimation(self):
plt.show()
''' End Class '''
if __name__== '__main__':
print("Define myGraph")
myGraph = CustomGraph()
myGraph.showMyAnimation()
Run Code Online (Sandbox Code Playgroud)
此代码生成一个简单的动画:
动画本身很好用.运行代码,动画在一个小窗口中弹出,然后开始运行.但是如何将动画嵌入到我自己的GUI中呢?
我做了一些研究以找出答案.这是我尝试过的一些事情.我已将以下代码添加到python文件中.请注意,此添加的代码实际上是一个额外的类定义:
from PyQt4 import QtGui
from PyQt4 import QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class CustomFigCanvas(FigureCanvas):
def __init__(self):
self.myGraph = CustomGraph()
FigureCanvas.__init__(self, self.myGraph.fig)
Run Code Online (Sandbox Code Playgroud)
我在这里尝试做的是将CustomGraph()对象 - 本质上是我的动画 - 嵌入到FigureCanvas中.
我在另一个python文件中编写了我的GUI(但仍然在同一个文件夹中).通常我可以在我的GUI中添加小部件.我相信CustomFigCanvas(..)类中的对象是一个通过继承的Widget.这就是我在GUI中尝试的内容:
..
myFigCanvas = CustomFigCanvas()
self.myLayout.addWidget(myFigCanvas)
..
Run Code Online (Sandbox Code Playgroud)
它在某种程度上起作用.我确实在我的GUI中显示了一个数字.但这个数字是空的.动画无法运行:
甚至还有另一种奇怪的现象.我的GUI显示这个空图,但我同时得到一个常规的Matplotlib弹出窗口,里面有我的动画人物.此动画也未运行.
显然我在这里缺少一些东西.请帮我弄清楚这个问题.我非常感谢所有的帮助.
K.M*_*ier 13
我想我找到了解决方案.所有的功劳都归功于制作Python教程网站https://pythonprogramming.net的 Harrison先生.他帮助了我.
所以这就是我所做的.两个主要变化:
我以前有两个类:CustomGraph(TimedAnimation)和CustomFigCanvas(FigureCanvas).现在我只剩下一个,但他继承了TimedAnimation和FigureCanvas:CustomFigCanvas(TimedAnimation,FigureCanvas)
这就是我之前制作这个数字的方式:
self.fig = plt.figure()
Run Code Online (Sandbox Code Playgroud)
来自import语句的'plt' 'import matplotlib.pyplot as plt'.当你想将它嵌入你自己的GUI时,这种制作图形的方式显然会引起麻烦.所以有更好的方法:
self.fig = Figure(figsize=(5,5), dpi=100)
Run Code Online (Sandbox Code Playgroud)
现在它有效!
这是完整的代码:
import numpy as np
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
# The data
self.n = np.linspace(0, 1000, 1001)
self.y = 1.5 + np.sin(self.n/20)
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
ax1 = self.fig.add_subplot(111)
# ax1 settings
ax1.set_xlabel('time')
ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
ax1.add_line(self.line1)
ax1.set_xlim(0, 1000)
ax1.set_ylim(0, 4)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 20, blit = True)
def _draw_frame(self, framedata):
i = framedata
print(i)
self.line1.set_data(self.n[ 0 : i ], self.y[ 0 : i ])
self._drawn_artists = [self.line1]
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1]
for l in lines:
l.set_data([], [])
''' End Class '''
Run Code Online (Sandbox Code Playgroud)
这是在matplotlib中制作动画的代码.现在您可以轻松地将其嵌入到您自己的Qt GUI中:
..
myFigCanvas = CustomFigCanvas()
self.myLayout.addWidget(myFigCanvas)
..
Run Code Online (Sandbox Code Playgroud)
它似乎工作得很好.谢谢哈里森先生!
编辑:
几个月后我回到了这个问题.这是完整的代码.只需将其复制粘贴到一个新.py文件中,然后运行它:
###################################################################
# #
# PLOTTING A LIVE GRAPH #
# ---------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
def setCustomSize(x, width, height):
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x.setSizePolicy(sizePolicy)
x.setMinimumSize(QtCore.QSize(width, height))
x.setMaximumSize(QtCore.QSize(width, height))
''''''
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QtGui.QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
self.LAYOUT_A = QtGui.QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QtGui.QPushButton(text = 'zoom')
setCustomSize(self.zoomBtn, 100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
''''''
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
''''''
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
def addData(self, value):
self.addedData.append(value)
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
data_signal = QtCore.pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
Run Code Online (Sandbox Code Playgroud)