7/31/2019 Visualizacin Grfica 3D con VTK (Visualization Toolkit) (2 parte)
1/5
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
En 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 tool-
kits de ventanas que se pueden impor-
tar 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 conTkInter, 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 cdi-
go 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 GUIEn 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 renderiza-
do de la escena, vamos a organizar el
trabajo siguiendo una estructura de cla-ses.
Si lo pensis bien, una GUI que con-
tenga un rea de renderizado (Figura
1), se puede comparar con un televisor.
El televisor con los botones y la panta-
lla, lo podis imaginar como la interfaz
de usuario que contiene el rea de ren-
derizado; 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 boto-
nes, 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 rende-
rizado, etc.
NOTA: Para mejor seguir este artcu-
lo, conviene tener a manos el cdigo
completo del programa, que se puede
descargar desde [6].
Creacin GUI TKInterLo primero de todo es importar Tkinter
y VTK,
DESARROLLO Infografa - VTK
62 Nmero 08 WWW.L INUX- M A GA ZINE.ES
7/31/2019 Visualizacin Grfica 3D con VTK (Visualization Toolkit) (2 parte)
2/5
Infografa - VTK DESARROLLO
63Nmero 08WWW.L INUX- M A GA ZINE.ES
padre, root, en el que colocamos los fra-
mes fr_renwin y fr_bot, en la parte supe-
rior y en la parte inferior, respectivamen-
te. Esta organizacin se corresponde con
el cdigo del Listado 1.
Para instanciar la clase MyGUI, debe-
mos hacerlo indicando que rootva a ser
la ventana padre que contenga los dife-
rentes 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 lla-
mado, miguivtk.py, por ejemplo. Si lo
ejecutis (python miguivtk.py), se os
abre una ventana con los dos frames
(ver Figura 3).
Widget vktRenderWindowLa clase vtkTkRenderWidgetes un wid-
get 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 reaccio-
nar a los eventos (de ratn, de
teclado) como cualquier otro
widget de Tk. Para poder utili-
zar este widget necesitamos enprimer lugar importarlo,
import vtkRenderWidget
Creamos la claseRenderWindow 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:
El constructor de esta clase se va a
encargar de instanciar
vtkTkRenderWidget, crear el rea de
renderizado y sus propiedades.
Recordad que vtkTkRenderWidgetes un
tipo de widget de Tk, por tanto, es nece-
sario 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 cons-
tructor.
Instanciamos la clase
vtkTkRenderWidgetpara crear un wid-
get de Tk (que hemos llamado
self.vtktkwidget), indicando que su
padre esparent. 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 elmtodo 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 claseRenderWindow. En
el constructor
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 quecontendr los diferentes botones.
Inicializamos Tkinter, creando una
ventana padre root, root=Tkinter.Tk(),
en la que colocaremos los frames fr_ren-
win 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:
En el constructor de la clase es necesario
indicarle quien va a ser el contenedor
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="bot-
tom", anchor="s",
09expand="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()
Listado 1: Constructorclase MyGUI
Figura 1: Estructura de una GUI con una ven-
tana de renderizado.
7/31/2019 Visualizacin Grfica 3D con VTK (Visualization Toolkit) (2 parte)
3/5
DESARROLLO Infografa - VTK
WWW.L INUX- M A GA ZINE.ES
(__init__) de la claseMyGUI, 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 situa-
mos dentro de dicho frame.
Ahora la ejecucin ha cambiado.
Tenemos un rea de color azul, que es
el rea de renderizado de la escena
(ver Figura 4). Seguro que os pregun-
taris, 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 apre-
cie la diferencia, sin embargo, si cerra-mos la ventana obtenemos el siguiente
warning: A TkRenderWidget is being
destroyed before its associated
vtkRenderWindow is destroyed.
Dicho mensaje, que antes no tena-
mos, nos avisa de que debemos des-
truir la ventana de renderizado antes
que el widget que la contiene, porque
en ciertas ocasiones podran quedar
procesos corriendo sin que lo aprecie-
mos.
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 cons-
tructor de la clase MyGUI debemos
escribir,
parent.protocol(U
"WM_DELETE_WINDOW",
self.quit)
El mtodo que estamos lla-mamos al cerrar la ventana
es self.quit,
def quit(self):
self.parent.quit()
Volvamos a ejecutar el cdigo. Fijos
que ya no aparece ninguna advertencia
al cerrar la ventana. De este modo des-
truimos los distintos objetos instancia-
dos en el orden correcto.
Actores a EscenaEn este punto ya tenemos todo lo nece-
sario para incluir actores en la escena.
En la claseRenderWindow tenemos que
crear los siguientes mtodos: (1)
add_actor: aade un actor a la escena;
(2) render_window: se ocupa del rende-
rizado de la escena; (3) resetcamera: se
ocupa de resetear la cmara y rende-
rizar la escena, permitiendo ver la tota-
lidad de la escena. Basta escribir las
siguientes lneas de cdigo,
def add_actor(self,nameactor):
self.ren.AddActor(nameactor)
def render_window(self):
self.renwin.Render()
def resetcamera(self,evt=None):
self.ren.ResetCamera()
self.renwin.Render()
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_esferaconstruye una esfera
segn los mtodos propor-
Nmero 0864
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)
11esferaActor.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.crea-
te_esfera()
18 self.add_actor(self.esfe-
ra_actor)
19 self.render_window()
Listado 3: Mtodos clase
RenderWindow para crearuna esfera.
Figura 2: Clases del programa.
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)
Listado 2:Constructor claseRenderWindow.
Figura 3: Ventana de
Tkinter con dos frames.
7/31/2019 Visualizacin Grfica 3D con VTK (Visualization Toolkit) (2 parte)
4/5
cionados por VTK (en el nmero 6
explicamos el modo de crear un actor y
acceder a sus propiedades), devolvien-
do el actor esferaActor. El mtodo
add_esfera incluye todos los comandos
necesarios para colocar la esfera dentrode nuestra escena.
Llamemos a la funcin add_esfera,
dentro del constructor de la clase
RenderWindow,
self.add_esfera()
Si habis seguido todos los pasos, debe-
rais estar viendo dentro de la escena
una esfera, igual que la
de la Figura 5. Seguro que
alguien se est preguntan-
do: Por qu no veo la
esfera en su totalidad?
Recordad que el mesanterior, explicamos que
cuando situamos un actor
en la escena, no se modi-
fica la posicin de la
cmara. Por tanto, si que-
remos ver la escena en su
totalidad basta resetear
la camara. Como somos
previsores, ya habamos
creado el mtodo resetca-
mera para dicha finali-
dad. Basta escribir
self.resetcamara(), justo
despus de self.add_esfe-
ra(). Ahora ya vemos la
esfera centrada en la esce-
na (Figura 6).
Recordaris que dentro
de la ventana de renderi-
zado se puede interactuar
con el ratn. Probad a
trasladar la esfera, hacer zoom, etc. No
os resulta ms sencillo mover la esfera
que en los ejemplos que vimos en el
nmero 6? Si lo comparis, os dariscuenta de que el actor se traslada y rota
de un modo diferente. La razn de esto
es que se pueden modificar el modo de
interactuar con el ratn dentro de una
ventana de renderizado de VTK. En este
caso concreto, la clase
vtkTkRenderWidgetya crea una ventana
de renderizado donde el tipo de interac-
cin con el ratn ya viene modificado,
para que podamos controlar mejor los
diferentes actores.
Control de EscenaHasta ahora lo que hemos implementa-
do nos permite abrir una
ventana de TkInter que con-
tiene un rea de renderiza-
do, cuya escena ya contiene
una actor. Pero la finalidad
de crear una interfaz grfi-
ca, es que
mediante
widgets
podamos
controlar,en la medi-
da de lo
posible,
los actores, la escena,
etc.
Dentro de nuestra ven-
tana vamos a incluir, en
el frame fr_bot, los
siguientes widgets: undeslizable (Tkinter.Scale)
para controlar la transpa-
rencia de la esfera; un
botn que nos permita
guardar la escena en una
imagen en formato .tiff y
un botn para cerrar la
aplicacin. Escribamos en
el constructor (__init__)
de la clase MyGUI, las
lneas de cdigo del
Listado 4.
El deslizable s_transp
llama al mtodo
self.ModifyTransp cuando
se pulsa el botn izquier-
do del ratn (evento
) o se despla-
za el ratn con dicho
botn presionado (evento
). Al clicke-
ar con el ratn sobre el botn bsave, se
llama al mtodo self.guardar_imagen;
mientras que si se pulsa bquitse ejecta
self.quit. El mtodo self.quitya lo tene-mos, as basta escribir el cdigo corres-
pondiente para los otros dos mtodos.
Antes de detallar cada mtodo por sepa-
rado, debemos comprobar que la estruc-
tura de nuestra GUI es correcta. Para no
tener problemas a la hora de probar el
programa, por el momento escribid,
Infografa - VTK DESARROLLO
65Nmero 08WWW.L INUX- M A GA ZINE.ES
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("",lam
bda e:self.ModifyTransp(e))
12
s_transp.bind("",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 com-
mand=self.quit)
19 bquit .pack(side="top",
expand="t")20 .
21 .
22 .
Listado 4: Deslizable y
Botones GUI
Figura 4: GUI con un rea
de renderizado.
Figura 5: GUI con una esce-
na que contiene una esfera
como actor.
7/31/2019 Visualizacin Grfica 3D con VTK (Visualization Toolkit) (2 parte)
5/5
Tened en cuenta, que
para generar la imagen,
es necesario tener permi-
sos de escritura en el
directorio donde se vaya
a guardar; en caso con-trario nos saldra un
error en la ejecucin avi-
sando de que no es posi-
ble generar dicha ima-
gen.
Probad de nuevo el
programa y pulsad el
botn Guardar Imagen.
En principio parece que
no hemos hecho nada,
pero en el mismo directo-
rio de trabajo tenis un
fichero llamado mysce-
ne.tif, donde se almace-
na exactamente la ltima
escena que hemos rende-
rizado.
Recordad que nos falta
implementar el cdigo
para modificar la transpa-
rencia de nuestra esfe-
ra. En la clase RenderWindow inclui-
mos el mtodo
modify_opacity_esfera(self,valor), donde
valorvara entre 0 y 1, porque es el argu-mento que vamos a utilizar para fijar la
opacidad. Escribamos las siguientes lne-
as de cdigo,
def modify_opacity_esferaU
(self, valor):
self.esfera_actor.GetProperty()
U
.SetOpacity(valor)
self.render_window()
Queremos que cada vez que deslizamos
el widget s_transp, vare la transparen-
cia de nuestro actor. Para ello, dentro de
la clase MyGUI escribimos el siguiente
cdigo correspondiente al mtodo
ModifyTransp,
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)
Mediante self.transp_
valor.get() se obtiene el
nuevo valor del Slice
s_transp cada vez que se
modifica ante un evento
del ratn. Fijos que losvalores del deslizable var-
an entre 0 y 100, sin
embargo, el mtodo que
modifica la transparencia
slo admite valores entre 0
y 1, por eso dividimos
valor_transp por 100.
Despus se llama al mto-
do modify_opacity_esfera
de la claseRenderWindow.
Si habis seguido todos
los pasos, comprobaris
como se modifica la trans-
parencia de la esfera a
medida que varan los
valores del deslizable.
Ahora que ya sabis todo
lo necesario para crear una
GUI con una ventana de
renderizado de VTK, podis
hacer todas las pruebas que
se os ocurran, como por ejemplo: modifi-
car propiedades de la escena, controlar
mediante widgets diferentes actores, etc. s
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 guar-
dar_imagen de la claseMyGUIva 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 ima-
gen, de formato .tiff.
La clase vtkWindowToImageFilteres
un filtro que convierteRenderWindows
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 clasevtkTIFFWriter, que se ocupa de escribir
la imagen en formato TIFF. Mediante
writer.SetFileName("myscene.tif") indi-
camos el nombre del fichero en el que
vamos a guardar la imagen.
DESARROLLO Infografa - VTK
66 Nmero 08 WWW.L INUX- M A GA ZINE.ES
Ana M. Ferreirro Ferreiro es mate-
mtica, 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 cientfi-
ca 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 des-
arrollo de cdigo paralelo y opti-
mizacin de cdigos en C++.
LOS
AUTORES
[1] Kitware. VTK:http://www.kitware.org
[2] Python: http://www.python.org
[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
RECURSOS
01 def
guardar_imagen(self,evt=None):
02 w2imgfil =vtk.vtkWindowToImageFilter()
03 writertiff =
vtk.vtkTIFFWriter()
04 w2imgfil
.SetInput(self.renwin.get_ren-
win())
05 w2imgfil .Update()
06 writertiff.SetInput(w2img-
fil .GetOutput())
07
writertiff.SetFileName("mysce-
ne.tif")
08 self.renwin.render_win-
dow()
09 writertiff.Write()
Listado 5: Mtodo GuardarImagen de Tipo .tiff
Figura 6: Recolocacin de
la cmara.
Figura 7: GUI con deslizable
y botones.
Top Related