我目前正在尝试基于matplotlib(Python 2.7)创建一个简单的GUI。我的目标是绘制一个2d强度图,并使用用户控制光标查看x和y切片。
它已经按照我想要的方式工作了。(见下面的例子)。但似乎有一个主要的限制,在程序中给出的数组大小,我可以使用。如果数组中的条目超过几百万个,进程就会开始滞后。我想原因是我在诅咒者移动的过程中重新绘制了所有的图形。
是否有一个选项,我可以只重绘光标和切片,但不重绘强度贴图?我什么都没找到。或者,除了使用像Tkinter这样的真实的GUI编写游标之外,还有其他选择吗?
在下面的示例中,光标的初始位置为00。如果您在光标位置附近按下鼠标右键,并一直按下,直到您将光标移动到所需位置并释放按钮,则将跟随鼠标移动。
# -*- noplot -*-
#from __future__ import print_function
import matplotlib.pyplot as plt
import numpy as np
class Cursor(object):
"""
creates a GUI object that plots given 2d data with imshow and creates a curser
which can be moved by drag and drop. The horizontal and vertical line of the curser
are giving the position of a sliced trough the 2d-data and are plotted separately.
"""
def __init__(self,data,scale_x,scale_y):
self.motion=False
self.data=data
self.scale_x=scale_x
self.scale_y=scale_y
self.create_fig()
# text location in axes coords
self.txt = self.ax1.text(0.7, 0.9, '', transform=self.ax1.transAxes)
self.create_events()
# print self.range_x,self.range_y
# print
# print
def create_events(self):
"""
Handles user events
"""
self.cid1=plt.connect('motion_notify_event', self.mouse_move)
self.cid2=plt.connect('button_press_event', self.mouse_press)
self.cid3=plt.connect('button_release_event', self.mouse_release)
def create_fig(self):
"""
Creates the GUI, initializes the cursers at minimum of the axes and plots the 2d-data
"""
#Create figure and axes
f=plt.figure(dpi=150)
self.ax1=f.add_subplot(221)
self.ax2=f.add_subplot(223,sharex=self.ax1)
self.ax3=f.add_subplot(222,sharey=self.ax1)
# plot in ax1
self.ax1.imshow(self.data,interpolation='none',aspect='auto',extent=[np.min(self.scale_x),np.max(self.scale_x),np.min(self.scale_y),np.max(self.scale_y)])
#Creates the limits
self.ax1.axis([np.min(self.scale_x),np.max(self.scale_x),np.min(self.scale_y),np.max(self.scale_y)])
self.ax3.set_xlim(np.min(self.data),np.max(self.data))
self.ax2.set_ylim(np.min(self.data),np.max(self.data))
#Create Curser @ minimum-minimum position of the axes
self.lx = self.ax1.axhline(color='k') # the horiz line
self.ly = self.ax1.axvline(color='k') # the vert line
self.lx.set_ydata(np.min(self.scale_y))
self.ly.set_xdata(np.min(self.scale_x))
#Creates sliced plots @ initial values of the curser
# the change of scale needs to be considered therefore
# the program checks for the minimum difference between curser pos and self.scale_... entries
# and uses the position of the entry to slice the data array
self.slice_y,=self.ax3.plot(np.flipud(self.data[:,np.argmin(np.abs(self.scale_x-self.ly.get_xdata()))]),self.scale_y)
self.slice_x,=self.ax2.plot(self.scale_x,self.data[np.shape(self.scale_y)-np.argmin(np.abs(self.scale_y-self.lx.get_ydata()))-1,:][0])
# garanties fixed distances beetween the plots
plt.tight_layout()
def sliced_vertical(self,ax):
#gets the sliced vertical sliced data
self.slice_y.set_xdata(np.flipud(self.data[:,np.argmin(np.abs(self.scale_x-self.ly.get_xdata()))]))
def sliced_horizontal(self,ax):
#gets the horizontal sliced data
self.slice_x.set_ydata(self.data[np.shape(self.scale_y)-np.argmin(np.abs(self.scale_y-self.lx.get_ydata()))-1,:])
def cursermovement(self,event):
"""
tracks the curser movement and if a left click appeard near the curser
the curser will folow the motion
"""
if not event.inaxes:
return
if self.motion:
x, y = event.xdata, event.ydata
# update the line positions
self.lx.set_ydata(y)
self.ly.set_xdata(x)
#update the text
self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
#update the sliced data
self.sliced_vertical(self.ax2)
self.sliced_horizontal(self.ax3)
#replot everything
plt.draw()
def mouse_move(self, event):
self.cursermovement(event)
def mouse_press(self,event):
#check range for moving the cursers here in case of zoom in or out
self.range_x=np.abs(self.ax1.get_xlim()[0]-self.ax1.get_xlim()[1])/20
self.range_y=np.abs(self.ax1.get_ylim()[0]-self.ax1.get_ylim()[1])/20
# check if click occurred near cursor
if (self.ly.get_xdata()+self.range_x>event.xdata>self.ly.get_xdata()-self.range_x) or (self.lx.get_ydata()+self.range_y>event.ydata>self.lx.get_ydata()-self.range_y):
self.motion=True
#curser jumps without motion to the mouse
self.cursermovement(event)
def mouse_release(self,event):
#checks if rigth mouse button was released
self.motion=False
"""
program starts here
"""
# define the plot range in x and y axes and change array size
t = np.arange(0.0, 40.0, 0.01)
t2 = np.arange(0.0, 20.0, 0.01)
#create a 2d grid to create the intensity map
t_x,t_y=np.meshgrid(t,t2)
#create the intensity map
s = 10*np.sin(0.1*np.pi*(t_x))*np.sin(0.5*np.pi*t_y)+t_x+t_y
#create the Gui class
cursor = Cursor(s,t,t2)
plt.show()
2条答案
按热度按时间fcg9iug31#
Matplotlib提供了一个名为
Cursor
的小部件。您已经可以将其用于热图图中的线。这可以使用blitting来避免总是重画画布,要更新旁边的图,您可以使用相同的blitting技术,但需要手动实现。这样,只有在移动鼠标时改变的线条将被更新,因此整个交互体验更加流畅。
如果你只想在点击时移动光标,而不是移动鼠标,你可以断开它的事件并连接一个新的
button_press_event
。代码的相关部分则为yhxst69z2#
我希望光标是移位超过2D强度图,然后离开他在某些点,所以我可以观看切片,也他们的变化在一个顺利的过程中。
这是我目前的问题解决方案。光标就像之前在左下角初始化,你需要在那个位置单击鼠标拖动到指定的位置,在那里你释放鼠标左键。我现在也实现了我自己的curser blitting。它是以类似于matplotlib游标的水平线和垂直线来完成的。我现在还补充说,你可以改变cursers的数量。在下面的示例中,它的5个(所有这些都在底部左角产生)如果你有一个更好的想法或实现,我会很高兴在这里它。不过,我欠你,因为它现在运行smoothley与arrray多达10万条目:)我的下一步是添加义务天使诅咒
Find a picture here
代码如下:
问候辛索拉斯