Create a FeaturePython object part I/pl: Difference between revisions

From FreeCAD Documentation
(Created page with "Tworzenie obiektów FeaturePython - część 1")
 
(Created page with "To wszystko, wiesz już jak zbudować podstawowy, funkcjonalny obiekt FeaturePython!")
 
(74 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages/>
<languages/>


{{Docnav
{{Docnav/pl
|
|
|[[Create_a_FeaturePython_object_part_II|Create a FeaturePython object part II]]
|[[Create_a_FeaturePython_object_part_II/pl|Tworzenie obiektów FeaturePython część II]]
|IconL=
|IconL=
|IconR=
|IconR=
Line 10: Line 10:
{{TOCright}}
{{TOCright}}


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


Obiekty FeaturePython ''(zwane również [[Scripted_objects/pl|obiektami tworzonymi przez skrypty]])'' zapewniają możliwość rozszerzenia FreeCAD o obiekty, które płynnie integrują się z frameworkiem FreeCAD.
FeaturePython objects (also referred to as [[Scripted_objects|Scripted objects]]) provide the ability to extend FreeCAD with objects that integrate seamlessly into the FreeCAD framework.


Zachęca to do:
This encourages:
*Szybkie prototypowanie nowych obiektów i narzędzi za pomocą niestandardowych klas środowiska Pyton.
*Rapid prototyping of new objects and tools with custom Python classes.
*Saving and restoring data (also known as serialization) through {{incode|App::Property}} objects, without embedding any script in the FreeCAD document file.
*Zapisywanie i przywracanie danych ''(znane również jako serializacja)'' za pomocą obiektów {{incode|App::Property}}, bez osadzania jakiegokolwiek skryptu w pliku dokumentu FreeCAD.
*Creative freedom to adapt FreeCAD for any task.
*Swoboda twórcza umożliwiająca dostosowanie FreeCAD do dowolnego zadania.


Na tej stronie skonstruujemy działający przykład niestandardowej klasy FeaturePython, identyfikując wszystkie główne komponenty i rozumiejąc, jak wszystko działa.
On this page we are going to construct a working example of a FeaturePython custom class, identifying all the major components and gaining an understanding of how everything works as we go along.


==How does it work?==
==How does it work?==


FreeCAD zawiera szereg domyślnych typów obiektów do zarządzania różnymi rodzajami geometrii. Niektóre z nich mają alternatywy "FeaturePython", które pozwalają na dostosowanie za pomocą zdefiniowanej przez użytkownika klasy Python.
FreeCAD comes with a number of default object types for managing different kinds of geometry. Some of them have "FeaturePython" alternatives that allow for customization with a user defined Python class.


Ta niestandardowa klasa Python pobiera odniesienie do jednego z tych obiektów i modyfikuje go. Na przykład, klasa Python może dodawać właściwości do obiektu lub łączyć go z innymi obiektami. Ponadto klasa Python może implementować pewne metody, aby umożliwić obiektowi reagowanie na zdarzenia dokumentu, umożliwiając przechwytywanie zmian właściwości obiektu i ponowne obliczanie dokumentu.
This custom Python class takes a reference to one of these objects and modifies it. For example, the Python class may add properties to the object or link it to other objects. In addition the Python class may implement certain methods to enable the object to respond to document events, making it possible to trap object property changes and document recomputes.


Podczas pracy z klasami niestandardowymi i obiektami FeaturePython ważne jest, aby wiedzieć, że klasa niestandardowa i jej stan nie są zapisywane w dokumencie, ponieważ wymagałoby to osadzenia skryptu w pliku dokumentu FreeCAD, co stanowiłoby poważne zagrożenie dla bezpieczeństwa. Zapisywany ''(serializowany)'' jest tylko sam obiekt FeaturePython. Ale ponieważ ścieżka modułu skryptu jest przechowywana w dokumencie, użytkownik musi tylko zainstalować niestandardowy kod klasy Python jako moduł do zaimportowania, zgodnie z tą samą strukturą folderów, aby odzyskać utraconą funkcjonalność.
When working with custom classes and FeaturePython objects it is important to know that the custom class and its state are not saved in the document as this would require embedding a script in a FreeCAD document file, which would pose a significant security risk. Only the FeaturePython object itself is saved (serialized). But since the script module path is stored in the document, a user need only install the custom Python class code as an importable module, following the same folder structure, to regain the lost functionality.


[[#top|top]]
[[#top|na początek strony]]


<span id="Setting_things_up"></span>
==Setting things up==
===Konfiguracja===


Klasy obiektów FeaturePython muszą działać jako moduły importowalne w FreeCAD. Oznacza to, że należy umieścić je w ścieżce, która istnieje w środowisku Python ''(lub dodać ją specjalnie)''. Na potrzeby tego samouczka użyjemy folderu Macro użytkownika FreeCAD. Ale jeśli masz inny pomysł, możesz go użyć zamiast tego.
FeaturePython Object classes need to act as importable modules in FreeCAD. That means you need to place them in a path that exists in your Python environment (or add it specifically). For the purposes of this tutorial, we're going to use the FreeCAD user Macro folder. But if you have another idea in mind, feel free to use that instead.


If you don't know where the FreeCAD Macro folder is type {{incode|FreeCAD.getUserMacroDir(True)}} in FreeCAD's [[Python_console|Python console]]:
Jeśli nie wiesz, gdzie znajduje się folder FreeCAD Macro, w [[Python_console/pl|konsoli Python]] programu FreeCAD wpisz polecenie {{incode|FreeCAD.getUserMacroDir(True)}} :
* On Linux it is usually {{FileName|/home/<username>/.FreeCAD/Macro/}}.
* W systemie Linux jest to zazwyczaj {{FileName|/home/<nazwa użytkownika>/.local/share/FreeCAD/Macro/}} ''({{VersionPlus/pl|0.20}})'' lub {{FileName|/home/<username>/.FreeCAD/Macro/}} ''({{VersionMinus/pl|0.19}})''.
* On Windows it is {{FileName|%APPDATA%\FreeCAD\Macro\}}, which is usually {{FileName|C:\Users\<username>\Appdata\Roaming\FreeCAD\Macro\}}.
* Na macOS jest to zazwyczaj {{FileName|/Users/<username>/Library/Application Support/FreeCAD/Macro/}}.
* On Mac OSX it is usually {{FileName|/Users/<username>/Library/Preferences/FreeCAD/Macro/}}.
* W systemie Windows jest to {{FileName|%APPDATA%\FreeCAD\Macro\}}, który zwykle jest {{FileName|C:\Users\<username>\Appdata\Roaming\FreeCAD\Macro\}}.


Teraz musimy utworzyć kilka folderów i plików:
Now we need to create some folders and files:
*In the {{FileName|Macro}} folder create a new folder called {{FileName|fpo}}.
*W folderze {{FileName|Macro}} utwórz nowy folder o nazwie {{FileName|fpo}}.
*In the {{FileName|fpo}} folder create an empty file: {{FileName|__init__.py}}.
*W folderze {{FileName|fpo}} utwórz pusty plik: {{FileName|__init__.py}}.
*In the {{FileName|fpo}} folder, create a new folder called {{FileName|box}}.
*W folderze {{FileName|fpo}} utwórz nowy folder o nazwie {{FileName|box}}.
*In the {{FileName|box}} folder create two files: {{FileName|__init__.py}} and {{FileName|box.py}} (leave both empty for now).
*W folderze {{FileName|box}} utwórz dwa pliki: {{FileName|__init__.py}} i {{FileName|box.py}} ''(na razie pozostaw oba puste)''.


Struktura folderów powinna wyglądać następująco:
Your folder structure should look like this:


Macro/
Macro/
Line 55: Line 57:
|--> box.py
|--> box.py


The {{FileName|fpo}} folder provides a nice place to play with new FeaturePython objects and the {{FileName|box}} folder is the module we will be working in. {{FileName|__init__.py}} tells Python that there is an importable module in the folder, and {{FileName|box.py}} will be the class file for our new FeaturePython Object.
Folder {{FileName|fpo}} zapewnia przyjemne miejsce do zabawy z nowymi obiektami FeaturePython, a folder {{FileName|box}} jest modułem, w którym będziemy pracować. {{FileName|__init__.py}} informuje środowisko Python, że w folderze znajduje się możliwy do zaimportowania moduł, a {{FileName|box.py}} będzie plikiem klasy dla naszego nowego obiektu FeaturePython.


Po utworzeniu ścieżek modułów i plików, upewnijmy się, że FreeCAD jest poprawnie skonfigurowany:
With our module paths and files created, let's make sure FreeCAD is set up properly:
*Start FreeCAD (if you haven't done so already).
*Uruchom FreeCAD ''(jeśli jeszcze tego nie zrobiłeś)''.
*Enable the [[Report_view|Report view]] ({{MenuCommand|ViewPanelsReport view}}).
*Włącz [[Report_view/pl|Widok raportu]] ''({{MenuCommand|WidokPaneleWidok raportu}})''.
*Enable the [[Python_console|Python console]] ({{MenuCommand|ViewPanelsPython console}}) see [[FreeCAD_Scripting_Basics|FreeCAD Scripting Basics]].
*Włącz [[Python_console/pl|konsole Python]] ''({{MenuCommand|WidokPaneleKonsola Python}})'' zapoznaj sie z informacjami na stronie [[FreeCAD_Scripting_Basics/pl|Podstawy tworzenia skryptów FreeCAD]].


Finally, navigate to the {{FileName|Macro/fpo/box}} folder and open {{FileName|box.py}} in your favorite code editor. We will only edit that file.
Na koniec przejdź do folderu {{FileName|Macro/fpo/box}} i otwórz {{FileName|box.py}} w swoim ulubionym edytorze kodu. Będziemy edytować tylko ten plik.


[[#top|top]]
[[#top|na początek strony]]


<span id="A_FeaturePython_object"></span>
==A FeaturePython object==
==Obiekt FeaturePython ==


Zacznijmy od napisania naszej klasy i jej konstruktora:
Let's get started by writing our class and its constructor:


{{Code|code=
{{Code|code=
Line 83: Line 86:
}}
}}


'''The {{incode|__init__()}} method breakdown:'''
'''Podział metody {{incode|__init__()}}:'''


{|class="wikitable" cellpadding="5px" width="100%"
{|class="wikitable" cellpadding="5px" width="100%"
|style="width:25%" | {{incode|def __init__(self, obj):}}
|style="width:25%" | {{incode|def __init__(self, obj):}}
|style="width:75%" | Parameters refer to the Python class itself and the FeaturePython object that it is attached to.
|style="width:75%" | Parametry odnoszą się do samej klasy Python i obiektu FeaturePython, do którego jest ona dołączona.
|-
|-
| {{incode|self.Type <nowiki>=</nowiki> 'box'}}
| {{incode|self.Type <nowiki>=</nowiki> 'box'}}
| Definicja niestandardowego typu Python w postaci ciągu znaków.
| String definition of the custom Python type.
|-
|-
| {{incode|obj.Proxy <nowiki>=</nowiki> self}}
| {{incode|obj.Proxy <nowiki>=</nowiki> self}}
| Stores a reference to the Python instance in the FeaturePython object.
| Przechowuje odniesienie do instancji Python w obiekcie FeaturePython.
|}
|}


Dodaj następujący kod na początku pliku:
Add the following code at the top of the file:


{{Code|code=
{{Code|code=
Line 113: Line 116:
}}
}}


'''The {{incode|create()}} method breakdown:'''
'''Podział metody {{incode|create()}}:'''


{|class="wikitable" cellpadding="5px" width="100%"
{|class="wikitable" cellpadding="5px" width="100%"
|style="width:25%" | {{incode|import FreeCAD as App}}
|style="width:25%" | {{incode|import FreeCAD as App}}
|style="width:75%" | Standard import for most Python scripts, the App alias is not required.
|style="width:75%" | Standardowy import dla większości skryptów Python, alias App nie jest wymagany.
|-
|-
| {{incode|obj <nowiki>=</nowiki> ... addObject(...)}}
| {{incode|obj <nowiki>=</nowiki> ... addObject(...)}}
| Creates a new FreeCAD FeaturePython object with the name passed to the method. If there is no name clash, this will be the label and the name of the created object. Otherwise, a unique name and label will be created based on 'obj_name'.
| Tworzy nowy obiekt FreeCAD FeaturePython o nazwie przekazanej do metody. Jeśli nie ma kolizji nazw, będzie to etykieta i nazwa utworzonego obiektu. W przeciwnym razie zostanie utworzona unikalna nazwa i etykieta na podstawie "obj_name".
|-
|-
| {{incode|box(obj)}}
| {{incode|box(obj)}}
| Tworzy instancję naszej niestandardowej klasy.
| Creates our custom class instance.
|-
|-
| {{incode|return obj}}
| {{incode|return obj}}
| Returns the FeaturePython object.
| Zwraca obiekt FeaturePython.
|}
|}


The {{incode|create()}} method is not required, but it provides a nice way to encapsulate the object creation code.
Metoda {{incode|create()}} nie jest wymagana, ale zapewnia przyjemny sposób enkapsulacji kodu tworzenia obiektu.


[[#top|top]]
[[#top|na początek strony]]


<span id="Testing_the_code"></span>
===Testing the code===
===Testowanie kodu===


Now we can test our new object. Save your code and return to FreeCAD. Make sure you have opened a new document, you can do this by pressing {{KEY|Ctrl}}+{{KEY|N}} or selecting {{MenuCommand|FileNew}}.
Teraz możemy przetestować nasz nowy obiekt. Zapisz kod i wróć do FreeCAD. Upewnij się, że otworzyłeś nowy dokument, możesz to zrobić naciskając {{KEY|Ctrl}} + {{KEY|N}} lub wybierając {{MenuCommand|PlikNowy}}.


In the Python console type the following:
W konsoli Python wpisz następujące polecenie:


{{Code|code=
{{Code|code=
Line 143: Line 147:
}}
}}


Teraz musimy stworzyć nasz obiekt:
Now we need to create our object:


{{Code|code=
{{Code|code=
Line 150: Line 154:


[[Image:Fpo_treeview.png | right]]
[[Image:Fpo_treeview.png | right]]
W [[Tree_view/pl|widoku drzewa]] powinien pojawić się nowy obiekt oznaczony jako "my_box".
You should see a new object appear in the [[Tree_view|Tree view]] labelled "my_box".


Note that the icon is gray. FreeCAD is telling us that the object is not able to display anything in the [[3D_view|3D view]]. Click on the object and look at its properties in the [[Property_editor|Property editor]]. There is not much there, just the name of the object.
Zauważ, że ikona jest szara. FreeCAD mówi nam, że obiekt nie jest w stanie wyświetlić niczego w [[3D_view/pl|widoku 3D]]. Kliknij na obiekt i spójrz na jego właściwości w [[Property_editor/pl|Edytorze właściwości]]. Nie ma tam zbyt wiele, tylko nazwa obiektu.


Also note that there is a small blue check mark next to the FeaturePython object in the Tree view. That is because when an object is created or changed it is "touched" and needs to be recomputed. Pressing the {{Button|[[Image:Std_Refresh.svg|16px]] [[Std_Refresh|Std Refresh]]}} button will accomplish this. We will add some code to automate this later.
Zwróć też uwagę, że obok obiektu FeaturePython w widoku drzewa znajduje się mały niebieski znacznik wyboru. Dzieje się tak, ponieważ gdy obiekt jest tworzony lub zmieniany, jest "dotykany" i musi zostać ponownie przeliczony. Naciśnięcie przycisku {{Button|[[Image:Std_Refresh.svg|16px]] [[Std_Refresh/pl|Odśwież]]}} pozwoli to osiągnąć. Później dodamy trochę kodu, aby to zautomatyzować.
{{Clear}}
{{Clear}}


Przyjrzyjmy się atrybutom naszego obiektu:
Let's look at our object's attributes:
{{Code|code=
{{Code|code=
dir(mybox)
dir(mybox)
}}
}}


To zwróci:
This will return:


{{Code|code=
{{Code|code=
Line 170: Line 174:
}}
}}


There are a lot of attributes because we're accessing the native FreeCAD FeaturePyton object created in the first line of our {{incode|create()}} method. The {{incode|Proxy}} property we added in our {{incode|__init__()}} method is there too.
Istnieje wiele atrybutów, ponieważ uzyskujemy dostęp do natywnego obiektu FreeCAD FeaturePyton utworzonego w pierwszej linii naszej metody {{incode|create()}}. Jest tam również właściwość {{incode|Proxy}}, którą dodaliśmy w naszej metodzie {{incode|__init__()}}.


Let's inspect it with the {{incode|dir()}} method:
Sprawdźmy to za pomocą metody {{incode|dir()}}:


{{Code|code=
{{Code|code=
Line 178: Line 182:
}}
}}


To zwróci:
This will return:


{{Code|code=
{{Code|code=
Line 186: Line 190:
}}
}}


We can see our {{incode|Type}} property. Let's check it:
Możemy zobaczyć naszą właściwość {{incode|Type}}. Sprawdźmy :


{{Code|code=
{{Code|code=
Line 192: Line 196:
}}
}}


To zwróci:
This will return:


{{Code|code=
{{Code|code=
Line 198: Line 202:
}}
}}


Jest to rzeczywiście przypisana wartość, więc wiemy, że uzyskujemy dostęp do klasy niestandardowej za pośrednictwem obiektu FeaturePython.
This is indeed the assigned value, so we know we're accessing the custom class through the FeaturePython object.


Zobaczmy teraz, czy możemy uczynić naszą klasę nieco bardziej interesującą, a może także bardziej użyteczną.
Now let's see if we can make our class a little more interesting, and maybe more useful as well.


[[#top|top]]
[[#top|na początek strony]]


<span id="Adding_properties"></span>
===Adding properties===
===Dodanie właściwości===


Properties are the lifeblood of a FeaturePython class. Fortunately, FreeCAD supports [[FeaturePython_Custom_Properties|a number of property types]] for FeaturePython classes. These properties are attached directly to the FeaturePython object and are fully serialized when the file is saved. To avoid having to serialize data yourself, it is advisable to only use these property types.
Właściwości siłą napędową klasy FeaturePython. Na szczęście FreeCAD obsługuje [[FeaturePython_Custom_Properties/pl|wiele typów właściwości]] dla klas FeaturePython. Właściwości te dołączane bezpośrednio do obiektu FeaturePython i w pełni serializowane podczas zapisywania pliku. Aby uniknąć konieczności samodzielnej serializacji danych, zaleca się używanie tylko tych typów właściwości.


Adding properties is done using the {{incode|add_property()}} method. The syntax for the method is:
Dodawanie właściwości odbywa się za pomocą metody {{incode|add_property()}}. Składnia tej metody to:


<!--Do not use Code template to avoid syntax highlighting-->
<!--Do not use Code template to avoid syntax highlighting-->
add_property(type, name, section, description)
add_property(type, name, section, description)


Listę obsługiwanych właściwości można wyświetlić, wpisując:
You can view the list of supported properties by typing:


{{Code|code=
{{Code|code=
Line 219: Line 224:
}}
}}


Let's try adding a property to our box class. Switch to your code editor, move to the {{incode|__init__()}} method, and at the end of the method add:
Spróbujmy dodać właściwość do naszej klasy box. Przełącz się do edytora kodu, przejdź do metody {{incode|__init__()}} i na końcu metody dodaj:


{{Code|code=
{{Code|code=
Line 225: Line 230:
}}
}}


Note how we're using the reference to the (serializable) FeaturePython object {{incode|obj}}, and not the (non-serializable) Python class instance {{incode|self}}.
Zauważ, że używamy odniesienia do ''(serializowalnego)'' obiektu FeaturePython {{incode|obj}}, a nie ''(nieserializowalnej)'' instancji klasy Python {{incode|self}}.


Po zakończeniu zapisz zmiany i przełącz się z powrotem do FreeCAD. Zanim będziemy mogli zaobserwować zmiany wprowadzone w naszym kodzie, musimy ponownie załadować moduł. Można to osiągnąć poprzez ponowne uruchomienie FreeCAD, ale ponowne uruchamianie FreeCAD za każdym razem, gdy edytujemy kod, byłoby niewygodne. Aby to ułatwić, wpisz następujące polecenie w konsoli Python:
Once you're done, save the changes and switch back to FreeCAD. Before we can observe the changes made to our code, we need to reload the module. This can be accomplished by restarting FreeCAD, but restarting FreeCAD every time we edit the code would be inconvenient. To make things easier type the following in the Python console:


{{Code|code=
{{Code|code=
Line 234: Line 239:
}}
}}


Po przeładowaniu modułu zobaczmy, co otrzymamy po utworzeniu obiektu:
With the module reloaded, let's see what we get when we create an object:


{{Code|code=
{{Code|code=
Line 240: Line 245:
}}
}}


W widoku drzewa powinien pojawić się nowy obiekt box:
You should see the new box object appear in the Tree view:
*Select it and look at the Property editor. There, you should see the ''Description'' property.
*Wybierz go i spójrz na edytor właściwości. Tam powinieneś zobaczyć właściwość ''Description''.
*Najedź kursorem na nazwę właściwości po lewej stronie, a pojawi się podpowiedź z opisem, który podałeś.
*Hover over the property name on the left and the tooltip should appear with the description you provided.
*Wybierz pole i wpisz dowolny tekst. Zauważysz, że polecenia aktualizacji Pythona są wykonywane i wyświetlane w konsoli podczas wpisywania liter i zmian właściwości.
*Select the field and type whatever you like. You'll notice that Python update commands are executed and displayed in the console as you type letters and the property changes.


[[#top|top]]
[[#top|na początek strony]]


Let's add some more properties. Return to your source code and add the following properties to the {{incode|__init__()}} method:
Dodajmy jeszcze kilka właściwości. Wróć do kodu źródłowego i dodaj następujące właściwości do metody {{incode|__init__()}}:


{{Code|code=
{{Code|code=
Line 255: Line 260:
}}
}}


And let's also add some code to recompute the document automatically. Add the following line above the {{incode|return()}} statement in the {{incode|create()}} method :
Dodajmy też trochę kodu, aby automatycznie przekompilować dokument. Dodajmy następującą linię nad instrukcją {{incode|return()}} w metodzie {{incode|create()}}:


{{Code|code=
{{Code|code=
Line 261: Line 266:
}}
}}


'''Uważaj, gdzie rekompilujesz obiekt FeaturePython. Ponowne obliczanie powinno być obsługiwane przez metodę zewnętrzną w stosunku do jego klasy.'''
'''Be careful where you recompute a FeaturePython object. Recomputing should be handled by a method external to its class.'''


[[Image:fpo_box_properties.png | right]]
[[Image:fpo_box_properties.png | right]]


Teraz przetestuj zmiany w następujący sposób:
Now, test your changes as follows:
*Zapisz zmiany i przeładuj moduł.
*Save your changes and reload your module.
*Usuń wszystkie obiekty w widoku drzewa.
*Delete all objects in the Tree view.
*Create a new box object from the Python console by calling {{incode|box.create('myBox')}}.
*Utwórz nowy obiekt box z konsoli Python, wywołując {{incode|box.create('myBox')}}.


Po utworzeniu pola i sprawdzeniu, czy zostało ono ponownie obliczone, wybierz obiekt i sprawdź jego właściwości. Powinieneś zauważyć dwie rzeczy:
Once the box is created and you've checked to make sure it has been recomputed, select the object and look at its properties. You should note two things:
*A new property group: ''Dimensions''.
*Nową grupę właściwości: ''Dimensions''.
*Three new properties: ''Height'', ''Length'' and ''Width''.
*Trzy nowe właściwości: ''Height'', ''Length'' and ''Width''.


Note also how the properties have units. More specifically, they have taken on the linear units set in the user preferences ({{MenuCommand|EditPreference... → GeneralUnits}}).
Zauważ również, że właściwości mają jednostki. Dokładniej mówiąc, przyjęły one jednostki liniowe ustawione w preferencjach użytkownika ({{MenuCommand|EdycjaPreferencje ... → OgólneJednostki}}).
{{Clear}}
{{Clear}}


No doubt you noticed that three different values were entered for the dimensions: a floating-point value ({{incode|10.0}}) and two different strings ({{incode|'10 mm'}} and {{incode|'1 cm'}}). The {{incode|App::PropertyLength}} type assumes floating-point values are in millimeters, string values are parsed according to the units specified, and in the GUI all values are converted to the units specified in the user preferences ({{incode|mm}} in the image). This built-in behavior makes the {{incode|App::PropertyLength}} type ideal for dimensions.
Bez wątpienia zauważyłeś, że wprowadzono trzy różne wartości dla wymiarów: wartość zmiennoprzecinkową ({{incode|10.0}}) i dwa różne ciągi znaków ({{incode|'10 mm'}} i {{incode|'1 cm'}}). Typ {{incode|App::PropertyLength}} zakłada, że wartości zmiennoprzecinkowe w milimetrach, wartości łańcuchowe analizowane zgodnie z określonymi jednostkami, a w GUI wszystkie wartości konwertowane na jednostki określone w preferencjach użytkownika ''({{incode|mm}} na obrazku)''. To wbudowane zachowanie sprawia, że typ {{incode|App::PropertyLength}} jest idealny dla wymiarów.


[[#top|top]]
[[#top|na początek strony]]


<span id="Trapping_events"></span>
===Trapping events===
===Zdarzenia związane z łapaniem===


Ostatnim elementem wymaganym dla podstawowego obiektu FeaturePython jest przechwytywanie zdarzeń. Obiekt FeaturePython może reagować na zdarzenia za pomocą funkcji zwrotnych. W naszym przypadku chcemy, aby obiekt reagował za każdym razem, gdy zostanie ponownie przeliczony. Innymi słowy, chcemy przechwytywać ponowne obliczenia. Aby to osiągnąć, musimy dodać do klasy obiektu funkcję o określonej nazwie {{incode|execute()}}. Istnieje kilka innych zdarzeń, które można przechwycić, zarówno w samym obiekcie FeaturePython, jak i w [[Viewprovider/pl|Dostawcay widoku
The last element required for a basic FeaturePython object is event trapping. A FeaturePython object can react to events with callback functions. In our case we want the object to react whenever it is recomputed. In other words we want to trap recomputes. To accomplish this we need to add a function with a specific name, {{incode|execute()}}, to the object class. There are several other events that can be trapped, both in the FeaturePython object itself and in the [[Viewprovider|ViewProvider]], which we'll cover in [[Create_a_FeaturePython_object_part_II|Create a FeaturePython object part II]].
]], które omówimy w poradniku [[Create_a_FeaturePython_object_part_II/pl|Tworzenie obiektów FeaturePython - część II]].


For a complete reference of methods available to implement on FeautrePython classes, see [[FeaturePython_methods|FeaturePython methods]].
Aby uzyskać pełną listę metod dostępnych do zaimplementowania w klasach FeautrePython, zapoznaj sie z informacjami na stronie [[FeaturePython_methods/pl|Metody FeaturePython]].


Add the following after the {{incode|__init__()}} function:
Dodaj następujące elementy po funkcji {{incode|__init__()}}:


{{Code|code=
{{Code|code=
Line 298: Line 305:
}}
}}


Przetestuj kod, ponownie wykonując następujące kroki:
Test the code by again following these steps:
*Zapisz i ponownie załaduj moduł.
*Save and reload the module.
*Usuń wszystkie obiekty.
*Delete all objects.
*Create a new box object.
*Utwórz nowy obiekt box.


You should see the printed output in the Python Console, thanks to the {{incode|recompute()}} call we added to the {{incode|create()}} method. Of course, the {{incode|execute()}} method doesn't do anything here, except tell us that it was called, but it is the key to the magic of FeaturePython objects.
Powinieneś zobaczyć wyniki drukowania w konsoli Python, dzięki wywołaniu {{incode|recompute()}}, które dodaliśmy do metody {{incode|create()}}. Oczywiście metoda {{incode|execute()}} nie robi tutaj nic poza poinformowaniem nas, że została wywołana, ale jest ona kluczem do magii obiektów FeaturePython.


That's it, you now know how to build a basic, functional FeaturePython object!
To wszystko, wiesz już jak zbudować podstawowy, funkcjonalny obiekt FeaturePython!


[[#top|top]]
[[#top|na początek strony]]


<span id="Complete_code"></span>
===Complete code===
===Kompletny kod===


{{Code|code=
{{Code|code=
Line 351: Line 359:
}}
}}


[[#top|top]]
[[#top|na początek strony]]


{{Docnav
{{Docnav/pl
|
|
|[[Create_a_FeaturePython_object_part_II|Create a FeaturePython object part II]]
|[[Create_a_FeaturePython_object_part_II/pl|Tworzenie obiektów FeaturePython część II]]
|IconL=
|IconL=
|IconR=
|IconR=

Latest revision as of 06:06, 17 May 2024

Wprowadzenie

Obiekty FeaturePython (zwane również obiektami tworzonymi przez skrypty) zapewniają możliwość rozszerzenia FreeCAD o obiekty, które płynnie integrują się z frameworkiem FreeCAD.

Zachęca to do:

  • Szybkie prototypowanie nowych obiektów i narzędzi za pomocą niestandardowych klas środowiska Pyton.
  • Zapisywanie i przywracanie danych (znane również jako serializacja) za pomocą obiektów App::Property, bez osadzania jakiegokolwiek skryptu w pliku dokumentu FreeCAD.
  • Swoboda twórcza umożliwiająca dostosowanie FreeCAD do dowolnego zadania.

Na tej stronie skonstruujemy działający przykład niestandardowej klasy FeaturePython, identyfikując wszystkie główne komponenty i rozumiejąc, jak wszystko działa.

How does it work?

FreeCAD zawiera szereg domyślnych typów obiektów do zarządzania różnymi rodzajami geometrii. Niektóre z nich mają alternatywy "FeaturePython", które pozwalają na dostosowanie za pomocą zdefiniowanej przez użytkownika klasy Python.

Ta niestandardowa klasa Python pobiera odniesienie do jednego z tych obiektów i modyfikuje go. Na przykład, klasa Python może dodawać właściwości do obiektu lub łączyć go z innymi obiektami. Ponadto klasa Python może implementować pewne metody, aby umożliwić obiektowi reagowanie na zdarzenia dokumentu, umożliwiając przechwytywanie zmian właściwości obiektu i ponowne obliczanie dokumentu.

Podczas pracy z klasami niestandardowymi i obiektami FeaturePython ważne jest, aby wiedzieć, że klasa niestandardowa i jej stan nie są zapisywane w dokumencie, ponieważ wymagałoby to osadzenia skryptu w pliku dokumentu FreeCAD, co stanowiłoby poważne zagrożenie dla bezpieczeństwa. Zapisywany (serializowany) jest tylko sam obiekt FeaturePython. Ale ponieważ ścieżka modułu skryptu jest przechowywana w dokumencie, użytkownik musi tylko zainstalować niestandardowy kod klasy Python jako moduł do zaimportowania, zgodnie z tą samą strukturą folderów, aby odzyskać utraconą funkcjonalność.

na początek strony

Konfiguracja

Klasy obiektów FeaturePython muszą działać jako moduły importowalne w FreeCAD. Oznacza to, że należy umieścić je w ścieżce, która istnieje w środowisku Python (lub dodać ją specjalnie). Na potrzeby tego samouczka użyjemy folderu Macro użytkownika FreeCAD. Ale jeśli masz inny pomysł, możesz go użyć zamiast tego.

Jeśli nie wiesz, gdzie znajduje się folder FreeCAD Macro, w konsoli Python programu FreeCAD wpisz polecenie FreeCAD.getUserMacroDir(True) :

  • W systemie Linux jest to zazwyczaj /home/<nazwa użytkownika>/.local/share/FreeCAD/Macro/ (wersja 0.20 i powyżej) lub /home/<username>/.FreeCAD/Macro/ (wersja 0.19 i poniżej).
  • Na macOS jest to zazwyczaj /Users/<username>/Library/Application Support/FreeCAD/Macro/.
  • W systemie Windows jest to %APPDATA%\FreeCAD\Macro\, który zwykle jest C:\Users\<username>\Appdata\Roaming\FreeCAD\Macro\.

Teraz musimy utworzyć kilka folderów i plików:

  • W folderze Macro utwórz nowy folder o nazwie fpo.
  • W folderze fpo utwórz pusty plik: __init__.py.
  • W folderze fpo utwórz nowy folder o nazwie box.
  • W folderze box utwórz dwa pliki: __init__.py i box.py (na razie pozostaw oba puste).

Struktura folderów powinna wyglądać następująco:

Macro/
    |--> fpo/
        |--> __init__.py
        |--> box/
            |--> __init__.py
            |--> box.py

Folder fpo zapewnia przyjemne miejsce do zabawy z nowymi obiektami FeaturePython, a folder box jest modułem, w którym będziemy pracować. __init__.py informuje środowisko Python, że w folderze znajduje się możliwy do zaimportowania moduł, a box.py będzie plikiem klasy dla naszego nowego obiektu FeaturePython.

Po utworzeniu ścieżek modułów i plików, upewnijmy się, że FreeCAD jest poprawnie skonfigurowany:

Na koniec przejdź do folderu Macro/fpo/box i otwórz box.py w swoim ulubionym edytorze kodu. Będziemy edytować tylko ten plik.

na początek strony

Obiekt FeaturePython

Zacznijmy od napisania naszej klasy i jej konstruktora:

class box():

    def __init__(self, obj):
        """
        Default constructor
        """

        self.Type = 'box'

        obj.Proxy = self

Podział metody __init__():

def __init__(self, obj): Parametry odnoszą się do samej klasy Python i obiektu FeaturePython, do którego jest ona dołączona.
self.Type = 'box' Definicja niestandardowego typu Python w postaci ciągu znaków.
obj.Proxy = self Przechowuje odniesienie do instancji Python w obiekcie FeaturePython.

Dodaj następujący kod na początku pliku:

import FreeCAD as App

def create(obj_name):
    """
    Object creation method
    """

    obj = App.ActiveDocument.addObject('App::FeaturePython', obj_name)

    box(obj)

    return obj

Podział metody create():

import FreeCAD as App Standardowy import dla większości skryptów Python, alias App nie jest wymagany.
obj = ... addObject(...) Tworzy nowy obiekt FreeCAD FeaturePython o nazwie przekazanej do metody. Jeśli nie ma kolizji nazw, będzie to etykieta i nazwa utworzonego obiektu. W przeciwnym razie zostanie utworzona unikalna nazwa i etykieta na podstawie "obj_name".
box(obj) Tworzy instancję naszej niestandardowej klasy.
return obj Zwraca obiekt FeaturePython.

Metoda create() nie jest wymagana, ale zapewnia przyjemny sposób enkapsulacji kodu tworzenia obiektu.

na początek strony

Testowanie kodu

Teraz możemy przetestować nasz nowy obiekt. Zapisz kod i wróć do FreeCAD. Upewnij się, że otworzyłeś nowy dokument, możesz to zrobić naciskając Ctrl + N lub wybierając Plik → Nowy.

W konsoli Python wpisz następujące polecenie:

from fpo.box import box

Teraz musimy stworzyć nasz obiekt:

mybox = box.create('my_box')

W widoku drzewa powinien pojawić się nowy obiekt oznaczony jako "my_box".

Zauważ, że ikona jest szara. FreeCAD mówi nam, że obiekt nie jest w stanie wyświetlić niczego w widoku 3D. Kliknij na obiekt i spójrz na jego właściwości w Edytorze właściwości. Nie ma tam zbyt wiele, tylko nazwa obiektu.

Zwróć też uwagę, że obok obiektu FeaturePython w widoku drzewa znajduje się mały niebieski znacznik wyboru. Dzieje się tak, ponieważ gdy obiekt jest tworzony lub zmieniany, jest "dotykany" i musi zostać ponownie przeliczony. Naciśnięcie przycisku Odśwież pozwoli to osiągnąć. Później dodamy trochę kodu, aby to zautomatyzować.

Przyjrzyjmy się atrybutom naszego obiektu:

dir(mybox)

To zwróci:

['Content', 'Document', 'ExpressionEngine', 'FullName', 'ID', 'InList',
...
'setPropertyStatus', 'supportedProperties', 'touch']

Istnieje wiele atrybutów, ponieważ uzyskujemy dostęp do natywnego obiektu FreeCAD FeaturePyton utworzonego w pierwszej linii naszej metody create(). Jest tam również właściwość Proxy, którą dodaliśmy w naszej metodzie __init__().

Sprawdźmy to za pomocą metody dir():

dir(mybox.Proxy)

To zwróci:

['Type', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
...
'__str__', '__subclasshook__', '__weakref__']

Możemy zobaczyć naszą właściwość Type. Sprawdźmy ją:

mybox.Proxy.Type

To zwróci:

'box'

Jest to rzeczywiście przypisana wartość, więc wiemy, że uzyskujemy dostęp do klasy niestandardowej za pośrednictwem obiektu FeaturePython.

Zobaczmy teraz, czy możemy uczynić naszą klasę nieco bardziej interesującą, a może także bardziej użyteczną.

na początek strony

Dodanie właściwości

Właściwości są siłą napędową klasy FeaturePython. Na szczęście FreeCAD obsługuje wiele typów właściwości dla klas FeaturePython. Właściwości te są dołączane bezpośrednio do obiektu FeaturePython i są w pełni serializowane podczas zapisywania pliku. Aby uniknąć konieczności samodzielnej serializacji danych, zaleca się używanie tylko tych typów właściwości.

Dodawanie właściwości odbywa się za pomocą metody add_property(). Składnia tej metody to:

add_property(type, name, section, description)

Listę obsługiwanych właściwości można wyświetlić, wpisując:

mybox.supportedProperties()

Spróbujmy dodać właściwość do naszej klasy box. Przełącz się do edytora kodu, przejdź do metody __init__() i na końcu metody dodaj:

obj.addProperty('App::PropertyString', 'Description', 'Base', 'Box description').Description = ""

Zauważ, że używamy odniesienia do (serializowalnego) obiektu FeaturePython obj, a nie (nieserializowalnej) instancji klasy Python self.

Po zakończeniu zapisz zmiany i przełącz się z powrotem do FreeCAD. Zanim będziemy mogli zaobserwować zmiany wprowadzone w naszym kodzie, musimy ponownie załadować moduł. Można to osiągnąć poprzez ponowne uruchomienie FreeCAD, ale ponowne uruchamianie FreeCAD za każdym razem, gdy edytujemy kod, byłoby niewygodne. Aby to ułatwić, wpisz następujące polecenie w konsoli Python:

from importlib import reload
reload(box)

Po przeładowaniu modułu zobaczmy, co otrzymamy po utworzeniu obiektu:

box.create('box_property_test')

W widoku drzewa powinien pojawić się nowy obiekt box:

  • Wybierz go i spójrz na edytor właściwości. Tam powinieneś zobaczyć właściwość Description.
  • Najedź kursorem na nazwę właściwości po lewej stronie, a pojawi się podpowiedź z opisem, który podałeś.
  • Wybierz pole i wpisz dowolny tekst. Zauważysz, że polecenia aktualizacji Pythona są wykonywane i wyświetlane w konsoli podczas wpisywania liter i zmian właściwości.

na początek strony

Dodajmy jeszcze kilka właściwości. Wróć do kodu źródłowego i dodaj następujące właściwości do metody __init__():

obj.addProperty('App::PropertyLength', 'Length', 'Dimensions', 'Box length').Length = 10.0
obj.addProperty('App::PropertyLength', 'Width', 'Dimensions', 'Box width').Width = '10 mm'
obj.addProperty('App::PropertyLength', 'Height', 'Dimensions', 'Box height').Height = '1 cm'

Dodajmy też trochę kodu, aby automatycznie przekompilować dokument. Dodajmy następującą linię nad instrukcją return() w metodzie create():

App.ActiveDocument.recompute()

Uważaj, gdzie rekompilujesz obiekt FeaturePython. Ponowne obliczanie powinno być obsługiwane przez metodę zewnętrzną w stosunku do jego klasy.

Teraz przetestuj zmiany w następujący sposób:

  • Zapisz zmiany i przeładuj moduł.
  • Usuń wszystkie obiekty w widoku drzewa.
  • Utwórz nowy obiekt box z konsoli Python, wywołując box.create('myBox').

Po utworzeniu pola i sprawdzeniu, czy zostało ono ponownie obliczone, wybierz obiekt i sprawdź jego właściwości. Powinieneś zauważyć dwie rzeczy:

  • Nową grupę właściwości: Dimensions.
  • Trzy nowe właściwości: Height, Length and Width.

Zauważ również, że właściwości mają jednostki. Dokładniej mówiąc, przyjęły one jednostki liniowe ustawione w preferencjach użytkownika (Edycja → Preferencje ... → Ogólne → Jednostki).

Bez wątpienia zauważyłeś, że wprowadzono trzy różne wartości dla wymiarów: wartość zmiennoprzecinkową (10.0) i dwa różne ciągi znaków ('10 mm' i '1 cm'). Typ App::PropertyLength zakłada, że wartości zmiennoprzecinkowe są w milimetrach, wartości łańcuchowe są analizowane zgodnie z określonymi jednostkami, a w GUI wszystkie wartości są konwertowane na jednostki określone w preferencjach użytkownika (mm na obrazku). To wbudowane zachowanie sprawia, że typ App::PropertyLength jest idealny dla wymiarów.

na początek strony

Zdarzenia związane z łapaniem

Ostatnim elementem wymaganym dla podstawowego obiektu FeaturePython jest przechwytywanie zdarzeń. Obiekt FeaturePython może reagować na zdarzenia za pomocą funkcji zwrotnych. W naszym przypadku chcemy, aby obiekt reagował za każdym razem, gdy zostanie ponownie przeliczony. Innymi słowy, chcemy przechwytywać ponowne obliczenia. Aby to osiągnąć, musimy dodać do klasy obiektu funkcję o określonej nazwie execute(). Istnieje kilka innych zdarzeń, które można przechwycić, zarówno w samym obiekcie FeaturePython, jak i w Dostawcay widoku , które omówimy w poradniku Tworzenie obiektów FeaturePython - część II.

Aby uzyskać pełną listę metod dostępnych do zaimplementowania w klasach FeautrePython, zapoznaj sie z informacjami na stronie Metody FeaturePython.

Dodaj następujące elementy po funkcji __init__():

def execute(self, obj):
    """
    Called on document recompute
    """

    print('Recomputing {0:s} ({1:s})'.format(obj.Name, self.Type))

Przetestuj kod, ponownie wykonując następujące kroki:

  • Zapisz i ponownie załaduj moduł.
  • Usuń wszystkie obiekty.
  • Utwórz nowy obiekt box.

Powinieneś zobaczyć wyniki drukowania w konsoli Python, dzięki wywołaniu recompute(), które dodaliśmy do metody create(). Oczywiście metoda execute() nie robi tutaj nic poza poinformowaniem nas, że została wywołana, ale jest ona kluczem do magii obiektów FeaturePython.

To wszystko, wiesz już jak zbudować podstawowy, funkcjonalny obiekt FeaturePython!

na początek strony

Kompletny kod

import FreeCAD as App

def create(obj_name):
    """
    Object creation method
    """

    obj = App.ActiveDocument.addObject('App::FeaturePython', obj_name)

    box(obj)

    App.ActiveDocument.recompute()

    return obj

class box():

    def __init__(self, obj):
        """
        Default constructor
        """

        self.Type = 'box'

        obj.Proxy = self

        obj.addProperty('App::PropertyString', 'Description', 'Base', 'Box description').Description = ""
        obj.addProperty('App::PropertyLength', 'Length', 'Dimensions', 'Box length').Length = 10.0
        obj.addProperty('App::PropertyLength', 'Width', 'Dimensions', 'Box width').Width = '10 mm'
        obj.addProperty('App::PropertyLength', 'Height', 'Dimensions', 'Box height').Height = '1 cm'

    def execute(self, obj):
        """
        Called on document recompute
        """

        print('Recomputing {0:s} ({1:s})'.format(obj.Name, self.Type))

na początek strony