pyqt中二维网格颜色图的优化
Optimization of color plot for 2D grid in pyqt
我有一个使用 PyQt4 实现的带有图形用户界面的程序。该程序控制光束分析仪,其中 returns 我有四个 7500 元素矢量。前两个是位置向量(X 和 Y),后两个分别是 X 和 Y 的强度向量。我想要做的是创建一个网格 (XY) 并将强度绘制为图中的颜色,如下图所示。
Figure 1 - Side Gaussian function are my raw data and I want to plot like this
我已经设法做到这一点,但我在优化方面遇到了问题。
def emitGraph(self):
zz = zeros([len(self.slit_data_pos[self.slit][::100]), len(self.slit_data_pos[self.slit + 1][::100])])
for i in xrange(len(self.slit_data_pos[self.slit][::100])):
for j in xrange(len(self.slit_data_pos[self.slit + 1][::100])):
zz[i,j] = self.mesh(i, j, self.slit_data_int[self.slit][::100], self.slit_data_int[self.slit + 1][::100])
self.graph.emit(
self.slit_data_pos[self.slit][::100],
self.slit_data_int[self.slit][::100],
self.slit_data_pos[self.slit + 1][::100],
self.slit_data_int[self.slit + 1][::100],
zz
)
self.timerGraph = threading.Timer(0.2, self.emitGraph).start()
def mesh(self, i, j, intx, inty):
norm_max = max(max(intx), max(inty))
intx = [x/norm_max for x in intx]
inty = [y/norm_max for y in inty]
return intx[i]*inty[j]
正如您在下面的代码中看到的,我有两个循环来填充 zz 矩阵(最初是 7500x7500)。我现在为每个矢量仅使用 75 个点,并使用计时器每 0.2 秒显示一次。我必须这样做,因为如果我使用所有数据,填充 zz 矩阵然后绘制需要很长时间。
我绘制它的代码部分是(在另一个 object/thread 中):
def graph_update(self, slit_samples_positionsX, slit_samples_intensitiesX, slit_samples_positionsY, slit_samples_intensitiesY, zz):
# self.matplotlibWidget.axis.plot(slit_samples_positionsX, slit_samples_intensitiesX)
# self.matplotlibWidget.axis.plot(slit_samples_positionsY, slit_samples_intensitiesY)
self.matplotlibWidget.axis.imshow(zz, cmap=cm.jet)
self.matplotlibWidget.canvas.draw()
我的问题是如何改进数据计算以提高图表的分辨率和显示速率?或者有没有其他更快的方法来创建这种情节?
谢谢!
您每 0.2 秒创建一次整个网格。您还每 0.2 秒创建一个新图。这都会减慢您的应用程序。
每次调用 emitGraph
时创建一个新计时器也可能是一个问题。
为了改进您的程序,我们的想法是创建一次网格并且只更新其中的数据。
此外,只创建一次图,用新数据更新它。
原则上,numpy 在数组操作中非常非常快,在屏幕上绘制数据应该不是问题。查看您的代码,一个问题可能是您实际上大量使用列表而不是 numpy 数组。所以尽量避免列表创建和 for 循环。实际上,我认为您可以完全摆脱 mesh
功能,从而节省很多时间。
关于剧情,不要每次更新都打imshow()
!例如,如果 self.image
是您的 imshow-plot,则只需调用 self.image.set_data(zz)
更新此图
如果您能提供有关您正在使用的阵列形状的更多信息并告诉我们 self.slit
是什么,我可能会为您提供更多帮助。
此时您可能还想看看下面的代码,它试图模仿光束分析仪并且运行速度非常快。
from PyQt4 import QtGui, QtCore
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import sys, time, random
class WorkerObject(QtCore.QObject):
signalStatus = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
self.data = np.zeros((600,800))
self.x = np.arange(0,800)
self.y = np.arange(0,600)
self.Y, self.X = np.meshgrid(self.x, self.y)
self.f = lambda x, y, x0, y0, sigmax, sigmay : 0.96*np.exp(-((x-x0)/float(sigmax))**2)*np.exp(-((y-y0)/float(sigmay))**2)
@QtCore.pyqtSlot()
def startWork(self):
print "StartWork"
x0 = random.randint(250,550)
y0 = random.randint(250,350)
sigmax = random.randint(100,200)
sigmay = random.randint(100,200)
while 1 > 0:
x0, y0, sigmax, sigmay = self.doWork(x0, y0, sigmax, sigmay)
def doWork(self, x0, y0, sigmax, sigmay):
dx, dy, dsx, dsy = random.randint(0,1)*2-1, random.randint(0,1)*2-1, random.randint(0,1)*2-1, random.randint(0,1)*2-1
self.data[:,:] = (self.f(self.X, self.Y, x0+dx, y0+dy, sigmax+dsx, sigmay+dsy)+np.random.random((600,800))*0.04)*(1.+np.random.rand()*0.04-0.02)
self.signalStatus.emit(self.data)
time.sleep(0.04)
return x0+dx, y0+dy, sigmax+dsx, sigmay+dsy
class App(QtGui.QMainWindow):
signalStatus = QtCore.pyqtSignal(object)
abortSignal = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(App, self).__init__(parent)
self.button_start = QtGui.QPushButton('Start',self)
self.button_cancel = QtGui.QPushButton('Cancel', self)
self.label_status = QtGui.QLabel('', self)
self.mainbox = QtGui.QWidget(self)
self.layout = QtGui.QVBoxLayout()
self.mainbox.setLayout(self.layout)
self.setCentralWidget(self.mainbox)
self.layout.addWidget(self.button_start)
self.layout.addWidget(self.button_cancel)
self.layout.addWidget(self.label_status)
self.fig = Figure((6.0, 3.0), dpi=72)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self)
self.canvastoolbar = NavigationToolbar(self.canvas, self)
self.fig.patch.set_alpha(0.0)
self.ax = self.fig.add_subplot(111)
self.x = np.arange( 0,800 ); self.y = np.arange( 0,600 )
self.im = self.ax.imshow(np.zeros((600, 800)), origin='upper', vmin=0, vmax=1 )
self.pv, = self.ax.plot( np.zeros(600) ,self.y , color="white" , alpha=0.6, lw=2 )
self.ph, = self.ax.plot( self.x ,np.zeros(800) , color="white" , alpha=0.6, lw=2)
self.ax.set_xlim([0,800]); self.ax.set_ylim([0,600])
self.layout.addWidget(self.canvas)
self.layout.addWidget(self.canvastoolbar)
self.initWorker()
def initWorker(self):
self.worker = WorkerObject()
self.worker_thread = QtCore.QThread()
self._connectSignals()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
def _connectSignals(self):
self.button_start.clicked.connect(self.worker.startWork)
self.button_cancel.clicked.connect(self.forceWorkerQuit)
self.worker.signalStatus.connect(self.updateStatus)
def forceWorkerQuit(self):
print "calculation aborted"
if self.worker_thread.isRunning():
self.worker_thread.terminate()
self.worker_thread.start()
@QtCore.pyqtSlot(object)
def updateStatus(self, obj):
self.im.set_data(obj)
argm = np.unravel_index(np.argmax(obj), (600,800))
self.pv.set_data(obj[:,argm[0]]*250, self.y)
self.ph.set_data(self.x, obj[argm[1], :]*250)
self.fig.canvas.draw()
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
编辑(回复下方评论,询问如何计算zz
):
如果我没理解错的话,你的slit_data_int[0]
是X方向的累积强度,slit_data_int[1]
是Y方向的强度。如果 zz[j,i] = slit_data_int[1][j]*slit_data_int[0][i]
那么最好使用 numpy 中的矩阵乘法 (np.outer
) 来计算:
zz[:,:] = np.outer(slit_data_int[1], slit_data_int[0])
我有一个使用 PyQt4 实现的带有图形用户界面的程序。该程序控制光束分析仪,其中 returns 我有四个 7500 元素矢量。前两个是位置向量(X 和 Y),后两个分别是 X 和 Y 的强度向量。我想要做的是创建一个网格 (XY) 并将强度绘制为图中的颜色,如下图所示。 Figure 1 - Side Gaussian function are my raw data and I want to plot like this
我已经设法做到这一点,但我在优化方面遇到了问题。
def emitGraph(self):
zz = zeros([len(self.slit_data_pos[self.slit][::100]), len(self.slit_data_pos[self.slit + 1][::100])])
for i in xrange(len(self.slit_data_pos[self.slit][::100])):
for j in xrange(len(self.slit_data_pos[self.slit + 1][::100])):
zz[i,j] = self.mesh(i, j, self.slit_data_int[self.slit][::100], self.slit_data_int[self.slit + 1][::100])
self.graph.emit(
self.slit_data_pos[self.slit][::100],
self.slit_data_int[self.slit][::100],
self.slit_data_pos[self.slit + 1][::100],
self.slit_data_int[self.slit + 1][::100],
zz
)
self.timerGraph = threading.Timer(0.2, self.emitGraph).start()
def mesh(self, i, j, intx, inty):
norm_max = max(max(intx), max(inty))
intx = [x/norm_max for x in intx]
inty = [y/norm_max for y in inty]
return intx[i]*inty[j]
正如您在下面的代码中看到的,我有两个循环来填充 zz 矩阵(最初是 7500x7500)。我现在为每个矢量仅使用 75 个点,并使用计时器每 0.2 秒显示一次。我必须这样做,因为如果我使用所有数据,填充 zz 矩阵然后绘制需要很长时间。
我绘制它的代码部分是(在另一个 object/thread 中):
def graph_update(self, slit_samples_positionsX, slit_samples_intensitiesX, slit_samples_positionsY, slit_samples_intensitiesY, zz):
# self.matplotlibWidget.axis.plot(slit_samples_positionsX, slit_samples_intensitiesX)
# self.matplotlibWidget.axis.plot(slit_samples_positionsY, slit_samples_intensitiesY)
self.matplotlibWidget.axis.imshow(zz, cmap=cm.jet)
self.matplotlibWidget.canvas.draw()
我的问题是如何改进数据计算以提高图表的分辨率和显示速率?或者有没有其他更快的方法来创建这种情节?
谢谢!
您每 0.2 秒创建一次整个网格。您还每 0.2 秒创建一个新图。这都会减慢您的应用程序。
每次调用 emitGraph
时创建一个新计时器也可能是一个问题。
为了改进您的程序,我们的想法是创建一次网格并且只更新其中的数据。
此外,只创建一次图,用新数据更新它。
原则上,numpy 在数组操作中非常非常快,在屏幕上绘制数据应该不是问题。查看您的代码,一个问题可能是您实际上大量使用列表而不是 numpy 数组。所以尽量避免列表创建和 for 循环。实际上,我认为您可以完全摆脱 mesh
功能,从而节省很多时间。
关于剧情,不要每次更新都打imshow()
!例如,如果 self.image
是您的 imshow-plot,则只需调用 self.image.set_data(zz)
如果您能提供有关您正在使用的阵列形状的更多信息并告诉我们 self.slit
是什么,我可能会为您提供更多帮助。
此时您可能还想看看下面的代码,它试图模仿光束分析仪并且运行速度非常快。
from PyQt4 import QtGui, QtCore
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import sys, time, random
class WorkerObject(QtCore.QObject):
signalStatus = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
self.data = np.zeros((600,800))
self.x = np.arange(0,800)
self.y = np.arange(0,600)
self.Y, self.X = np.meshgrid(self.x, self.y)
self.f = lambda x, y, x0, y0, sigmax, sigmay : 0.96*np.exp(-((x-x0)/float(sigmax))**2)*np.exp(-((y-y0)/float(sigmay))**2)
@QtCore.pyqtSlot()
def startWork(self):
print "StartWork"
x0 = random.randint(250,550)
y0 = random.randint(250,350)
sigmax = random.randint(100,200)
sigmay = random.randint(100,200)
while 1 > 0:
x0, y0, sigmax, sigmay = self.doWork(x0, y0, sigmax, sigmay)
def doWork(self, x0, y0, sigmax, sigmay):
dx, dy, dsx, dsy = random.randint(0,1)*2-1, random.randint(0,1)*2-1, random.randint(0,1)*2-1, random.randint(0,1)*2-1
self.data[:,:] = (self.f(self.X, self.Y, x0+dx, y0+dy, sigmax+dsx, sigmay+dsy)+np.random.random((600,800))*0.04)*(1.+np.random.rand()*0.04-0.02)
self.signalStatus.emit(self.data)
time.sleep(0.04)
return x0+dx, y0+dy, sigmax+dsx, sigmay+dsy
class App(QtGui.QMainWindow):
signalStatus = QtCore.pyqtSignal(object)
abortSignal = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(App, self).__init__(parent)
self.button_start = QtGui.QPushButton('Start',self)
self.button_cancel = QtGui.QPushButton('Cancel', self)
self.label_status = QtGui.QLabel('', self)
self.mainbox = QtGui.QWidget(self)
self.layout = QtGui.QVBoxLayout()
self.mainbox.setLayout(self.layout)
self.setCentralWidget(self.mainbox)
self.layout.addWidget(self.button_start)
self.layout.addWidget(self.button_cancel)
self.layout.addWidget(self.label_status)
self.fig = Figure((6.0, 3.0), dpi=72)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self)
self.canvastoolbar = NavigationToolbar(self.canvas, self)
self.fig.patch.set_alpha(0.0)
self.ax = self.fig.add_subplot(111)
self.x = np.arange( 0,800 ); self.y = np.arange( 0,600 )
self.im = self.ax.imshow(np.zeros((600, 800)), origin='upper', vmin=0, vmax=1 )
self.pv, = self.ax.plot( np.zeros(600) ,self.y , color="white" , alpha=0.6, lw=2 )
self.ph, = self.ax.plot( self.x ,np.zeros(800) , color="white" , alpha=0.6, lw=2)
self.ax.set_xlim([0,800]); self.ax.set_ylim([0,600])
self.layout.addWidget(self.canvas)
self.layout.addWidget(self.canvastoolbar)
self.initWorker()
def initWorker(self):
self.worker = WorkerObject()
self.worker_thread = QtCore.QThread()
self._connectSignals()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
def _connectSignals(self):
self.button_start.clicked.connect(self.worker.startWork)
self.button_cancel.clicked.connect(self.forceWorkerQuit)
self.worker.signalStatus.connect(self.updateStatus)
def forceWorkerQuit(self):
print "calculation aborted"
if self.worker_thread.isRunning():
self.worker_thread.terminate()
self.worker_thread.start()
@QtCore.pyqtSlot(object)
def updateStatus(self, obj):
self.im.set_data(obj)
argm = np.unravel_index(np.argmax(obj), (600,800))
self.pv.set_data(obj[:,argm[0]]*250, self.y)
self.ph.set_data(self.x, obj[argm[1], :]*250)
self.fig.canvas.draw()
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
编辑(回复下方评论,询问如何计算zz
):
如果我没理解错的话,你的slit_data_int[0]
是X方向的累积强度,slit_data_int[1]
是Y方向的强度。如果 zz[j,i] = slit_data_int[1][j]*slit_data_int[0][i]
那么最好使用 numpy 中的矩阵乘法 (np.outer
) 来计算:
zz[:,:] = np.outer(slit_data_int[1], slit_data_int[0])