Set_xlim, set_ylim, set_zlim commands in matplotlib cannot copy displayed data

I am creating a GUI with Tkinter and ttk and using matplotlib to create interactive stories - again, like millions of other people. Although most of the issues I have encountered so far are well documented, this seems rare:

When plotting in 3d and adjusting the axis scale using the set_lim() commands, the plotted line is superior to the coordinate system, which does not look very good. Also, I'm not happy with the frame, which seems a bit 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 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 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 press the "SET" button. There is my problem.

Edit : Added screenshot: enter image description here

+6
source share
2 answers

The problem here is that mplot3d does not have an OpenGL backend. Thus, the calculations for displaying data are based on 2d. I found the same problem here and workaround here . Although the workaround is not the best, in my opinion, because it depends on the resolution of your data.

In any case, I followed the second link . So what I'm doing now is copying the array and setting all the values ​​above and below my desired scale in NaN. When building these lines, the lines will be cut off where the data will 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 in the range from 0 to 10, and you have six data points: [-1, 3 4 12 5 1] The line will go from 3 to 4 and from 5 to 1, because -1 and 12 will be set to NaN. Improving this problem would be good. Mayavi might be better, but I have not tried this since I wanted to stick with matplotlib.

+3
source

The following code works even to represent meshgrid data:

 @ 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 cannot handle additional attributes.

+1
source

All Articles