Line drawing function/pl: Difference between revisions

From FreeCAD Documentation
(Created page with "Do tej pory prawdopodobnie rozumiesz powyższy skrypt. Tworzymy nową klasę, którą nazywamy {{incode|MyWorkbench}}, nadajemy jej tytuł {{incode|MenuText}}, i definiujemy funkcję {{incode|Initialize()}}, która zostanie wykonana, gdy środowisko pracy zostanie załadowane do FreeCAD. W tej funkcji wczytujemy zawartość naszego pliku ćwiczenia i dodajemy znalezione w nim komendy FreeCAD do listy komend. Następnie tworzymy pasek narzędzi o nazwie "Moje skrypty" i p...")
(Created page with "Ale to wciąż nie zadziała, ponieważ komenda FreeCAD musi być sformatowana w określony sposób, będziemy musieli zmienić nasze narzędzie {{incode|line()}}. Nasz nowy skrypt {{FileName|exercise.py}} powinien wyglądać tak:")
Line 198: Line 198:
Do tej pory prawdopodobnie rozumiesz powyższy skrypt. Tworzymy nową klasę, którą nazywamy {{incode|MyWorkbench}}, nadajemy jej tytuł {{incode|MenuText}}, i definiujemy funkcję {{incode|Initialize()}}, która zostanie wykonana, gdy środowisko pracy zostanie załadowane do FreeCAD. W tej funkcji wczytujemy zawartość naszego pliku ćwiczenia i dodajemy znalezione w nim komendy FreeCAD do listy komend. Następnie tworzymy pasek narzędzi o nazwie "Moje skrypty" i przypisujemy naszą listę komend do niego. Aktualnie, oczywiście, mamy tylko jedno narzędzie, więc nasza lista komend zawiera tylko jeden element. Następnie, gdy nasze środowisko pracy jest gotowe, dodajemy je do głównego interfejsu.
Do tej pory prawdopodobnie rozumiesz powyższy skrypt. Tworzymy nową klasę, którą nazywamy {{incode|MyWorkbench}}, nadajemy jej tytuł {{incode|MenuText}}, i definiujemy funkcję {{incode|Initialize()}}, która zostanie wykonana, gdy środowisko pracy zostanie załadowane do FreeCAD. W tej funkcji wczytujemy zawartość naszego pliku ćwiczenia i dodajemy znalezione w nim komendy FreeCAD do listy komend. Następnie tworzymy pasek narzędzi o nazwie "Moje skrypty" i przypisujemy naszą listę komend do niego. Aktualnie, oczywiście, mamy tylko jedno narzędzie, więc nasza lista komend zawiera tylko jeden element. Następnie, gdy nasze środowisko pracy jest gotowe, dodajemy je do głównego interfejsu.


But this still won't work because a FreeCAD command must be formatted in a certain manner to work, we will need to change our {{incode|line()}} tool. Our new {{FileName|exercise.py}} script should look like this:
Ale to wciąż nie zadziała, ponieważ komenda FreeCAD musi być sformatowana w określony sposób, będziemy musieli zmienić nasze narzędzie {{incode|line()}}. Nasz nowy skrypt {{FileName|exercise.py}} powinien wyglądać tak:


{{Code|code=
{{Code|code=

Revision as of 13:38, 19 March 2024

Wprowadzenie

Ta strona pokazuje, jak łatwo można tworzyć zaawansowane funkcje w środowisku Python. W tym ćwiczeniu zbudujemy nowe narzędzie, które rysuje linię. Narzędzie to można następnie połączyć z poleceniem FreeCAD, a polecenie to można wywołać za pomocą dowolnego elementu interfejsu, takiego jak element menu lub przycisk paska narzędzi.

Skrypt główny

Najpierw napiszemy skrypt zawierający całą naszą funkcjonalność. Następnie zapiszemy go w pliku i zaimportujemy do FreeCAD, aby udostępnić wszystkie jego klasy i funkcje. Uruchom swój ulubiony edytor kodu i wpisz następujące linie:

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)

Przewiń na górę strony

Szczegółowe wyjaśnienia

import Part, FreeCADGui
from pivy.coin import *

W środowisku Python, gdy chcesz użyć funkcji z innego modułu, musisz go zaimportować. W naszym przypadku będziemy potrzebować funkcji z modułu Część do tworzenia linii oraz z modułu Gui FreeCADGui, aby uzyskać dostęp do widoku 3D. Potrzebujemy również pełnej zawartości biblioteki Coin, abyśmy mogli bezpośrednio korzystać ze wszystkich obiektów Coin, takich jak SoMouseButtonEvent, itp.

class line:

Tutaj definiujemy naszą główną klasę. Dlaczego używamy klasy, a nie funkcji? Powodem jest to, że potrzebujemy, aby nasze narzędzie pozostało "żywe", podczas gdy czekamy na kliknięcie użytkownika na ekranie. Funkcja kończy się, gdy jej zadanie zostanie wykonane, ale obiekt (klasa definiuje obiekt) pozostaje aktywny do momentu jego zniszczenia.

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

W Pythonie każda klasa lub funkcja może mieć ciąg dokumentacji (docstring). Jest to szczególnie przydatne w FreeCAD, ponieważ po wywołaniu tej klasy w interpreterze, ciąg opisu zostanie wyświetlony jako podpowiedź.

def __init__(self):

Klasy Python zawsze mogą zawierać funkcję __init__, która jest wykonywana, gdy klasa jest wywoływana w celu utworzenia obiektu. Tutaj umieścimy wszystko, co chcemy, aby się działo, gdy nasze narzędzie liniowe się uruchomi.

self.view = FreeCADGui.ActiveDocument.ActiveView

W klasie zazwyczaj chcesz poprzedzić nazwy zmiennych słowem kluczowym self., aby umożliwić łatwy dostęp do zmiennych we wszystkich funkcjach wewnątrz i na zewnątrz klasy. Tutaj będziemy używać self.view do dostępu i manipulacji aktywnym widokiem 3D.

self.stack = []

Tutaj tworzymy pustą listę, która będzie zawierać punkty 3D wysłane przez funkcję getpoint().

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

To ważna część. Ponieważ mamy do czynienia z sceną Coin3D, używamy mechanizmu zwrotnego Coin, który pozwala na wywołanie funkcji za każdym razem, gdy wystąpi określone zdarzenie sceny. W naszym przypadku tworzymy zwrotkę dla zdarzeń SoMouseButtonEvent i wiążemy ją z funkcją getpoint(). Teraz za każdym razem, gdy zostanie naciśnięty lub zwolniony przycisk myszy, zostanie wykonana funkcja getpoint().

Należy pamiętać, że istnieje również alternatywa dla addEventCallbackPivy() o nazwie addEventCallback(), która nie opiera się na pivy. Ale ponieważ pivy jest bardzo wydajnym i naturalnym sposobem dostępu do dowolnej części sceny Coin, jest to lepszy wybór.

Przewiń na górę strony

def getpoint(self, event_cb):

Teraz zdefiniujemy funkcję getpoint(), która będzie wykonywana po naciśnięciu przycisku myszy w widoku 3D. Funkcja ta otrzyma argument, który nazwiemy event_cb. Z tego wywołania zwrotnego zdarzenia możemy uzyskać dostęp do obiektu zdarzenia, który zawiera kilka informacji (więcej informacji znajdziesz na stronie wycinki kodu).

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

Funkcja getpoint() zostanie wywołana, gdy zostanie naciśnięty lub zwolniony przycisk myszy. Jednak chcemy wybrać punkt 3D tylko wtedy, gdy zostanie naciśnięty przycisk, w przeciwnym razie otrzymalibyśmy dwa punkty 3D bardzo blisko siebie. Dlatego musimy to sprawdzić tutaj.

pos = event.getPosition()

Tutaj otrzymujemy współrzędne ekranowe kursora myszy.

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

Funkcja ta daje nam wektor FreeCAD (x,y,z) zawierający punkt 3D, który leży na płaszczyźnie ogniskowej, tuż pod kursorem myszy. Jeśli jesteś w widoku kamery, wyobraź sobie promień wychodzący z kamery, przechodzący przez kursor myszy i uderzający w płaszczyznę ogniskową. Jest to lokalizacja naszego punktu 3D. Jeśli jesteśmy w widoku ortogonalnym, promień jest równoległy do kierunku widoku.

self.stack.append(point)

Dodajemy nasz nowy punkt do stosu.

if len(self.stack) == 2:

Czy mamy już wystarczająco dużo punktów? Jeśli tak, to wyznaczmy granicę!

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

Tutaj używamy funkcji LineSegment() z modułu Część, która tworzy linię z dwóch wektorów FreeCAD. Linia nie jest powiązana z żadnym obiektem w naszym aktywnym dokumencie, więc nic nie pojawia się na ekranie.

shape = l.toShape()

Dokument FreeCAD może akceptować tylko kształty z modułu Część. Kształty są najbardziej ogólnym typem modułu Część. Musimy więc przekonwertować naszą linię na kształt przed dodaniem jej do dokumentu.

Part.show(shape)

Moduł Część posiada bardzo przydatną funkcję show(), która tworzy nowy obiekt w dokumencie i wiąże z nim kształt. Mogliśmy również najpierw utworzyć nowy obiekt w dokumencie, a następnie ręcznie powiązać z nim kształt.

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

Ponieważ skończyliśmy z naszą linią, usuwamy tutaj mechanizm wywołania zwrotnego.

Przewiń na górę strony

Testowanie skryptu

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.

Now everything is ready. Let's start FreeCAD, create a new document, and in the Python interpreter issue:

import exercise

If no error message appears our exercise script has been loaded successfully. We can now check its contents with:

dir(exercise)

The command dir() is a built-in Python command that lists the contents of a module. We can check that our line() class is there with:

'line' in dir(exercise)

Now let's test it:

exercise.line()

Click two times in the 3D view and bingo: here is our line! To repeat it just type exercise.line() again.

Przewiń na górę strony

Zarejestrowanie skryptu

Aby nasze nowe narzędzie do rysowania linii było naprawdę użyteczne i uniknąć konieczności wpisywania wszystkich tych komend, powinno ono mieć przycisk w interfejsie. Jednym sposobem na to jest przekształcenie naszego nowego folderu MyScripts w pełne środowisko pracy FreeCAD. To jest proste, wystarczy umieścić plik o nazwie InitGui.py wewnątrz folderu MyScripts. Plik InitGui.py będzie zawierać instrukcje do stworzenia nowego środowiska pracy oraz dodania naszego nowego narzędzia do niego. Poza tym będziemy musieli trochę zmodyfikować nasz kod ćwiczenia, aby narzędzie line() było rozpoznawane jako oficjalna komenda FreeCAD. Zacznijmy od stworzenia pliku InitGui.py i wpisanie w nim następującego kodu:

class MyWorkbench (Workbench):

    MenuText = "MyScripts"

    def Initialize(self):
        import exercise
        commandslist = ["line"]
        self.appendToolbar("My Scripts", commandslist)

Gui.addWorkbench(MyWorkbench())

Do tej pory prawdopodobnie rozumiesz powyższy skrypt. Tworzymy nową klasę, którą nazywamy MyWorkbench, nadajemy jej tytuł MenuText, i definiujemy funkcję Initialize(), która zostanie wykonana, gdy środowisko pracy zostanie załadowane do FreeCAD. W tej funkcji wczytujemy zawartość naszego pliku ćwiczenia i dodajemy znalezione w nim komendy FreeCAD do listy komend. Następnie tworzymy pasek narzędzi o nazwie "Moje skrypty" i przypisujemy naszą listę komend do niego. Aktualnie, oczywiście, mamy tylko jedno narzędzie, więc nasza lista komend zawiera tylko jeden element. Następnie, gdy nasze środowisko pracy jest gotowe, dodajemy je do głównego interfejsu.

Ale to wciąż nie zadziała, ponieważ komenda FreeCAD musi być sformatowana w określony sposób, będziemy musieli zmienić nasze narzędzie line(). Nasz nowy skrypt exercise.py powinien wyglądać tak:

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

What we did here is transform our __init__() function into an Activated() function. When FreeCAD commands are run, they automatically execute the Activated() function. We also added a GetResources() function, that informs FreeCAD where it can find the 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.

That's it, now we just need to restart FreeCAD and we'll have a nice new workbench with our brand new line tool!

Przewiń na górę strony

Chcesz czegoś więcej?

Jeśli spodobało Ci się to ćwiczenie, dlaczego nie spróbujesz ulepszyć tego narzędzia? Jest wiele rzeczy, które można zrobić, na przykład:

  • Dodaj informacje zwrotne dla użytkownika: do tej pory stworzyliśmy bardzo podstawowe narzędzie, więc użytkownik może trochę się zgubić podczas jego użytkowania. Możemy dodać jakieś komunikaty zwrotne, informujące użytkownika, co zrobić następnie. Możesz wyświetlać komunikaty w konsoli FreeCAD. Spójrz na moduł FreeCAD.Console.
  • Dodaj możliwość wprowadzania współrzędnych punktów 3D ręcznie. Zajrzyj do funkcji Pythona input() na przykład.
  • Dodaj możliwość dodawania więcej niż 2 punktów.
  • Dodaj zdarzenia dla innych elementów: Obecnie sprawdzamy tylko zdarzenia przycisków myszy, ale co jeśli chcielibyśmy zrobić coś także, gdy myszka jest przesuwana, na przykład wyświetlanie aktualnych współrzędnych?
  • Nadaj nazwę utworzonemu obiektowi.

Nie wahaj się zadawać pytań lub dzielić się pomysłami na forum [1]!

Przewiń na górę strony