Line drawing function/sv: Difference between revisions

From FreeCAD Documentation
(Created page with "Vi lägger till vår nya punkt till stacken")
(Updating to match new version of source page)
(29 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages/>

{{TOCright}}

==Introduction==

<div class="mw-translate-fuzzy">
Denna sida visar hur lätt avancerad funktionalitet kan byggas i Python. I denna övning, så kommer vi att bygga ett nytt verktyg som ritar en linje. Detta verktyg kan sedan länkas till ett FreeCAD kommando, och det kommandot kan anropas av ett element i gränssnittet, som en menypunkt eller en knapp i en verktygslåda.
Denna sida visar hur lätt avancerad funktionalitet kan byggas i Python. I denna övning, så kommer vi att bygga ett nytt verktyg som ritar en linje. Detta verktyg kan sedan länkas till ett FreeCAD kommando, och det kommandot kan anropas av ett element i gränssnittet, som en menypunkt eller en knapp i en verktygslåda.
</div>


<div class="mw-translate-fuzzy">
==Huvudskriptet==
==Huvudskriptet==




Först kommer vi att skriva ett skript som innehåller all vår funktionalitet. Sedan kommer vi att spara detta i en fil, och importera den i FreeCAD, så att alla klasser och funktioner som vi skriver kommer att vara tillgängliga för FreeCAD. Så, starta din favorit textredigerare, , och skriv följande rader:
Först kommer vi att skriva ett skript som innehåller all vår funktionalitet. Sedan kommer vi att spara detta i en fil, och importera den i FreeCAD, så att alla klasser och funktioner som vi skriver kommer att vara tillgängliga för FreeCAD. Så, starta din favorit textredigerare, , och skriv följande rader:
</div>
<syntaxhighlight>

import FreeCADGui, Part
First we will write a script containing all our functionality. Then we will save this in a file and import it in FreeCAD to make all its classes and functions available. Launch your favorite code editor and type the following lines:
from pivy.coin import *

{{Code|code=
class line:
import FreeCADGui, Part
"this class will create a line after the user clicked 2 points on the screen"
from pivy.coin import *
def __init__(self):

self.view = FreeCADGui.ActiveDocument.ActiveView
class line:
self.stack = []

self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint)
"""This class will create a line after the user clicked 2 points on the screen"""

def getpoint(self,event_cb):
def __init__(self):
event = event_cb.getEvent()
self.view = FreeCADGui.ActiveDocument.ActiveView
if event.getState() == SoMouseButtonEvent.DOWN:
pos = event.getPosition()
self.stack = []
point = self.view.getPoint(pos[0],pos[1])
self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(), self.getpoint)

self.stack.append(point)
if len(self.stack) == 2:
def getpoint(self, event_cb):
l = Part.Line(self.stack[0],self.stack[1])
event = event_cb.getEvent()
shape = l.toShape()
if event.getState() == SoMouseButtonEvent.DOWN:
Part.show(shape)
pos = event.getPosition()
self.view.removeEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.callback)
point = self.view.getPoint(pos[0], pos[1])
self.stack.append(point)
</syntaxhighlight>
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)
}}

[[#top|top]]

==Detaljerad förklaring ==
==Detaljerad förklaring ==

<syntaxhighlight>
{{Code|code=
import Part, FreeCADGui
import Part, FreeCADGui
from pivy.coin import *
from pivy.coin import *
</syntaxhighlight>
}}

<div class="mw-translate-fuzzy">
När du vill använda funktioner från en annan modul I Python, så behöver du importera den. I vårt fall, så behöver vi funktioner från [[Part Module/sv|Del Modulen]], för att skapa linjen, och från gränssnittsmodulen (FreeCADGui), för att komma åt 3D vyn. Vi behöver även innehållet i coin biblioteket, så vi kan använda alla coin objekt som SoMouseButtonEvent, etc...
När du vill använda funktioner från en annan modul I Python, så behöver du importera den. I vårt fall, så behöver vi funktioner från [[Part Module/sv|Del Modulen]], för att skapa linjen, och från gränssnittsmodulen (FreeCADGui), för att komma åt 3D vyn. Vi behöver även innehållet i coin biblioteket, så vi kan använda alla coin objekt som SoMouseButtonEvent, etc...
</div>
<syntaxhighlight>

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

Här definierar vi vår huvudklass. Varför använder vi en klass och inte en funktion? Skälet är att vårt verktyg behöver "stanna vid liv" medan vi väntar på att användaren ska klicka på skärmen. En funktion avslutas när dess uppgift är klar, men ett objekt (en klass definierar ett objekt) stannar vid liv ända tills den förstörs.
Här definierar vi vår huvudklass. Varför använder vi en klass och inte en funktion? Skälet är att vårt verktyg behöver "stanna vid liv" medan vi väntar på att användaren ska klicka på skärmen. En funktion avslutas när dess uppgift är klar, men ett objekt (en klass definierar ett objekt) stannar vid liv ända tills den förstörs.

<syntaxhighlight>
{{Code|code=
"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"""
</syntaxhighlight>
}}

<div class="mw-translate-fuzzy">
I Python, så kan varje klass eller funktion ha en beskrivningssträng. Detta är speciellt användbart i FreeCAD, därför att när du anropar den klassen i tolken, så visas beskrivningssträngen som ett verktygstips.
I Python, så kan varje klass eller funktion ha en beskrivningssträng. Detta är speciellt användbart i FreeCAD, därför att när du anropar den klassen i tolken, så visas beskrivningssträngen som ett verktygstips.
</div>
<syntaxhighlight>

def __init__(self):
{{Code|code=
</syntaxhighlight>
def __init__(self):
}}

<div class="mw-translate-fuzzy">
Python klasser kan alltid innehålla en __init__ funktion, vilken utförs när klassen anropas för att skapa ett objekt. Så här lägger vi allt som vi vill ska hända när vårt linjeverktyg börjar.
Python klasser kan alltid innehålla en __init__ funktion, vilken utförs när klassen anropas för att skapa ett objekt. Så här lägger vi allt som vi vill ska hända när vårt linjeverktyg börjar.
</div>
<syntaxhighlight>

self.view = FreeCADGui.ActiveDocument.ActiveView
{{Code|code=
</syntaxhighlight>
self.view = FreeCADGui.ActiveDocument.ActiveView
}}

<div class="mw-translate-fuzzy">
I en klass, så vill du vanligtvis lägga till ''self.'' innan ett variabelnamn, så att den kan kommas åt lätt för alla funktioner inuti och utanför den klassen. Här kommer vi att använda self.view för att komma åt och manipulera den aktiva 3D vyn.
I en klass, så vill du vanligtvis lägga till ''self.'' innan ett variabelnamn, så att den kan kommas åt lätt för alla funktioner inuti och utanför den klassen. Här kommer vi att använda self.view för att komma åt och manipulera den aktiva 3D vyn.
</div>
<syntaxhighlight>

self.stack = []
{{Code|code=
</syntaxhighlight>
self.stack = []
}}

<div class="mw-translate-fuzzy">
Här skapar vi en tom lista som kommer att innehålla 3D punkterna som sänds av getpoint funktionen.
Här skapar vi en tom lista som kommer att innehålla 3D punkterna som sänds av getpoint funktionen.
</div>
<syntaxhighlight>

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

<div class="mw-translate-fuzzy">
Detta är den viktiga delen: Eftersom det egentligen är en [http://www.coin3d.org/ coin3D] scen, så använder FreeCAD coin's återanropsmekanism, som tillåter en funktion att anropas varje gång en viss scenhändelse inträffar. I vårt fall, så skapar vi ett återanrop för [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent] händelser, och vi binder den till getpoint funktionen. Nu, varje gång som en musknapp trycks ned eller släpps, så kommer getpoint funktionen att utföras.
Detta är den viktiga delen: Eftersom det egentligen är en [http://www.coin3d.org/ coin3D] scen, så använder FreeCAD coin's återanropsmekanism, som tillåter en funktion att anropas varje gång en viss scenhändelse inträffar. I vårt fall, så skapar vi ett återanrop för [http://doc.coin3d.org/Coin/group__events.html SoMouseButtonEvent] händelser, och vi binder den till getpoint funktionen. Nu, varje gång som en musknapp trycks ned eller släpps, så kommer getpoint funktionen att utföras.
</div>


<div class="mw-translate-fuzzy">
Notera att det finns ett alternativ till addEventCallbackPivy() kallat addEventCallback() vilket dispenserar bruket av pivy. Men eftersom pivy är en mycket effektiv och naturlig väg att komma åt en del av coin scenen, så är det mycket bättre att använda den så mycket du kan!
Notera att det finns ett alternativ till addEventCallbackPivy() kallat addEventCallback() vilket dispenserar bruket av pivy. Men eftersom pivy är en mycket effektiv och naturlig väg att komma åt en del av coin scenen, så är det mycket bättre att använda den så mycket du kan!
</div>
<syntaxhighlight>

def getpoint(self,event_cb):
[[#top|top]]
</syntaxhighlight>

{{Code|code=
def getpoint(self, event_cb):
}}

<div class="mw-translate-fuzzy">
Nu definierar vi getpoint funktionen, som kommer utföras när en musknapp trycks ned i en 3D vy. Denna funktion kommer att ta emot ett argument, som vi kommer kalla event_cb. Från denna händelses återanrop kan vi komma åt händelseobjektet, vilket innehåller flera informationsbitar (läge info [[Code_snippets#Observing_mouse_events_in_the_3D_viewer_via_Python/sv|här]]).
Nu definierar vi getpoint funktionen, som kommer utföras när en musknapp trycks ned i en 3D vy. Denna funktion kommer att ta emot ett argument, som vi kommer kalla event_cb. Från denna händelses återanrop kan vi komma åt händelseobjektet, vilket innehåller flera informationsbitar (läge info [[Code_snippets#Observing_mouse_events_in_the_3D_viewer_via_Python/sv|här]]).
</div>
<syntaxhighlight>

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

<div class="mw-translate-fuzzy">
Getpoint funktionen kommer att anropas när en musknapp trycks ned eller släpps. Men vi vill bara välja en 3D punkt när den trycks ned (annars skulle vi få två 3D punkter mycket nära varann). Så vi måste kontrollera det här.
Getpoint funktionen kommer att anropas när en musknapp trycks ned eller släpps. Men vi vill bara välja en 3D punkt när den trycks ned (annars skulle vi få två 3D punkter mycket nära varann). Så vi måste kontrollera det här.
</div>
<syntaxhighlight>

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

<div class="mw-translate-fuzzy">
Här får vi musmarkörens skärmkoordinater
Här får vi musmarkörens skärmkoordinater
</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">
Denna funktion ger oss en FreeCAD vektor (x,y,z) som innehåller den 3D punkt som ligger på fokalplanet, precis under vår musmarkör. om du är i kameravy, tänk dig en stråle som kommer från kameran, och passerar genom musmarkören, och träffar fokalplanet. Där är vår 3D punkt. On vi är i ortogonal vy, så är strålen parallell med vyriktningen.
Denna funktion ger oss en FreeCAD vektor (x,y,z) som innehåller den 3D punkt som ligger på fokalplanet, precis under vår musmarkör. om du är i kameravy, tänk dig en stråle som kommer från kameran, och passerar genom musmarkören, och träffar fokalplanet. Där är vår 3D punkt. On vi är i ortogonal vy, så är strålen parallell med vyriktningen.
</div>
<syntaxhighlight>

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

<div class="mw-translate-fuzzy">
Vi lägger till vår nya punkt till stacken
Vi lägger till vår nya punkt till stacken
</div>
<syntaxhighlight>
if len(self.stack) == 2:
</syntaxhighlight>
Do we have enough points already? if yes, then let's draw the line!
<syntaxhighlight>
l = Part.Line(self.stack[0],self.stack[1])
</syntaxhighlight>
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.
<syntaxhighlight>
shape = l.toShape()
</syntaxhighlight>
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.
<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==
if len(self.stack) == 2:
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".
}}


Har vi tillräckligt med punkter? Om ja, låt oss då rita linjen!
Now, everything is ready, let's start FreeCAD, create a new document, and, in the python interpreter, issue:

<syntaxhighlight>
{{Code|code=
import exercise
l = Part.LineSegment(self.stack[0], self.stack[1])
</syntaxhighlight>
}}
If no error message appear, that means our exercise script has been loaded. We can now check its contents with:

<syntaxhighlight>
<div class="mw-translate-fuzzy">
dir(exercise)
Här använder vi funktionen Line() från [[Part Module/sv|Del Modulen]] som skapar en linje från två FreeCAD vektorer. Allt som vi skapar och ändrar inuti Del modulen, stannar i Del modulen. Så, tills nu, så har vi skapat en Linje Del. Den är inte bunden till något objekt i vårt aktiva dokument, så inget syns på skärmen.
</syntaxhighlight>
</div>
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>
{{Code|code=
exercise.line()
shape = l.toShape()
</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?

<div class="mw-translate-fuzzy">
FreeCAD dokumentet kan bara acceptera former från Del modulen. Former är Del modulens mest allmäna typ. Så vi behöver konvertera vår linje till en form innan vi lägger till den till dokumentet.
</div>

{{Code|code=
Part.show(shape)
}}

<div class="mw-translate-fuzzy">
Del modulen har en mycket smidig show() funktion som skapar ett nytt objekt i dokumentet och binder en form till den. Vi skulle också kunna ha skapat ett nytt objekt i dokumentet först, och sedan ha bundit formen till den manuellt.
</div>

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

<div class="mw-translate-fuzzy">
Eftersom vi är klara med vår linje, låt oss ta bort vår återanropsmekanism, som använder värdefulla CPU cykler.
</div>

[[#top|top]]

<div class="mw-translate-fuzzy">
==Testa & använda skriptet==

Låt oss nu spara vårt skript på någon plats där FreeCAD's python tolk kommer att hitta den. När moduler importerass, så kommer tolken att leta på följande platser: python's installationssökväg, FreeCAD bin katalogen, och alla FreeCAD modulkataloger. Så, den bästa lösningen är att skapa en ny katalog i en av FreeCAD's [[Installing_more_workbenches/sv|Mod kataloger]], och spara vårt skript i den. Låt oss till exempel skapa en "MinaSkript" katalog, och spara vårt skript som "ovning.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">
Nu är allt klart, låt oss starta FreeCAD, skapa ett nytt dokument, och i pythontolken skriva:
</div>

{{Code|code=
import exercise
}}

<div class="mw-translate-fuzzy">
Om inga felmeddelanden kommer fram, så innebär det att vårt övningsskript har laddats. Vi kan nu kontrollera dess innehåll med :
</div>

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

<div class="mw-translate-fuzzy">
Kommandot dir() är ett inbyggt python kommando som listar modulens innehåll. Vi kan se att vår line() klass är där, som väntar på oss. Låt oss nu testa den:
</div>

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

Now let's test it:

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

<div class="mw-translate-fuzzy">
Klicka sedan på två olika ställen i 3D vyn, och bingo, där är vår linje! För att göra det igen, skriv bara ovning.line() igen, och igen, och igen... Känns det bra?
</div>

[[#top|top]]

<div class="mw-translate-fuzzy">
==Registrera skriptet i FreeCAD's gränssnitt==
För att vårt nya linjeverktyg ska bli ännu häftigare, så borde det ha en knapp i gränssnittet, så vi inte behöver skriva allt det varje gång. Den lättaste vägen är att omvandla vår nya MinaSkript katalog till en full FreeCAD arbetsbänk. Det är lätt, allt som behövs är att lägga in en fil som kallas '''InitGui.py''' inuti din MinaSkript katalog. InitGui.py kommer att innehålla instruktioner för att skapa en ny arbetsbänk, och addera vårt nya verktyg till den. Förutom det så behöver vi ändra vår övningskod lite, så att line() verktyget igenkänns som ett officiellt FreeCAD kommando. Låt oss starta genom att göra en InitGui.py fil, och skriva följande kod i den:
</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!
Jag tror att du redan nu förstår det ovanstående skriptet: Vi skapar en ny klass som vi kallar MinArbetsbänk, vi ger den en titel (MenuText), och vi definierar en Initialize() funktion som kommer att utföras när arbetsbänken laddas till FreeCAD. I den funktionen så laddar vi innehållet i vår övningsfil, och lägger till de FreeCAD kommandon som hittas i det i en kommandolista. Sedan gör vi en verktygslåda som kallas "Mina Skript" och vi tilldelar vår kommandolista till den. För närvarande har vi förstås endast ett verktyg, så vår kommandolista innehåller endast ett element. Sedan, när vår arbetsbänk är klar, så lägger vi till den till huvudgränssnittet.
</div>


<div class="mw-translate-fuzzy">
==So you want more?==
Men det kommer fortfarande inte att fungera, därför att ett FreeCAD kommando måste formatteras på ett visst sätt för att fungera. så vi behöver ändra vårt line() verktyg lite. Vår nya ovning.py skript kommer nu att se ut så här:
</div>


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

<div class="mw-translate-fuzzy">
Vad vi gjorde här var att omvandla vår __init__() funktion till en Activated() funktion, därför att när FreeCAD kommandon körs, så utför de automatiskt Activated() funktionen. Vi lade också till en GetResources() funktion, som informerar FreeCAD var den kan hitta en ikon för verktyget, och namnet och verktygstipset för vårt verktyg. en jpg, png eller svg bild kommer att fungera som en ikon, den kan vara av valfri storlek, men det är bäst att använda en storlek osm är nära den slutliga, som 16x16, 24x24 eller 32x32.

Sedan lägger vi till line() klassen som ett officiellt FreeCAD kommando med addCommand() metoden.
</div>

<div class="mw-translate-fuzzy">
Klart, nu behöver vi bara starta om FreeCAD och vi kommer att ha en ny snygg arbetsbänk med vårt splitternya verktyg!
</div>

[[#top|top]]

==Så du vill ha mer?==

<div class="mw-translate-fuzzy">
Om du gillade denna övningen, varför inte försöka förbättra detta lilla verktyg? Det finns många saker som kan göras, som till exempel:

* Lägga till feedback för användaren: nu är det ett mycket 'naket' verktyg, så användaren kan bli lite osäker när det används. Så vi skulle kunna lägga till lite feedback, tala om vad som ska göras. Du skulle till exempel kunna mata ur meddelanden till FreeCAD konsolen. Ta en titt i FreeCAD.Console modulen
* Lägga till en möjlighet att skriva in 3D punkterna manuellt. Titta på python input() funktionen, till exempel
* Lägga till möjligheten att lägga till mer än 2 punkter
* Lägga till händelser för andra saker: Nu kontrollerar vi endast musknappshändelser, men om vi också kunde göra något när musen förflyttas, som att visa koordinater?
* Ge ett namn till det skapade objektet

Tveka inte att skriva dina frågor eller ideer på [http://forum.freecadweb.org/ forum]!
</div>

Don't hesitate to ask questions or share ideas on the [https://forum.freecadweb.org/ forum]!

[[#top|top]]

{{Powerdocnavi{{#translation:}}}}
[[Category:Developer Documentation{{#translation:}}]]
[[Category:Python Code{{#translation:}}]]
{{clear}}
{{clear}}
<languages/>

Revision as of 21:07, 23 August 2020

Introduction

Denna sida visar hur lätt avancerad funktionalitet kan byggas i Python. I denna övning, så kommer vi att bygga ett nytt verktyg som ritar en linje. Detta verktyg kan sedan länkas till ett FreeCAD kommando, och det kommandot kan anropas av ett element i gränssnittet, som en menypunkt eller en knapp i en verktygslåda.

Huvudskriptet

Först kommer vi att skriva ett skript som innehåller all vår funktionalitet. Sedan kommer vi att spara detta i en fil, och importera den i FreeCAD, så att alla klasser och funktioner som vi skriver kommer att vara tillgängliga för FreeCAD. Så, starta din favorit textredigerare, , och skriv följande rader:

First we will write a script containing all our functionality. Then we will save this in a file and import it in FreeCAD to make all its classes and functions available. Launch your favorite code editor and type the following lines:

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)

top

Detaljerad förklaring

import Part, FreeCADGui
from pivy.coin import *

När du vill använda funktioner från en annan modul I Python, så behöver du importera den. I vårt fall, så behöver vi funktioner från Del Modulen, för att skapa linjen, och från gränssnittsmodulen (FreeCADGui), för att komma åt 3D vyn. Vi behöver även innehållet i coin biblioteket, så vi kan använda alla coin objekt som SoMouseButtonEvent, etc...

class line:

Här definierar vi vår huvudklass. Varför använder vi en klass och inte en funktion? Skälet är att vårt verktyg behöver "stanna vid liv" medan vi väntar på att användaren ska klicka på skärmen. En funktion avslutas när dess uppgift är klar, men ett objekt (en klass definierar ett objekt) stannar vid liv ända tills den förstörs.

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

I Python, så kan varje klass eller funktion ha en beskrivningssträng. Detta är speciellt användbart i FreeCAD, därför att när du anropar den klassen i tolken, så visas beskrivningssträngen som ett verktygstips.

def __init__(self):

Python klasser kan alltid innehålla en __init__ funktion, vilken utförs när klassen anropas för att skapa ett objekt. Så här lägger vi allt som vi vill ska hända när vårt linjeverktyg börjar.

self.view = FreeCADGui.ActiveDocument.ActiveView

I en klass, så vill du vanligtvis lägga till self. innan ett variabelnamn, så att den kan kommas åt lätt för alla funktioner inuti och utanför den klassen. Här kommer vi att använda self.view för att komma åt och manipulera den aktiva 3D vyn.

self.stack = []

Här skapar vi en tom lista som kommer att innehålla 3D punkterna som sänds av getpoint funktionen.

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

Detta är den viktiga delen: Eftersom det egentligen är en coin3D scen, så använder FreeCAD coin's återanropsmekanism, som tillåter en funktion att anropas varje gång en viss scenhändelse inträffar. I vårt fall, så skapar vi ett återanrop för SoMouseButtonEvent händelser, och vi binder den till getpoint funktionen. Nu, varje gång som en musknapp trycks ned eller släpps, så kommer getpoint funktionen att utföras.

Notera att det finns ett alternativ till addEventCallbackPivy() kallat addEventCallback() vilket dispenserar bruket av pivy. Men eftersom pivy är en mycket effektiv och naturlig väg att komma åt en del av coin scenen, så är det mycket bättre att använda den så mycket du kan!

top

def getpoint(self, event_cb):

Nu definierar vi getpoint funktionen, som kommer utföras när en musknapp trycks ned i en 3D vy. Denna funktion kommer att ta emot ett argument, som vi kommer kalla event_cb. Från denna händelses återanrop kan vi komma åt händelseobjektet, vilket innehåller flera informationsbitar (läge info här).

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

Getpoint funktionen kommer att anropas när en musknapp trycks ned eller släpps. Men vi vill bara välja en 3D punkt när den trycks ned (annars skulle vi få två 3D punkter mycket nära varann). Så vi måste kontrollera det här.

pos = event.getPosition()

Här får vi musmarkörens skärmkoordinater

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

Denna funktion ger oss en FreeCAD vektor (x,y,z) som innehåller den 3D punkt som ligger på fokalplanet, precis under vår musmarkör. om du är i kameravy, tänk dig en stråle som kommer från kameran, och passerar genom musmarkören, och träffar fokalplanet. Där är vår 3D punkt. On vi är i ortogonal vy, så är strålen parallell med vyriktningen.

self.stack.append(point)

Vi lägger till vår nya punkt till stacken

if len(self.stack) == 2:

Har vi tillräckligt med punkter? Om ja, låt oss då rita linjen!

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

Här använder vi funktionen Line() från Del Modulen som skapar en linje från två FreeCAD vektorer. Allt som vi skapar och ändrar inuti Del modulen, stannar i Del modulen. Så, tills nu, så har vi skapat en Linje Del. Den är inte bunden till något objekt i vårt aktiva dokument, så inget syns på skärmen.

shape = l.toShape()

FreeCAD dokumentet kan bara acceptera former från Del modulen. Former är Del modulens mest allmäna typ. Så vi behöver konvertera vår linje till en form innan vi lägger till den till dokumentet.

Part.show(shape)

Del modulen har en mycket smidig show() funktion som skapar ett nytt objekt i dokumentet och binder en form till den. Vi skulle också kunna ha skapat ett nytt objekt i dokumentet först, och sedan ha bundit formen till den manuellt.

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

Eftersom vi är klara med vår linje, låt oss ta bort vår återanropsmekanism, som använder värdefulla CPU cykler.

top

Testa & använda skriptet

Låt oss nu spara vårt skript på någon plats där FreeCAD's python tolk kommer att hitta den. När moduler importerass, så kommer tolken att leta på följande platser: python's installationssökväg, FreeCAD bin katalogen, och alla FreeCAD modulkataloger. Så, den bästa lösningen är att skapa en ny katalog i en av FreeCAD's Mod kataloger, och spara vårt skript i den. Låt oss till exempel skapa en "MinaSkript" katalog, och spara vårt skript som "ovning.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.

Nu är allt klart, låt oss starta FreeCAD, skapa ett nytt dokument, och i pythontolken skriva:

import exercise

Om inga felmeddelanden kommer fram, så innebär det att vårt övningsskript har laddats. Vi kan nu kontrollera dess innehåll med :

dir(exercise)

Kommandot dir() är ett inbyggt python kommando som listar modulens innehåll. Vi kan se att vår line() klass är där, som väntar på oss. Låt oss nu testa den:

'line' in dir(exercise)

Now let's test it:

exercise.line()

Klicka sedan på två olika ställen i 3D vyn, och bingo, där är vår linje! För att göra det igen, skriv bara ovning.line() igen, och igen, och igen... Känns det bra?

top

Registrera skriptet i FreeCAD's gränssnitt

För att vårt nya linjeverktyg ska bli ännu häftigare, så borde det ha en knapp i gränssnittet, så vi inte behöver skriva allt det varje gång. Den lättaste vägen är att omvandla vår nya MinaSkript katalog till en full FreeCAD arbetsbänk. Det är lätt, allt som behövs är att lägga in en fil som kallas InitGui.py inuti din MinaSkript katalog. InitGui.py kommer att innehålla instruktioner för att skapa en ny arbetsbänk, och addera vårt nya verktyg till den. Förutom det så behöver vi ändra vår övningskod lite, så att line() verktyget igenkänns som ett officiellt FreeCAD kommando. Låt oss starta genom att göra en InitGui.py fil, och skriva följande kod i den:

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

Jag tror att du redan nu förstår det ovanstående skriptet: Vi skapar en ny klass som vi kallar MinArbetsbänk, vi ger den en titel (MenuText), och vi definierar en Initialize() funktion som kommer att utföras när arbetsbänken laddas till FreeCAD. I den funktionen så laddar vi innehållet i vår övningsfil, och lägger till de FreeCAD kommandon som hittas i det i en kommandolista. Sedan gör vi en verktygslåda som kallas "Mina Skript" och vi tilldelar vår kommandolista till den. För närvarande har vi förstås endast ett verktyg, så vår kommandolista innehåller endast ett element. Sedan, när vår arbetsbänk är klar, så lägger vi till den till huvudgränssnittet.

Men det kommer fortfarande inte att fungera, därför att ett FreeCAD kommando måste formatteras på ett visst sätt för att fungera. så vi behöver ändra vårt line() verktyg lite. Vår nya ovning.py skript kommer nu att se ut så här:

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

Vad vi gjorde här var att omvandla vår __init__() funktion till en Activated() funktion, därför att när FreeCAD kommandon körs, så utför de automatiskt Activated() funktionen. Vi lade också till en GetResources() funktion, som informerar FreeCAD var den kan hitta en ikon för verktyget, och namnet och verktygstipset för vårt verktyg. en jpg, png eller svg bild kommer att fungera som en ikon, den kan vara av valfri storlek, men det är bäst att använda en storlek osm är nära den slutliga, som 16x16, 24x24 eller 32x32.

Sedan lägger vi till line() klassen som ett officiellt FreeCAD kommando med addCommand() metoden.

Klart, nu behöver vi bara starta om FreeCAD och vi kommer att ha en ny snygg arbetsbänk med vårt splitternya verktyg!

top

Så du vill ha mer?

Om du gillade denna övningen, varför inte försöka förbättra detta lilla verktyg? Det finns många saker som kan göras, som till exempel:

  • Lägga till feedback för användaren: nu är det ett mycket 'naket' verktyg, så användaren kan bli lite osäker när det används. Så vi skulle kunna lägga till lite feedback, tala om vad som ska göras. Du skulle till exempel kunna mata ur meddelanden till FreeCAD konsolen. Ta en titt i FreeCAD.Console modulen
  • Lägga till en möjlighet att skriva in 3D punkterna manuellt. Titta på python input() funktionen, till exempel
  • Lägga till möjligheten att lägga till mer än 2 punkter
  • Lägga till händelser för andra saker: Nu kontrollerar vi endast musknappshändelser, men om vi också kunde göra något när musen förflyttas, som att visa koordinater?
  • Ge ett namn till det skapade objektet

Tveka inte att skriva dina frågor eller ideer på forum!

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

top