Line drawing function/fr: Difference between revisions

From FreeCAD Documentation
(Created page with "La commande '''dir()''' est une commande intégrée dans python, et lister le contenu d'un module. Nous pouvons voir que notre '''classe line()''' est là qui nous attend.<br ...")
(Created page with "Puis, cliquez deux fois dans la vue 3D, et bingo, voici notre ligne ! Pour la faire de nouveau, tapez juste exercise.line(), encore et encore, et encore ... C'est bien, non?")
Line 143: Line 143:
exercise.line()
exercise.line()
</syntaxhighlight>
</syntaxhighlight>
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?
Puis, cliquez deux fois dans la vue 3D, et bingo, voici notre ligne ! Pour la faire de nouveau, tapez juste exercise.line(), encore et encore, et encore ... C'est bien, non?


==Registering the script in the FreeCAD interface==
==Registering the script in the FreeCAD interface==

Revision as of 22:05, 4 November 2014

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.

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.
Alors, lancez votre éditeur de texte favori, et entrez 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.Line(self.stack[0],self.stack[1])
                 shape = l.toShape()
                 Part.show(shape)
                 self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)

Explications détaillées

 import Part, FreeCADGui
 from pivy.coin import *

En Python, lorsque vous voulez utiliser les fonctions d'un autre module, vous avez besoin de l'importer.
Dans notre cas, nous aurons besoin de fonctions du Part Module, pour la création de la ligne, et du Gui module (FreeCADGui), pour accéder à la vue 3D.
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 ..

 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 description.
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.

 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.
Donc, 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, 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.
Ici, nous allons utiliser self.view pour accéder et manipuler la vue active 3D.

 self.stack = []

Ici, nous créons une liste vide qui contiendra les points en 3D envoyés par la fonction GetPoint.

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

Ceci est un point important:
Du fait qu'il s'agit d'une scène 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.
Dans notre cas, nous créons un appel pour SoMouseButtonEvent, et nous le lions à la fonction GetPoint.
Maintenant, chaque fois qu'un bouton de la souris est enfoncé ou relâché, la fonction GetPoint sera exécutée.

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 !

 def getpoint(self,event_cb):

Maintenant, nous définissons la fonction GetPoint, qui sera exécutée quand un bouton de la souris sera pressé dans une vue 3D.
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 (plus d'informations sur cette page).

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

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).
Donc, nous devons vérifier cela avec:

 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 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.
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.

 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.Line(self.stack[0],self.stack[1])

Ici, nous utilisons la fonction line() de Part Module qui crée une ligne de deux vecteurs FreeCAD.
Tout ce que nous créons et modifions l'intérieur de Part Module, reste dans le Part Module.
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.

 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, puis 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, qui consomme de précieux cycles de CPU.

Tester et utiliser un script

Maintenant, nous allons enregistrer notre script dans un endroit où l'interpréteur Python de FreeCAD le trouvera.
Lors de l'importation de modules, l’interpréteur cherchera dans les endroits suivants:

  • les chemins d'installation de python,
  • le répertoire bin FreeCAD,
  • et tous les répertoires des modules FreeCAD.

Donc, la meilleure solution est de créer un nouveau répertoire dans le répertoire Mod de FreeCAD , et sauver votre script dans ce répertoire.
Par exemple, nous allons créer un répertoire "myscripts", et sauver notre script comme "exercise.py".

Maintenant, tout est prêt, nous allons commencer par créez un nouveau document FreeCAD, et, dans l'interpréteur Python, tapons:

 import exercise

Si aucun message d'erreur n'apparaît, cela signifie que notre script exercise a été chargé.
Nous pouvons maintenant lister son contenu avec:

 dir(exercise)

La commande dir() est une commande intégrée dans python, et lister le contenu d'un module. Nous pouvons voir que notre classe line() est là qui nous attend.
Maintenant, nous allons le tester:

 exercise.line()

Puis, cliquez deux fois dans la vue 3D, et bingo, voici notre ligne ! Pour la faire de nouveau, tapez juste exercise.line(), encore et encore, et encore ... C'est bien, non?

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:

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

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.

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:

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

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.

That's it, we now just need to restart FreeCAD and we'll have a nice new workbench with our brand new line tool!

So you want more?

If you liked this exercise, why not try to improve this little tool? There are many things that can be done, like for example:

  • 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
  • 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 page!

Code snippets
Dialog creation