2013-04-22 6 views
6

Sto costruendo una GUI con Tkinter e ttk e usando matplotlib per creare grafici interattivi - di nuovo, come fanno milioni di altre persone. Anche se la maggior parte dei problemi che ho incontrato finora sono ben documentati, questo sembra raro:set_xlim, set_ylim, set_zlim comandi in matplotlib non riescono a ritagliare i dati visualizzati

Quando la stampa in 3d e regolando la scala dell'asse con set_lim() comandi in seguito, la linea tracciata supera la coordinata-sistema che sembra non buona. Inoltre, non sono contento della cornice che sembra essere un po 'piccola. Ecco un esempio:

# 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() 

Basta eseguire il codice e premere il pulsante "SET". C'è il mio problema

Edit: Aggiunto Screenshot: enter image description here

+0

Per essere chiari, i limiti stanno cambiando come ci si aspetta, ma gli artisti non sono tagliati ai bordi degli assi? Se questo è il caso, penso che questo sia un modo strano in cui MPL fa grafici 3D ed è tecnicamente il comportamento corretto. Se hai bisogno di un vero rendering 3D, guarda a 'mayavi' (basato su opengl) dalle persone enthought. – tacaswell

+0

@tcaswell: Sì, hai ragione. La mia ricerca è venuta fuori con 'mayavi'as bene. La mia GUI utilizza la barra degli strumenti da matplotlib e vorrei comunque mantenere queste opzioni. Ho trovato un altro modo per "risolvere" il mio problema. Comunque non è perfetto (affatto). – lyvic

risposta

3

Il problema qui è che non ha mplot3d backend OpenGL. I calcoli per la visualizzazione dei dati sono quindi basati su 2d. Ho trovato lo stesso problema here e una soluzione alternativa here. Anche se la soluzione alternativa non è la migliore secondo me, dipende dalla risoluzione dei dati.

Ho seguito the second link in ogni caso. Quindi, quello che sto facendo ora è copiare l'array e impostare tutti i valori sopra e sotto la mia scala desiderata su NaN. Quando si tracciano quelli, le linee verranno tagliate dove i punti dati superano il limite desiderato.

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) 

Se adesso la tua scala è impostato da 0 a 10 e si dispone di sei datapoints: [-1, 3 4 12 5 1] La linea andrà da 3 a 4 e 5 a 1, perché -1 e 12 verrà impostato su NaN. Un miglioramento riguardo a quel problema sarebbe buono. Mayavi potrebbe essere migliore, ma non ho provato questo perché volevo rimanere con matplotlib.

1

Seguendo codice funziona anche per la rappresentazione dei dati meshgrid:

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

z = clip_z_data(z) 

min_z e Z_MAX sono globali, a causa vectorize non può gestire attributi extra.

Problemi correlati