Scripted objects/pl

Wprowadzenie
Oprócz standardowych typów obiektów, takich jak adnotacje, siatki i obiekty części, FreeCAD oferuje również niesamowitą możliwość tworzenia obiektów parametrycznych w 100% napisanych w języku Python, zwanych właściwościami Python. Obiekty te zachowują się dokładnie tak, jak każdy inny obiekt programu FreeCAD i są zapisywane i przywracane automatycznie podczas zapisywania/wczytywania pliku.

Należy pamiętać o jednej szczególnej kwestii: Ze względów bezpieczeństwa pliki FreeCAD nigdy nie zawierają żadnego osadzonego kodu. Kod Pythona, który piszesz, aby utworzyć obiekty parametryczne, nigdy nie jest zapisywany wewnątrz pliku. Oznacza to, że jeśli otworzysz plik zawierający taki obiekt na innym komputerze, to jeśli ten kod nie będzie dostępny na tym komputerze, obiekt nie zostanie w pełni odtworzony. Jeśli rozpowszechniasz takie obiekty, będziesz musiał rozpowszechnić również swój skrypt Python, na przykład jako makrodefinicję.

Uwaga: Możliwe jest spakowanie kodu Python wewnątrz pliku FreeCAD za pomocą serializacji json z obiektem App::PropertyPythonObject, ale ten kod nigdy nie może być bezpośrednio uruchomiony i dlatego jest mało przydatny w naszym przypadku.

Funkcje Python działają według tej samej zasady, co wszystkie funkcje programu FreeCAD. Są podzielone na część aplikacji i część GUI. Część aplikacji, Obiekt Dokumentu, definiuje geometrię naszego obiektu, podczas gdy jego część GUI, Obiekt Dostawcy Widoku, definiuje sposób, w jaki obiekt będzie rysowany na ekranie. Obiekt View Provider, jak każda inna funkcja programu FreeCAD, jest dostępny tylko wtedy, gdy uruchamiamy program FreeCAD w jego własnym GUI. Istnieje kilka właściwości i metod dostępnych w celu zbudowania obiektu. Właściwości muszą należeć do jednego z predefiniowanych typów właściwości, które oferuje FreeCAD, i będą wyświetlane w oknie widoku właściwości, tak aby użytkownik mógł je edytować. W ten sposób obiekty FeaturePython są prawdziwie i całkowicie parametryczne. Możesz zdefiniować właściwości osobno dla obiektu i osobno dla jego obiektu ViewObject.

Przykład podstawowy
Poniższy przykład można znaleźć w pliku FeaturePython.py, wraz z kilkoma innymi przykładami:

Warto wiedzieć
Jeśli twój obiekt wymaga ponownego obliczenia zaraz po utworzeniu, musisz to zrobić ręcznie w funkcji, ponieważ nie jest ona wywoływana automatycznie. W tym przykładzie nie jest to wymagane, ponieważ metoda klasy  wywołuje ten sam rezultat, co funkcja, ale w poniższych przykładach wymagane jest ponowne obliczenie, zanim cokolwiek zostanie wyświetlone w widoku 3D. W przykładach jest to wykonywane ręcznie za pomocą funkcji, ale w bardziej złożonych scenariuszach należy zdecydować, w którym miejscu ponownie obliczyć cały dokument lub obiekt FeaturePython.

Ten przykład powoduje powstanie wielu śladów stosu wyjątków w oknie widoku raportu. Dzieje się tak, ponieważ metoda klasy  jest wywoływana za każdym razem, gdy w  dodawana jest jakaś właściwość. Gdy dodawana jest pierwsza właściwość, właściwości Width i Height jeszcze nie istnieją, więc próba uzyskania do nich dostępu kończy się niepowodzeniem.

Wyjaśnienie działania funkcji i  znajduje się w wątku na forum obj.Proxy.Type jest wartością typu dict, a nie string.

Dostępne metody
Zobacz stronę Metody FeaturePython, aby zapoznać się z pełnym opisem.

Dostępne własności
Właściwości są prawdziwymi kamieniami węgielnymi obiektów FeaturePython. Dzięki nim użytkownik będzie mógł wchodzić w interakcje z obiektem i modyfikować go. Po utworzeniu nowego obiektu FeaturePython w swoim dokumencie (obj=FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Box") ) możesz uzyskać listę dostępnych właściwości, wydając polecenie:

Pojawi się lista dostępnych właściwości, które są opisane bardziej szczegółowo na stronie Właściwości niestandardowe funkcji Python:


 * App::PropertyAcceleration
 * App::PropertyAngle
 * App::PropertyArea
 * App::PropertyBool
 * App::PropertyBoolList
 * App::PropertyColor
 * App::PropertyColorList
 * App::PropertyDirection
 * App::PropertyDistance
 * App::PropertyEnumeration
 * App::PropertyExpressionEngine
 * App::PropertyFile
 * App::PropertyFileIncluded
 * App::PropertyFloat
 * App::PropertyFloatConstraint
 * App::PropertyFloatList
 * App::PropertyFont
 * App::PropertyForce
 * App::PropertyFrequency
 * App::PropertyInteger
 * App::PropertyIntegerConstraint
 * App::PropertyIntegerList
 * App::PropertyIntegerSet
 * App::PropertyLength
 * App::PropertyLink
 * App::PropertyLinkChild
 * App::PropertyLinkGlobal
 * App::PropertyLinkHidden
 * App::PropertyLinkList
 * App::PropertyLinkListChild
 * App::PropertyLinkListGlobal
 * App::PropertyLinkListHidden
 * App::PropertyLinkSub
 * App::PropertyLinkSubChild
 * App::PropertyLinkSubGlobal
 * App::PropertyLinkSubHidden
 * App::PropertyLinkSubList
 * App::PropertyLinkSubListChild
 * App::PropertyLinkSubListGlobal
 * App::PropertyLinkSubListHidden
 * App::PropertyMap
 * App::PropertyMaterial
 * App::PropertyMaterialList
 * App::PropertyMatrix
 * App::PropertyPath
 * App::PropertyPercent
 * App::PropertyPersistentObject
 * App::PropertyPlacement
 * App::PropertyPlacementLink
 * App::PropertyPlacementList
 * App::PropertyPosition
 * App::PropertyPrecision
 * App::PropertyPressure
 * App::PropertyPythonObject
 * App::PropertyQuantity
 * App::PropertyQuantityConstraint
 * App::PropertySpeed
 * App::PropertyString
 * App::PropertyStringList
 * App::PropertyUUID
 * App::PropertyVacuumPermittivity
 * App::PropertyVector
 * App::PropertyVectorDistance
 * App::PropertyVectorList
 * App::PropertyVolume
 * App::PropertyXLink
 * App::PropertyXLinkList
 * App::PropertyXLinkSub
 * App::PropertyXLinkSubList
 * Mesh::PropertyCurvatureList
 * Mesh::PropertyMeshKernel
 * Mesh::PropertyNormalList
 * Part::PropertyFilletEdges
 * Part::PropertyGeometryList
 * Part::PropertyPartShape
 * Part::PropertyShapeHistory
 * Path::PropertyPath
 * Path::PropertyTool
 * Path::PropertyTooltable
 * Sketcher::PropertyConstraintList
 * Spreadsheet::PropertyColumnWidths
 * Spreadsheet::PropertyRowHeights
 * Spreadsheet::PropertySheet
 * Spreadsheet::PropertySpreadsheetQuantity
 * TechDraw::PropertyCenterLineList
 * TechDraw::PropertyCosmeticEdgeList
 * TechDraw::PropertyCosmeticVertexList
 * TechDraw::PropertyGeomFormatList

Podczas dodawania właściwości do obiektów niestandardowych należy zwrócić uwagę na następujące kwestie:
 * Nie używaj znaków lub  w opisach właściwości (spowodowałoby to przerwanie fragmentów xml w pliku .fcstd)
 * Właściwości są przechowywane w pliku .fcstd w porządku alfabetycznym. Jeśli we właściwościach znajduje się kształt, każda właściwość, której nazwa w porządku alfabetycznym znajduje się za "Kształtem", zostanie wczytana PRZED kształtem, co może powodować dziwne zachowania.

Pełną listę atrybutów właściwości można znaleźć w pliku nagłówkowym PropertyStandard C++. Na przykład, jeśli chcesz pozwolić użytkownikowi na wprowadzenie tylko ograniczonego zakresu wartości (np. używając PropertyIntegerConstraint), w Pythonie przypiszesz tuple zawierające nie tylko wartość właściwości, ale także dolny i górny zakres oraz wielkość kroku, jak przedstawiono poniżej:

Typy właściwości
Domyślnie właściwości mogą być aktualizowane. Możliwe jest nadanie właściwościom statusu tylko do odczytu, na przykład w sytuacji, gdy chcemy pokazać wynik działania metody. Możliwe jest także ukrycie właściwości. Typ właściwości można ustawić za pomocą:

gdzie mode jest krótką liczbą całkowitą, której można nadać następujące wartości: 0 -- tryb domyślny, odczyt i zapis 1 -- tylko do odczytu 2 -- ukryty

The EditorModes are not set at FreeCAD file reload. This could to be done by the __setstate__ function. See http://forum.freecadweb.org/viewtopic.php?f=18&t=13460&start=10#p108072. By using the setEditorMode the properties are only read only in PropertyEditor. They could still be changed from python. To really make them read only the setting has to be passed directly inside the addProperty function. See http://forum.freecadweb.org/viewtopic.php?f=18&t=13460&start=20#p109709 for an example.

Korzystając z bezpośredniego ustawienia w funkcji addProperty, masz też więcej możliwości. W szczególności interesującą możliwością jest oznaczenie właściwości jako właściwości wyjściowej. W ten sposób FreeCAD nie będzie oznaczał właściwości jako dotkniętej podczas jej zmiany (nie ma więc potrzeby ponownego obliczania).

Przykład właściwości wyjściowej (zob. też https://forum.freecadweb.org/viewtopic.php?t=24928):

Typy właściwości, które można ustawić w ostatnim parametrze funkcji addProperty, są następujące: 0 -- Prop_None, brak specjalnego typu właściwości 1 -- Prop_ReadOnly, właściwość jest tylko do odczytu w edytorze 2 -- Prop_Transient, właściwość nie zostanie zapisana do pliku 4 -- Prop_Hidden, właściwość nie będzie widoczna w edytorze 8 -- Prop_Output, Zmodyfikowana właściwość nie dotyka swojego kontenera nadrzędnego 16 -- Prop_NoRecompute, Zmodyfikowana właściwość nie dotyka swojego kontenera do rekompilacji

Te różne typy właściwości można znaleźć w source code C++ header for PropertyContainer.

Inny, bardziej złożony przykład
Ten przykład wykorzystuje środowisko pracy Część do utworzenia ośmiościanu, a następnie tworzy jego reprezentację "coin" za pomocą Pivy.

Pierwszym z nich jest sam obiekt Dokumentu:

Następnie mamy obiekt dostawcy widoku, odpowiedzialny za wyświetlanie obiektu na scenie 3D:

Na koniec, gdy nasz obiekt i jego viewobject są już zdefiniowane, wystarczy je wywołać (kod klasy Octahedron i klasy viewprovider można skopiować bezpośrednio w konsoli Python programu FreeCAD):

Robienie obiektów wybieralnymi
Jeśli chcesz, aby Twój obiekt był wybieralny, lub przynajmniej jego część, przez kliknięcie na nim w rzutni, musisz umieścić jego geometrię coin w węźle SoFCSelection. Jeśli obiekt ma złożoną reprezentację, z widżetami, adnotacjami itp, możesz chcieć zawrzeć tylko jego część w węźle SoFCSelection. Wszystko, co jest węzłem SoFCSelection, jest stale skanowane przez FreeCAD w celu wykrycia zaznaczenia/poprzedzenia zaznaczenia, więc warto spróbować nie przeciążać go niepotrzebnym skanowaniem.

Gdy fragmenty scenogramu, które mają być wybieralne, znajdą się w węzłach SoFCSelection, należy dostarczyć dwie metody do obsługi ścieżki wyboru. Ścieżka wyboru może mieć postać łańcucha podającego nazwy poszczególnych elementów ścieżki lub tablicy obiektów scenogramu. Dwie metody, które udostępniasz, to, która konwertuje ścieżkę łańcuchową na tablicę obiektów scenograficznych, oraz , która pobiera element kliknięty w scenogramie i zwraca jego nazwę w postaci łańcucha (uwaga, nie jego ścieżkę w postaci łańcucha).

Oto powyższy przykład cząsteczki, dostosowany do wyboru elementów cząsteczki:

Praca z prostymi kształtami
Jeśli obiekt parametryczny po prostu wyprowadza kształt, nie trzeba używać obiektu dostawcy widoku. Kształt zostanie wyświetlony przy użyciu standardowej reprezentacji kształtu programu FreeCAD:

Ten sam kod z użyciem ViewProviderLine.

Struktura scenograficzna
Być może zauważyłeś, że powyższe przykłady konstruują swoje scenagramy w nieco inny sposób. Niektóre używają, podczas gdy inne.

Każdy element w dokumencie FreeCAD jest oparty na następującej strukturze scenogramu:

Węzeł wyświetla tylko jeden ze swoich potomków, w zależności od tego, jaki tryb wyświetlania został wybrany w programie FreeCAD.

Przykłady, które używają, konstruują swoje scenagramy wyłącznie z elementów scenogramu coin3d. Pod przykrywką, dodaje nowego potomka do węzła. Nazwa tego węzła będzie odpowiadać trybowi wyświetlania, który został mu przekazany.

Przykłady, które używają, konstruują również część swojej geometrii przy użyciu funkcji ze środowiska pracy Część, takich jak. Dzięki temu pod węzłem powstają scenografie z różnymi trybami wyświetlania. Gdy później chcemy dodać do nich elementy coin3d, musimy je dodać do istniejących scenogramów z trybami wyświetlania, używając, a nie tworząc nowego potomka węzła.

Podczas używania w celu dodania geometrii do scenogramu, każdy tryb wyświetlania powinien mieć swój własny węzeł, który jest przekazywany do ; nie używaj do tego ponownie tego samego węzła. Spowoduje to zmylenie mechanizmu selekcji. W porządku, jeśli węzeł każdego trybu wyświetlania ma te same węzły geometrii dodane poniżej, tylko korzeń każdego trybu wyświetlania musi być inny.

Here is the above molecule example, adapted to be drawn only with Coin3D scenegraph objects instead of using objects from the Part workbench:

Part Design scripted objects
When making scripted objects in Part Design the process is similar to the scripted objects discussed above, but with a few additional considerations. We must handle 2 shape properties, one for the shape we see in the 3D view and another for the shape used by the pattern tools, such as polar pattern features. The object shapes also needs to be fused to any existing material already in the Body (or cut from it in the case of Subtractive features). And we must account for the placement and attachment of our objects a little bit differently.

Part Design scripted solid object features should be based on either PartDesign::FeaturePython, PartDesign::FeatureAdditivePython, or PartDesign::FeatureSubtractivePython rather than Part::FeaturePython. Only the Additive and Subtractive variants can be used in pattern features, and if based on Part::FeaturePython when the user drops the object into a Part Design Body it becomes a BaseFeature rather than being treated by the Body as a native Part Design object. Note: all of these are expected to be solids, so if you are making a non-solid feature it should be based on Part::FeaturePython or else the next feature in the tree will attempt to fuse to as a solid and it will fail.

Here is a simple example of making a Tube primitive, similar to the Tube primitive in Part Workbench except this one will be a Part Design solid feature object. For this we will 2 separate files: pdtube.FCMacro and pdtube.py. The .FCMacro file will be executed by the user to create the object. The .py file will hold the class definitions, imported by the .FCMacro. The reason for doing it this way is to maintain the parametric nature of the object after restarting FreeCAD and opening a document containing one of our Tubes.

First, the class definition file:

And now the macro file to create the object:

Informacje dodatkowe
Dodatkowe strony:
 * Obiekty tworzone skryptami, zapis atrybutów
 * Obiekty tworzone skryptami, migracja
 * Obiekty tworzone skryptami, z załącznikiem
 * Dostawca widoku

Ciekawe wątki na forum dotyczące obiektów tworzonych skryptami:


 * Python object attributes lost at load
 * New FeaturePython is grey
 * Explanation on __getstate__ and __setstate__, official documentation
 * Eigenmode frequency always 0?
 * how to implement python feature's setEdit properly?

Oprócz przykładów przedstawionych tutaj więcej przykładów można znaleźć w kodzie źródłowym programu FreeCAD src/Mod/TemplatePyMod/FeaturePython.py.