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

Tryby edytora nie są ustawiane przy ponownym wczytywaniu pliku FreeCAD. Można to zrobić za pomocą funkcji __setstate__. Zobacz http://forum.freecadweb.org/viewtopic.php?f=18&t=13460&start=10#p108072. Dzięki użyciu setEditorMode właściwości są tylko do odczytu w Edytorze właściwości. Nadal można je zmieniać z poziomu Pythona. Aby naprawdę uczynić je tylko do odczytu, ustawienie musi być przekazane bezpośrednio wewnątrz funkcji addProperty. Przykład można znaleźć na stronie http://forum.freecadweb.org/viewtopic.php?f=18&t=13460&start=20#p109709.

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 32 -- Prop_NoPersist, właściwość nie zostanie zapisana do pliku

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.

Oto powyższy przykład cząsteczki, przystosowany do rysowania tylko obiektami scenograficznymi Coin3D, a nie obiektami z środowiska pracy Część:



Obiekty skryptowe środowiska pracy Projekt Części
Podczas tworzenia obiektów skryptowych w środowisku pracy Projekt Części proces jest podobny do tworzenia obiektów skryptowych omówionych powyżej, ale z kilkoma dodatkowymi uwagami. Musimy obsługiwać dwie właściwości kształtu, jedną dla kształtu, który widzimy w oknie widoku 3D, a drugą dla kształtu używanego przez narzędzia wzorca, takie jak cechy wzoru biegunowego. Kształty obiektów muszą również zostać połączone z istniejącym materiałem już znajdującym się w zawartości (lub wycięte z niego w przypadku cech typu Subtractive). Musimy także w nieco inny sposób uwzględniać umieszczanie i mocowanie naszych obiektów.

Opisane w skryptach funkcje obiektów bryłowych Projekt Części powinny opierać się na: PartDesign::FeaturePython, PartDesign::FeatureAdditivePython lub PartDesign::FeatureSubtractivePython, a nie na Part::FeaturePython. Tylko warianty Additive i Subtractive mogą być używane we wzorcach cech, a jeśli bazują na Part::FeaturePython, gdy użytkownik upuści obiekt do bryły Projekt Części, staje się on BaseFeature, zamiast być traktowany przez bryłę jako natywny obiekt Projekt Części. Uwaga: wszystkie te obiekty mają być bryłami, więc jeśli tworzysz obiekt nie będący bryłą, powinien on być oparty na Part::FeaturePython, w przeciwnym razie następny obiekt w drzewie będzie próbował połączyć się z nim jako bryłą i to się nie uda.

Oto prosty przykład tworzenia elementu pierwotnego rury, podobnego do elementu pierwotnego rury w środowisku pracy Część, z tą różnicą, że ten element będzie obiektem bryłowym środowiska pracy Projekt Części. W tym celu utworzymy dwa osobne pliki: pdtube.FCMacro i pdtube.py. Plik .FCMacro będzie wykonywany przez użytkownika w celu utworzenia obiektu. Plik .py będzie zawierał definicje klas zaimportowane przez plik .FCMacro. Powodem takiego postępowania jest zachowanie parametrycznego charakteru obiektu po ponownym uruchomieniu programu FreeCAD i otwarciu dokumentu zawierającego jedną z naszych rur.

Po pierwsze, plik definicji klasy:

A teraz plik makrodefinicji do utworzenia obiektu:



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.