Line drawing function/fr: Difference between revisions

From FreeCAD Documentation
(Created page with "Le document FreeCAD ne peut accepter que des formes à partir de Part Module. Les formes sont le type le plus courant de Part Module.<br /> Donc, nous devons transformer notre...")
No edit summary
 
(85 intermediate revisions by 6 users not shown)
Line 1: Line 1:
<languages/>
Cette page montre comment construire facilement des fonctionnalités avancées en Python. Dans cet exercice, nous allons construire un nouvel outil qui trace une ligne. Cet outil peut alors être lié à une commande FreeCAD, et cette commande peut être appelée par n'importe quel élément de l'interface, comme un élément de menu ou un bouton de la barre d'outils.


{{TOCright}}

<span id="Introduction"></span>
== Introduction ==

Cette page montre comment des fonctionnalités avancées peuvent être facilement créées en Python. Dans cet exercice, nous allons créer un nouvel outil qui trace une ligne. Cet outil peut ensuite être lié à une commande FreeCAD, et cette commande peut être appelée par n'importe quel élément de l'interface, comme un élément de menu ou un bouton de barre d'outils.

<span id="The_main_script"></span>
==Script principal==
==Script principal==


Première chose, nous allons écrire un script contenant toutes nos fonctionnalités, puis, nous allons l'enregistrer dans un fichier, et l'importer dans FreeCAD, alors toutes les classes et fonctions que nous écrirons seront accessibles à partir de FreeCAD.<br />
Nous allons d'abord écrire un script contenant toutes nos fonctionnalités. Ensuite, nous l'enregistrerons dans un fichier et l'importerons dans FreeCAD pour rendre toutes ses classes et fonctions disponibles. Lancez votre éditeur de code préféré et tapez les lignes suivantes :

Alors, lancez votre éditeur de texte favori, et entrez les lignes suivantes:
{{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 __init__(self):

self.view = FreeCADGui.ActiveDocument.ActiveView
self.stack = []
def __init__(self):
self.view = FreeCADGui.ActiveDocument.ActiveView
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
self.stack = []
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:
pos = event.getPosition()
event = event_cb.getEvent()
if event.getState() == SoMouseButtonEvent.DOWN:
point = self.view.getPoint(pos[0],pos[1])
self.stack.append(point)
pos = event.getPosition()
if len(self.stack) == 2:
point = self.view.getPoint(pos[0], pos[1])
l = Part.Line(self.stack[0],self.stack[1])
self.stack.append(point)
shape = l.toShape()
if len(self.stack) == 2:
Part.show(shape)
l = Part.LineSegment(self.stack[0], self.stack[1])
shape = l.toShape()
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
Part.show(shape)
</syntaxhighlight>
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(), self.callback)
}}
{{Top}}
<span id="Detailed_explanation"></span>
==Explications détaillées==
==Explications détaillées==

<syntaxhighlight>
{{Code|code=
import Part, FreeCADGui
import Part, FreeCADGui
from pivy.coin import *
from pivy.coin import *
</syntaxhighlight>
}}
En Python, lorsque vous voulez utiliser les fonctions d'un autre module, vous avez besoin de l'importer.<br />

Dans notre cas, nous aurons besoin de fonctions du [[Part Module/fr|'''Part Module''']], pour la création de la ligne, et du '''Gui module''' (FreeCADGui), pour accéder à la vue 3D.<br />
Nous avons également besoin de tout le contenu de la bibliothèque de pièces, afin que nous puissions utiliser directement tous les objets comme '''coin''', '''SoMouseButtonEvent''' (évènement souris) etc ..
En Python, lorsque vous souhaitez utiliser des fonctions d'un autre module, vous devez l'importer. Dans notre cas, nous aurons besoin des fonctions de l'[[Part_Workbench/fr|atelier Part]], pour créer la ligne, et du module Gui {{incode|FreeCADGui}}, pour accéder à la [[3D_view/fr|Vue 3D]]. Nous avons également besoin du contenu complet de la bibliothèque Coin afin de pouvoir utiliser directement tous les objets Coin comme {{incode|SoMouseButtonEvent}} etc...

<syntaxhighlight>
{{Code|code=
class line:
class line:
</syntaxhighlight>
}}

Ici, nous définissons notre classe principale.<br />
Ici, nous définissons notre classe principale.<br />
Mais pourquoi utilisons-nous une classe et non une fonction ? La raison en est que nous avons besoin que notre outil reste "vivant" en attendant que l'utilisateur clique sur l'écran.<br />
Mais pourquoi utilisons-nous une classe et non une fonction ? La raison en est que nous avons besoin que notre outil reste "vivant" en attendant que l'utilisateur clique sur l'écran.<br />
* Une fonction se termine lorsque sa tâche est terminée,
* Une fonction se termine lorsque sa tâche est terminée,
* mais un objet, '''(une classe définit un objet)''' reste en vie (actif) jusqu'à ce qu'il soit détruit.
* mais un objet, '''(une classe définit un objet)''' reste en vie (actif) jusqu'à ce qu'il soit détruit.
<syntaxhighlight>
"this class will create a line after the user clicked 2 points on the screen"
</syntaxhighlight>
En Python, toutes les classes ou fonctions peuvent avoir une description.<br />
Ceci est particulièrement utile dans FreeCAD, parce que quand vous appelez cette classe dans l'interpréteur, la description sera affichée comme une '''info-bulle'''.
<syntaxhighlight>
def __init__(self):
</syntaxhighlight>
Les classes Python doivent toujours contenir une fonction '''__ init__''', qui est exécutée lorsque la classe est appelée pour créer un objet.<br />
Donc, nous allons mettre ici tout ce que nous voulons produire lorsque notre outil de création de ligne commence (appelé).
<syntaxhighlight>
self.view = FreeCADGui.ActiveDocument.ActiveView
</syntaxhighlight>
Dans une classe, il est généralement souhaitable d'ajouter '''self.''' devant un nom de variable, de sorte que la variable sera facilement accessible à toutes les fonctions à l'intérieur et à l'extérieur de cette classe.<br />
Ici, nous allons utiliser '''self.view''' pour accéder et manipuler la vue active 3D.
<syntaxhighlight>
self.stack = []
</syntaxhighlight>
Ici, nous créons une liste vide qui contiendra les '''points''' en 3D envoyés par la fonction '''GetPoint'''.
<syntaxhighlight>
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
</syntaxhighlight>
'''Ceci est un point important:'''<br />
Du fait qu'il s'agit d'une scène [http://www.coin3d.org/ coin3D], FreeCAD utilise les mécanismes de rappel de '''coin''', qui permet à une fonction d'être appelée à chaque fois qu'un évènement se passe sur la scène.<br />
Dans notre cas, nous créons un appel pour [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent], et nous le lions à la fonction '''GetPoint'''.<br />
Maintenant, chaque fois qu'un bouton de la souris est enfoncé ou relâché, la fonction '''GetPoint''' sera exécutée.


{{Code|code=
Notez qu'il existe aussi une alternative à '''addEventCallbackPivy()''' appelée '''addEventCallback()''' qui dispense l'utilisation de '''pivy'''. Mais, '''pivy''' est un moyen très simple et efficace d'accéder à n'importe quelle partie de la scène '''coin''', il est conseillé de l'utiliser autant que possible !
"""This class will create a line after the user clicked 2 points on the screen"""
<syntaxhighlight>
}}
def getpoint(self,event_cb):

</syntaxhighlight>
En Python, toutes les classes ou fonctions peuvent avoir une documentation string(docstring). Ceci est particulièrement utile dans FreeCAD, parce que quand vous appelez cette classe dans l'interpréteur, la description sera affichée comme une infobulle.
Maintenant, nous définissons la fonction '''GetPoint''', qui sera exécutée quand un bouton de la souris sera pressé dans une vue 3D.<br />

Cette fonction recevra un argument, que nous appellerons '''event_cb'''. A partir de l'appel de cet événement, nous pouvons accéder à l'objet événement, qui contient plusieurs éléments d'information ([http://www.freecadweb.org/wiki/index.php?title=Code_snippets/fr#Observation_des_.C3.A9v.C3.A8nements_de_la_souris_dans_la_vue_3D_via_Python plus d'informations sur cette page]).
{{Code|code=
<syntaxhighlight>
def __init__(self):
if event.getState() == SoMouseButtonEvent.DOWN:
}}
</syntaxhighlight>

La fonction '''GetPoint''' sera appelée dès qu'un bouton de la souris est enfoncé ou relâché. Mais, nous ne voulons prendre un point 3D uniquement lorsqu'il est pressé (sinon, nous aurons deux points 3D très proches l'un de l'autre).<br />
Les classes Python doivent toujours contenir une fonction {{incode|__init__}} qui est exécutée lorsque la classe est appelée pour créer un objet. Ici, nous allons mettre ici tout ce que nous voulons produire lorsque notre outil de création de ligne commence (appelé).
Donc, nous devons vérifier cela avec:

<syntaxhighlight>
{{Code|code=
pos = event.getPosition()
self.view = FreeCADGui.ActiveDocument.ActiveView
</syntaxhighlight>
}}
Ici, nous avons les coordonnées du curseur de la souris sur l'écran

<syntaxhighlight>
Dans une classe, vous souhaitez généralement faire précéder {{incode|self.}} les noms des variables afin de rendre les variables facilement accessibles à toutes les fonctions à l'intérieur et à l'extérieur de la classe. Ici, nous utiliserons {{incode|self.view}} pour accéder à la vue 3D active et la manipuler.
point = self.view.getPoint(pos[0],pos[1])

</syntaxhighlight>
{{Code|code=
Cette fonction nous donne le vecteur ('''x, y, z''') du point qui se trouve sur le plan focal, juste sous curseur de notre souris.<br />
self.stack = []
Si vous êtes dans la vue caméra, imaginez un rayon provenant de la caméra, en passant par le curseur de la souris, et en appuyant sur le plan focal.<br />
}}
C'est notre point dans la vue 3D. Si l'on est en mode orthogonal, le rayon est parallèle à la direction de la vue.

<syntaxhighlight>
Nous créons ici une liste vide qui contiendra les points 3D envoyés par la fonction {{incode|getpoint()}}.
self.stack.append(point)

</syntaxhighlight>
{{Code|code=
Nous ajoutons notre nouveau point sur la pile
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(), self.getpoint)
<syntaxhighlight>
}}
if len(self.stack) == 2:

</syntaxhighlight>
C'est la partie importante. Comme il s'agit d'une scène [https://github.com/coin3d/coin/wiki Coin3D], nous utilisons un mécanisme de rappel de Coin qui permet d'appeler une fonction chaque fois qu'un certain événement de la scène se produit. Dans notre cas, nous créons un rappel pour les événements [https://coin3d.github.io/Coin/html/classSoMouseButtonEvent.html SoMouseButtonEvent] et nous le lions à la fonction {{incode|getpoint()}}. Désormais, chaque fois qu'un bouton de la souris est enfoncé ou relâché, la fonction {{incode|getpoint()}} sera exécutée.

Remarquez qu'il existe également une alternative à {{incode|addEventCallbackPivy()}} appelée {{incode|addEventCallback()}} qui ne repose pas sur pivy. Mais comme le pivot est un moyen très efficace et naturel d'accéder à n'importe quelle partie de la scène des pièces, il est le meilleur choix.
{{Top}}
{{Code|code=
def getpoint(self, event_cb):
}}

Nous définissons maintenant la fonction {{incode|getpoint()}} qui sera exécutée lorsqu'un bouton de la souris est enfoncé dans une vue 3D. Cette fonction recevra un argument que nous appellerons {{incode|event_cb}}. A partir de ce rappel d'événement, nous pouvons accéder à l'objet événement qui contient plusieurs informations (plus d'informations [[Code_snippets/fr#Observer_des_évènements_de_la_souris_dans_la_vue_3D_via_Python|ici]]).

{{Code|code=
if event.getState() == SoMouseButtonEvent.DOWN:
}}

La fonction {{incode|getpoint()}} sera appelée lorsqu'un bouton de la souris est enfoncé ou relâché. Mais nous ne voulons choisir un point 3D que lorsqu'un bouton est pressé, sinon nous nous retrouverions avec deux points 3D très proches l'un de l'autre. Nous devons donc vérifier cela ici.

{{Code|code=
pos = event.getPosition()
}}

Ici, nous avons les coordonnées du curseur de la souris sur l'écran.

{{Code|code=
point = self.view.getPoint(pos[0], pos[1])
}}

Cette fonction nous donne le vecteur (x,y,z) du point qui se trouve sur le plan focal, juste sous curseur de notre souris. Si vous êtes dans la vue de la prise de vue, imaginez un rayon provenant de la prise de vue, en passant par le curseur de la souris, et en appuyant sur le plan focal. C'est l'emplacement de notre point 3D. Si nous sommes en vue orthogonale, le rayon est parallèle à la direction de la vue.

{{Code|code=
self.stack.append(point)
}}

Nous ajoutons notre nouveau point sur la pile.

{{Code|code=
if len(self.stack) == 2:
}}

Avons nous tous les points ? si oui, alors nous allons tracer la ligne !
Avons nous tous les points ? si oui, alors nous allons tracer la ligne !
<syntaxhighlight>
l = Part.Line(self.stack[0],self.stack[1])
</syntaxhighlight>
Ici, nous utilisons la fonction '''line()''' de [[Part Module/fr|Part Module]] qui crée une ligne de deux vecteurs FreeCAD.<br />
Tout ce que nous créons et modifions l'intérieur de '''Part Module''', reste dans le '''Part Module'''.<br />
Donc, jusqu'à présent, nous avons créé une '''Line Part'''. Il n'est lié à aucun objet de notre document actif, c'est pour cela que rien ne s'affiche sur l'écran.
<syntaxhighlight>
shape = l.toShape()
</syntaxhighlight>
Le document FreeCAD ne peut accepter que des formes à partir de Part Module. Les formes sont le type le plus courant de Part Module.<br />
Donc, nous devons transformer notre ligne en une forme avant de l'ajouter au document.
<syntaxhighlight>
Part.show(shape)
</syntaxhighlight>
The Part module has a very handy show() function that creates a new object in the document and binds a shape to it. We could also have created a new object in the document first, then bound the shape to it manually.
<syntaxhighlight>
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
</syntaxhighlight>
Since we are done with our line, let's remove the callback mechanism, that consumes precious CPU cycles.


{{Code|code=
==Testing & Using the script==
l = Part.LineSegment(self.stack[0], self.stack[1])
Now, let's save our script to some place where the FreeCAD python interpreter will find it. When importing modules, the interpreter will look in the following places: the python installation paths, the FreeCAD bin directory, and all FreeCAD modules directories. So, the best solution is to create a new directory in one of the FreeCAD [[Installing_more_workbenches|Mod directories]], and to save our script in it. For example, let's make a "MyScripts" directory, and save our script as "exercise.py".
}}


Nous utilisons ici la fonction {{incode|LineSegment()}} de l'atelier Part qui crée une ligne à partir de deux vecteurs FreeCAD. La ligne n'est liée à aucun objet de notre document actif, donc rien n'apparaît à l'écran.
Now, everything is ready, let's start FreeCAD, create a new document, and, in the python interpreter, issue:

<syntaxhighlight>
{{Code|code=
import exercise
shape = l.toShape()
</syntaxhighlight>
}}
If no error message appear, that means our exercise script has been loaded. We can now check its contents with:

<syntaxhighlight>
Le document FreeCAD ne peut accepter que des formes à partir de Part Module. Les formes sont le type le plus courant de Part Module. Donc, nous devons transformer notre ligne en une forme avant de l'ajouter au document.
dir(exercise)

</syntaxhighlight>
{{Code|code=
The command dir() is a built-in python command that lists the contents of a module. We can see that our line() class is there, waiting for us. Now let's test it:
Part.show(shape)
<syntaxhighlight>
}}
exercise.line()

</syntaxhighlight>
Le Part module a une fonction très pratique {{incode|show()}} qui crée un nouvel objet dans le document et se lie a une forme. Nous aurions aussi pu créer un nouvel objet dans le premier document et le lier à la forme manuellement.
Then, click two times in the 3D view, and bingo, here is our line! To do it again, just type exercise.line() again, and again, and again... Feels great, no?

{{Code|code=
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(), self.callback)
}}

Maintenant, nous en avons fini avec notre ligne, nous allons supprimer le mécanisme de rappel ici.
{{Top}}
<span id="Testing_the_script"></span>
==Tester le script==

Maintenant, sauvegardons notre script dans un dossier où l'interpréteur FreeCAD Python peut le trouver. Lors de l'importation de modules, l'interpréteur cherchera aux endroits suivants: les chemins d'installation de Python, le dossier FreeCAD {{FileName|bin}} et tous les dossiers FreeCAD {{FileName|Mod}} (module). La meilleure solution est donc de créer un nouveau dossier dans l'un des dossiers {{FileName|Mod}}. Créons là un dossier {{FileName|MyScripts}} et enregistrons notre script dedans sous le nom {{FileName|exercise.py}}.

Maintenant, tout est prêt. Démarrons FreeCAD, créons un nouveau document, et dans le numéro de l'interpréteur Python :

{{Code|code=
import exercise
}}

Si aucun message d'erreur n'apparaît, c'est que notre script d'exercice a été chargé avec succès. Nous pouvons maintenant vérifier son contenu avec :

{{Code|code=
dir(exercise)
}}

La commande {{incode|dir()}} est une commande Python intégrée qui répertorie le contenu d'un module. Nous pouvons vérifier que notre classe {{incode|line()}} est là avec :

{{Code|code=
'line' in dir(exercise)
}}

Maintenant, testons-le :

{{Code|code=
exercise.line()
}}

Cliquez deux fois dans la vue 3D et bingo: voici notre ligne! Pour la répéter, tapez à nouveau {{incode|exercise.line()}}.
{{Top}}
<span id="Registering_the_script"></span>
==Enregistrement du script==

Pour que notre nouvel outil ligne soit vraiment utile et pour éviter d'avoir à taper tout cela, il devrait avoir un bouton dans l'interface. Une façon de faire est de transformer notre nouveau dossier {{FileName|MyScripts}} en un atelier FreeCAD complet. Il suffit de mettre un fichier appelé {{FileName|InitGui.py}} dans le dossier {{FileName|MyScripts}}. {{FileName|InitGui.py}} contiendra les instructions pour créer un nouvel atelier et y ajouter notre nouvel outil. En plus de cela, nous devrons également changer un peu notre code d'exercice, de sorte que l'outil {{incode|line()}} soit reconnu comme une commande officielle de FreeCAD. Commençons par créer un fichier {{FileName|InitGui.py}} et y écrire le code suivant :

{{Code|code=
class MyWorkbench (Workbench):


==Registering the script in the FreeCAD interface==
Now, for our new line tool to be really cool, it should have a button on the interface, so we don't need to type all that stuff everytime. The easiest way is to transform our new MyScripts directory into a full FreeCAD workbench. It is easy, all that is needed is to put a file called '''InitGui.py''' inside your MyScripts directory. The InitGui.py will contain the instructions to create a new workbench, and add our new tool to it. Besides that we will also need to transform a bit our exercise code, so the line() tool is recognized as an official FreeCAD command. Let's start by making an InitGui.py file, and write the following code in it:
<syntaxhighlight>
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())
</syntaxhighlight>
By now, you should already understand the above script by yourself, I think: We create a new class that we call MyWorkbench, we give it a title (MenuText), and we define an Initialize() function that will be executed when the workbench is loaded into FreeCAD. In that function, we load in the contents of our exercise file, and append the FreeCAD commands found inside to a command list. Then, we make a toolbar called "My Scripts" and we assign our commands list to it. Currently, of course, we have only one tool, so our command list contains only one element. Then, once our workbench is ready, we add it to the main interface.


Gui.addWorkbench(MyWorkbench())
But this still won't work, because a FreeCAD command must be formatted in a certain way to work. So we will need to transform a bit our line() tool. Our new exercise.py script will now look like this:
}}
<syntaxhighlight>
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.Line(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())
</syntaxhighlight>
What we did here is transform our __init__() function into an Activated() function, because when FreeCAD commands are run, they automatically execute the Activated() function. We also added a GetResources() function, that informs FreeCAD where it can find an icon for the tool, and what will be the name and tooltip of our tool. Any jpg, png or svg image will work as an icon, it can be any size, but it is best to use a size that is close to the final aspect, like 16x16, 24x24 or 32x32.
Then, we add the line() class as an official FreeCAD command with the addCommand() method.


A présent, vous comprenez probablement le script ci-dessus. Nous créons une nouvelle classe que nous appelons {{incode|MyWorkbench}}, nous lui donnons un titre {{incode|MenuText}} et nous définissons une fonction {{incode|Initialize()}} qui sera exécutée lorsque l'atelier est chargé dans FreeCAD. Dans cette fonction, nous chargeons le contenu de notre fichier d'exercice et ajoutons les commandes FreeCAD trouvées à l'intérieur à une liste de commandes. Ensuite, nous créons une barre d'outils appelée "Mes scripts" et nous y assignons notre liste de commandes. Actuellement, bien sûr, nous n'avons qu'un seul outil, donc notre liste de commandes ne contient qu'un seul élément. Ensuite, une fois notre atelier prêt, nous l'ajoutons à l'interface principale.
That's it, we now just need to restart FreeCAD and we'll have a nice new workbench with our brand new line tool!


Mais cela ne fonctionnera toujours pas car une commande FreeCAD doit être formatée d'une certaine manière pour fonctionner, nous devrons changer notre outil {{incode|line()}}. Notre nouveau script {{FileName|exercise.py}} devrait ressembler à ceci :
==So you want more?==


{{Code|code=
If you liked this exercise, why not try to improve this little tool? There are many things that can be done, like for example:
import FreeCADGui, Part
* 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
from pivy.coin import *
* Add a possibility to type the 3D points coordinates manually. Look at the python input() function, for example
* Add the possibility to add more than 2 points
* 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?
* Give a name to the created object
Don't hesitate to write your questions or ideas on the [[Talk:Line_drawing_function|talk page]]!


class line:
{{docnav|Code snippets|Dialog creation}}


"""This class will create a line after the user clicked 2 points on the screen"""
[[Category:Poweruser Documentation]]
[[Category:Python Code]]


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

Ce que nous avons fait ici est de transformer notre fonction {{incode|__init __()}} en une fonction {{incode|Activated()}}. Lorsque les commandes FreeCAD sont exécutées, elles exécutent automatiquement la fonction {{incode|Activated()}}. Nous avons également ajouté une fonction {{incode|GetResources()}}, qui informe FreeCAD où il peut trouver l'icône de l'outil et quel sera le nom et l'infobulle de notre outil. Toute image {{FileName|jpg}}, {{FileName|png}} ou {{FileName|svg}} fonctionnera comme une icône, elle peut être de n'importe quelle taille, mais il est préférable d'utiliser une taille proche de l'aspect final, comme 16x16, 24x24 ou 32x32.
Ensuite, nous ajoutons la classe {{incode|line()}} en tant que commande officielle de FreeCAD avec la méthode {{incode|addCommand()}}.

Ça y est, nous avons juste besoin de redémarrer FreeCAD et nous aurons un plan de travail agréable avec notre nouvel outil '''ligne''' tout neuf !
{{Top}}
<span id="So_you_want_more?"></span>
==Vous voulez en savoir plus ?==

Si vous avez aimé cet exercise, pourquoi ne pas essayer d'améliorer ce petit outil? Il y a beaucoup de choses à faire, comme par exemple :
* Ajouter des commentaires d'utilisateurs : jusqu'à présent nous avons fait un outil très dépouillé, l'utilisateur peut être un peu perdu lors de son utilisation. Vous pouvez ajouter vos commentaires, en guidant l'utilisateur. Par exemple, vous pourriez émettre des messages à la console FreeCAD. "Jetez" un oeil dans le module {{incode|FreeCAD.Console}}.
* Ajouter la possibilité d'entrer les coordonnées 3D manuellement. Regardez les fonctions Python {{incode|input()}}, par exemple.
* Ajouter la possibilité d'ajouter plus de 2 points.
* Ajouter des événements pour d'autres fonctions: Maintenant que nous venons d'apprendre les événements de bouton de souris, si nous souhaitons également faire quelque chose quand la souris est déplacée, comme par exemple l'affichage des coordonnées actuelles ?
* Donnez un nom à l'objet créé et bien d'autres choses.

N'hésitez pas à poser des questions ou à partager des idées sur le [https://forum.freecadweb.org/ forum]!
{{Top}}
{{Powerdocnavi{{#translation:}}}}
[[Category:Developer Documentation{{#translation:}}]]
[[Category:Python Code{{#translation:}}]]
{{clear}}
{{clear}}
<languages/>

Latest revision as of 13:18, 13 April 2024

Introduction

Cette page montre comment des fonctionnalités avancées peuvent être facilement créées en Python. Dans cet exercice, nous allons créer un nouvel outil qui trace une ligne. Cet outil peut ensuite être lié à une commande FreeCAD, et cette commande peut être appelée par n'importe quel élément de l'interface, comme un élément de menu ou un bouton de barre d'outils.

Script principal

Nous allons d'abord écrire un script contenant toutes nos fonctionnalités. Ensuite, nous l'enregistrerons dans un fichier et l'importerons dans FreeCAD pour rendre toutes ses classes et fonctions disponibles. Lancez votre éditeur de code préféré et tapez les lignes suivantes :

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)

En haut

Explications détaillées

import Part, FreeCADGui
from pivy.coin import *

En Python, lorsque vous souhaitez utiliser des fonctions d'un autre module, vous devez l'importer. Dans notre cas, nous aurons besoin des fonctions de l'atelier Part, pour créer la ligne, et du module Gui FreeCADGui, pour accéder à la Vue 3D. Nous avons également besoin du contenu complet de la bibliothèque Coin afin de pouvoir utiliser directement tous les objets Coin comme SoMouseButtonEvent etc...

class line:

Ici, nous définissons notre classe principale.
Mais pourquoi utilisons-nous une classe et non une fonction ? La raison en est que nous avons besoin que notre outil reste "vivant" en attendant que l'utilisateur clique sur l'écran.

  • Une fonction se termine lorsque sa tâche est terminée,
  • mais un objet, (une classe définit un objet) reste en vie (actif) jusqu'à ce qu'il soit détruit.
"""This class will create a line after the user clicked 2 points on the screen"""

En Python, toutes les classes ou fonctions peuvent avoir une documentation string(docstring). Ceci est particulièrement utile dans FreeCAD, parce que quand vous appelez cette classe dans l'interpréteur, la description sera affichée comme une infobulle.

def __init__(self):

Les classes Python doivent toujours contenir une fonction __init__ qui est exécutée lorsque la classe est appelée pour créer un objet. Ici, nous allons mettre ici tout ce que nous voulons produire lorsque notre outil de création de ligne commence (appelé).

self.view = FreeCADGui.ActiveDocument.ActiveView

Dans une classe, vous souhaitez généralement faire précéder self. les noms des variables afin de rendre les variables facilement accessibles à toutes les fonctions à l'intérieur et à l'extérieur de la classe. Ici, nous utiliserons self.view pour accéder à la vue 3D active et la manipuler.

self.stack = []

Nous créons ici une liste vide qui contiendra les points 3D envoyés par la fonction getpoint().

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

C'est la partie importante. Comme il s'agit d'une scène Coin3D, nous utilisons un mécanisme de rappel de Coin qui permet d'appeler une fonction chaque fois qu'un certain événement de la scène se produit. Dans notre cas, nous créons un rappel pour les événements SoMouseButtonEvent et nous le lions à la fonction getpoint(). Désormais, chaque fois qu'un bouton de la souris est enfoncé ou relâché, la fonction getpoint() sera exécutée.

Remarquez qu'il existe également une alternative à addEventCallbackPivy() appelée addEventCallback() qui ne repose pas sur pivy. Mais comme le pivot est un moyen très efficace et naturel d'accéder à n'importe quelle partie de la scène des pièces, il est le meilleur choix.

En haut

def getpoint(self, event_cb):

Nous définissons maintenant la fonction getpoint() qui sera exécutée lorsqu'un bouton de la souris est enfoncé dans une vue 3D. Cette fonction recevra un argument que nous appellerons event_cb. A partir de ce rappel d'événement, nous pouvons accéder à l'objet événement qui contient plusieurs informations (plus d'informations ici).

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

La fonction getpoint() sera appelée lorsqu'un bouton de la souris est enfoncé ou relâché. Mais nous ne voulons choisir un point 3D que lorsqu'un bouton est pressé, sinon nous nous retrouverions avec deux points 3D très proches l'un de l'autre. Nous devons donc vérifier cela ici.

pos = event.getPosition()

Ici, nous avons les coordonnées du curseur de la souris sur l'écran.

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

Cette fonction nous donne le vecteur (x,y,z) du point qui se trouve sur le plan focal, juste sous curseur de notre souris. Si vous êtes dans la vue de la prise de vue, imaginez un rayon provenant de la prise de vue, en passant par le curseur de la souris, et en appuyant sur le plan focal. C'est l'emplacement de notre point 3D. Si nous sommes en vue orthogonale, le rayon est parallèle à la direction de la vue.

self.stack.append(point)

Nous ajoutons notre nouveau point sur la pile.

if len(self.stack) == 2:

Avons nous tous les points ? si oui, alors nous allons tracer la ligne !

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

Nous utilisons ici la fonction LineSegment() de l'atelier Part qui crée une ligne à partir de deux vecteurs FreeCAD. La ligne n'est liée à aucun objet de notre document actif, donc rien n'apparaît à l'écran.

shape = l.toShape()

Le document FreeCAD ne peut accepter que des formes à partir de Part Module. Les formes sont le type le plus courant de Part Module. Donc, nous devons transformer notre ligne en une forme avant de l'ajouter au document.

Part.show(shape)

Le Part module a une fonction très pratique show() qui crée un nouvel objet dans le document et se lie a une forme. Nous aurions aussi pu créer un nouvel objet dans le premier document et le lier à la forme manuellement.

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

Maintenant, nous en avons fini avec notre ligne, nous allons supprimer le mécanisme de rappel ici.

En haut

Tester le script

Maintenant, sauvegardons notre script dans un dossier où l'interpréteur FreeCAD Python peut le trouver. Lors de l'importation de modules, l'interpréteur cherchera aux endroits suivants: les chemins d'installation de Python, le dossier FreeCAD bin et tous les dossiers FreeCAD Mod (module). La meilleure solution est donc de créer un nouveau dossier dans l'un des dossiers Mod. Créons là un dossier MyScripts et enregistrons notre script dedans sous le nom exercise.py.

Maintenant, tout est prêt. Démarrons FreeCAD, créons un nouveau document, et dans le numéro de l'interpréteur Python :

import exercise

Si aucun message d'erreur n'apparaît, c'est que notre script d'exercice a été chargé avec succès. Nous pouvons maintenant vérifier son contenu avec :

dir(exercise)

La commande dir() est une commande Python intégrée qui répertorie le contenu d'un module. Nous pouvons vérifier que notre classe line() est là avec :

'line' in dir(exercise)

Maintenant, testons-le :

exercise.line()

Cliquez deux fois dans la vue 3D et bingo: voici notre ligne! Pour la répéter, tapez à nouveau exercise.line().

En haut

Enregistrement du script

Pour que notre nouvel outil ligne soit vraiment utile et pour éviter d'avoir à taper tout cela, il devrait avoir un bouton dans l'interface. Une façon de faire est de transformer notre nouveau dossier MyScripts en un atelier FreeCAD complet. Il suffit de mettre un fichier appelé InitGui.py dans le dossier MyScripts. InitGui.py contiendra les instructions pour créer un nouvel atelier et y ajouter notre nouvel outil. En plus de cela, nous devrons également changer un peu notre code d'exercice, de sorte que l'outil line() soit reconnu comme une commande officielle de FreeCAD. Commençons par créer un fichier InitGui.py et y écrire le code suivant :

class MyWorkbench (Workbench):

    MenuText = "MyScripts"

    def Initialize(self):
        import exercise
        commandslist = ["line"]
        self.appendToolbar("My Scripts", commandslist)

Gui.addWorkbench(MyWorkbench())

A présent, vous comprenez probablement le script ci-dessus. Nous créons une nouvelle classe que nous appelons MyWorkbench, nous lui donnons un titre MenuText et nous définissons une fonction Initialize() qui sera exécutée lorsque l'atelier est chargé dans FreeCAD. Dans cette fonction, nous chargeons le contenu de notre fichier d'exercice et ajoutons les commandes FreeCAD trouvées à l'intérieur à une liste de commandes. Ensuite, nous créons une barre d'outils appelée "Mes scripts" et nous y assignons notre liste de commandes. Actuellement, bien sûr, nous n'avons qu'un seul outil, donc notre liste de commandes ne contient qu'un seul élément. Ensuite, une fois notre atelier prêt, nous l'ajoutons à l'interface principale.

Mais cela ne fonctionnera toujours pas car une commande FreeCAD doit être formatée d'une certaine manière pour fonctionner, nous devrons changer notre outil line(). Notre nouveau script exercise.py devrait ressembler à ceci :

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

Ce que nous avons fait ici est de transformer notre fonction __init __() en une fonction Activated(). Lorsque les commandes FreeCAD sont exécutées, elles exécutent automatiquement la fonction Activated(). Nous avons également ajouté une fonction GetResources(), qui informe FreeCAD où il peut trouver l'icône de l'outil et quel sera le nom et l'infobulle de notre outil. Toute image jpg, png ou svg fonctionnera comme une icône, elle peut être de n'importe quelle taille, mais il est préférable d'utiliser une taille proche de l'aspect final, comme 16x16, 24x24 ou 32x32. Ensuite, nous ajoutons la classe line() en tant que commande officielle de FreeCAD avec la méthode addCommand().

Ça y est, nous avons juste besoin de redémarrer FreeCAD et nous aurons un plan de travail agréable avec notre nouvel outil ligne tout neuf !

En haut

Vous voulez en savoir plus ?

Si vous avez aimé cet exercise, pourquoi ne pas essayer d'améliorer ce petit outil? Il y a beaucoup de choses à faire, comme par exemple :

  • Ajouter des commentaires d'utilisateurs : jusqu'à présent nous avons fait un outil très dépouillé, l'utilisateur peut être un peu perdu lors de son utilisation. Vous pouvez ajouter vos commentaires, en guidant l'utilisateur. Par exemple, vous pourriez émettre des messages à la console FreeCAD. "Jetez" un oeil dans le module FreeCAD.Console.
  • Ajouter la possibilité d'entrer les coordonnées 3D manuellement. Regardez les fonctions Python input(), par exemple.
  • Ajouter la possibilité d'ajouter plus de 2 points.
  • Ajouter des événements pour d'autres fonctions: Maintenant que nous venons d'apprendre les événements de bouton de souris, si nous souhaitons également faire quelque chose quand la souris est déplacée, comme par exemple l'affichage des coordonnées actuelles ?
  • Donnez un nom à l'objet créé et bien d'autres choses.

N'hésitez pas à poser des questions ou à partager des idées sur le forum!

En haut