简体   繁体   English

matplotlib中的set_xlim,set_ylim,set_zlim命令无法裁剪显示的数据

[英]set_xlim,set_ylim,set_zlim commands in matplotlib fail to clip displayed data

I'm building a GUI with Tkinter and ttk and using matplotlib in order to creat interactive plots - again, like millions other people do. 我正在用Tkinter和ttk构建一个GUI,并使用matplotlib来创建交互式绘图-就像数百万其他人一样。 Even though most problems I encountered so far are well documented, this one seems rare: 即使到目前为止,我遇到的大多数问题都有详细的记录,但这种情况似乎很少见:

When plotting in 3d and adjusting the axis scale with set_lim() commands afterwards, the plotted line exceeds the coordinate-system which looks not good. 在3d中进行绘制并随后使用set_lim()命令调整轴比例时,绘制的线超出了看起来不太好的坐标系。 Also, I'm not happy with the frame that seems to be a little to small. 另外,我对看起来有点小的框架也不满意。 Here is an example: 这是一个例子:

# Missmatch.py
"""Graphical User Interface for plotting the results
calculated in the script in Octave"""

# importing libraries
import matplotlib, ttk, threading
matplotlib.use('TkAgg')
import numpy as nm
import scipy as sc
import pylab as pl
import decimal as dc
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
from oct2py import octave as oc
import Tkinter as tki

class CS:
    """CS - Controlset. This part creates the GUI with all important
    Elements. Major changes and calculations will be executed 
    in the Calculation-Class in a seperate thread. This prevents the 
    GUI from hanging"""

    def __init__(self,parent):
        """Building the main GUI"""
        self.ThisParent=parent
        ### Entire Window
        # Mainframe that contains everything.
        self.main=tki.Frame(parent) 
        # Pack manager to expand the mainframe as the windowsize changes.
        self.main.pack(fill=tki.BOTH, expand=tki.YES)
        # Configure the grid of the mainframe so that only the top left
        # cell grows if the users expands the window.
        self.main.grid_rowconfigure(0, weight=1)
        self.main.grid_rowconfigure(1, weight=1)


        ### Canvas for drawings
        # Creating a figure of desired size
        self.f = Figure(figsize=(6,6), dpi=100)
        # Creating a canvas that lives inside the figure
        self.Paper=FigureCanvasTkAgg(self.f, master=self.main)
        # Making the canvas's drawings visible (updating)
        self.Paper.show()
        # positioning the canvas
        self.Paper.get_tk_widget().grid(row=0,rowspan=3, column=0, sticky='NSWE')
        # creating a toolbarframe for options regarding the plots
        self.toolbarframe=tki.Frame(self.main)
        self.toolbarframe.grid(row=3, column=0, sticky='NWE')
        # Creating a toolbar for saving, zooming etc. (matplotlib standard)
        self.toolbar = NavigationToolbar2TkAgg(self.Paper, self.toolbarframe)
        self.toolbar.grid(row=0,column=0, sticky='NWE')
        # setting the standard option on zoom
        self.toolbar.zoom()



        ### Axis configuration toolbar
        # A frame containing the axis config-menu
        self.axisscaleframe=tki.Frame(self.main)
        self.axisscaleframe.grid(row=5, column=0, sticky='SNEW')
        # In that Frame, some Entry-boxes to specify scale
        self.xaxisscalef=ttk.Entry(self.axisscaleframe, width=10)
        self.xaxisscalef.insert(0,0)
        self.xaxisscalet=ttk.Entry(self.axisscaleframe, width=10)
        self.xaxisscalet.insert(0,15)
        self.yaxisscalef=ttk.Entry(self.axisscaleframe, width=10)
        self.yaxisscalef.insert(0,0)
        self.yaxisscalet=ttk.Entry(self.axisscaleframe, width=10)
        self.yaxisscalet.insert(0,15)
        self.zaxisscalef=ttk.Entry(self.axisscaleframe, width=10)
        self.zaxisscalef.insert(0,0)
        self.zaxisscalet=ttk.Entry(self.axisscaleframe, width=10)
        self.zaxisscalet.insert(0,15)
        # And some Labels so we know what the boxes are for
        self.xaxlab=ttk.Label(self.axisscaleframe, text='X-Axis', width=10)
        self.yaxlab=ttk.Label(self.axisscaleframe, text='Y-Axis', width=10)
        self.zaxlab=ttk.Label(self.axisscaleframe, text='Z-Axis', width=10)
        self.axinfolab=ttk.Label(self.axisscaleframe, text='Adjust axis scale:')
        # And a Button to validate the desired configuration
        self.scaleset=ttk.Button(self.axisscaleframe, text='Set', command=self.SetAxis2)
        self.scaleset.bind('<Return>', self.SetAxis)
        # Let's organize all this in the axisscaleframe-grid
        self.axinfolab.grid(row=0, column=0, sticky='W')
        self.xaxlab.grid(row=1, column=0, sticky='W')
        self.yaxlab.grid(row=2, column=0, sticky='W')
        self.zaxlab.grid(row=3, column=0, sticky='W')
        self.xaxisscalef.grid(row=1,column=1, sticky='W')
        self.yaxisscalef.grid(row=2,column=1, sticky='W')
        self.xaxisscalet.grid(row=1,column=2, sticky='W')
        self.yaxisscalet.grid(row=2,column=2, sticky='W')
        self.zaxisscalef.grid(row=3,column=1,sticky='W')
        self.zaxisscalet.grid(row=3,column=2,sticky='W')
        self.scaleset.grid(row=3,column=3,sticky='E')


    def SetAxis(self,event):
        self.SetAxis2()

    def SetAxis2(self):
        self.x1=float(self.xaxisscalef.get())
        self.x2=float(self.xaxisscalet.get())
        self.y1=float(self.yaxisscalef.get())
        self.y2=float(self.yaxisscalet.get())
        self.z1=float(self.zaxisscalef.get())
        self.z2=float(self.zaxisscalet.get())
        self.a.set_xlim(self.x1, self.x2)
        self.a.set_ylim(self.y1, self.y2)
        self.a.set_zlim(self.z1, self.z2)
        self.Paper.show()
        print "Set axis"



class Calculate3D(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.x=range(100)
        self.y=range(100)
        self.z=range(100)
        print 'Done!'
        controlset.a = controlset.f.add_subplot(111, projection='3d')
        controlset.a.clear()
        controlset.a.plot(self.x,self.y,self.z)
        controlset.a.mouse_init()
        controlset.a.set_xlabel('X')
        controlset.a.set_ylabel('Y')
        controlset.a.set_zlabel('Z')
        controlset.a.set_title('Title')
        controlset.Paper.show()
        return


mainw=tki.Tk()
mainw.title("Example")
mainw.geometry('+10+10')
controlset=CS(mainw) 
#for this example code, we run our Calculate3D class automatically
CL=Calculate3D()
CL.run()

mainw.mainloop()

Just run the code, and hit the "SET" Button. 只需运行代码,然后点击“ SET”按钮。 There is my problem. 这是我的问题。

Edit : Added Screenshot: 编辑 :添加了截图: 在此处输入图片说明

The Problem here is, that mplot3d has no OpenGL backend. 这里的问题是,mplot3d没有OpenGL后端。 The calculations for displaying the data are thus based on 2d. 因此,用于显示数据的计算基于2d。 I found the same issue here and a workaround here . 我在这里找到了相同的问题并在这里找到了解决方法。 Even though the workaround is not the best in my opinion because it depends on the resolution of your data. 即使在我看来,这种解决方法也不是最好的,因为它取决于您数据的分辨率。

I followed the second link anyway. 无论如何,我遵循了第二个链接 So, what I'm doing now is copying the array and setting all the values above and under my desired scale to NaN. 因此,我现在要做的是复制数组,并将所需缩放以上和之下的所有值都设置为NaN。 When plotting those, the lines will be cut off where the datapoints exceed the desired limit. 在绘制这些图形时,将在数据点超出所需限制的地方切断线。

def SetAxis2(self):
    self.dummyx=CL.x*1
    self.dummyy=CL.y*1
    self.dummyz=CL.z*1
    #clipping manually
    for i in nm.arange(len(self.dummyx)):
        if self.dummyx[i] < self.x1:
            self.dummyx[i] = nm.NaN
        else:
            pass

    for i in nm.arange(len(self.dummyy)):
        if self.dummyy[i] < self.y1:
            self.dummyy[i] = nm.NaN
        else:
            pass

    for i in nm.arange(len(self.dummyz)):
        if self.dummyz[i] < self.z1:
            self.dummyz[i] = nm.NaN
        else:
            pass     

    controlset.a.plot(self.dummyx,\
    self.dummyy,\
    self.dummyz)

    self.a.set_xlim3d(self.x1, self.x2)
    self.a.set_ylim3d(self.y1, self.y2)
    self.a.set_zlim3d(self.z1, self.z2)

If now your scale is set from 0 to 10 and you have six datapoints: [-1, 3 4 12 5 1] The line will go from 3 to 4 and 5 to 1 because -1 and 12 will be set to NaN. 如果现在将比例尺设置为0到10,并且有六个数据点: [-1, 3 4 12 5 1]该行将从3变为4,从5变为1,因为-1和12将被设置为NaN。 An improvement regarding that problem would be good. 关于该问题的改进将是不错的。 Mayavi might be better, but I haven't tried this as I wanted to stick with matplotlib. Mayavi可能会更好,但是我没有尝试过,因为我想坚持使用matplotlib。

Following code works even for meshgrid data representation: 以下代码甚至适用于网格数据表示:

@ numpy.vectorize
def clip_z_data(z):
  return z if Z_MIN <= z <= Z_MAX else n.nan

z = clip_z_data(z)

Z_MIN and Z_MAX are global, because vectorize can't handle extra attributes. Z_MIN和Z_MAX是全局的,因为矢量化无法处理额外的属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM