Fonction - tracer une ligne

From FreeCAD Documentation
Revision as of 12:07, 18 September 2020 by David69 (talk | contribs)

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

top

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

Now let's save our script in a folder where the FreeCAD Python interpreter can find it. When importing modules, the interpreter will look in the following places: the Python installation paths, the FreeCAD bin folder, and all FreeCAD Mod (module) folders. So the best solution is to create a new folder in one of the Mod folders. Let's create a MyScripts folder there and save our script in it as 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:

'line' in dir(exercise)

Now let's test it:

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?

top

Enregistrement du script dans l'interface de FreeCAD

Maintenant, pour que notre outil de création de ligne soit vraiment cool, il devrait y avoir un bouton sur l'interface, nous n'aurons donc pas besoin de taper tout ce code à chaque fois.
Le plus simple est de transformer notre nouveau répertoire myscripts dans un plan de travail FreeCAD. C'est facile, tout ce qui est nécessaire de faire, est de mettre un fichier appelé InitGui.py à l'intérieur de votre répertoire myscripts.
Le fichier InitGui.py contiendra les instructions pour créer un nouveau plan de travail, et s'ajoutera notre nouvel outil.
Sans oublier, que nous aurons aussi besoin de transformer un peu notre code exercise, de sorte que l'outil line() soit reconnu comme une commande FreeCAD officielle.
Commençons par faire un fichier InitGui.py, et écrivons le code suivant à l'intérieur:

For our new line tool to be really useful, and to avoid having to type all that stuff, it should have a button in the interface. One way to do this is to transform our new MyScripts folder into a full FreeCAD workbench. This is easy, all that is needed is to put a file called InitGui.py inside the MyScripts folder. 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 change our exercise code a bit, so the line() tool is recognized as an official FreeCAD command. Let's start by creating an InitGui.py file, and writing 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())

Actuellement, vous devriez comprendre le script ci-dessus par vous-même, du moins, je pense:
Nous créons une nouvelle classe que nous appelons MyWorkbench, nous lui donnons un nom (MenuText), et nous définissons une fonction Initialize() qui sera exécutée quand le plan de travail sera chargé dans FreeCAD.
Dans cette fonction, nous chargeons le contenus de notre fichier 'exercise, et ajoutons les commandes FreeCAD trouvées dans une liste de commandes. Ensuite, nous faisons une barre d'outils appelée "Mes scripts" et nous attribuons notre liste des commandes.

Actuellement, bien sûr, nous n'avons qu'un seul outil, puisque notre liste de commandes ne contient qu'un seul élément. Puis, une fois que notre plan de travail est prêt, nous l'ajoutons à l'interface principale.

Mais, cela ne fonctionne toujours pas, car une commande FreeCAD doit être formatée d'une certaine façon pour travailler. Nous aurons donc besoin de transformer un peu notre outil ligne().
Notre nouveau script exercise.py va maintenant 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())

Qu'avons fait ici ? nous avons transformé notre fonction __ init__ () en une fonction Activated(), parce que lorsque les commandes sont exécutées dans FreeCAD, il exécute automatiquement la fonction Activated().
Nous avons également ajouté une fonction GetResources(), qui informe FreeCAD où se trouve l'icône de l'outil, le nom et l'info-bulle de l'outil.
Toute image, jpg, png ou svg peut être utilisé comme icône, il peut être de n'importe quelle taille, mais il est préférable d'utiliser une taille standard qui est proche de l'aspect final, comme 16x16, 24x24 ou 32x32.
Puis, nous ajoutons notre class line() comme une 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 !

top

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 utilisateur: 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 de commenter vos idées ou questions sur le forum !

Don't hesitate to ask questions or share ideas on the forum!

top