Функция рисования линии

From FreeCAD Documentation
Revision as of 21:07, 23 August 2020 by FuzzyBot (talk | contribs) (Updating to match new version of source page)

Introduction

На этой странице показано лего можно создать продвинутую фукнкциональность в Python. В данном примере , мы создадим новый инструмент, для рисования линии. Этот инструмент может быть свзае с командой FreeCAD, и эта команда может быть вызвана из любого элемента интерфейса, таком как как опция меню или кнопка на панели инструментов.

Сам сценарий

Сначало мы напишем сценарий содержащий всю о нашу функциональность. Затем, мы сохраним его в файле и импортируем в FreeCAD, так все классы и функции что мы описали будут доступны в FreeCAD. Так что, запускатите ваш любимы текстовый редактор, и введите следующие строки:

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

Подробное описание

import Part, FreeCADGui
from pivy.coin import *

В Python, когда вам требуется использовать функции из другого модуля, вам нужно импортировать его. В нашем случае, нам нужны функции из Модуля Деталей, для создания линии, и из Gui модуля (FreeCADGui), для доступа к 3D виду. Нам также нужно полное содержание библиотеки coin, так мы сможем напрямую использовать все coin объекта такие как SoMouseButtonEvent, и.т.д...

class line:

Здесь мы задаем наш основной класс. Почему мы используем класс а не функцию? Причина в том чтобы нащ инструмент оставался "живым" когда мы ждем что пользователь нажмет на экран. Функция завершится когда её задача будет выполнена, но объект (класс заданый как объект) остается живым пока не будет уничтожен.

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

В Python, каждый класс или функция могут обладать строкой описания. Это особенно полезно в FreeCAD, потому что когда вы будете вызывать этот класс через интепритатор, строка описания будет отображатся в виде всплывающей подсказки.

def __init__(self):

Python класс всегда может содержать __init__ функцию, которая выполняется когда класс вызывается для создания объекта. Таким образом, мы положим сюда всё что мы хотим чтобы случилось, когда запустится наш инструмент "линия".

self.view = FreeCADGui.ActiveDocument.ActiveView

В классе, вам обычно нужно добавить self. перед именем переменной, для того чтобы получить легкий доступ к функциям в и вне класса. Здесь мы используем self.view для доступа к управлению активным 3D видом.

self.stack = []

Здесь мы создаем простой список содержащий 3D точки, переданные функцией getpoint.

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

Это важная часть: Посколько это фактически coin3D сцена(в смысле окно отображения), FreeCAD использует механизм обратного вызова coin, что позваляет вызывать функцию каждый раз когда на сцене что-то происходит. В нашем случае, мы создаем обратный вызов для события SoMouseButtonEvent , и мы привязываем его getpoint функции. Теперь, каждый раз когда клавиша мыши будет нажата или отпущена, будет выполнятся функция getpoint.

Замети также что существует альтернативна addEventCallbackPivy(), зовется addEventCallback() которая обходится без использование pivy. Но так как pivy это очень эффективный и естественный способ получить доступ к любой части coin сцены, он гораздо лучше для использования так как вы можете!

top

def getpoint(self, event_cb):

Теперь вы задали getpoint функцию, которая выполняется когда клавиша мыши щелкает по окну 3D вида. Эта функция будет получать аргумент, который мы назовем event_cb. В время обратного вызова мы можем получить доступ к объекту события, который содержит некоторую информацию (информационный режим описан здесь).

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

Getpoint функция будет вызыватся когда клавиши мыши будет нажата или отжата. Но мы хотим фиксировать 3D точку только когда нажимаем (в противном случае мы мы можем получить две 3D точки очень близко друг от друга). Так что мы проверяем это.

pos = event.getPosition()

Здесь мы получаем координаты курсора мыши

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

Эта функция дает нам FreeCAD вектор (x,y,z) содержащий точку лежащую в фокальной плоскости, т.е. под вашим курсором. Если вы находитесь в режиме камеры, изображается луч идущий из камеры проходящий через курсор мыши, и достигающий фокальной плоскости. Это наша 3D точка. Если мы находимся в режиме ортогонального отображения, луч паралелен направлению вида.

self.stack.append(point)

Мы добавляем новую точку в stack

if len(self.stack) == 2:

Если у вас, уже достаточное количество точек? если да, тогда давайте рисовать линию!

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

Здесь мы используем Line() функцию из Модуля Деталей которая создает линию по двум FreeCAD векторам. Все что мы создаем и модифицируем внутри модуля Деталей, остается в модуле(Part module). Так что, до сих пор, когда мы создавали Line Part. Она не была привязана к какому либо объекту в нашем документе, поэтому ничего и не отображалось на экране.

shape = l.toShape()

FreeCAD документ может принимать только формы(shapes) из модуля Деталей. Формы являются наиболее универсальным типом из модуля Деталей. Таким образом, мы должны преобразовать нашу линию в форму и добавить её в документ.

Part.show(shape)

Модуль деталей обладает очень удобной функцией show() ,которая создает новый объект в документе и привязывает форму к нему. Мы таже могли создать новый объект и привязать к нему форму вручную.

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

Теперь, когда мы закончили нашу линию, давайте уберем механизм обратного вызова, который потребляет драгоценные циклы ЦПУ.

top

Тестирвание & Использование сценария

Теперь, давайте сохраним наш сценарий где нибудь где FreeCAD python интепритатор сможет его найти. Когда импортируются модули, интепритатор просматривает следующие места: директорию куда установлен python,FreeCAD bin директорию, и все папки FreeCAD модулей. Так что, лучшим решением будет создать новую папку в одной из FreeCAD Mod папках, и сохранит наш сценарий в ней. Например, давайте создадим папку "MyScripts" , и сохраним наш сценарий как "exercise.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.

Теперь, когда все готово, давайте запустим FreeCAD, создадим новый документ, и введем в python интерпритатор:

import exercise

Если сообщений об ошибке не появится, это означает, что наш учебный сценарий был загружен. Теперь мы можем проверить его содержимое:

dir(exercise)

Команда dir() встроенная python которая выдает список содержащегося в модуле. Мы можем видеть здесь нас ждет, наш класс line(). Теперь давайте протестируем его:

'line' in dir(exercise)

Now let's test it:

exercise.line()

Затем, щелкнем два раза на 3D виде, и бинго, вот наша линия! Чтобы сделать это снова , просто опять введите exercise.line(), и ещё раз, и ещё раз... Чувствуете себе прекрасно, не так ли?

top

Регистрация сценария в FreeCAD интерфейсе

Теперь, для нашего нового инструмента "линия" будет здорово, если он будет обладать кнопкой в интерфейсе, так чтобы нам не нужно было его каждый раз вводить. Простейший путь это преобразовать наш новый каталог MyScripts в полноценнный FreeCAD инструментарий(workbench). Это просто, все что нужно это поместить в файл зовущийся InitGui.py внутрь вашей MyScripts папки. InitGui.py будет содержать инструкции создания нового инструментария, м добавлять наш новый инструмент в него. Кроме того мы должны трансформировать код нашего примера, так чтобы инструмент line() был признан как официальная FreeCAD команда. Начнем с создания InitGui.py файла, и запишем в него следующий код:

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

К этому моменту , я думаю, вы должны понимать сценарий выше: Мы создали новый класс назвали его MyWorkbench, мы задаем заглавие(MenuText), и задаем Initialize() функцию, которая будет выполняться когда инструментарий будт загружен в FreeCAD. В этой функции, мы загружаем вск что содержится в нашем exercise файле, и добавляем в FreeCAD команды найденные внутри списка команд(commandlist). Затем, мы создаем панель инструментов названую "My Scripts" и сопоставляем наш список команд с ней. Конечно, сейчас у нас есть только один инструмент, так как наш список команд содержит один элемент. Затем, когда наш инструментарий готов, мы добавляем его в основной(главный) интерфейс.

Но это пока не будет работать, потому что FreeCAD команда для работы должна быть отформатирована определенным образом. Так что нам нужно преобразовать(доработать) наш line() инструмент. Наш новый exercise.py сценарий будет выглядеть следующим образом:

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

То, что мы сделали здесь является преобразованием нашей __init__() функции в Activated() функцию, потому что, когда FreeCAD команды выполняются, они автоматически выполняют Activated() функцию. Мы также добавили функцию GetResources(), которая сообщает FreeCAD, где он может найти значок инструмента, и каким будет название и подсказка нашего инструмента. Любой JPG, PNG или SVG изображения будут работать, как иконка, они могут быть любого размера, но лучше использовать размер, близкий к последним аспектам, как 16x16, 24x24 или 32x32. Затем, мы добавили line() класс в качестве официального команды FreeCAD с AddCommand() методом.

Это всё, теперь вам нужно перезапустить FreeCAD и вы получите новый хороший инструментарий с нашим новым инструментом линии!

top

Вы хотите большего?

Если вам понравился этот пример, почему не попытаться улучшить этот маленький инструмент? Существует множесво вещей которые нужно сделать, как например:

  • Добавить обратную связь с пользователем: до сих пор мы делали очень голый инструмент, пользователь может потерятся при его использовании. Таким образом мы могли бы добавить обратную связь, сообщающую ему что делать дальше. Например, мы можем выводит сообщения в FreeCAD консоль. Загляните в FreeCAD.Console модуль
  • Добавить возможность вводить координаты 3D точек вручную. Посмотрите, на пример, python input() функцию
  • Добавить способность добавлять более двух точек
  • Добавить события для других вещей: сейчас мы только проверяем события кнопок мыши, что если мы хотели бы также сделать что-то когда мышь перемещается, например отображать текущие координаты?
  • Давать имя созданному объекту

Не стесняйтесь писать ваши вопросы или идеи на forum!

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

top