Funcția de Desenare a liniei

From FreeCAD Documentation
Revision as of 11:33, 20 August 2018 by Luc (talk | contribs)

Această pagină arată modul în care se poate constru cu ușurință o funcționalitatea avansată în Python. În acest exercițiu, vom construi un nou instrument care atrage o linie. Acest instrument poate fi apoi legat la o comandă FreeCAD și această comandă poate fi apelată de orice element al interfeței, cum ar fi un element de meniu sau un buton din bara de unelte.

Scriptul Principal

Mai întâi vom scrie un script care conține toate funcționalitățile noastre. Apoi, vom salva acest lucru într-un fișier și îl vom importa în FreeCAD, astfel încât toate clasele și funcțiile pe care le scriem vor fi disponibile pentru FreeCAD. Deci, lansați editorul de text preferat și tastați următoarele rânduri:

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)

Explicații detaliate

import Part, FreeCADGui
from pivy.coin import *

În Python, atunci când doriți să utilizați funcții dintr-un alt modul, trebuie să-l importați. În cazul nostru, vom avea nevoie de funcții de la Part Module, pentru crearea liniei și de la modulul Gui (FreeCADGui), pentru a accesa vizualizarea 3D. De asemenea, avem nevoie de conținutul complet al bibliotecii de monede, astfel încât să putem folosi direct toate obiectele precum coin, SoMouseButtonEvent, (eveniment mouse) etc ...

class line:

Here we define our main class. Why do we use a class and not a function? The reason is that we need our tool to stay "alive" while we are waiting for the user to click on the screen. A function ends when its task has been done, but an object (a class defines an object) stays alive until it is destroyed.

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

In Python, every class or function can have a description string. This is particularly useful in FreeCAD, because when you'll call that class in the interpreter, the description string will be displayed as a tooltip.

def __init__(self):

Python classes can always contain an __init__ function, which is executed when the class is called to create an object. So, we will put here everything we want to happen when our line tool begins.

self.view = FreeCADGui.ActiveDocument.ActiveView

In a class, you usually want to append self. before a variable name, so it will be easily accessible to all functions inside and outside that class. Here, we will use self.view to access and manipulate the active 3D view.

self.stack = []

Here we create an empty list that will contain the 3D points sent by the getpoint function.

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

This is the important part: Since it is actually a coin3D scene, the FreeCAD uses coin callback mechanism, that allows a function to be called everytime a certain scene event happens. In our case, we are creating a callback for SoMouseButtonEvent events, and we bind it to the getpoint function. Now, everytime a mouse button is pressed or released, the getpoint function will be executed.

Note that there is also an alternative to addEventCallbackPivy() called addEventCallback() which dispenses the use of pivy. But since pivy is a very efficient and natural way to access any part of the coin scene, it is much better to use it as much as you can!

def getpoint(self,event_cb):

Now we define the getpoint function, that will be executed when a mouse button is pressed in a 3D view. This function will receive an argument, that we will call event_cb. From this event callback we can access the event object, which contains several pieces of information (mode info here).

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

The getpoint function will be called when a mouse button is pressed or released. But we want to pick a 3D point only when pressed (otherwise we would get two 3D points very close to each other). So we must check for that here.

pos = event.getPosition()

Here we get the screen coordinates of the mouse cursor

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

This function gives us a FreeCAD vector (x,y,z) containing the 3D point that lies on the focal plane, just under our mouse cursor. If you are in camera view, imagine a ray coming from the camera, passing through the mouse cursor, and hitting the focal plane. There is our 3D point. If we are in orthogonal view, the ray is parallel to the view direction.

self.stack.append(point)

We add our new point to the stack

if len(self.stack) == 2:

Do we have enough points already? if yes, then let's draw the line!

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

Here we use the function Line() from the Part Module that creates a line from two FreeCAD vectors. Everything we create and modify inside the Part module, stays in the Part module. So, until now, we created a Line Part. It is not bound to any object of our active document, so nothing appears on the screen.

shape = l.toShape()

The FreeCAD document can only accept shapes from the Part module. Shapes are the most generic type of the Part module. So, we must convert our line to a shape before adding it to the document.

Part.show(shape)

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.

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

Since we are done with our line, let's remove the callback mechanism, that consumes precious CPU cycles.

Testing & Using the script

Acum, hai să salvăm scriptul într-un loc unde interpretul Python de la FreeCAD îl va găsi. Când importați module, interpretorul va căuta în următoarele locuri: căile de instalare python, directorul bin FreeCAD și toate directoarele modulelor FreeCAD. Deci, cea mai bună soluție este să creați un director nou într-unul din FreeCAD Mod directories și să salvați scriptul în el. De exemplu, să facem un director "MyScripts" și să salvăm scriptul nostru ca "exercise.py".

Acum, totul este gata, să începem FreeCAD, să creăm un nou document și, în interpretorul python, să tastăm:

import exercise

If no error message appear, that means our exercise script has been loaded. We can now check its contents with:

dir(exercise)

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:

exercise.line()

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?

Înregistrarea scriptului în interfața FreeCAD

Acum, pentru ca noul nostru instrument de trasat linii să fie foarte cool, ar trebui să aibă un buton pe interfață, deci să nu trebuiescă să tastăm toate acele lucruri de fiecare dată. Cea mai ușoară cale este să ne transformăm noul director MyScripts într-un atelier de lucru complet FreeCAD. Este ușor, tot ce este necesar este să plasați un fișier numit InitGui.py în directorul MyScripts. InitGui.py va conține instrucțiunile pentru a crea un nou atelier de lucru și va adăuga noul instrument. În plus, va trebui să transformăm un pic codul nostru de exerciții, astfel încât instrumentul line() este recunoscut ca o comandă oficială FreeCAD. Să începem prin a face un fișier InitGui.py și scrieți următorul cod în el:

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!

Deci vrei mai mult?

Dacă ți-a plăcut acest exercițiu, de ce să nu încerci să îmbunătățești acest mic instrument? Sunt multe lucruri care pot fi făcute, ca de exemplu:

  • Adăugați comentarii utilizatorilor: până acum am făcut un instrument foarte subțire îmbrăcat, utilizatorul ar putea fi un pic pierdut atunci când îl utilizează. Așadar, am putea adăuga comentarii, spunându-i ce să facă în continuare. De exemplu, puteți emite mesaje către consola FreeCAD. ”Aruncați o privire” în modulul FreeCAD.Console
  • Adăugați posibilitatea de a introduce manual coordonatele punctelor 3D. Uitați-vă la funcția python input(), de exemplu,
  • Adăugați posibilitatea de a adăuga mai mult de 2 puncte
  • Adăugați evenimente pentru alte lucruri: Acum verificăm doar evenimentele butonului mouse-ului, dacă am face și ceva când mouse-ul este mutat, cum ar fi afișarea coordonatelor curente?
  • Dați un nume obiectului creat și multe alte lucruri.

Nu ezitați să vă scrieți întrebările sau ideile pe forum!

Code snippets
Dialog creation