Line drawing function/es: Difference between revisions

From FreeCAD Documentation
(Created page with "== ¿Quieres más? ==")
(Updating to match new version of source page)
(10 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages/>
{{docnav|Code snippets|Dialog creation}}

<div class="mw-translate-fuzzy">
Esta página muestra cómo se puede crear funcionalidades avanzadas con Python. En este ejercicio, construiremos una nueva herramienta que dibuja una línea. Esta herramienta puede ser vinculada a un comando de FreeCAD, y ese comando se puede llamar desde cualquier elemento de la interfaz, tal como un elemento de menú o un botón de una barra de herramientas.
Esta página muestra cómo se puede crear funcionalidades avanzadas con Python. En este ejercicio, construiremos una nueva herramienta que dibuja una línea. Esta herramienta puede ser vinculada a un comando de FreeCAD, y ese comando se puede llamar desde cualquier elemento de la interfaz, tal como un elemento de menú o un botón de una barra de herramientas.
</div>


== El archivo de guión principal ==
== El archivo de guión principal ==


En primer lugar vamos a escribir un archivo de guión que contenga toda nuestra funcionalidad. Después, vamos a guardar esto en un archivo, e importarlo en FreeCAD, así todas las clases y funciones que escribas estarán disponibles para FreeCAD. De modo que inicia tu editor de texto favorito y escribe las siguientes líneas:
En primer lugar vamos a escribir un archivo de guión que contenga toda nuestra funcionalidad. Después, vamos a guardar esto en un archivo, e importarlo en FreeCAD, así todas las clases y funciones que escribas estarán disponibles para FreeCAD. De modo que inicia tu editor de texto favorito y escribe las siguientes líneas:
{{Code|code=
<syntaxhighlight>
import FreeCADGui, Part
import FreeCADGui, Part
from pivy.coin import *
from pivy.coin import *
class line:
"this class will create a line after the user clicked 2 points on the screen"
def __init__(self):
self.view = FreeCADGui.ActiveDocument.ActiveView
self.stack = []
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
class line:
def getpoint(self,event_cb):
"""This class will create a line after the user clicked 2 points on the screen"""
event = event_cb.getEvent()
def __init__(self):
if event.getState() == SoMouseButtonEvent.DOWN:
pos = event.getPosition()
self.view = FreeCADGui.ActiveDocument.ActiveView
point = self.view.getPoint(pos[0],pos[1])
self.stack = []
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
self.stack.append(point)

if len(self.stack) == 2:
def getpoint(self,event_cb):
l = Part.Line(self.stack[0],self.stack[1])
shape = l.toShape()
event = event_cb.getEvent()
if event.getState() == SoMouseButtonEvent.DOWN:
Part.show(shape)
pos = event.getPosition()
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
point = self.view.getPoint(pos[0],pos[1])
</syntaxhighlight>
self.stack.append(point)
if len(self.stack) == 2:
l = Part.LineSegment(self.stack[0],self.stack[1])
shape = l.toShape()
Part.show(shape)
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
}}

==Explicación detallada==
==Explicación detallada==
{{Code|code=
<syntaxhighlight>
import Part, FreeCADGui
import Part, FreeCADGui
from pivy.coin import *
from pivy.coin import *
}}
</syntaxhighlight>
En Python, cuando desees utilizar las funciones de otro módulo, tienes que importarlo. En nuestro caso, vamos a necesitar las funciones del [[Part Module/es|Módulo de Pieza]], para la creación de la línea, y del módulo GUI (FreeCADGui), para acceder a la vista 3D. También necesitamos el contenido completo de la biblioteca de Coin, para que podamos utilizar directamente todos los objetos Coin, como SoMouseButtonEvent, etc ..
En Python, cuando desees utilizar las funciones de otro módulo, tienes que importarlo. En nuestro caso, vamos a necesitar las funciones del [[Part Module/es|Módulo de Pieza]], para la creación de la línea, y del módulo GUI (FreeCADGui), para acceder a la vista 3D. También necesitamos el contenido completo de la biblioteca de Coin, para que podamos utilizar directamente todos los objetos Coin, como SoMouseButtonEvent, etc ..
{{Code|code=
<syntaxhighlight>
class line:
class line:
}}
</syntaxhighlight>
Aquí definimos nuestra clase principal. ¿Por qué utilizar una clase y no una función? La razón es que necesitamos que nuestra herramienta se mantenga "viva" mientras esperamos a que el usuario haga clic en la pantalla. Una función termina cuando su tarea se ha hecho, pero un objeto (una clase se define como un objeto) se mantiene vivo hasta que se destruye.
Aquí definimos nuestra clase principal. ¿Por qué utilizar una clase y no una función? La razón es que necesitamos que nuestra herramienta se mantenga "viva" mientras esperamos a que el usuario haga clic en la pantalla. Una función termina cuando su tarea se ha hecho, pero un objeto (una clase se define como un objeto) se mantiene vivo hasta que se destruye.
{{Code|code=
<syntaxhighlight>
"this class will create a line after the user clicked 2 points on the screen"
"""This class will create a line after the user clicked 2 points on the screen"""
}}
</syntaxhighlight>
<div class="mw-translate-fuzzy">
En Python, cada clase o función puede tener una cadena de descripción. Esto es particularmente útil en FreeCAD, porque cuando vas a llamar a esa clase en el intérprete, la cadena de descripción se mostrará como una nota.
En Python, cada clase o función puede tener una cadena de descripción. Esto es particularmente útil en FreeCAD, porque cuando vas a llamar a esa clase en el intérprete, la cadena de descripción se mostrará como una nota.
</div>
<syntaxhighlight>
{{Code|code=
def __init__(self):
def __init__(self):
</syntaxhighlight>
}}
Las clases en Python siempre pueden contener una función __init__, que se ejecuta cuando la clase es llamada para crear un objeto. Por lo tanto, vamos a poner aquí todo lo que queremos que ocurra cuando nuestra herramienta línea comienza.
Las clases en Python siempre pueden contener una función __init__, que se ejecuta cuando la clase es llamada para crear un objeto. Por lo tanto, vamos a poner aquí todo lo que queremos que ocurra cuando nuestra herramienta línea comienza.
{{Code|code=
<syntaxhighlight>
self.view = FreeCADGui.ActiveDocument.ActiveView
self.view = FreeCADGui.ActiveDocument.ActiveView
}}
</syntaxhighlight>
En una clase, por lo general querrás incluir ''self.'' antes de un nombre de variable, para que sea fácilmente accesible por todas las funciones dentro y fuera de esa clase. Aquí, vamos a utilizar self.view para acceder y manipular la vista 3D activa.
En una clase, por lo general querrás incluir ''self.'' antes de un nombre de variable, para que sea fácilmente accesible por todas las funciones dentro y fuera de esa clase. Aquí, vamos a utilizar self.view para acceder y manipular la vista 3D activa.
{{Code|code=
<syntaxhighlight>
self.stack = []
self.stack = []
}}
</syntaxhighlight>
Aquí creamos una lista vacía que contendrá los puntos 3D enviados por la función getpoint.
Aquí creamos una lista vacía que contendrá los puntos 3D enviados por la función getpoint.
{{Code|code=
<syntaxhighlight>
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
}}
</syntaxhighlight>
Aquí viene la parte importante: Dado que en realidad se trata de una escena [http://www.coin3d.org/ Coin3d], FreeCAD utiliza el mecanismo de devolución de llamada de Coin, que permite que una función sea llamada cada vez que sucede un determinado evento de escena. En nuestro caso, estamos creando una devolución de llamada para eventos [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent], y lo conduciremos a la función getpoint. Ahora, cada vez que un botón del ratón sea pulsado o soltado, la función getpoint será ejecutada.
Aquí viene la parte importante: Dado que en realidad se trata de una escena [http://www.coin3d.org/ Coin3d], FreeCAD utiliza el mecanismo de devolución de llamada de Coin, que permite que una función sea llamada cada vez que sucede un determinado evento de escena. En nuestro caso, estamos creando una devolución de llamada para eventos [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent], y lo conduciremos a la función getpoint. Ahora, cada vez que un botón del ratón sea pulsado o soltado, la función getpoint será ejecutada.


Ten en cuenta que también hay una alternativa a addEventCallbackPivy(), llamada addEventCallback(), que dispensa del uso de pivy. Pero como pivy es una forma muy eficaz y natural para acceder a cualquier parte de la escena de Coin, es mucho mejor usarlo tanto como se pueda!
Ten en cuenta que también hay una alternativa a addEventCallbackPivy(), llamada addEventCallback(), que dispensa del uso de pivy. Pero como pivy es una forma muy eficaz y natural para acceder a cualquier parte de la escena de Coin, es mucho mejor usarlo tanto como se pueda!
{{Code|code=
<syntaxhighlight>
def getpoint(self,event_cb):
def getpoint(self,event_cb):
}}
</syntaxhighlight>
Ahora definimos la función getpoint, que se ejecutará cuando el botón del ratón se pulsa en una vista 3D. Esta función recibe un argumento, que llamaremos event_cb. A partir de este evento de devolución de llamada podemos tener acceso al objeto de evento, que contiene varias piezas de información (más información [[Code_snippets#Observing_mouse_events_in_the_3D_viewer_via_Python|aquí]]).
Ahora definimos la función getpoint, que se ejecutará cuando el botón del ratón se pulsa en una vista 3D. Esta función recibe un argumento, que llamaremos event_cb. A partir de este evento de devolución de llamada podemos tener acceso al objeto de evento, que contiene varias piezas de información (más información [[Code_snippets#Observing_mouse_events_in_the_3D_viewer_via_Python|aquí]]).
{{Code|code=
<syntaxhighlight>
if event.getState() == SoMouseButtonEvent.DOWN:
if event.getState() == SoMouseButtonEvent.DOWN:
}}
</syntaxhighlight>
La función getpoint se llamará cuando un botón del ratón sea pulsado o soltado. Pero queremos escoger un punto 3D sólo cuando se presiona (de lo contrario obtendríamos dos puntos 3D muy cerca uno del otro). Por lo tanto, debes comprobar eso aquí.
La función getpoint se llamará cuando un botón del ratón sea pulsado o soltado. Pero queremos escoger un punto 3D sólo cuando se presiona (de lo contrario obtendríamos dos puntos 3D muy cerca uno del otro). Por lo tanto, debes comprobar eso aquí.
{{Code|code=
<syntaxhighlight>
pos = event.getPosition()
pos = event.getPosition()
}}
</syntaxhighlight>
Aquí obtenemos las coordenadas de pantalla del cursor del ratón
Aquí obtenemos las coordenadas de pantalla del cursor del ratón
{{Code|code=
<syntaxhighlight>
point = self.view.getPoint(pos[0],pos[1])
point = self.view.getPoint(pos[0],pos[1])
}}
</syntaxhighlight>
Esta función nos da un vector de FreeCAD (x,y,z) que contiene el punto 3D que se encuentra en el plano focal, justo debajo del cursor de nuestro ratón. Si estás en vista de cámara, imagina un rayo proveniente de la cámara, pasando por el cursor del ratón, y alcanzando el plano focal. Ahí está nuestro punto 3D. Si estamos en una vista ortogonal, el rayo es paralelo a la dirección de la vista.
Esta función nos da un vector de FreeCAD (x,y,z) que contiene el punto 3D que se encuentra en el plano focal, justo debajo del cursor de nuestro ratón. Si estás en vista de cámara, imagina un rayo proveniente de la cámara, pasando por el cursor del ratón, y alcanzando el plano focal. Ahí está nuestro punto 3D. Si estamos en una vista ortogonal, el rayo es paralelo a la dirección de la vista.
{{Code|code=
<syntaxhighlight>
self.stack.append(point)
self.stack.append(point)
}}
</syntaxhighlight>
Añadimos nuestro nuevo punto a la pila
Añadimos nuestro nuevo punto a la pila
{{Code|code=
<syntaxhighlight>
if len(self.stack) == 2:
if len(self.stack) == 2:
}}
</syntaxhighlight>
¿Tenemos ya suficientes puntos? si es así, entonces vamos a trazar la línea!
¿Tenemos ya suficientes puntos? si es así, entonces vamos a trazar la línea!
{{Code|code=
<syntaxhighlight>
l = Part.Line(self.stack[0],self.stack[1])
l = Part.LineSegment(self.stack[0],self.stack[1])
}}
</syntaxhighlight>
Aquí se utiliza la función line() del [[Part Module/es|Módulo de Pieza]] que crea una línea a partir de dos vectores de FreeCAD. Todo lo que creamos y modificamos dentro del módulo de Pieza, se queda en el módulo de Pieza . Así, hasta ahora, hemos creado un elemento de línea. No está ligado a un objeto de nuestro documento activo, por lo que no aparece nada en la pantalla.
Aquí se utiliza la función line() del [[Part Module/es|Módulo de Pieza]] que crea una línea a partir de dos vectores de FreeCAD. Todo lo que creamos y modificamos dentro del módulo de Pieza, se queda en el módulo de Pieza . Así, hasta ahora, hemos creado un elemento de línea. No está ligado a un objeto de nuestro documento activo, por lo que no aparece nada en la pantalla.
{{Code|code=
<syntaxhighlight>
shape = l.toShape()
shape = l.toShape()
}}
</syntaxhighlight>
El documento de FreeCAD sólo puede aceptar formas desde el módulo de Pieza. Las formas son el tipo más genérico del módulo de Pieza. Por lo tanto, debemos convertir nuestra línea en una forma antes de añadirla al documento.
El documento de FreeCAD sólo puede aceptar formas desde el módulo de Pieza. Las formas son el tipo más genérico del módulo de Pieza. Por lo tanto, debemos convertir nuestra línea en una forma antes de añadirla al documento.
{{Code|code=
<syntaxhighlight>
Part.show(shape)
Part.show(shape)
}}
</syntaxhighlight>
El módulo de Pieza tiene una función, show(), que es muy útil ya que crea un nuevo objeto en el documento y le conecta a una forma. También podrías haber creado primero un nuevo objeto en el documento, y a continuación vincularle a la forma manualmente.
El módulo de Pieza tiene una función, show(), que es muy útil ya que crea un nuevo objeto en el documento y le conecta a una forma. También podrías haber creado primero un nuevo objeto en el documento, y a continuación vincularle a la forma manualmente.
{{Code|code=
<syntaxhighlight>
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
}}
</syntaxhighlight>
Como ya hemos terminado con nuestra línea, vamos a quitar el mecanismo de devolución de llamada, que consume unos preciosos ciclos de CPU.
Como ya hemos terminado con nuestra línea, vamos a quitar el mecanismo de devolución de llamada, que consume unos preciosos ciclos de CPU.


== Pruebas y Uso del archivo de guión ==
== Pruebas y Uso del archivo de guión ==


Ahora, vamos a guardar nuestro archivo de guión en un lugar donde el intérprete de Python de FreeCAD lo encuentre. Cuando importa los módulos, el intérprete mirará en los siguientes lugares: las rutas de instalación de Python, el directorio bin de FreeCAD, y todos los directorios de módulos FreeCAD. Por lo tanto, la mejor solución es crear un nuevo directorio en una de los FreeCAD [[Installing_more_workbenches|Mod directories]], y salvar nuestro script en él. Por ejemplo, vamos a hacer un directorio "MyScripts", y salvamos nuestro script como "exercise.py".
Ahora, vamos a guardar nuestro archivo de guión en un lugar donde el intérprete de Python de FreeCAD lo encuentre. Cuando importa los módulos, el intérprete mirará en los siguientes lugares: las rutas de instalación de Python, el directorio bin de FreeCAD, y todos los directorios de módulos FreeCAD. Por lo tanto, la mejor solución es crear un nuevo directorio en una de los FreeCAD [[Installing_more_workbenches/es|Mod directories]], y salvar nuestro script en él. Por ejemplo, vamos a hacer un directorio "MyScripts", y salvamos nuestro script como "exercise.py".


Ahora, todo está listo, vamos a empezar FreeCAD, cree un nuevo documento, y, en el intérprete de Python, ejecute:
Ahora, todo está listo, vamos a empezar FreeCAD, cree un nuevo documento, y, en el intérprete de Python, ejecute:
{{Code|code=
<syntaxhighlight>
import exercise
import exercise
}}
</syntaxhighlight>
Si no aparece ningún mensaje de error, eso significa que nuestro script de ejercicio se ha cargado. Ahora puede comprobar su contenido con:
Si no aparece ningún mensaje de error, eso significa que nuestro script de ejercicio se ha cargado. Ahora puede comprobar su contenido con:
{{Code|code=
<syntaxhighlight>
dir(exercise)
dir(exercise)
}}
</syntaxhighlight>
El comando dir() es un comando integrado de Python que muestra el contenido de un módulo. Podemos ver que nuestra clase line() está ahí, esperandonos. Ahora vamos a probarlo:
El comando dir() es un comando integrado de Python que muestra el contenido de un módulo. Podemos ver que nuestra clase line() está ahí, esperandonos. Ahora vamos a probarlo:
{{Code|code=
<syntaxhighlight>
exercise.line()
exercise.line()
}}
</syntaxhighlight>
A continuación, haz clic dos veces en la vista 3D, y .... ¡bingo!, ¡aquí está nuestra línea! Para hacerlo de nuevo, simplemente escribe exercise.line() otra vez, y otra vez, y otra vez ... Estas contento, ¿no?
A continuación, haz clic dos veces en la vista 3D, y .... ¡bingo!, ¡aquí está nuestra línea! Para hacerlo de nuevo, simplemente escribe exercise.line() otra vez, y otra vez, y otra vez ... Estas contento, ¿no?


Line 121: Line 129:


Ahora, para que nuestra nueva herramienta línea sea realmente buena, debe tener un botón en la interfaz, para que no sea necesario escribir todas estas cosas cada vez. La forma más fácil es transformar nuestro nuevo directorio MyScripts en un completo entorno de FreeCAD. Es fácil, todo lo que se necesita es poner un archivo llamado '''InitGui.py''' dentro de tu directorio MyScripts. El InitGui.py contendrá las instrucciones para crear un nuevo entorno (workbench), y le añadimos nuestra nueva herramienta. Además, también habrá que transformar un poco nuestro código del ejercicio, para que la herramienta line() sea reconocida como un comando oficial de FreeCAD. Comencemos por crear un archivo InitGui.py, y escribir el siguiente código en él:
Ahora, para que nuestra nueva herramienta línea sea realmente buena, debe tener un botón en la interfaz, para que no sea necesario escribir todas estas cosas cada vez. La forma más fácil es transformar nuestro nuevo directorio MyScripts en un completo entorno de FreeCAD. Es fácil, todo lo que se necesita es poner un archivo llamado '''InitGui.py''' dentro de tu directorio MyScripts. El InitGui.py contendrá las instrucciones para crear un nuevo entorno (workbench), y le añadimos nuestra nueva herramienta. Además, también habrá que transformar un poco nuestro código del ejercicio, para que la herramienta line() sea reconocida como un comando oficial de FreeCAD. Comencemos por crear un archivo InitGui.py, y escribir el siguiente código en él:
{{Code|code=
<syntaxhighlight>
class MyWorkbench (Workbench):
class MyWorkbench (Workbench):
MenuText = "MyScripts"
MenuText = "MyScripts"
def Initialize(self):
def Initialize(self):
import exercise
import exercise
commandslist = ["line"]
commandslist = ["line"]
self.appendToolbar("My Scripts",commandslist)
self.appendToolbar("My Scripts",commandslist)
Gui.addWorkbench(MyWorkbench())
Gui.addWorkbench(MyWorkbench())
}}
</syntaxhighlight>
A estas alturas, ya debes entender el archivo de guión anterior por ti mismo, supongo: Creamos una nueva clase que llamamos MyWorkbench, le damos un título (MenuText), y definimos una función initialize() que se ejecutará cuando el entorno se cargue en FreeCAD. En esa función, se carga el contenido de nuestro archivo del ejercicio, y los comandos de FreeCAD que se encuentran dentro se anexan en una lista de comandos. A continuación, hacemos una barra de herramientas llamada "Mi Scripts" y le asignamos nuestra lista de comandos. Finalmente, por supuesto, sólo tenemos una herramienta, por lo que nuestra lista de comandos contiene un solo elemento. Ahora, una vez que nuestro entorno está listo, lo añadimos a la interfaz principal.
A estas alturas, ya debes entender el archivo de guión anterior por ti mismo, supongo: Creamos una nueva clase que llamamos MyWorkbench, le damos un título (MenuText), y definimos una función initialize() que se ejecutará cuando el entorno se cargue en FreeCAD. En esa función, se carga el contenido de nuestro archivo del ejercicio, y los comandos de FreeCAD que se encuentran dentro se anexan en una lista de comandos. A continuación, hacemos una barra de herramientas llamada "Mi Scripts" y le asignamos nuestra lista de comandos. Finalmente, por supuesto, sólo tenemos una herramienta, por lo que nuestra lista de comandos contiene un solo elemento. Ahora, una vez que nuestro entorno está listo, lo añadimos a la interfaz principal.


Pero esto aún no funciona, porque un comando de FreeCAD debe estar formateado de una determinada manera para poder funcionar. Así que tendremos que transformar un poco nuestra herramienta line(). Nuestro nuevo archivo de guión exercise.py tendrá después este aspecto:
Pero esto aún no funciona, porque un comando de FreeCAD debe estar formateado de una determinada manera para poder funcionar. Así que tendremos que transformar un poco nuestra herramienta line(). Nuestro nuevo archivo de guión exercise.py tendrá después este aspecto:
{{Code|code=
<syntaxhighlight>
import FreeCADGui, Part
import FreeCADGui, Part
from pivy.coin import *
from pivy.coin import *
class line:
class line:
"this class will create a line after the user clicked 2 points on the screen"
"""This class will create a line after the user clicked 2 points on the screen"""

def Activated(self):
def Activated(self):
self.view = FreeCADGui.ActiveDocument.ActiveView
self.stack = []
self.view = FreeCADGui.ActiveDocument.ActiveView
self.stack = []
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
def getpoint(self,event_cb):
event = event_cb.getEvent()
def getpoint(self,event_cb):
if event.getState() == SoMouseButtonEvent.DOWN:
event = event_cb.getEvent()
pos = event.getPosition()
if event.getState() == SoMouseButtonEvent.DOWN:
point = self.view.getPoint(pos[0],pos[1])
pos = event.getPosition()
self.stack.append(point)
point = self.view.getPoint(pos[0],pos[1])
if len(self.stack) == 2:
self.stack.append(point)
l = Part.Line(self.stack[0],self.stack[1])
if len(self.stack) == 2:
l = Part.LineSegment(self.stack[0],self.stack[1])
shape = l.toShape()
Part.show(shape)
shape = l.toShape()
Part.show(shape)
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
def GetResources(self):
def GetResources(self):
return {'Pixmap' : 'path_to_an_icon/line_icon.png', 'MenuText': 'Line', 'ToolTip': 'Creates a line by clicking 2 points on the screen'}
return {'Pixmap' : 'path_to_an_icon/line_icon.png', 'MenuText': 'Line', 'ToolTip': 'Creates a line by clicking 2 points on the screen'}
FreeCADGui.addCommand('line', line())
FreeCADGui.addCommand('line', line())
</syntaxhighlight>
}}
Lo que hicimos aquí es transformar nuestra función __init__() en una función Activated(), porque cuando se ejecutan comandos de FreeCAD, automáticamente se ejecuta la función Activated(). También hemos añadido una función GetResources(), que informa a FreeCAD de donde puede encontrar el icono de la herramienta, y cual será el nombre y la descripción de nuestra herramienta. Cualquier imagen jpg, png o svg funcionará como un icono. Puede ser de cualquier tamaño, pero lo mejor es utilizar un tamaño que esté cerca del aspecto final, como 16x16, 24x24 o 32x32.
Lo que hicimos aquí es transformar nuestra función __init__() en una función Activated(), porque cuando se ejecutan comandos de FreeCAD, automáticamente se ejecuta la función Activated(). También hemos añadido una función GetResources(), que informa a FreeCAD de donde puede encontrar el icono de la herramienta, y cual será el nombre y la descripción de nuestra herramienta. Cualquier imagen jpg, png o svg funcionará como un icono. Puede ser de cualquier tamaño, pero lo mejor es utilizar un tamaño que esté cerca del aspecto final, como 16x16, 24x24 o 32x32.
A continuación, añadimos la clase line() como un comando oficial de FreeCAD con el método addCommand().
A continuación, añadimos la clase line() como un comando oficial de FreeCAD con el método addCommand().
Line 164: Line 173:
== ¿Quieres más? ==
== ¿Quieres más? ==


<div class="mw-translate-fuzzy">
If you liked this exercise, why not try to improve this little tool? There are many things that can be done, like for example:
Si te gustó este ejercicio, ¿por qué no tratar de mejorar esta pequeña herramienta? Hay muchas cosas que se pueden hacer, como por ejemplo:
* Add user feedback: until now we did a very bare tool, the user might be a bit lost when using it. So we could add some feedback, telling him what to do next. For example, you could issue messages to the FreeCAD console. Have a look in the FreeCAD.Console module
* Agregar asistencia para los usuarios: hasta ahora hemos hecho una herramienta muy burda, el usuario podría verse un poco perdido cuando la utiliza. Se podría añadir alguna información, diciéndole qué hacer a continuación. Por ejemplo, podrías mostrar mensajes en la consola de FreeCAD. ¡Echa un vistazo en el módulo de FreeCAD.Console
* Add a possibility to type the 3D points coordinates manually. Look at the python input() function, for example
* Añadir la posibilidad de teclear de forma manual las coordenadas de los puntos 3D. Mira la función input() de Python, por ejemplo
* Add the possibility to add more than 2 points
* Añadir la posibilidad de incluir más de 2 puntos
* Add events for other things: Now we just check for Mouse button events, what if we would also do something when the mouse is moved, like displaying current coordinates?
* Añadir eventos para otras cosas: Ahora sólo comprobamos eventos de botón del ratón, ¿que tal si también hace algo cuando el ratón se mueva, como mostrar las coordenadas actuales?
* Give a name to the created object
* Dar un nombre al objeto creado
Don't hesitate to write your questions or ideas on the [[Talk:Line_drawing_function|talk page]]!


No dudes en escribir tus preguntas o ideas en la [http://forum.freecadweb.org/ forum]!
{{docnav|Code snippets|Dialog creation}}
</div>

{{docnav/es|Code snippets/es|Dialog creation/es}}

{{Userdocnavi}}

<div class="mw-translate-fuzzy">
[[Category:Poweruser Documentation/es]]
[[Category:Python Code/es]]
</div>


[[Category:Poweruser Documentation]]
[[Category:Python Code]]
[[Category:Python Code]]


{{clear}}
{{clear}}
<languages/>

Revision as of 20:54, 13 February 2020

Code snippets
Dialog creation

Esta página muestra cómo se puede crear funcionalidades avanzadas con Python. En este ejercicio, construiremos una nueva herramienta que dibuja una línea. Esta herramienta puede ser vinculada a un comando de FreeCAD, y ese comando se puede llamar desde cualquier elemento de la interfaz, tal como un elemento de menú o un botón de una barra de herramientas.

El archivo de guión principal

En primer lugar vamos a escribir un archivo de guión que contenga toda nuestra funcionalidad. Después, vamos a guardar esto en un archivo, e importarlo en FreeCAD, así todas las clases y funciones que escribas estarán disponibles para FreeCAD. De modo que inicia tu editor de texto favorito y escribe las siguientes líneas:

import FreeCADGui, Part
from pivy.coin import *
 
class line:
    """This class will create a line after the user clicked 2 points on the screen"""
    def __init__(self):
        self.view = FreeCADGui.ActiveDocument.ActiveView
        self.stack = []
        self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)  

    def getpoint(self,event_cb):
        event = event_cb.getEvent()
        if event.getState() == SoMouseButtonEvent.DOWN:
            pos = event.getPosition()
            point = self.view.getPoint(pos[0],pos[1])
            self.stack.append(point)
            if len(self.stack) == 2:
                l = Part.LineSegment(self.stack[0],self.stack[1])
                shape = l.toShape()
                Part.show(shape)
                self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)

Explicación detallada

import Part, FreeCADGui
from pivy.coin import *

En Python, cuando desees utilizar las funciones de otro módulo, tienes que importarlo. En nuestro caso, vamos a necesitar las funciones del Módulo de Pieza, para la creación de la línea, y del módulo GUI (FreeCADGui), para acceder a la vista 3D. También necesitamos el contenido completo de la biblioteca de Coin, para que podamos utilizar directamente todos los objetos Coin, como SoMouseButtonEvent, etc ..

class line:

Aquí definimos nuestra clase principal. ¿Por qué utilizar una clase y no una función? La razón es que necesitamos que nuestra herramienta se mantenga "viva" mientras esperamos a que el usuario haga clic en la pantalla. Una función termina cuando su tarea se ha hecho, pero un objeto (una clase se define como un objeto) se mantiene vivo hasta que se destruye.

"""This class will create a line after the user clicked 2 points on the screen"""

En Python, cada clase o función puede tener una cadena de descripción. Esto es particularmente útil en FreeCAD, porque cuando vas a llamar a esa clase en el intérprete, la cadena de descripción se mostrará como una nota.

def __init__(self):

Las clases en Python siempre pueden contener una función __init__, que se ejecuta cuando la clase es llamada para crear un objeto. Por lo tanto, vamos a poner aquí todo lo que queremos que ocurra cuando nuestra herramienta línea comienza.

self.view = FreeCADGui.ActiveDocument.ActiveView

En una clase, por lo general querrás incluir self. antes de un nombre de variable, para que sea fácilmente accesible por todas las funciones dentro y fuera de esa clase. Aquí, vamos a utilizar self.view para acceder y manipular la vista 3D activa.

self.stack = []

Aquí creamos una lista vacía que contendrá los puntos 3D enviados por la función getpoint.

self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)

Aquí viene la parte importante: Dado que en realidad se trata de una escena Coin3d, FreeCAD utiliza el mecanismo de devolución de llamada de Coin, que permite que una función sea llamada cada vez que sucede un determinado evento de escena. En nuestro caso, estamos creando una devolución de llamada para eventos SoMouseButtonEvent, y lo conduciremos a la función getpoint. Ahora, cada vez que un botón del ratón sea pulsado o soltado, la función getpoint será ejecutada.

Ten en cuenta que también hay una alternativa a addEventCallbackPivy(), llamada addEventCallback(), que dispensa del uso de pivy. Pero como pivy es una forma muy eficaz y natural para acceder a cualquier parte de la escena de Coin, es mucho mejor usarlo tanto como se pueda!

def getpoint(self,event_cb):

Ahora definimos la función getpoint, que se ejecutará cuando el botón del ratón se pulsa en una vista 3D. Esta función recibe un argumento, que llamaremos event_cb. A partir de este evento de devolución de llamada podemos tener acceso al objeto de evento, que contiene varias piezas de información (más información aquí).

if event.getState() == SoMouseButtonEvent.DOWN:

La función getpoint se llamará cuando un botón del ratón sea pulsado o soltado. Pero queremos escoger un punto 3D sólo cuando se presiona (de lo contrario obtendríamos dos puntos 3D muy cerca uno del otro). Por lo tanto, debes comprobar eso aquí.

pos = event.getPosition()

Aquí obtenemos las coordenadas de pantalla del cursor del ratón

point = self.view.getPoint(pos[0],pos[1])

Esta función nos da un vector de FreeCAD (x,y,z) que contiene el punto 3D que se encuentra en el plano focal, justo debajo del cursor de nuestro ratón. Si estás en vista de cámara, imagina un rayo proveniente de la cámara, pasando por el cursor del ratón, y alcanzando el plano focal. Ahí está nuestro punto 3D. Si estamos en una vista ortogonal, el rayo es paralelo a la dirección de la vista.

self.stack.append(point)

Añadimos nuestro nuevo punto a la pila

if len(self.stack) == 2:

¿Tenemos ya suficientes puntos? si es así, entonces vamos a trazar la línea!

l = Part.LineSegment(self.stack[0],self.stack[1])

Aquí se utiliza la función line() del Módulo de Pieza que crea una línea a partir de dos vectores de FreeCAD. Todo lo que creamos y modificamos dentro del módulo de Pieza, se queda en el módulo de Pieza . Así, hasta ahora, hemos creado un elemento de línea. No está ligado a un objeto de nuestro documento activo, por lo que no aparece nada en la pantalla.

shape = l.toShape()

El documento de FreeCAD sólo puede aceptar formas desde el módulo de Pieza. Las formas son el tipo más genérico del módulo de Pieza. Por lo tanto, debemos convertir nuestra línea en una forma antes de añadirla al documento.

Part.show(shape)

El módulo de Pieza tiene una función, show(), que es muy útil ya que crea un nuevo objeto en el documento y le conecta a una forma. También podrías haber creado primero un nuevo objeto en el documento, y a continuación vincularle a la forma manualmente.

self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)

Como ya hemos terminado con nuestra línea, vamos a quitar el mecanismo de devolución de llamada, que consume unos preciosos ciclos de CPU.

Pruebas y Uso del archivo de guión

Ahora, vamos a guardar nuestro archivo de guión en un lugar donde el intérprete de Python de FreeCAD lo encuentre. Cuando importa los módulos, el intérprete mirará en los siguientes lugares: las rutas de instalación de Python, el directorio bin de FreeCAD, y todos los directorios de módulos FreeCAD. Por lo tanto, la mejor solución es crear un nuevo directorio en una de los FreeCAD Mod directories, y salvar nuestro script en él. Por ejemplo, vamos a hacer un directorio "MyScripts", y salvamos nuestro script como "exercise.py".

Ahora, todo está listo, vamos a empezar FreeCAD, cree un nuevo documento, y, en el intérprete de Python, ejecute:

import exercise

Si no aparece ningún mensaje de error, eso significa que nuestro script de ejercicio se ha cargado. Ahora puede comprobar su contenido con:

dir(exercise)

El comando dir() es un comando integrado de Python que muestra el contenido de un módulo. Podemos ver que nuestra clase line() está ahí, esperandonos. Ahora vamos a probarlo:

exercise.line()

A continuación, haz clic dos veces en la vista 3D, y .... ¡bingo!, ¡aquí está nuestra línea! Para hacerlo de nuevo, simplemente escribe exercise.line() otra vez, y otra vez, y otra vez ... Estas contento, ¿no?

Incluyendo el archivo de guión en la interfaz de FreeCAD

Ahora, para que nuestra nueva herramienta línea sea realmente buena, debe tener un botón en la interfaz, para que no sea necesario escribir todas estas cosas cada vez. La forma más fácil es transformar nuestro nuevo directorio MyScripts en un completo entorno de FreeCAD. Es fácil, todo lo que se necesita es poner un archivo llamado InitGui.py dentro de tu directorio MyScripts. El InitGui.py contendrá las instrucciones para crear un nuevo entorno (workbench), y le añadimos nuestra nueva herramienta. Además, también habrá que transformar un poco nuestro código del ejercicio, para que la herramienta line() sea reconocida como un comando oficial de FreeCAD. Comencemos por crear un archivo InitGui.py, y escribir el siguiente código en él:

class MyWorkbench (Workbench): 
   MenuText = "MyScripts"
   def Initialize(self):
       import exercise
       commandslist = ["line"]
       self.appendToolbar("My Scripts",commandslist)
Gui.addWorkbench(MyWorkbench())

A estas alturas, ya debes entender el archivo de guión anterior por ti mismo, supongo: Creamos una nueva clase que llamamos MyWorkbench, le damos un título (MenuText), y definimos una función initialize() que se ejecutará cuando el entorno se cargue en FreeCAD. En esa función, se carga el contenido de nuestro archivo del ejercicio, y los comandos de FreeCAD que se encuentran dentro se anexan en una lista de comandos. A continuación, hacemos una barra de herramientas llamada "Mi Scripts" y le asignamos nuestra lista de comandos. Finalmente, por supuesto, sólo tenemos una herramienta, por lo que nuestra lista de comandos contiene un solo elemento. Ahora, una vez que nuestro entorno está listo, lo añadimos a la interfaz principal.

Pero esto aún no funciona, porque un comando de FreeCAD debe estar formateado de una determinada manera para poder funcionar. Así que tendremos que transformar un poco nuestra herramienta line(). Nuestro nuevo archivo de guión exercise.py tendrá después este aspecto:

import FreeCADGui, Part
from pivy.coin import *
class line:
    """This class will create a line after the user clicked 2 points on the screen"""

    def Activated(self):
        self.view = FreeCADGui.ActiveDocument.ActiveView
        self.stack = []
        self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint) 
    def getpoint(self,event_cb):
        event = event_cb.getEvent()
        if event.getState() == SoMouseButtonEvent.DOWN:
            pos = event.getPosition()
            point = self.view.getPoint(pos[0],pos[1])
            self.stack.append(point)
            if len(self.stack) == 2:
                l = Part.LineSegment(self.stack[0],self.stack[1])
                shape = l.toShape()
                Part.show(shape)
                self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
    def GetResources(self): 
        return {'Pixmap' : 'path_to_an_icon/line_icon.png', 'MenuText': 'Line', 'ToolTip': 'Creates a line by clicking 2 points on the screen'} 
FreeCADGui.addCommand('line', line())

Lo que hicimos aquí es transformar nuestra función __init__() en una función Activated(), porque cuando se ejecutan comandos de FreeCAD, automáticamente se ejecuta la función Activated(). También hemos añadido una función GetResources(), que informa a FreeCAD de donde puede encontrar el icono de la herramienta, y cual será el nombre y la descripción de nuestra herramienta. Cualquier imagen jpg, png o svg funcionará como un icono. Puede ser de cualquier tamaño, pero lo mejor es utilizar un tamaño que esté cerca del aspecto final, como 16x16, 24x24 o 32x32. A continuación, añadimos la clase line() como un comando oficial de FreeCAD con el método addCommand().

Eso es todo, ahora sólo hay que reiniciar FreeCAD y tendremos un agradable entorno nuevo con una nueva herramienta line() de nuestra marca!

¿Quieres más?

Si te gustó este ejercicio, ¿por qué no tratar de mejorar esta pequeña herramienta? Hay muchas cosas que se pueden hacer, como por ejemplo:

  • Agregar asistencia para los usuarios: hasta ahora hemos hecho una herramienta muy burda, el usuario podría verse un poco perdido cuando la utiliza. Se podría añadir alguna información, diciéndole qué hacer a continuación. Por ejemplo, podrías mostrar mensajes en la consola de FreeCAD. ¡Echa un vistazo en el módulo de FreeCAD.Console
  • Añadir la posibilidad de teclear de forma manual las coordenadas de los puntos 3D. Mira la función input() de Python, por ejemplo
  • Añadir la posibilidad de incluir más de 2 puntos
  • Añadir eventos para otras cosas: Ahora sólo comprobamos eventos de botón del ratón, ¿que tal si también hace algo cuando el ratón se mueva, como mostrar las coordenadas actuales?
  • Dar un nombre al objeto creado

No dudes en escribir tus preguntas o ideas en la forum!

Code snippets/es
Dialog creation/es