Line drawing function/it: Difference between revisions

From FreeCAD Documentation
(Created page with "Se non viene visualizzato nessun messaggio di errore, significa che il nostro script "exercise" è stato caricato. Ora possiamo controllare il suo contenuto con:")
(Updating to match new version of source page)
 
(42 intermediate revisions by 4 users not shown)
Line 1: Line 1:
<languages/>
Questa pagina mostra come si possono facilmente costruire delle funzionalità avanzate con Python.


{{TOCright}}
In questo esercizio, costruiremo un nuovo strumento che disegna una linea partendo da due punti cliccati nella vista 3D.

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

Questa pagina mostra come si possono facilmente creare delle funzionalità avanzate con Python.

In questo esercizio, andremo a creare un nuovo strumento che disegna una linea partendo da due punti cliccati nella vista 3D.


Questo strumento può essere collegato a un comando di FreeCAD, e tale comando può essere chiamato da un qualsiasi elemento dell'interfaccia, ad esempio da una voce di menu o da un pulsante in una barra degli strumenti.
Questo strumento può essere collegato a un comando di FreeCAD, e tale comando può essere chiamato da un qualsiasi elemento dell'interfaccia, ad esempio da una voce di menu o da un pulsante in una barra degli strumenti.


<span id="The_main_script"></span>
=== Lo script principale ===
==Lo script principale==

Per prima cosa scriviamo uno script che contenga tutta la nostra funzionalità. Dopo, salviamo questo script in un file e lo importiamo in FreeCAD, in modo che tutte le classi e le funzioni che scriviamo diventino disponibili in FreeCAD. Lanciamo perciò il nostro editor di testo preferito, e digitiamo le seguenti righe:

{{Code|code=
import FreeCADGui, Part
from pivy.coin import *

class line:


"""This class will create a line after the user clicked 2 points on the screen"""
Per prima cosa scriviamo uno script che contenga tutta la nostra funzionalità.


def __init__(self):
Dopo, salviamo questo script in un file e lo importiamo in FreeCAD, in modo che tutte le classi e le funzioni che scriviamo diventino disponibili in FreeCAD.
self.view = FreeCADGui.ActiveDocument.ActiveView
self.stack = []
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(), self.getpoint)


def getpoint(self, event_cb):
Lanciamo perciò il nostro editor di testo preferito, e digitiamo le seguenti righe:
event = event_cb.getEvent()
<syntaxhighlight>
if event.getState() == SoMouseButtonEvent.DOWN:
import FreeCADGui, Part
pos = event.getPosition()
from pivy.coin import *
point = self.view.getPoint(pos[0], pos[1])
self.stack.append(point)
class line:
if len(self.stack) == 2:
"this class will create a line after the user clicked 2 points on the screen"
l = Part.LineSegment(self.stack[0], self.stack[1])
def __init__(self):
self.view = FreeCADGui.ActiveDocument.ActiveView
shape = l.toShape()
self.stack = []
Part.show(shape)
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(), self.callback)
}}
{{Top}}
def getpoint(self,event_cb):
<span id="Detailed_explanation"></span>
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)
</syntaxhighlight>
=== Spiegazione dettagliata ===
=== Spiegazione dettagliata ===
<syntaxhighlight>
import Part, FreeCADGui
from pivy.coin import *
</syntaxhighlight>
In Python, quando si desidera utilizzare le funzioni di un altro modulo è necessario importarlo.


{{Code|code=
Nel nostro caso, abbiamo bisogno delle funzioni del [[Part Module/it|Modulo Parte]] per creare la linea, e del modulo Gui (FreeCADGui) per accedere alla visualizzazione 3D.
import Part, FreeCADGui
from pivy.coin import *
}}

In Python, quando si desidera utilizzare le funzioni di un altro modulo è necessario importarlo. Nel nostro caso, abbiamo bisogno delle funzioni del [[Part_Workbench/it|Ambiente Parte]] per creare la linea, e del modulo Gui {{incode|FreeCADGui}} per accedere alla [[3D_view/it|Vista 3D]]. Inoltre, abbiamo anche bisogno del contenuto completo della libreria di Coin, in modo da poter utilizzare direttamente tutti gli oggetti Coin, come, ad esempio, {{incode|SoMouseButtonEvent}}, ecc ..

{{Code|code=
class line:
}}


Inoltre, abbiamo anche bisogno del contenuto completo della libreria di Coin, in modo da poter utilizzare direttamente tutti gli oggetti Coin, come, ad esempio, SoMouseButtonEvent, ecc ..
<syntaxhighlight>
class line:
</syntaxhighlight>
Qui definiamo la nostra classe principale.
Qui definiamo la nostra classe principale.


Line 53: Line 62:


Una funzione termina quando il suo compito è stato svolto, invece un oggetto (una classe definisce un oggetto) rimane attivo finché non viene distrutto.
Una funzione termina quando il suo compito è stato svolto, invece un oggetto (una classe definisce un oggetto) rimane attivo finché non viene distrutto.
<syntaxhighlight>
"this class will create a line after the user clicked 2 points on the screen"
</syntaxhighlight>
In Python, ogni classe o funzione può avere una stringa di descrizione.


{{Code|code=
In FreeCAD, questo è particolarmente utile perché quando chiameremo la classe nell'interprete, la stringa di descrizione verrà visualizzata come tooltip (nota di descrizione o aiuto).
"""This class will create a line after the user clicked 2 points on the screen"""
<syntaxhighlight>
}}
def __init__(self):

</syntaxhighlight>
In Python, ogni classe o funzione può avere una stringa di documentazione (docstring). In FreeCAD, questo è particolarmente utile perché quando chiameremo la classe nell'interprete, la stringa di descrizione verrà visualizzata come tooltip (nota di descrizione o aiuto).
Le classi di Python possono sempre contenere una funzione __init__, che viene eseguita quando la classe viene chiamata per creare un oggetto. Quindi, metteremo qui tutto quello che vogliamo che accada quando il nostro strumento line (linea) inizia.

<syntaxhighlight>
{{Code|code=
self.view = FreeCADGui.ActiveDocument.ActiveView
def __init__(self):
</syntaxhighlight>
}}
In una classe, di solito si aggiunge ''self.'' prima di un nome di variabile, in modo che sia facilmente accessibile a tutte le funzioni dentro e fuori questa classe. In questo script, usiamo self.view per accedere e manipolare la vista 3D attiva.

<syntaxhighlight>
Le classi di Python possono sempre contenere una funzione {{incode|__init__}}, che viene eseguita quando la classe viene chiamata per creare un oggetto. Qui metteremo tutto quello che vogliamo che accada quando il nostro strumento line inizia.
self.stack = []

</syntaxhighlight>
{{Code|code=
Qui creiamo una lista vuota per archiviare i punti 3D inviati dalla funzione getpoint.
self.view = FreeCADGui.ActiveDocument.ActiveView
<syntaxhighlight>
}}
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)

</syntaxhighlight>
In una classe, di solito si aggiunge {{incode|self.}} prima di un nome di variabile, in modo che sia facilmente accessibile a tutte le funzioni dentro e fuori la classe. In questo script, usiamo {{incode|self.view}} per accedere e manipolare la vista 3D attiva.

{{Code|code=
self.stack = []
}}

Qui creiamo una lista vuota per archiviare i punti 3D inviati dalla funzione {{incode|getpoint()}}.

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

<div class="mw-translate-fuzzy">
Questa è la parte importante.
Questa è la parte importante.


Line 79: Line 97:


Nel nostro caso, stiamo creando un callback per gli eventi [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent] e li colleghiamo alla funzione getpoint. Adesso, ogni volta che un pulsante del mouse viene premuto o rilasciato, viene eseguita la funzione getpoint.
Nel nostro caso, stiamo creando un callback per gli eventi [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent] e li colleghiamo alla funzione getpoint. Adesso, ogni volta che un pulsante del mouse viene premuto o rilasciato, viene eseguita la funzione getpoint.
</div>


<div class="mw-translate-fuzzy">
Notare che alla addEventCallbackPivy() esiste anche un'alternativa, chiamata addEventCallback() la quale dispensa dall'uso di pivy. Ma, dal momento che pivy è un modo molto efficace e naturale per accedere a ogni parte di una scena di Coin, è meglio utilizzarlo il più possibile!
Notare che alla addEventCallbackPivy() esiste anche un'alternativa, chiamata addEventCallback() la quale dispensa dall'uso di pivy. Ma, dal momento che pivy è un modo molto efficace e naturale per accedere a ogni parte di una scena di Coin, è meglio utilizzarlo il più possibile!
</div>
<syntaxhighlight>
{{Top}}
def getpoint(self,event_cb):
{{Code|code=
</syntaxhighlight>
def getpoint(self, event_cb):
}}

<div class="mw-translate-fuzzy">
Ora definiamo la funzione getpoint, che sarà eseguita quando un pulsante del mouse verrà premuto in una vista 3D. Questa funzione riceverà un argomento, che chiamiamo event_cb.
Ora definiamo la funzione getpoint, che sarà eseguita quando un pulsante del mouse verrà premuto in una vista 3D. Questa funzione riceverà un argomento, che chiamiamo event_cb.


Line 89: Line 113:


Per maggiori informazioni sugli eventi controllabili consultate [[Code_snippets/it#Observing_mouse_events_in_the_3D_viewer_via_Python|questa pagina]].
Per maggiori informazioni sugli eventi controllabili consultate [[Code_snippets/it#Observing_mouse_events_in_the_3D_viewer_via_Python|questa pagina]].
</div>
<syntaxhighlight>

if event.getState() == SoMouseButtonEvent.DOWN:
{{Code|code=
</syntaxhighlight>
if event.getState() == SoMouseButtonEvent.DOWN:
}}

<div class="mw-translate-fuzzy">
La funzione getpoint viene chiamata ogni volta che un pulsante del mouse viene premuto o rilasciato. Invece, noi vogliamo definire un punto 3D solo quando viene premuto (altrimenti otteniamo due punti 3D molto vicini l'uno all'altro). Pertanto quì dobbiamo verificare e stabilire questo.
La funzione getpoint viene chiamata ogni volta che un pulsante del mouse viene premuto o rilasciato. Invece, noi vogliamo definire un punto 3D solo quando viene premuto (altrimenti otteniamo due punti 3D molto vicini l'uno all'altro). Pertanto quì dobbiamo verificare e stabilire questo.
</div>
<syntaxhighlight>

pos = event.getPosition()
{{Code|code=
</syntaxhighlight>
pos = event.getPosition()
}}

<div class="mw-translate-fuzzy">
Qui otteniamo le coordinate dello schermo nella posizione del cursore del mouse
Qui otteniamo le coordinate dello schermo nella posizione del cursore del mouse
</div>
<syntaxhighlight>

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

<div class="mw-translate-fuzzy">
Questa funzione ci fornisce un vettore di FreeCAD (x, y, z) contenente il punto 3D che giace sul piano focale, esattamente sotto il cursore del mouse. Se siamo in vista camera, immaginiamo un raggio proveniente dalla fotocamera, passante per il cursore del mouse, che colpisce il piano focale. Questo è il nostro punto 3D. Se siamo in vista ortogonale, il raggio è parallelo alla direzione di visualizzazione.
Questa funzione ci fornisce un vettore di FreeCAD (x, y, z) contenente il punto 3D che giace sul piano focale, esattamente sotto il cursore del mouse. Se siamo in vista camera, immaginiamo un raggio proveniente dalla fotocamera, passante per il cursore del mouse, che colpisce il piano focale. Questo è il nostro punto 3D. Se siamo in vista ortogonale, il raggio è parallelo alla direzione di visualizzazione.
</div>
<syntaxhighlight>

self.stack.append(point)
{{Code|code=
</syntaxhighlight>
self.stack.append(point)
}}

<div class="mw-translate-fuzzy">
Aggiungiamo il nostro nuovo punto nella pila (stack)
Aggiungiamo il nostro nuovo punto nella pila (stack)
</div>
<syntaxhighlight>

if len(self.stack) == 2:
{{Code|code=
</syntaxhighlight>
if len(self.stack) == 2:
}}

Abbiamo già abbastanza punti? Se sì, allora disegnamo la linea!
Abbiamo già abbastanza punti? Se sì, allora disegnamo la linea!

<syntaxhighlight>
{{Code|code=
l = Part.Line(self.stack[0],self.stack[1])
l = Part.LineSegment(self.stack[0], self.stack[1])
</syntaxhighlight>
}}
Qui usiamo la funzione Line() del [[Part Module/it|Modulo Parte]] che crea una linea da due vettori di FreeCAD.

<div class="mw-translate-fuzzy">
Qui usiamo la funzione Line() del [[Part_Workbench/it|Modulo Parte]] che crea una linea da due vettori di FreeCAD.


Tutto quanto che si crea e si modifica all'interno del modulo Parte, rimane nel modulo Parte. Quindi, fino ad ora, abbiamo creato una linea Parte. Essa non è legata ad alcun oggetto del nostro documento attivo, perciò sullo schermo non viene ancora visualizzato nulla.
Tutto quanto che si crea e si modifica all'interno del modulo Parte, rimane nel modulo Parte. Quindi, fino ad ora, abbiamo creato una linea Parte. Essa non è legata ad alcun oggetto del nostro documento attivo, perciò sullo schermo non viene ancora visualizzato nulla.
</div>
<syntaxhighlight>

shape = l.toShape()
{{Code|code=
</syntaxhighlight>
shape = l.toShape()
}}

<div class="mw-translate-fuzzy">
Il documento di FreeCAD può accettare solo shape (forme) dal modulo Parte. Le shape sono il tipo più generico di forme del modulo Part. Dobbiamo quindi convertire la nostra linea in una shape prima di poterla aggiunge al documento.
Il documento di FreeCAD può accettare solo shape (forme) dal modulo Parte. Le shape sono il tipo più generico di forme del modulo Part. Dobbiamo quindi convertire la nostra linea in una shape prima di poterla aggiunge al documento.
</div>
<syntaxhighlight>

Part.show(shape)
{{Code|code=
</syntaxhighlight>
Part.show(shape)
}}

<div class="mw-translate-fuzzy">
Il modulo Parte ha una funzione molto utile, show(), che crea un nuovo oggetto nel documento e collega ad esso una shape (forma).
Il modulo Parte ha una funzione molto utile, show(), che crea un nuovo oggetto nel documento e collega ad esso una shape (forma).


Possiamo anche creare prima un nuovo oggetto nel documento, poi associare manualmente ad esso la shape.
Possiamo anche creare prima un nuovo oggetto nel documento, poi associare manualmente ad esso la shape.
</div>
<syntaxhighlight>
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
</syntaxhighlight>
Siccome con la nostra linea abbiamo finito, terminiamo il meccanismo di callback, che consuma preziosi cicli di CPU.


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

<div class="mw-translate-fuzzy">
Siccome con la nostra linea abbiamo finito, terminiamo il meccanismo di callback, che consuma preziosi cicli di CPU.
</div>
{{Top}}
<span id="Testing_the_script"></span>
<div class="mw-translate-fuzzy">
=== Test e Utilizzo dello script ===
=== Test e Utilizzo dello script ===


Line 135: Line 196:


Durante l'importazione dei moduli, l'interprete punta nei seguenti luoghi: i percorsi di installazione di Python, la directory bin di FreeCAD, e tutte le directory dei moduli FreeCAD. Quindi, la soluzione migliore è quella di creare una nuova directory in una delle FreeCAD [[Installing_more_workbenches/it|Mod directories]], e salvare in essa il nostro script. Per esempio, creiamo una directory "MyScripts", e salviamo il nostro script come "exercise.py".
Durante l'importazione dei moduli, l'interprete punta nei seguenti luoghi: i percorsi di installazione di Python, la directory bin di FreeCAD, e tutte le directory dei moduli FreeCAD. Quindi, la soluzione migliore è quella di creare una nuova directory in una delle FreeCAD [[Installing_more_workbenches/it|Mod directories]], e salvare in essa il nostro script. Per esempio, creiamo una directory "MyScripts", e salviamo il nostro script come "exercise.py".
</div>


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 {{FileName|bin}} folder, and all FreeCAD {{FileName|Mod}} (module) folders. So the best solution is to create a new folder in one of the {{FileName|Mod}} folders. Let's create a {{FileName|MyScripts}} folder there and save our script in it as {{FileName|exercise.py}}.

<div class="mw-translate-fuzzy">
Adesso che tutto è pronto, basta avviare FreeCAD, creare un nuovo documento, e, nell'interprete Python, eseguire:
Adesso che tutto è pronto, basta avviare FreeCAD, creare un nuovo documento, e, nell'interprete Python, eseguire:
</div>
<syntaxhighlight>

import exercise
{{Code|code=
</syntaxhighlight>
import exercise
}}

<div class="mw-translate-fuzzy">
Se non viene visualizzato nessun messaggio di errore, significa che il nostro script "exercise" è stato caricato.
Se non viene visualizzato nessun messaggio di errore, significa che il nostro script "exercise" è stato caricato.


Ora possiamo controllare il suo contenuto con:
Ora possiamo controllare il suo contenuto con:
</div>
<syntaxhighlight>

dir(exercise)
{{Code|code=
</syntaxhighlight>
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:
}}
<syntaxhighlight>

exercise.line()
<div class="mw-translate-fuzzy">
</syntaxhighlight>
Il comando dir() è un comando integrato di Python che elenca il contenuto di un modulo. Possiamo vedere che la nostra classe line() è lì, in attesa.
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?

Non rimane che provarla scrivendo:
</div>

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

Now let's test it:

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

<div class="mw-translate-fuzzy">
Poi, clicchiamo due volte nella vista 3D, e bingo, ecco la nostra linea! Per farne una nuova, basta riscrivere ancora exercise.line(), e ancora, e ancora ... Siete contenti, no?
</div>
{{Top}}
<span id="Registering_the_script"></span>
<div class="mw-translate-fuzzy">
=== Includere lo script nell'interfaccia di FreeCAD ===

Per essere davvero efficace il nostro nuovo strumento linea (line) dovrebbe avere un suo pulsante sull'interfaccia, in modo da non dover digitare ogni volta tutte queste cose.

Il modo più semplice è quello di trasformare la nostra nuova directory MyScripts in un ambiente di lavoro completo di FreeCAD.

È facile. Basta solo inserire un file chiamato '''InitGui.py''' nella directory MyScripts. Il file InitGui.py conterrà le istruzioni per creare un nuovo ambiente di lavoro (workbench), poi aggiungere ad esso il nostro nuovo strumento.

Oltre a questo dobbiamo anche modificare un po' il nostro codice di exercise, in modo che lo strumento line() sia riconosciuto come un comando ufficiale di FreeCAD.

Cominciamo creando un file InitGui.py, e scriviamo in esso il seguente codice:
</div>

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 {{FileName|MyScripts}} folder into a full FreeCAD workbench. This is easy, all that is needed is to put a file called {{FileName|InitGui.py}} inside the {{FileName|MyScripts}} folder. {{FileName|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 {{incode|line()}} tool is recognized as an official FreeCAD command. Let's start by creating an {{FileName|InitGui.py}} file, and writing the following code in it:

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


<div class="mw-translate-fuzzy">
That's it, we now just need to restart FreeCAD and we'll have a nice new workbench with our brand new line tool!
A questo punto, penso che dovreste già capire da soli lo script precedente.


Creiamo una nuova classe che chiamiamo MyWorkbench, le diamo un titolo (MenuText), e definiamo una funzione Initialize() che verrà eseguita quando l'ambiente di lavoro verrà caricato in FreeCAD. In tale funzione, si carica il contenuto del nostro file exercise, e si aggiungono i comandi di FreeCAD contenuti in una lista di comandi.
==So you want more?==


Dopo, costruiamo una barra degli strumenti denominata "My Scripts" a cui assegniamo la nostra lista comandi. Al momento, ovviamente, disponiamo di un solo strumento, quindi la nostra lista dei comandi contiene un solo elemento.
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:Line_drawing_function|talk page]]!


Quando il nostro ambiente di lavoro è pronto, lo aggiungiamo all'interfaccia principale.
{{docnav|Code snippets|Dialog creation}}
</div>


<div class="mw-translate-fuzzy">
[[Category:Poweruser Documentation]]
Questo non basta ancora perché un comando di FreeCAD deve essere formattato in un certo modo per poter funzionare. Quindi è necessario modificare un po' il nostro strumento line().
[[Category:Python Code]]


Ora il nostro nuovo script exercise.py deve essere come questo:
</div>

{{Code|code=
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())
}}

<div class="mw-translate-fuzzy">
Quì abbiamo trasformato la nostra funzione __init__() in una funzione Activated(), perchè, quando i comandi di FreeCAD vengono eseguiti, essi eseguono automaticamente la funzione Activated().

Inoltre, abbiamo aggiunto una funzione GetResources(), che fornisce a FreeCAD le informazioni per trovare l'icona dello strumento, il nome e la descrizione (tooltip) del nostro strumento.

Qualunque immagine jpg, png o svg può fungere da icona, essa può essere di qualsiasi dimensione, ma è meglio usare una dimensione vicina all'aspetto finale, come, ad esempio, 16x16, 24x24 o 32x32.

Poi, abbiamo aggiunto la classe line() come un comando ufficiale di FreeCAD con il metodo AddCommand().
</div>

<div class="mw-translate-fuzzy">
Questo è tutto, ora basta riavviare FreeCAD e avremo un bell'ambiente di lavoro con il nostro nuovo strumento line()!
</div>
{{Top}}
<span id="So_you_want_more?"></span>
=== Cosa si può aggiungere? ===

<div class="mw-translate-fuzzy">
Se questo esercizio vi è piaciuto, perché non cercare di migliorare questo piccolo strumento? Si possono fare molte cose, come ad esempio:
* Aggiungere assistenza per gli utenti: fino ad ora abbiamo fatto uno strumento molto spoglio, l'utente potrebbe essere un po' disorientato quando lo utilizza. Perciò si potrebbe aggiungere qualche informazione, che suggerisca come procedere. Ad esempio, si potrebbero mostrare dei messaggi nella console di FreeCAD. In merito, visita il modulo FreeCAD.Console
* Aggiungere la possibilità di digitare manualmente le coordinate dei punti 3D. Guarda la funzione input() di Python, per esempio
* Aggiungere la possibilità di definire più di 2 punti
* Aggiungere controlli per altri eventi. Al momento verifichiamo solo gli eventi del pulsante del mouse, ma se ​​vogliamo anche fare qualcosa quando il mouse viene spostato oppure visualizzare le coordinate attuali?
* Assegnare un nome all'oggetto creato

Non esitate a scrivere le vostre domande o idee nel [http://forum.freecadweb.org/ forum]!
</div>

Don't hesitate to ask questions or share ideas on the [https://forum.freecadweb.org/ forum]!
{{Top}}
{{Powerdocnavi{{#translation:}}}}
[[Category:Developer Documentation{{#translation:}}]]
[[Category:Python Code{{#translation:}}]]
{{clear}}
{{clear}}
<languages/>

Latest revision as of 12:57, 19 March 2024

Introduzione

Questa pagina mostra come si possono facilmente creare delle funzionalità avanzate con Python.

In questo esercizio, andremo a creare un nuovo strumento che disegna una linea partendo da due punti cliccati nella vista 3D.

Questo strumento può essere collegato a un comando di FreeCAD, e tale comando può essere chiamato da un qualsiasi elemento dell'interfaccia, ad esempio da una voce di menu o da un pulsante in una barra degli strumenti.

Lo script principale

Per prima cosa scriviamo uno script che contenga tutta la nostra funzionalità. Dopo, salviamo questo script in un file e lo importiamo in FreeCAD, in modo che tutte le classi e le funzioni che scriviamo diventino disponibili in FreeCAD. Lanciamo perciò il nostro editor di testo preferito, e digitiamo le seguenti righe:

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)

Inizio

Spiegazione dettagliata

import Part, FreeCADGui
from pivy.coin import *

In Python, quando si desidera utilizzare le funzioni di un altro modulo è necessario importarlo. Nel nostro caso, abbiamo bisogno delle funzioni del Ambiente Parte per creare la linea, e del modulo Gui FreeCADGui per accedere alla Vista 3D. Inoltre, abbiamo anche bisogno del contenuto completo della libreria di Coin, in modo da poter utilizzare direttamente tutti gli oggetti Coin, come, ad esempio, SoMouseButtonEvent, ecc ..

class line:

Qui definiamo la nostra classe principale.

Perché utilizzare una classe e non una funzione? Il motivo è che abbiamo bisogno che il nostro strumento rimanga "vivo" mentre aspettiamo che l'utente clicchi sullo schermo.

Una funzione termina quando il suo compito è stato svolto, invece un oggetto (una classe definisce un oggetto) rimane attivo finché non viene distrutto.

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

In Python, ogni classe o funzione può avere una stringa di documentazione (docstring). In FreeCAD, questo è particolarmente utile perché quando chiameremo la classe nell'interprete, la stringa di descrizione verrà visualizzata come tooltip (nota di descrizione o aiuto).

def __init__(self):

Le classi di Python possono sempre contenere una funzione __init__, che viene eseguita quando la classe viene chiamata per creare un oggetto. Qui metteremo tutto quello che vogliamo che accada quando il nostro strumento line inizia.

self.view = FreeCADGui.ActiveDocument.ActiveView

In una classe, di solito si aggiunge self. prima di un nome di variabile, in modo che sia facilmente accessibile a tutte le funzioni dentro e fuori la classe. In questo script, usiamo self.view per accedere e manipolare la vista 3D attiva.

self.stack = []

Qui creiamo una lista vuota per archiviare i punti 3D inviati dalla funzione getpoint().

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

Questa è la parte importante.

Dato che in realtà si tratta una scena Coin3d, FreeCAD utilizza il meccanismo di callback (richiamo) di Coin, il quale permette di chiamare una funzione ogni volta che nella scena accade un determinato evento.

Nel nostro caso, stiamo creando un callback per gli eventi SoMouseButtonEvent e li colleghiamo alla funzione getpoint. Adesso, ogni volta che un pulsante del mouse viene premuto o rilasciato, viene eseguita la funzione getpoint.

Notare che alla addEventCallbackPivy() esiste anche un'alternativa, chiamata addEventCallback() la quale dispensa dall'uso di pivy. Ma, dal momento che pivy è un modo molto efficace e naturale per accedere a ogni parte di una scena di Coin, è meglio utilizzarlo il più possibile!

Inizio

def getpoint(self, event_cb):

Ora definiamo la funzione getpoint, che sarà eseguita quando un pulsante del mouse verrà premuto in una vista 3D. Questa funzione riceverà un argomento, che chiamiamo event_cb.

Da questo evento callback possiamo accedere all'oggetto event, che contiene diverse parti di informazioni

Per maggiori informazioni sugli eventi controllabili consultate questa pagina.

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

La funzione getpoint viene chiamata ogni volta che un pulsante del mouse viene premuto o rilasciato. Invece, noi vogliamo definire un punto 3D solo quando viene premuto (altrimenti otteniamo due punti 3D molto vicini l'uno all'altro). Pertanto quì dobbiamo verificare e stabilire questo.

pos = event.getPosition()

Qui otteniamo le coordinate dello schermo nella posizione del cursore del mouse

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

Questa funzione ci fornisce un vettore di FreeCAD (x, y, z) contenente il punto 3D che giace sul piano focale, esattamente sotto il cursore del mouse. Se siamo in vista camera, immaginiamo un raggio proveniente dalla fotocamera, passante per il cursore del mouse, che colpisce il piano focale. Questo è il nostro punto 3D. Se siamo in vista ortogonale, il raggio è parallelo alla direzione di visualizzazione.

self.stack.append(point)

Aggiungiamo il nostro nuovo punto nella pila (stack)

if len(self.stack) == 2:

Abbiamo già abbastanza punti? Se sì, allora disegnamo la linea!

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

Qui usiamo la funzione Line() del Modulo Parte che crea una linea da due vettori di FreeCAD.

Tutto quanto che si crea e si modifica all'interno del modulo Parte, rimane nel modulo Parte. Quindi, fino ad ora, abbiamo creato una linea Parte. Essa non è legata ad alcun oggetto del nostro documento attivo, perciò sullo schermo non viene ancora visualizzato nulla.

shape = l.toShape()

Il documento di FreeCAD può accettare solo shape (forme) dal modulo Parte. Le shape sono il tipo più generico di forme del modulo Part. Dobbiamo quindi convertire la nostra linea in una shape prima di poterla aggiunge al documento.

Part.show(shape)

Il modulo Parte ha una funzione molto utile, show(), che crea un nuovo oggetto nel documento e collega ad esso una shape (forma).

Possiamo anche creare prima un nuovo oggetto nel documento, poi associare manualmente ad esso la shape.

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

Siccome con la nostra linea abbiamo finito, terminiamo il meccanismo di callback, che consuma preziosi cicli di CPU.

Inizio

Test e Utilizzo dello script

Ora, salviamo il nostro script in qualche posizione in cui l'interprete Python di FreeCAD possa trovarlo.

Durante l'importazione dei moduli, l'interprete punta nei seguenti luoghi: i percorsi di installazione di Python, la directory bin di FreeCAD, e tutte le directory dei moduli FreeCAD. Quindi, la soluzione migliore è quella di creare una nuova directory in una delle FreeCAD Mod directories, e salvare in essa il nostro script. Per esempio, creiamo una directory "MyScripts", e salviamo il nostro script come "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.

Adesso che tutto è pronto, basta avviare FreeCAD, creare un nuovo documento, e, nell'interprete Python, eseguire:

import exercise

Se non viene visualizzato nessun messaggio di errore, significa che il nostro script "exercise" è stato caricato.

Ora possiamo controllare il suo contenuto con:

dir(exercise)

Il comando dir() è un comando integrato di Python che elenca il contenuto di un modulo. Possiamo vedere che la nostra classe line() è lì, in attesa.

Non rimane che provarla scrivendo:

'line' in dir(exercise)

Now let's test it:

exercise.line()

Poi, clicchiamo due volte nella vista 3D, e bingo, ecco la nostra linea! Per farne una nuova, basta riscrivere ancora exercise.line(), e ancora, e ancora ... Siete contenti, no?

Inizio

Includere lo script nell'interfaccia di FreeCAD

Per essere davvero efficace il nostro nuovo strumento linea (line) dovrebbe avere un suo pulsante sull'interfaccia, in modo da non dover digitare ogni volta tutte queste cose.

Il modo più semplice è quello di trasformare la nostra nuova directory MyScripts in un ambiente di lavoro completo di FreeCAD.

È facile. Basta solo inserire un file chiamato InitGui.py nella directory MyScripts. Il file InitGui.py conterrà le istruzioni per creare un nuovo ambiente di lavoro (workbench), poi aggiungere ad esso il nostro nuovo strumento.

Oltre a questo dobbiamo anche modificare un po' il nostro codice di exercise, in modo che lo strumento line() sia riconosciuto come un comando ufficiale di FreeCAD.

Cominciamo creando un file InitGui.py, e scriviamo in esso il seguente codice:

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

A questo punto, penso che dovreste già capire da soli lo script precedente.

Creiamo una nuova classe che chiamiamo MyWorkbench, le diamo un titolo (MenuText), e definiamo una funzione Initialize() che verrà eseguita quando l'ambiente di lavoro verrà caricato in FreeCAD. In tale funzione, si carica il contenuto del nostro file exercise, e si aggiungono i comandi di FreeCAD contenuti in una lista di comandi.

Dopo, costruiamo una barra degli strumenti denominata "My Scripts" a cui assegniamo la nostra lista comandi. Al momento, ovviamente, disponiamo di un solo strumento, quindi la nostra lista dei comandi contiene un solo elemento.

Quando il nostro ambiente di lavoro è pronto, lo aggiungiamo all'interfaccia principale.

Questo non basta ancora perché un comando di FreeCAD deve essere formattato in un certo modo per poter funzionare. Quindi è necessario modificare un po' il nostro strumento line().

Ora il nostro nuovo script exercise.py deve essere come questo:

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ì abbiamo trasformato la nostra funzione __init__() in una funzione Activated(), perchè, quando i comandi di FreeCAD vengono eseguiti, essi eseguono automaticamente la funzione Activated().

Inoltre, abbiamo aggiunto una funzione GetResources(), che fornisce a FreeCAD le informazioni per trovare l'icona dello strumento, il nome e la descrizione (tooltip) del nostro strumento.

Qualunque immagine jpg, png o svg può fungere da icona, essa può essere di qualsiasi dimensione, ma è meglio usare una dimensione vicina all'aspetto finale, come, ad esempio, 16x16, 24x24 o 32x32.

Poi, abbiamo aggiunto la classe line() come un comando ufficiale di FreeCAD con il metodo AddCommand().

Questo è tutto, ora basta riavviare FreeCAD e avremo un bell'ambiente di lavoro con il nostro nuovo strumento line()!

Inizio

Cosa si può aggiungere?

Se questo esercizio vi è piaciuto, perché non cercare di migliorare questo piccolo strumento? Si possono fare molte cose, come ad esempio:

  • Aggiungere assistenza per gli utenti: fino ad ora abbiamo fatto uno strumento molto spoglio, l'utente potrebbe essere un po' disorientato quando lo utilizza. Perciò si potrebbe aggiungere qualche informazione, che suggerisca come procedere. Ad esempio, si potrebbero mostrare dei messaggi nella console di FreeCAD. In merito, visita il modulo FreeCAD.Console
  • Aggiungere la possibilità di digitare manualmente le coordinate dei punti 3D. Guarda la funzione input() di Python, per esempio
  • Aggiungere la possibilità di definire più di 2 punti
  • Aggiungere controlli per altri eventi. Al momento verifichiamo solo gli eventi del pulsante del mouse, ma se ​​vogliamo anche fare qualcosa quando il mouse viene spostato oppure visualizzare le coordinate attuali?
  • Assegnare un nome all'oggetto creato

Non esitate a scrivere le vostre domande o idee nel forum!

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

Inizio