Sie sind auf Seite 1von 5

DESARROLLO Infografa - VTK

Visualizacin Grfica 3D con VTK (Visualization Toolkit) (2 parte)

INTERFAZ GRFICA 3D

Vemos como crear una Interfaz Grfica de Usuario (GUI) que contenga un rea de renderizado y diferentes widgets que permitan modificar,propiedades de la escena, de los actores, etc. POR ANA M. FERREIRO Y JOS A. GARCA

n el nmero 6 de Linux Magazine, vimos como crear una escena de renderizado en la que incluimos diferentes actores, a los que modificamos sus propiedades. Sin embargo, lo mejor es tener un Interfaz Grfico (GUI) que permita a cualquier usuario, sin necesidad de entender el cdigo, modificar cualquier propiedad de los actores, del rea de renderizado, etc. Hoy por hoy, existen diferentes toolkits de ventanas que se pueden importar desde Python, y que adems constan de un widget especfico para contener un rea de renderizado de VTK. Entre las diferentes herramientas grficas, cabe destacar Tkinter, WxPython, PythonQT y Motif; siendo TKinter la opcin por la que nos hemos decidido, puesto que por defecto se incluye en cualquier instalacin de Python. En lo que sigue veremos los pasos necesarios para incluir una escena de VTK dentro de una GUI generada con TkInter, para terminar incluyendo dos botones, uno que nos permita modificar alguna propiedad del objeto que haya-

mos colocado en nuestra escena y otro que capture el contenido de la escena para guardarla en una imagen .tiff. En el Listado 5, tenis escrito el cdigo completo. Lo mejor es que cada vez que tengis dudas lo miris, as os va a ser ms fcil seguir cada uno de los pasos que se os proponen.

Interpretacin de una GUI


En el nmero 6 de la revista vimos que al crear una escena VTK se abre una ventana de renderizado, que permite la interaccin con el ratn. Sin embargo, dicha escena ocupa la totalidad de la ventana. Al combinar VTK con TkInter podremos colocar la escena de VTK en un panel concreto de la GUI, segn nos interese. Para que resulte sencillo diferenciar los mtodos propios de la GUI de los mtodos relacionados con el renderizado de la escena, vamos a organizar el trabajo siguiendo una estructura de clases. Si lo pensis bien, una GUI que contenga un rea de renderizado (Figura

1), se puede comparar con un televisor. El televisor con los botones y la pantalla, lo podis imaginar como la interfaz de usuario que contiene el rea de renderizado; mientras que lo que vemos, segn el canal que se sintonice, va a ser la escena de VTK. Todo esto nos lleva a organizar, de un modo natural, nuestro programa en dos clases (Figura 2): 1) class MyGUI: clase que controla la estructura de la GUI, es decir, los botones, los paneles, los mens, los eventos asociados a los widgets, etc. 2) class RenderWindow: clase que controla el rea de visualizacin, la escena, los actores, las propiedades de los objetos, la cmara, el renderizado, los eventos asociados al rea de renderizado, etc. NOTA: Para mejor seguir este artculo, conviene tener a manos el cdigo completo del programa, que se puede descargar desde [6].

Creacin GUI TKInter


Lo primero de todo es importar Tkinter y VTK,

62

Nmero 08

WWW.LINUX- MAGAZINE.ES

Infografa - VTK DESARROLLO

import vtk import Tkinter

La interfaz la vamos a organizar en dos frames: fr_renwin donde se colocar la ventana de renderizado y fr_gui que contendr los diferentes botones. Inicializamos Tkinter, creando una ventana padre root, root=Tkinter.Tk(), en la que colocaremos los frames fr_renwin y fr_gui. Es necesario que nuestra ventana permanezca abierta hasta que el usuario decida cerrarla, lo que se logra mediante root.mainloop(). Segn la organizacin que hemos propuesto, tenemos la clase MyGUI, que se ocupa de los widgets de la interfaz,
class MyGUI:

Creamos la clase RenderWindow que se va a encargar de todo lo relacionado con la visualizacin, es decir, con el renderizado, el control de la cmara y las luces, el tipo de interaccin del usuario con la escena, etc.
class RenderWindow:

Figura 1: Estructura de una GUI con una ventana de renderizado.

En el constructor de la clase es necesario indicarle quien va a ser el contenedor

Listado 1: Constructor clase MyGUI


01 def __init__(self,parent): 02 self.parent=parent 03 parent.title("Mi GUI") 04 fr_renwin = Tkinter.Frame(parent,bg="gray" ) 05 fr_bot= Tkinter.Frame(parent) 06 fr_renwin.pack(side="top", anchor="n", 07 expand=1, fill="both") 08 fr_bot.pack(side="bottom", anchor="s", 09 expand="t", fill="x") 10 lsup = Tkinter.Label( fr_renwin, 11 text="Frame de la escena",fg="red") 12 lsup.pack(side="top", expand="t") 13 14 l1 = Tkinter.Label(fr_bot, text="Frame de botones") 15 l1.pack(side="top", expand="t", fill="x") 16 parent.update()

padre, root, en el que colocamos los frames fr_renwin y fr_bot, en la parte superior y en la parte inferior, respectivamente. Esta organizacin se corresponde con el cdigo del Listado 1. Para instanciar la clase MyGUI, debemos hacerlo indicando que root va a ser la ventana padre que contenga los diferentes widgets que vamos a ir creando. Esto se indica escribiendo las siguientes lneas de cdigo.
root = Tkinter.Tk() app=MyGUI(root) root.minsize(350, 300) root.mainloop()

Guardad este cdigo en un fichero llamado, miguivtk.py, por ejemplo. Si lo ejecutis (python miguivtk.py), se os abre una ventana con los dos frames (ver Figura 3).

Widget vktRenderWindow
La clase vtkTkRenderWidget es un widget de Tk, que permite renderizar en su interior. Mediante el mtodo GetRenderWindow se devuelve una ventana de renderizado, vtkRenderWindow, a la que se le puede asociar un rea de renderizado vtkRenderer. Este widget puede reaccionar a los eventos (de ratn, de teclado) como cualquier otro widget de Tk. Para poder utilizar este widget necesitamos en primer lugar importarlo,
import vtkRenderWidget

El constructor de esta clase se va a encargar de instanciar vtkTkRenderWidget, crear el rea de renderizado y sus propiedades. Recordad que vtkTkRenderWidget es un tipo de widget de Tk, por tanto, es necesario decirle cual va a ser su widget padre, pasndolo como argumento cada vez que instanciemos nuestra clase RenderWindow. En el Listado 2 tenis el cdigo correspondiente a dicho constructor. Instanciamos la clase vtkTkRenderWidget para crear un widget de Tk (que hemos llamado self.vtktkwidget), indicando que su padre es parent. Mediante self.ren = vtkpython.vtkRenderer() se crea un rea de renderizado de VTK, que aadimos a la ventana de renderizado que tiene asociada el widget self.vtktkwidget. A dicha ventana se accede mediante el mtodo GetRenderWindow. Mediante el mtodo TwoSidedLightingOn colocamos dos luces encendidas en nuestra escena. El mtodo GetActiveCamera() permite obtener la cmara que por defecto viene incluida en la escena. Si ejecutamos de nuevo el cdigo nada ha cambiado! La razn es obvia: Desde la GUI no hemos instanciado nuestra clase RenderWindow. En el constructor

WWW.LINUX- MAGAZINE.ES

Nmero 08

63

DESARROLLO Infografa - VTK

(__init__) de la clase MyGUI, debemos instanciar la clase RenderWindow. Basta aadir en dicho constructor las sigientes lneas de cdigo
self.renwin=U RenderWindow(fr_renwin)

Fijaos que como argumento pasamos fr_renwin, esto es porque el widget que contiene el rea de renderizado lo situamos dentro de dicho frame. Ahora la ejecucin ha cambiado. Tenemos un rea de color azul, que es el rea de renderizado de la escena

Listado 3: Mtodos clase RenderWindow para crear una esfera.


01 def create_esfera(self): 02 esfera = vtk.vtkSphereSource() 03 esferaMapper = vtk.vtkPolyDataMapper() 04 esfera.SetPhiResolution(10) 05 esfera.SetThetaResolution(20) 06 esfera.SetCenter(0.3,0.0,0.0) 07 esferaMapper.SetInput(esfera.G etOutput()) 08 esferaActor = vtk.vtkActor() 09 esferaActor.SetMapper(esferaMa pper) 10 esferaActor.GetProperty().SetC olor(0.7,0.0,0.25) 11 esferaActor.GetProperty().SetO pacity(1) 12 esferaActor.GetProperty().SetL ineWidth(1) 13 return esferaActor 14 15 16 def add_esfera(self): 17 self.esfera_actor=self.create_esfera() 18 self.add_actor(self.esfera_actor) 19 self.render_window()

(ver Figura 4). Seguro que os preguntaris, cmo s yo que es un rea de renderizado y no un simple frame de color azul? Por el momento no hemos creado ningn actor para que se aprecie la diferencia, sin embargo, si cerramos la ventana obtenemos el siguiente warning: A TkRenderWidget is being destroyed before its associated vtkRenderWindow is destroyed. Dicho mensaje, que antes no tenamos, nos avisa de que debemos destruir la ventana de renderizado antes que el widget que la contiene, porque en ciertas ocasiones podran quedar procesos corriendo sin que lo apreciemos. Antes de nada, debemos decirle a nuestra GUI qu hacer cuando se activa el protocolo WM_DELETE_WINDOW (define lo que ocurre cuando el usuario explcitamente cierra la ventana mediante el botn cerrar). En el constructor de la clase MyGUI debemos escribir,
parent.protocol(U "WM_DELETE_WINDOW", self.quit)

Figura 2: Clases del programa.

self.renwin.Render() def resetcamera(self,evt=None): self.ren.ResetCamera() self.renwin.Render()

El mtodo que estamos llamamos al cerrar la ventana es self.quit,


def quit(self): self.parent.quit()

Figura 3: Ventana de Tkinter con dos frames.

En la escena vamos a situar una esfera; para ello, dentro de la clase RenderWindow incluimos, las funciones create_esfera y add_esfera del Listado 3. El mtodo create_esfera construye una esfera segn los mtodos propor-

Listado 2:Constructor clase RenderWindow.


01 def __init__(self, parent): 02 self.vtktkwidget = vtkRenderWidget.vtkTkRenderWid get (parent) 03 self.vtktkwidget.pack (expand='true',fill='both') 04 self.ren = vtk.vtkRenderer () 05 self.ren.TwoSidedLightingOn () 06 self.renwin = self.vtktkwidget.GetRenderWind ow () 07 self.renwin.AddRenderer (self.ren) 08 self.camera = self.ren.GetActiveCamera() 09 self.ren.SetBackground(0.1, 0.1, 0.9)

Volvamos a ejecutar el cdigo. Fijos que ya no aparece ninguna advertencia al cerrar la ventana. De este modo destruimos los distintos objetos instanciados en el orden correcto.

Actores a Escena
En este punto ya tenemos todo lo necesario para incluir actores en la escena. En la clase RenderWindow tenemos que crear los siguientes mtodos: (1) add_actor: aade un actor a la escena; (2) render_window: se ocupa del renderizado de la escena; (3) resetcamera: se ocupa de resetear la cmara y renderizar la escena, permitiendo ver la totalidad de la escena. Basta escribir las siguientes lneas de cdigo,
def add_actor(self,nameactor): self.ren.AddActor(nameactor) def render_window(self):

64

Nmero 08

WWW.LINUX- MAGAZINE.ES

Infografa - VTK DESARROLLO

cionados por VTK (en el nmero 6 explicamos el modo de crear un actor y acceder a sus propiedades), devolviendo el actor esferaActor. El mtodo add_esfera incluye todos los comandos necesarios para colocar la esfera dentro de nuestra escena. Llamemos a la funcin add_esfera, dentro del constructor de la clase RenderWindow,
self.add_esfera()

Si habis seguido todos los pasos, deberais estar viendo dentro de la escena

Listado 4: Deslizable y Botones GUI s01


.

02 . 03 . 04 self.transp_valor = Tkinter.IntVar() 05 self.transp_valor.set(100) 06 s_transp = Tkinter.Scale(fr_bot,from_=0,t o=100, 07 orient="horizontal", 08 variable=self.transp_valor, 09 label="Transparencia") 10 s_transp.pack(side="top", fill="x", expand="false") 11 s_transp.bind("<Button-1>",lam bda e:self.ModifyTransp(e)) 12 s_transp.bind("<B1-Motion>",la mbda e:self.ModifyTransp(e)) 13 14 bsave= Tkinter.Button(fr_bot, text="Guardar Imagen", 15 command=self.guardar_imagen) 16 bsave .pack(side="top", expand="t") 17 bquit = Tkinter.Button(fr_bot, text="Salir", 18 command=self.quit) 19 bquit .pack(side="top", expand="t") 20 . 21 . 22 .

los actores, la escena, una esfera, igual que la etc. de la Figura 5. Seguro que Dentro de nuestra venalguien se est preguntantana vamos a incluir, en do: Por qu no veo la el frame fr_bot, los esfera en su totalidad? siguientes widgets: un Recordad que el mes deslizable (Tkinter.Scale) anterior, explicamos que para controlar la transpacuando situamos un actor rencia de la esfera; un en la escena, no se modibotn que nos permita fica la posicin de la guardar la escena en una cmara. Por tanto, si queFigura 4: GUI con un rea imagen en formato .tiff y remos ver la escena en su de renderizado. un botn para cerrar la totalidad basta resetear aplicacin. Escribamos en la camara. Como somos el constructor (__init__) previsores, ya habamos de la clase MyGUI, las creado el mtodo resetcalneas de cdigo del mera para dicha finaliListado 4. dad. Basta escribir El deslizable s_transp self.resetcamara(), justo llama al mtodo despus de self.add_esfeself.ModifyTransp cuando ra(). Ahora ya vemos la se pulsa el botn izquieresfera centrada en la escedo del ratn (evento na (Figura 6). <Button-1>) o se desplaRecordaris que dentro Figura 5: GUI con una esceza el ratn con dicho de la ventana de renderina que contiene una esfera botn presionado (evento zado se puede interactuar como actor. <B1-Motion>). Al clickecon el ratn. Probad a ar con el ratn sobre el botn bsave, se trasladar la esfera, hacer zoom, etc. No llama al mtodo self.guardar_imagen; os resulta ms sencillo mover la esfera mientras que si se pulsa bquit se ejecta que en los ejemplos que vimos en el self.quit. El mtodo self.quit ya lo tenenmero 6? Si lo comparis, os daris mos, as basta escribir el cdigo correscuenta de que el actor se traslada y rota pondiente para los otros dos mtodos. de un modo diferente. La razn de esto Antes de detallar cada mtodo por sepaes que se pueden modificar el modo de rado, debemos comprobar que la estrucinteractuar con el ratn dentro de una tura de nuestra GUI es correcta. Para no ventana de renderizado de VTK. En este tener problemas a la hora de probar el caso concreto, la clase programa, por el momento escribid, vtkTkRenderWidget ya crea una ventana de renderizado donde el tipo de interaccin con el ratn ya viene modificado, para que podamos controlar mejor los diferentes actores.

Control de Escena
Hasta ahora lo que hemos implementado nos permite abrir una ventana de TkInter que contiene un rea de renderizado, cuya escena ya contiene una actor. Pero la finalidad de crear una interfaz grfica, es que mediante widgets podamos controlar, en la medida de lo posible,

WWW.LINUX- MAGAZINE.ES

Nmero 08

65

DESARROLLO Infografa - VTK

def ModifyTransp(self,U evt=None): pass def guardar_imagen(self,U evt=None): pass

Ahora la GUI tendra que tener la estructura de la Figura 7, donde el nico botn que funciona es el de Salir. Para implementar el mtodo guardar_imagen de la clase MyGUI va a ser necesario acceder, desde la clase MyGUI, a la ventana de renderizado (renwin) de la clase RenderWindow. Para ello,dentro de RenderWindow escribimos el siguiente mtodo,
def get_renwin(self): return self.renwin

El Listado 5 contiene el cdigo que nos permite guardar la escena en una imagen, de formato .tiff. La clase vtkWindowToImageFilter es un filtro que convierte RenderWindows o ImageWindows al formato de una imagen; produciendo una imagen de la escena de renderizado. La salida de este filtro se pasa como argumento a la clase vtkTIFFWriter, que se ocupa de escribir la imagen en formato TIFF. Mediante writer.SetFileName("myscene.tif") indicamos el nombre del fichero en el que vamos a guardar la imagen.

Tened en cuenta, que Mediante self.transp_ para generar la imagen, valor.get() se obtiene el es necesario tener perminuevo valor del Slice sos de escritura en el s_transp cada vez que se directorio donde se vaya modifica ante un evento a guardar; en caso condel ratn. Fijos que los trario nos saldra un valores del deslizable varerror en la ejecucin avian entre 0 y 100, sin sando de que no es posiembargo, el mtodo que ble generar dicha imamodifica la transparencia gen. slo admite valores entre 0 Figura 6: Recolocacin de Probad de nuevo el y 1, por eso dividimos la cmara. programa y pulsad el valor_transp por 100. botn Guardar Imagen. Despus se llama al mtoEn principio parece que do modify_opacity_esfera no hemos hecho nada, de la clase RenderWindow. pero en el mismo directoSi habis seguido todos rio de trabajo tenis un los pasos, comprobaris fichero llamado myscecomo se modifica la transne.tif, donde se almaceparencia de la esfera a na exactamente la ltima medida que varan los escena que hemos rendevalores del deslizable. rizado. Ahora que ya sabis todo Recordad que nos falta lo necesario para crear una implementar el cdigo GUI con una ventana de Figura 7: GUI con deslizable para modificar la transparenderizado de VTK, podis y botones. rencia de nuestra esfehacer todas las pruebas que ra. En la clase RenderWindow incluise os ocurran, como por ejemplo: modifimos el mtodo car propiedades de la escena, controlar modify_opacity_esfera(self,valor), donde mediante widgets diferentes actores, etc. s valor vara entre 0 y 1, porque es el argumento que vamos a utilizar para fijar la RECURSOS opacidad. Escribamos las siguientes lne[1] Kitware. VTK: http://www.kitware.org as de cdigo,
[2] Python: http://www.python.org

def modify_opacity_esferaU (self, valor): self.esfera_actor.GetProperty() U .SetOpacity(valor) self.render_window()

[3] Enthought. Scientific python: http:// www.scipy.org [4] Rediris Espaa:ftp://ftp.rediris.es/ [5] MayaVi: http://mayavi.sourceforge.net [6] Descarga de los Listados completos correspondientes a este artculo: http://www.linux-magazine.es/ Magazine/Downloads/08

Listado 5: Mtodo Guardar Imagen de Tipo .tiff


01 def guardar_imagen(self,evt=None): 02 w2imgfil = vtk.vtkWindowToImageFilter() 03 writertiff = vtk.vtkTIFFWriter() 04 w2imgfil .SetInput(self.renwin.get_renwin()) 05 w2imgfil .Update() 06 writertiff.SetInput(w2imgfil .GetOutput()) 07 writertiff.SetFileName("myscene.tif") 08 self.renwin.render_window() 09 writertiff.Write()

def ModifyTransp(self,evtU =None): valor_transp=U self.transp_valor.get() valor_transp=U valor_transp/100.0 self.renwin.U modify_opacity_esferaU (valor_transp)

LOS AUTORES

Queremos que cada vez que deslizamos el widget s_transp, vare la transparencia de nuestro actor. Para ello, dentro de la clase MyGUI escribimos el siguiente cdigo correspondiente al mtodo ModifyTransp,

Ana M. Ferreirro Ferreiro es matemtica, pero su verdadera pasin es la informtica. As que parte de su tiempo lo dedica al desarrollo en Python de interfaces grficas multiplataforma, y al desarrollo de software de visualizacin cientfica 3D. Jos A. Garca Rodrguez tambin es matemtico, y actualmente est finalizando su tesis en la Universidad de Mlaga. Desde hace unos aos se dedica al desarrollo de cdigo paralelo y optimizacin de cdigos en C++.

66

Nmero 08

WWW.LINUX- MAGAZINE.ES

Das könnte Ihnen auch gefallen