TechDraw TemplateGenerator/pl: Difference between revisions
(Created page with "Ten poradnik opisuje jak z kilku linijek kodu Pythona wygenerować prosty szablon do wykorzystania w środowisku pracy Rysunek Techniczny.") |
(Created page with "Kolorowanie edytowalnych tekstów na niebiesko jest tylko osobistym wyborem, aby łatwiej odróżnić statyczne i edytowalne teksty na gotowym rysunku.") |
||
(104 intermediate revisions by 3 users not shown) | |||
Line 2: | Line 2: | ||
{{TutorialInfo/pl |
{{TutorialInfo/pl |
||
|Topic= |
|Topic=Tworzenie szablonu Rysunku Technicznego - makrodefinicja Python |
||
|Level=Podstawowa znajomość środowiska Python i struktur svg jest przydatna |
|Level=Podstawowa znajomość środowiska Python i struktur svg jest przydatna |
||
|FCVersion=0. |
|FCVersion=0.21 i nowszy |
||
|Time=''(Jeszcze nie wiem)'' |
|Time=''(Jeszcze nie wiem)'' |
||
|Author=[[User:FBXL5|FBXL5]] |
|Author=[[User:FBXL5|FBXL5]] |
||
|SeeAlso=[[Macro_TemplateHelper/pl|Makro TemplateHelper]] |
|||
}} |
}} |
||
<span id="Introduction"></span> |
|||
==Wprowadzenie== |
==Wprowadzenie== |
||
Ten poradnik opisuje jak z kilku linijek kodu Pythona wygenerować prosty szablon do wykorzystania w środowisku pracy Rysunek Techniczny. |
Ten poradnik opisuje jak z kilku linijek kodu Pythona wygenerować prosty szablon do wykorzystania w środowisku pracy Rysunek Techniczny. |
||
Do kodowania można użyć dowolnego edytora tekstu. Mój wybór to Atom, ale wbudowany edytor FreeCAD też działa dobrze. |
|||
Any text editor can be used to code. My choice is Atom, but FreeCAD's built-in editor works well, too. |
|||
Poniższe przykłady kodu można skopiować i wkleić do pustego pliku tekstowego, a następnie zapisać pod wybraną nazwą jako plik typu {{FileName|*.py}} lub {{FileName|*.FCMacro}}. |
|||
The following code examples can be copied and pasted into an empty text file and then saved under a name of your choice as a *.py or *.FCMacro file. |
|||
Szablon stanowi tło dla zadań rysunkowych, a jego wymiary są wykorzystywane przez sterowniki drukarki do prawidłowego skalowania rysunku. |
|||
A template provides a background for the drawing tasks and its dimensions are used by the printer drivers to scale the drawing correctly. |
|||
Szablony są plikami svg, więc makrodefinicja musi skomponować kilka linii kodu svg ''(który jest podzbiorem kodu xml)''. |
|||
The templates are svg-files and so a macro has to compose some lines of svg code (which is a subset of xml code). |
|||
'''Uwaga:''' Kiedy FreeCAD został przeniesiony z '''freecadweb.org''' do '''freecad.org''', ta strona została odpowiednio zaktualizowana, a wynikowy kod SVG nie jest już kompatybilny z wersjami FreeCAD starszymi niż v0.21. W przypadku tych wersji należy ręcznie zmienić {{Incode|freecad.org}} na {{Incode|freecadweb.org}} w wierszu deklaracji przestrzeni nazw w wynikowym kodzie SVG, w przeciwnym razie edytowalne teksty nie zostaną rozpoznane. |
|||
== Structure of a simple blank page == |
|||
<span id="Structure_of_a_simple_blank_page"></span> |
|||
The SVG format is a subset of the XML format. |
|||
==Struktura zwykłej pustej strony== |
|||
That is why an SVG file, like any XML file, consists of two parts: |
|||
* Head, a format declaration |
|||
* Body, containing the information what to show and where to place it |
|||
: (I don't know why there should be a headline, the svg-file is still a valid template file without it...) |
|||
Format SVG jest podzbiorem formatu XML. |
|||
=== Head === |
|||
Dlatego plik SVG, jak każdy plik XML, składa się z dwóch części: |
|||
* Nagłówka, czyli deklaracji formatu. |
|||
* Zawartości, zawierającej informacje co pokazać i gdzie to umieścić |
|||
: ''(nie wiem po co ma być nagłówek, plik svg jest nadal poprawnym plikiem szablonu bez niego ...)'' |
|||
<span id="Head"></span> |
|||
The head is just one line to declare which version of the XML language an interpreter should use to handle the instructions in the body. |
|||
===Nagłówek=== |
|||
Nagłówek to tylko jedna linia deklarująca, której wersji języka XML powinien użyć interpreter do obsługi instrukcji w treści. |
|||
{{Code|lang=xml|code= |
{{Code|lang=xml|code= |
||
Line 37: | Line 43: | ||
}} |
}} |
||
<span id="Body"></span> |
|||
=== Body === |
|||
===Zawartość=== |
|||
Struktura zaczyna się od znacznika otwierającego, który zawiera informacje o przestrzeniach nazw oraz o wielkości szablonu i miejscu jego umieszczenia. A kończy się znacznikiem zamykającym. |
|||
The Body starts with an opening tag which contains information about name spaces and about the size of the template and where to place it. And it finishes with a closing tag. |
|||
{{Code|lang=xml|code= |
{{Code|lang=xml|code= |
||
<svg |
<svg |
||
xmlns="http://www.w3.org/2000/svg" version="1.1" |
xmlns="http://www.w3.org/2000/svg" version="1.1" |
||
xmlns:freecad="http://www. |
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace" |
||
width="420mm" |
width="420mm" |
||
height="297mm" |
height="297mm" |
||
Line 51: | Line 58: | ||
}} |
}} |
||
: '''xmlns=''' |
: '''xmlns='''http://www.w3.org/2000/svg''': Zewnętrzny link do przestrzeni nazw xml, aby wyszukać standardowe polecenia xml. |
||
: '''version='''"1.1": |
: '''version='''"1.1": Używana wersja xml to 1.1 |
||
: '''xmlns:freecad='''...=Svg_Namespace": Zewnętrzny link do strony Wiki FreeCAD [[Svg_Namespace/pl|Svg Namespace]]. Link nie jest używany do pobierania informacji lub wartości w czasie wykonywania, ale jest kluczem do aktywacji niestandardowych atrybutów, np. tych dla edytowalnych tekstów. |
|||
: '''xmlns:freecad='''"[https://wiki.freecadweb.org/index.php?title=Svg_Namespace Svg Namespace]": External link to FreeCAD's name space extension |
|||
: "freecad:" zostaną poprzedzone nazwami atrybutów niestandardowych. |
|||
:: to look up special commands that are only used inside a FreeCAD environment e.g. for editable texts |
|||
: '''width='''"420mm": |
: '''width=''' "420mm": Szerokość obszaru rysowania. |
||
: '''height='''"297mm": |
: '''height=''' "297mm": Wysokość obszaru rysowania. |
||
: '''viewBox='''"0 0 420 297": |
: '''viewBox=''' "0 0 420 297": Położenie lewego górnego rogu ''(0;0)'' i prawego dolnego rogu ''(420;297)'' w przestrzeni konstrukcyjnej svg ''(w jednostkach svg)''. |
||
: |
: Szerokość, wysokość i viewBox w tej kombinacji ustawiają 1 jednostkę svg na 1 mm dla całego dokumentu. Jednostka wymiarowa może być od tej pory pomijana. |
||
: |
: W tym przypadku 420 i 297 dają stronę A3. Dostosuj te wartości, aby wygenerować inne rozmiary stron. |
||
Dla pustej strony rozmiar DIN A3 w orientacji poziomej to wszystko. |
|||
{{Code|lang=xml|code= |
{{Code|lang=xml|code= |
||
Line 67: | Line 74: | ||
<svg |
<svg |
||
xmlns="http://www.w3.org/2000/svg" version="1.1" |
xmlns="http://www.w3.org/2000/svg" version="1.1" |
||
xmlns:freecad="http://www. |
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace" |
||
width="420mm" |
width="420mm" |
||
height="297mm" |
height="297mm" |
||
Line 74: | Line 81: | ||
}} |
}} |
||
<span id="Python_code..."></span> |
|||
== Python code... == |
|||
==Kod Python ...== |
|||
Kodowanie rozpoczyna się od frameworka zawierającego oddzielną funkcję główną: |
|||
Before any code is generated a folder is needed to store the new template file. |
|||
{{Code| |code= |
|||
The user should have selected a template folder. Its path is then stored in the TechDraw preferences. </br> |
|||
#! python |
|||
It is not necessary to know where the preferences are store, because FreeCAD has commands to address needed parameters directly. |
|||
# -*- coding: utf-8 -*- |
|||
# (c) 2024 Your name LGPL |
|||
def main(): |
|||
"""Here is where the magic happens""" |
|||
return |
|||
if __name__ == '__main__': |
|||
# This will be true only if the file is "executed" |
|||
# but not if imported as module |
|||
main() |
|||
}} |
|||
<span id="..._to_create_a_blank_page"></span> |
|||
=== ... aby stworzyć pustą stronę === |
|||
Proces tworzenia szablonu składa się z następujących etapów |
|||
* Zlokalizowanie folderu szablonów. |
|||
* Otwarcie pliku do zapisu w celu utworzenia pliku svg od podstaw, napisanie linii nagłówka i zamknięcie pliku w pierwszym kroku. |
|||
* A następnie wielokrotne otwieranie pliku w celu dodania kolejnych segmentów, zamykając go ponownie za każdym razem. |
|||
Makrodefinicja składa się z kilku funkcji, które są wywoływane z sekcji głównej.<br> |
|||
Dodatkowe funkcje można wstawić przed funkcją EndSvg, a potrzebne wywołania przed wywołaniem EndSvg().<br> |
|||
Aby wcięcie było prawidłowe, musimy zwracać uwagę na liczbę spacji używanych podczas operacji zapisu. |
|||
<span id="pathToTemplate()"></span> |
|||
====pathToTemplate()==== |
|||
Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu i należy ustawić nazwę pliku. |
|||
Użytkownik powinien wybrać folder szablonu. Jego ścieżka jest następnie zapisywana w preferencjach środowiska pracy Rysunek Techniczny. </br> |
|||
Nie jest konieczne, aby wiedzieć, gdzie przechowywane są preferencje, ponieważ FreeCAD posiada polecenia pozwalające na bezpośrednie adresowanie potrzebnych parametrów. |
|||
{{Code| |code= |
{{Code| |code= |
||
def pathToTemplate(template_name): |
|||
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files") |
|||
"""Link a given template name to the path of the template folder""" |
|||
template_path = parameter_path.GetString("TemplateDir") |
|||
#- Get the path to the template folder that is set in the FreeCAD parameters |
|||
template_name = "MyTemplate.svg" |
|||
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files") |
|||
template_file = os.path.join(template_path, template_name) |
|||
template_path = parameter_path.GetString("TemplateDir") |
|||
#- Link template_path and template_name for any OS |
|||
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral |
|||
return path_to_file |
|||
}} |
}} |
||
'''parameter_path''' |
'''parameter_path''' przyjmuje ścieżkę do "folderu" w ramach pliku konfiguracyjnego, w którym znajduje się parametr "TemplateDir". </br> |
||
'''template_path''' |
'''template_path''' przyjmuje zawartość "TemplateDir", która jest ścieżką do katalogu z szablonem. <br> |
||
'''template_name''' |
'''template_name''' przyjmuje nazwę szablonu, który ma zostać utworzony. |
||
Teraz nazwa szablonu musi być połączona ze ścieżką szablonu w sposób, który jest kompatybilny z systemami operacyjnymi opartymi na Uniksie i Windows.</br> |
|||
Now the template name needs to be linked to the template path in a way that is compatible to unix based OSs and Windows.</br> |
|||
Robi się to za pomocą polecenia "os.path.join" i przechowuje w '''pliku szablonu'''. Aby włączyć to polecenie, wymagana jest instrukcja "import os". |
|||
This is done with the "os.path.join" command and stored into the '''template_file'''. |
|||
<span id="createSvgFile()"></span> |
|||
=== ... to create a blank page === |
|||
====createSvgFile()==== |
|||
Spowoduje to utworzenie nowego pliku szablonu i zapisanie nagłówka xml. |
|||
{{Code| |code= |
|||
def createSvgFile(file_path): |
|||
# Create a file and insert a header line (with t as the space saving variant of template) |
|||
t = open(file_path, "w") # w = write, overwrites existing files |
|||
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>") |
|||
t.close |
|||
}} |
|||
<span id="startSvg()"></span> |
|||
====startSvg()==== |
|||
Powoduje to dołączenie pliku szablonu i utworzenie znacznika otwierającego svg wraz z jego atrybutami.<br> |
|||
Każda instrukcja zapisu zawiera jedną linię kodu svg zakończoną "\n", znacznikiem CR/LF. |
|||
{{Code| |code= |
|||
def startSvg(file_path, sheet_width, sheet_height): |
|||
# Create svg-tag including namespace and format definitions |
|||
t = open(file_path, "a", encoding="utf-8") |
|||
# a = append, new lines are added at the end of an existing file |
|||
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode |
|||
t.write("\n" + "\n") |
|||
t.write("<svg\n") |
|||
#- Namespace declarations |
|||
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n") |
|||
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n") |
|||
#- Format definition |
|||
t.write(" width =\"" + sheet_width + "mm\"\n") |
|||
t.write(" height=\"" + sheet_height + "mm\"\n") |
|||
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n") |
|||
# identical values for width and height and Viewbox' width and height |
|||
# will synchronise mm and svg-units |
|||
t.close |
|||
}} |
|||
<span id="endSvg()"></span> |
|||
====endSvg()==== |
|||
To dalej dołącza plik szablonu i tworzy znacznik zamykający svg; To ostatecznie kończy kod szablonu. |
|||
{{Code| |code= |
|||
def endSvg(file_path): |
|||
# Create closing svg-tag |
|||
t = open(file_path, "a", encoding="utf-8") |
|||
t.write("</svg>") |
|||
t.close |
|||
}} |
|||
<span id="main()"></span> |
|||
====main()==== |
|||
Funkcja main() wywołuje funkcje i przekazuje pewne parametry. |
|||
{{Code| |code= |
|||
def main(): |
|||
"""This one creates an empty A3 template""" |
|||
#- Set the name of the template file and get its location |
|||
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here |
|||
#- Here starts the compiling of the svg file |
|||
createSvgFile(template_file) # overwrites existing File |
|||
#- Set sheet format (DIN A3) |
|||
format_width = "420" |
|||
format_height = "297" |
|||
startSvg(template_file, format_width, format_height) # adds svg start tag |
|||
endSvg(template_file) # adds svg end tag |
|||
# At this point a new SVG-file is generated and saved |
|||
return |
|||
}} |
|||
W tym przykładzie {{Incode|format_width}} i {{Incode|format_height}} są twardo zakodowanymi wymiarami, obie niepotrzebne linie oznaczają punkty, w których inne sposoby pobierania danych formatu mogłyby umieścić swoją zawartość. |
|||
<div class="mw-collapsible mw-collapsed toccolours"> |
<div class="mw-collapsible mw-collapsed toccolours"> |
||
====Kompletna makrodefinicja pustej strony==== |
|||
This macro shows the principle how an svg-file can be put together. (Format is A3) |
|||
To makro składa się z powyższych segmentów kodu, gotowych do uruchomienia. |
|||
<div class="mw-collapsible-content"> |
<div class="mw-collapsible-content"> |
||
Line 104: | Line 224: | ||
#! python |
#! python |
||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
# (c) |
# (c) 2024 Your name LGPL |
||
# |
|||
# |
|||
#- Get the path to the template folder that is set in the FreeCAD parameters |
|||
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files") |
|||
template_path = parameter_path.GetString("TemplateDir") |
|||
template_name = "MyTemplate.svg" |
|||
#- Link template_path and template_name for any OS |
|||
template_file = os.path.join(template_path, template_name) |
|||
import os # to enable the use of os.path.join() |
|||
# - SVG creation - |
|||
#- Create a file and insert a header line |
|||
# - SVG creation - |
|||
# (with t as the space saving variant of template) |
|||
def |
def createSvgFile(file_path): |
||
# Create a file and insert a header line (with t as the space saving variant of template) |
|||
t=open(filePath,"w") # w = write, overwrites existing files |
|||
t = open(file_path, "w") # w = write, overwrites existing files |
|||
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>") |
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>") |
||
t.close |
t.close |
||
def startSvg(file_path, sheet_width, sheet_height): |
|||
#- Create opening svg-tag |
|||
# Create svg-tag including namespace and format definitions |
|||
# Namespace section |
|||
t = open(file_path, "a", encoding="utf-8") |
|||
def StartSvg(filePath): |
|||
# a = append, new lines are added at the end of an existing file |
|||
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode |
|||
t.write("\n"+"\n") |
|||
t.write("\n" + "\n") |
|||
t.write("<svg\n") |
t.write("<svg\n") |
||
#- Namespace declarations |
|||
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n") |
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n") |
||
t.write(" xmlns:freecad=\"http://www. |
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n") |
||
#- Format definition |
|||
t.close |
|||
t.write(" width =\"" + sheet_width + "mm\"\n") |
|||
# Sheet size section |
|||
t.write(" height=\"" + sheet_height + "mm\"\n") |
|||
def CreateSheet(filePath,shWidth,shHeight): |
|||
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n") |
|||
t=open(filePath,"a") |
|||
# identical values for width and height and Viewbox' width and height |
|||
t.write(" width =\""+shWidth+"mm\"\n") |
|||
# will synchronise mm and svg-units |
|||
t.write(" height=\""+shHeight+"mm\"\n") |
|||
t.write(" viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n") |
|||
t.close |
t.close |
||
# identical values for width and height and Viewbox' width and height will synchronise mm and svg-units |
|||
def endSvg(file_path): |
|||
#- Create closing svg-tag |
|||
# Create closing svg-tag |
|||
def EndSvg(filePath): |
|||
t=open( |
t = open(file_path, "a", encoding="utf-8") |
||
t.write("</svg>") |
t.write("</svg>") |
||
t.close |
t.close |
||
def pathToTemplate(template_name): |
|||
# --- Main section --- |
|||
"""Link a given template name to the path of the template folder""" |
|||
#- Get the path to the template folder that is set in the FreeCAD parameters |
|||
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files") |
|||
template_path = parameter_path.GetString("TemplateDir") |
|||
#- Link template_path and template_name for any OS |
|||
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral |
|||
return path_to_file |
|||
def main(): |
|||
CreateSvgFile(template_file) # overwrites existing File |
|||
"""This one creates an empty A3 template""" |
|||
#- Set the name of the template file and get its location |
|||
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here |
|||
#- Here starts the compiling of the svg file |
|||
createSvgFile(template_file) # overwrites existing File |
|||
#- Set sheet format (DIN A3) |
|||
format_width = "420" |
|||
format_height = "297" |
|||
startSvg(template_file, format_width, format_height) # adds svg start tag |
|||
endSvg(template_file) # adds svg end tag |
|||
# At this point a new SVG-file is generated and saved |
|||
return |
|||
if __name__ == '__main__': |
|||
# Set sheet format (DIN A3) |
|||
# This will be true only if the file is "executed" |
|||
formatWidth = "420" |
|||
# but not if imported as module |
|||
formatHeight = "297" |
|||
main() |
|||
StartSvg(template_file) # adds Start tag and namespaces |
|||
CreateSheet(template_file, formatWidth, formatHeight) |
|||
EndSvg(template_file) |
|||
# At this point a new SVG-file is generated and saved |
|||
}} |
}} |
||
</div> <!-- |
</div> <!-- Koniec elementu zwijanego dla sekcji treści. Nie usuwaj! --> |
||
</div> <!-- |
</div> <!-- Koniec elementu zwijanego dla ... sekcji. Nie usuwaj! --> |
||
: The main principle is: |
|||
:* to open a file for writing and so start an svg-file from scratch, write the header line and close the file as a first step. |
|||
:* and then to repeatedly open the file to append further segments and then close it again after appending. |
|||
: |
|||
:The macro is made of several functions which are called from the main section. |
|||
:Additional functions could be inserted before the EndSvg function and the needed calls are put before the EndSvg() call. |
|||
:We need to have an eye on the number of spaces used with the write operations for a correct indentation. |
|||
<span id="..._to_create_a_page_with_some_ink"></span> |
|||
=== ... to create a page with some ink === |
|||
=== ... aby stworzyć stronę w której jest trochę tuszu === |
|||
Aby stworzyć rysunek z pustej kartki potrzebujemy: |
|||
To make a drawing out of a blank page we need: |
|||
* ramki czyli prostokąty utworzone za pomocą instrukcji '''rect''', |
|||
bloku tytułowego i więcej linii utworzonych za pomocą instrukcji '''path''', |
|||
:* a title block and more made of lines created with the '''path''' instruction |
|||
* proste teksty do indeksów i etykietowania komórek bloku tytułowego, |
|||
:* simple texts for indexes and labeling title block cells |
|||
* teksty edytowalne, takie jak numer lub nazwa części, |
|||
:* editable texts like part number or part name |
|||
Zwykle te elementy graficzne są używane wielokrotnie, więc kod generujący jest umieszczony w czterech funkcjach: |
|||
Normally these graphical elements are used repeatedly and so the generating code is put into functions. |
|||
* svgRect() dla prostokątnych elementów ramki. |
|||
* svgPath() dla elementów prostych. |
|||
* svgText() dla tekstów statycznych. |
|||
* ediText() dla tekstów edytowalnych. |
|||
Wszystkie funkcje są umieszczane wewnątrz powyższej makrodefinicji przed funkcją main(), a powiązane z nimi wywołania funkcji są wstawiane do funkcji main() między {{Incode|startSvg(...)}} i {{Incode|endSvg(...)}}. |
|||
=== svgrect function === |
|||
<span id="svgRect()"></span> |
|||
To draw a rectangle we just need to call the '''svgrect''' function and hand over the values for width, height, and position of the upper left corner. That is better readable than the content of the svgline which had to be used instead. |
|||
====funkcja svgRect()==== |
|||
Aby narysować prostokąt musimy tylko wywołać funkcję '''svgRect''' i przekazać wartości szerokości, wysokości i położenia lewego górnego rogu. Jest to lepiej czytelne w tym miejscu, niż gdyby svg_line zawierał całą linię kodu svg. |
|||
{{Code| |code= |
{{Code| |code= |
||
def svgRect(width, height, x, y): |
|||
#- Function to generate an svg-instruction to draw a rectangle with the given values |
|||
# Generates an svg-instruction to draw a rectangle with the given values |
|||
def svgrect(width,height,x,y): |
|||
svg_line = ( |
|||
svgLine=("<rect width=\""+width+"\" height=\""+height+"\" x=\""+x+"\" y=\""+y+"\" />") |
|||
"<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x |
|||
return svgLine |
|||
+ "\" y=\"" + y + "\" />" |
|||
) |
|||
return svg_line |
|||
}} |
}} |
||
Instrukcja svg jest podzielona, aby pozostać w zalecanej długości linii Pythona, mimo to spowoduje to utworzenie pojedynczej linii svg. |
|||
=== svgpath function === |
|||
<span id="svgPath()"></span> |
|||
====funkcja svgPath()==== |
|||
Aby narysować linię wystarczy wywołać funkcję '''svgPath''' i przekazać współrzędne punktu początkowego i końcowego linii. |
|||
To draw a line we just need to call the '''svgpath''' function and hand over the coordinates of the start point and the endpoint of a line. |
|||
Zamiast współrzędnych punktu końcowego możemy przekazać znacznik do rysowania linii poziomej ''(h)'' lub pionowej ''(v)'', a następnie długość linii lub znacznik do rysowania linii poziomej ''(H)'' lub pionowej ''(V)'', a następnie rzędną x lub y punktu końcowego. |
|||
Instead of the end point coordinates we can hand over a tag to draw a horizontal (h) or vertical (v) line followed by the length of the line or the tag to draw a horizontal (H) or vertical (V) line followed by the x or y ordinate of the end point. |
|||
Każda ścieżka zaczyna się w punkcie początkowym i pierwszą czynnością jest ruch z "podniesionym piórem" ''(bez rysowania)'' do punktu początkowego. W tym przypadku ruch względny i ruch bezwzględny są takie same i dlatego nie ma znaczenia, czy znacznik m jest pisany wielką czy małą literą. |
|||
Each path starts at the origin and the first action is a movement with "raised pen" (not drawing) to the start point. In this case the relative movement and the absolute movement are the same and so it is irrelevant whether the m-tag is upper or lower case. |
|||
{{Code| |code= |
{{Code| |code= |
||
def svgPath(x1, y1, x2, y2): |
|||
#- Function to generate an svg-instruction to draw a path element (line) with the given values |
|||
# Generates an svg-instruction to draw a path element (line) with the given values |
|||
def svgpath(x1,y1,x2,y2): |
|||
if x2=="v" or x2=="V" or x2=="h" or x2=="H": |
if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H": |
||
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />") |
|||
else: |
else: |
||
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />") |
|||
return |
return svg_line |
||
}} |
}} |
||
<span id="svgText()"></span> |
|||
=== svgtext function === |
|||
====funkcja svgText()==== |
|||
Aby narysować fragment tekstu wystarczy wywołać funkcję '''svgText''' i przekazać współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu. |
|||
To draw a piece of text we just need to call the '''svgtext''' function and hand over the coordinates of the text's anchor point and the text string itself. |
|||
''(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej)''. |
|||
(The text alignment is controlled by the surrounding group tag or left-aligned as default). |
|||
{{Code| |code= |
{{Code| |code= |
||
def svgText(x, y, str_value, str_angle="0"): |
|||
#- Function to generate an svg-instruction to place a text element with the given values |
|||
""" |
|||
def svgtext(posX,posY,strValue): |
|||
Generates an svg-instruction to place a text element with the given values. |
|||
svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>") |
|||
Optional str_angle enables vertical and arbitrarily rotated texts |
|||
return svgLine |
|||
""" |
|||
if str_angle == "0": |
|||
svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>") |
|||
else: |
|||
svg_line = ( |
|||
"<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle |
|||
+ "," + x + "," + y + ")\">" + str_value + "</text>" |
|||
) |
|||
return svg_line |
|||
}} |
}} |
||
<span id="ediText()"></span> |
|||
=== FCeditext function === |
|||
====funkcja ediText()==== |
|||
Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję '''ediText''' i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu. |
|||
To draw a piece of editable text we just need to call the '''FCeditext''' function and hand over a name, the coordinates of the text's anchor point, and the text string itself. |
|||
FreeCAD tworzy obiekt słownika z każdym wstawionym szablonem, a każdy wpis ma nazwę ''(klucz)'' i wartość. |
|||
FreeCAD creates a dictionary object with every inserted template, and each entry has a name (key) and a value. |
|||
''(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej)''. |
|||
(The text alignment is controlled by the surrounding group tag or left-aligned as default). |
|||
{{Code| |code= |
{{Code| |code= |
||
def ediText(entry_name, x, y, str_value, str_angle="0"): |
|||
#- Function to generate an svg-instruction to place an editable text element with the given values |
|||
""" |
|||
def FCeditext(entryName,posX,posY,strValue): |
|||
Generates an svg-instruction to place an editable text element with the given values. |
|||
svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \ |
|||
Optional str_angle enables vertical and arbitrarily rotated editable texts |
|||
+"\"> <tspan>"+strValue+"</tspan> </text>") |
|||
""" |
|||
if str_angle == "0": |
|||
svg_line = ( |
|||
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y |
|||
+ "\"> <tspan>" + str_value + "</tspan> </text>" |
|||
) |
|||
else: |
|||
svg_line = ( |
|||
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y |
|||
+ "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\"> <tspan>" |
|||
+ str_value + "</tspan> </text>" |
|||
) |
|||
return svg_line |
|||
}} |
}} |
||
<span id="createXxxx_functions"></span> |
|||
=== CreateXxxx functions === |
|||
===Funkcja createXxxx=== |
|||
Funkcje te rozpoczynają się od kodu otwierającego plik w trybie dołączania i zapisującego znacznik otwierający grupę. |
|||
These functions start with code to open a file in append mode and to write the group opening tag. |
|||
Następnie widzimy sekcję do ustawiania i obliczania wartości oraz z instrukcjami zapisu, które wywołują powyższe funkcje w celu wygenerowania kodu svg. |
|||
Then follows a section to set and calculate values and with write instructions that call the above functions to generate svg-code. |
|||
I wreszcie znacznik zamykający grupę, po którym następuje instrukcja zamknięcia pliku. |
|||
And finally the group closing tag followed by an instruction to close the file. |
|||
{{Code| |code= |
{{Code| |code= |
||
def createFrame(file_path, sheet_width, sheet_height): |
|||
#- Frame creation |
|||
# Creates rectangles for sheet frame and drawing area |
|||
def CreateFrame(filePath,shWidth,shHeight): |
|||
t=open( |
t = open(file_path, "a", encoding="utf-8") |
||
t.write(" <g id=\"drawing-frame\"\n") |
t.write(" <g id=\"drawing-frame\"\n") |
||
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;\ |
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n") |
||
#- upper left corner of inner Frame, drawing area |
|||
stroke-linecap:round\">\n") |
|||
frame_x = str(20) |
|||
# inner Frame, drawing area |
|||
frame_y = str(10) |
|||
drawingX=str(20) |
|||
drawingY=str(10) |
|||
#- frame dimensions |
#- frame dimensions |
||
frame_width = str(int(sheet_width) - 20 - 10) |
|||
frame_height = str(int(sheet_height) - 10 - 10) |
|||
#- frame rectangle |
#- frame rectangle |
||
t.write(" "+ |
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n") |
||
# outer frame |
#- upper left corner outer frame, sheet frame |
||
frame_x = str(15) |
|||
frame_y = str(5) |
|||
drawingY=str(5) |
|||
#- frame dimensions |
#- frame dimensions |
||
frame_width = str(int(sheet_width)-20) |
|||
frame_height = str(int(sheet_height)-10) |
|||
#- frame rectangle |
#- frame rectangle |
||
t.write(" "+ |
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n") |
||
t.write(" </g>\n\n") |
t.write(" </g>\n\n") |
||
t.close |
t.close |
||
}} |
}} |
||
<span id="Resulting_macro"></span> |
|||
=== Resulting macro === |
|||
===Rezultat=== |
|||
<div class="mw-collapsible mw-collapsed toccolours"> |
<div class="mw-collapsible mw-collapsed toccolours"> |
||
Makro to dodaje kilka podstawowych elementów graficznych potrzebnych do prawidłowego działania szablonów tj. elementy linii, etykiety oraz teksty do edycji. |
|||
This macro adds some basic graphical elements needed for proper templates i.e. line elements, texts, and editable texts. |
|||
<div class="mw-collapsible-content"> |
<div class="mw-collapsible-content"> |
||
Line 286: | Line 443: | ||
#! python |
#! python |
||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
# (c) |
# (c) 2024 Your name LGPL |
||
# |
|||
# |
|||
#- Get the path to the template folder that is set in the FreeCAD parameters |
|||
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files") |
|||
template_path = parameter_path.GetString("TemplateDir") |
|||
template_name = "MyTemplate.svg" |
|||
#- Link template_path and template_name for any OS |
|||
template_file = os.path.join(template_path, template_name) |
|||
import os # to enable the use of os.path.join() |
|||
# - SVG creation - |
|||
#- Create a file and insert a header line |
|||
# - SVG creation - |
|||
# (with t as the space saving variant of template) |
|||
def |
def createSvgFile(file_path): |
||
# Create a file and insert a header line (with t as the space saving variant of template) |
|||
t=open(filePath,"w") # w = write, overwrites existing files |
|||
t = open(file_path, "w") # w = write, overwrites existing files |
|||
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>") |
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>") |
||
t.close |
t.close |
||
def startSvg(file_path, sheet_width, sheet_height): |
|||
#- Create opening svg-tag |
|||
# Create svg-tag including namespace and format definitions |
|||
# Namespace section |
|||
t = open(file_path, "a", encoding="utf-8") |
|||
def StartSvg(filePath): |
|||
# a = append, new lines are added at the end of an existing file |
|||
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode |
|||
t.write("\n"+"\n") |
|||
t.write("\n" + "\n") |
|||
t.write("<svg\n") |
t.write("<svg\n") |
||
#- Namespace declarations |
|||
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n") |
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n") |
||
t.write(" xmlns:freecad=\"http://www. |
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n") |
||
#- Format definition |
|||
t.close |
|||
t.write(" width =\"" + sheet_width + "mm\"\n") |
|||
# Sheet size section |
|||
t.write(" height=\"" + sheet_height + "mm\"\n") |
|||
def CreateSheet(filePath,shWidth,shHeight): |
|||
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n") |
|||
t=open(filePath,"a") |
|||
# identical values for width and height and Viewbox' width and height |
|||
t.write(" width =\""+shWidth+"mm\"\n") |
|||
# will synchronise mm and svg-units |
|||
t.write(" height=\""+shHeight+"mm\"\n") |
|||
t.write(" viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n") |
|||
t.close |
t.close |
||
# identical values for width and height and Viewbox' width and height will synchronise mm and svg-units |
|||
def endSvg(file_path): |
|||
#- Create closing svg-tag |
|||
# Create closing svg-tag |
|||
def EndSvg(filePath): |
|||
t=open( |
t = open(file_path, "a", encoding="utf-8") |
||
t.write("</svg>") |
t.write("</svg>") |
||
t.close |
t.close |
||
def svgRect(width, height, x, y): |
|||
#- Function to generate an svg-instruction to draw a rectangle with the given values |
|||
# Ggenerates an svg-instruction to draw a rectangle with the given values |
|||
def svgrect(width,height,x,y): |
|||
svg_line = ( |
|||
svgLine=("<rect width=\""+width+"\" height=\""+height+"\" x=\""+x+"\" y=\""+y+"\" />") |
|||
"<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x |
|||
return svgLine |
|||
+ "\" y=\"" + y + "\" />" |
|||
) |
|||
return svg_line |
|||
def svgPath(x1, y1, x2, y2): |
|||
#- Function to generate an svg-instruction to draw a path element (line) with the given values |
|||
# Generates an svg-instruction to draw a path element (line) with the given values |
|||
def svgpath(x1,y1,x2,y2): |
|||
if x2=="v" or x2=="V" or x2=="h" or x2=="H": |
if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H": |
||
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />") |
|||
else: |
else: |
||
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />") |
|||
return |
return svg_line |
||
def svgText(x, y, str_value, str_angle="0"): |
|||
#- Function to generate an svg-instruction to place a text element with the given values |
|||
""" |
|||
def svgtext(posX,posY,strValue): |
|||
Generates an svg-instruction to place a text element with the given values. |
|||
svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>") |
|||
Optional str_angle enables vertical and arbitrarily rotated texts |
|||
return svgLine |
|||
""" |
|||
if str_angle == "0": |
|||
svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>") |
|||
else: |
|||
svg_line = ( |
|||
"<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle |
|||
+ "," + x + "," + y + ")\">" + str_value + "</text>" |
|||
) |
|||
return svg_line |
|||
def ediText(entry_name, x, y, str_value, str_angle="0"): |
|||
#- Function to generate an svg-instruction to place an editable text element with the given values |
|||
""" |
|||
def FCeditext(entryName,posX,posY,strValue): |
|||
Generates an svg-instruction to place an editable text element with the given values. |
|||
svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \ |
|||
Optional str_angle enables vertical and arbitrarily rotated editable texts |
|||
+"\"> <tspan>"+strValue+"</tspan> </text>") |
|||
""" |
|||
if str_angle == "0": |
|||
svg_line = ( |
|||
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y |
|||
+ "\"> <tspan>" + str_value + "</tspan> </text>" |
|||
) |
|||
else: |
|||
svg_line = ( |
|||
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y |
|||
+ "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\"> <tspan>" |
|||
+ str_value + "</tspan> </text>" |
|||
) |
|||
return svg_line |
|||
def createFrame(file_path, sheet_width, sheet_height): |
|||
#- Frame creation |
|||
# Creates rectangles for sheet frame and drawing area |
|||
def CreateFrame(filePath,shWidth,shHeight): |
|||
t=open( |
t = open(file_path, "a", encoding="utf-8") |
||
t.write(" <g id=\"drawing-frame\"\n") |
t.write(" <g id=\"drawing-frame\"\n") |
||
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;\ |
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n") |
||
#- calculate upper left corner of inner Frame, drawing area |
|||
stroke-linecap:round\">\n") |
|||
frame_x = str(20) |
|||
# inner Frame, drawing area |
|||
frame_y = str(10) |
|||
drawingX=str(20) |
|||
drawingY=str(10) |
|||
#- frame dimensions |
#- frame dimensions |
||
frame_width = str(int(sheet_width) - 20 - 10) |
|||
frame_height = str(int(sheet_height) - 10 - 10) |
|||
#- frame rectangle |
#- frame rectangle |
||
t.write(" "+ |
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n") |
||
# outer frame |
#- calculate upper left corner outer frame, sheet frame |
||
frame_x = str(15) |
|||
frame_y = str(5) |
|||
drawingY=str(5) |
|||
#- frame dimensions |
#- frame dimensions |
||
frame_width = str(int(sheet_width)-20) |
|||
frame_height = str(int(sheet_height)-10) |
|||
#- frame rectangle |
#- frame rectangle |
||
t.write(" "+ |
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n") |
||
t.write(" </g>\n\n") |
t.write(" </g>\n\n") |
||
t.close |
t.close |
||
def createTitleBlock(file_path, sheet_width, sheet_height): |
|||
#- Title block movable |
|||
"""Creates a title block and transfers it to the position according to the sheet dimensions""" |
|||
def CreateTitleBlock(filePath,shWidth,shHeight): |
|||
#- calculate title block origin (lower left corner), offset from page origin |
|||
tbX = str(int(sheet_width) - 10 - 180) # 180 according to DIN EN ISO 7200 |
|||
#- lower left corner |
|||
tbY = str(int(sheet_height) - 10) |
|||
t = open(file_path, "a", encoding="utf-8") |
|||
tbY=str(int(shHeight)-10) |
|||
#- group to |
#- group to transfer all included title block elements at once |
||
t=open(filePath,"a") |
|||
t.write(" <g id=\"titleblock\"\n") |
t.write(" <g id=\"titleblock\"\n") |
||
t.write(" transform=\"translate("+tbX+","+tbY+")\">\n") |
t.write(" transform=\"translate(" + tbX + "," + tbY + ")\">\n") |
||
#- title block |
#- sub-group of title block line framework |
||
t.write(" <g id=\"titleblock-frame\"\n") |
t.write(" <g id=\"titleblock-frame\"\n") |
||
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.35;\ |
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.35;\ |
||
stroke-linecap:miter;stroke-miterlimit:4\">\n") |
stroke-linecap:miter;stroke-miterlimit:4\">\n") |
||
t.write(" "+ |
t.write(" " + svgPath(" 0"," 0"," 0","-63") + "\n") |
||
t.write(" "+ |
t.write(" " + svgPath(" 0","-63","180"," 0") + "\n") |
||
t.write(" "+ |
t.write(" " + svgPath(" 0","-30","h","155") + "\n") |
||
t.write(" "+ |
t.write(" " + svgPath("155"," 0","v","-63") + "\n") |
||
t.write(" </g>\n") |
t.write(" </g>\n") |
||
#- |
#- sub-group of title block static texts (left-aligned by default) |
||
t.write(" <g id=\"titleblock-text-non-editable\"\n") |
t.write(" <g id=\"titleblock-text-non-editable\"\n") |
||
t.write(" style=\"font-size:5.0;text-anchor:start;fill:#000000;\ |
t.write(" style=\"font-size:5.0;text-anchor:start;fill:#000000;\ |
||
font-family:osifont\">\n") |
font-family:osifont\">\n") |
||
t.write(" "+ |
t.write(" " + svgText(" 4.5","-43.5 ","Some static text") + "\n") |
||
t.write(" "+ |
t.write(" " + svgText(" 4.5","-13.5 ","More static text") + "\n") |
||
t.write(" " + svgText("162.5","-3.5 ","Vertical static text","-90") + "\n") |
|||
t.write(" </g>\n") |
t.write(" </g>\n") |
||
t.write(" </g>\n\n") |
t.write(" </g>\n\n") |
||
t.close |
t.close |
||
def createEditableText(file_path, sheet_width, sheet_height): |
|||
#- Title block editable texts |
|||
"""Creates editable texts positioned according to the page origin""" |
|||
def CreateEditableText(filePath,shWidth,shHeight): |
|||
#- calculate offset to titleblock origin |
|||
edX = int(sheet_width) - 10 - 180 # 180 according to DIN EN ISO 7200 |
|||
#- offsets for editable texts |
|||
edY = int(sheet_height) - 10 |
|||
edX=int(shWidth)-10-180 # 180 according to DIN EN ISO 7200 |
|||
t = open(file_path, "a", encoding="utf-8") |
|||
edY=int(shHeight)-10 |
|||
#- group editable texts using the same attributes |
|||
t=open(filePath,"a") |
|||
t.write(" <g id=\"titleblock-editable-texts\"\n") |
t.write(" <g id=\"titleblock-editable-texts\"\n") |
||
t.write(" style=\"font-size:7.0;text-anchor:start;fill:#0000d0;\ |
t.write(" style=\"font-size:7.0;text-anchor:start;fill:#0000d0;\ |
||
font-family:osifont\">\n") |
font-family:osifont\">\n") |
||
t.write( |
|||
t.write(" "+FCeditext("EdiText-1",str(edX+60),str(edY-43.5),"Some editable text")+"\n") |
|||
" " + ediText("EdiText-1",str(edX + 60),str(edY - 43.5),"Some editable text") + "\n" |
|||
) |
|||
t.write( |
|||
" " + ediText("EdiText-2",str(edX + 60),str(edY - 13.5),"More editable text") + "\n" |
|||
) |
|||
t.write( |
|||
" " + ediText("EdiText-3",str(edX + 173),str(edY - 4.5),"90° editable text","-90") |
|||
+ "\n" |
|||
) |
|||
t.write(" </g>\n\n") |
t.write(" </g>\n\n") |
||
t.close |
t.close |
||
def pathToTemplate(template_name): |
|||
# --- Main section --- |
|||
"""Link a given template name to the path of the template folder""" |
|||
#- Get the path to the template folder that is set in the FreeCAD parameters |
|||
CreateSvgFile(template_file) # overwrites existing File |
|||
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files") |
|||
template_path = parameter_path.GetString("TemplateDir") |
|||
#- Link template_path and template_name for any OS |
|||
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral |
|||
return path_to_file |
|||
def main(): |
|||
# Set sheet format (A3) |
|||
"""This one creates an A3 template with simple frame and title block""" |
|||
formatWidth = "420" |
|||
#- Set the name of the template file and get its location |
|||
formatHeight = "297" |
|||
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here |
|||
#- Here starts the compiling of the svg file |
|||
createSvgFile(template_file) # overwrites existing File |
|||
#- Set sheet format (DIN A3) |
|||
format_width = "420" |
|||
format_height = "297" |
|||
startSvg(template_file, format_width, format_height) # adds svg start tag |
|||
createFrame(template_file, format_width, format_height) |
|||
createTitleBlock(template_file, format_width, format_height) |
|||
createEditableText(template_file, format_width, format_height) |
|||
endSvg(template_file) # adds svg end tag |
|||
# At this point a new SVG-file is generated and saved |
|||
return |
|||
if __name__ == '__main__': |
|||
StartSvg(template_file) # adds Start tag and namespaces |
|||
# This will be true only if the file is "executed" |
|||
CreateSheet(template_file, formatWidth, formatHeight) |
|||
# but not if imported as module |
|||
CreateFrame(template_file, formatWidth, formatHeight) |
|||
main() |
|||
CreateTitleBlock(template_file, formatWidth, formatHeight) |
|||
CreateEditableText(template_file, formatWidth, formatHeight) |
|||
EndSvg(template_file) |
|||
# At this point a new SVG-file is generated and saved |
|||
}} |
}} |
||
A to jest kod svg pochodzący z tej makrodefinicji: |
|||
And this is the svg-code coming out of this macro: |
|||
{{Code|lang=xml|code= |
{{Code|lang=xml|code= |
||
Line 450: | Line 642: | ||
<svg |
<svg |
||
xmlns="http://www.w3.org/2000/svg" version="1.1" |
xmlns="http://www.w3.org/2000/svg" version="1.1" |
||
xmlns:freecad="http://www. |
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace" |
||
width ="420mm" |
width ="420mm" |
||
height="297mm" |
height="297mm" |
||
Line 473: | Line 665: | ||
<text x=" 4.5" y="-43.5 ">Some static text</text> |
<text x=" 4.5" y="-43.5 ">Some static text</text> |
||
<text x=" 4.5" y="-13.5 ">More static text</text> |
<text x=" 4.5" y="-13.5 ">More static text</text> |
||
<text x="162.5" y="-3.5 " transform="rotate(-90,162.5,-3.5 )">Vertical static text</text> |
|||
</g> |
</g> |
||
</g> |
</g> |
||
Line 480: | Line 673: | ||
<text freecad:editable="EdiText-1" x="290" y="243.5"> <tspan>Some editable text</tspan> </text> |
<text freecad:editable="EdiText-1" x="290" y="243.5"> <tspan>Some editable text</tspan> </text> |
||
<text freecad:editable="EdiText-2" x="290" y="273.5"> <tspan>More editable text</tspan> </text> |
<text freecad:editable="EdiText-2" x="290" y="273.5"> <tspan>More editable text</tspan> </text> |
||
<text freecad:editable="EdiText-3" x="403" y="282.5" transform="rotate(-90,403,282.5)"> <tspan>90° editable text</tspan> </text> |
|||
</g> |
</g> |
||
Line 485: | Line 679: | ||
}} |
}} |
||
</div> <!-- |
</div> <!-- Koniec elementu zwijanego dla sekcji treści. Nie usuwaj! --> |
||
</div> <!-- |
</div> <!-- Koniec elementu zwijanego dla ... sekcji. Nie usuwaj! --> |
||
I jak to powinno wyglądać po wstawieniu ''(plus powiększony blok tytułowy)'': |
|||
[[Image:TechDraw TemplateGenerator.png|600px]] |
|||
And what it should look like when inserted (plus magnified title block): |
|||
Kolorowanie edytowalnych tekstów na niebiesko jest tylko osobistym wyborem, aby łatwiej odróżnić statyczne i edytowalne teksty na gotowym rysunku. |
|||
[[Image:TechDraw TemplateGenerator.png|TechDraw TemplateGenerator.png]] |
Latest revision as of 18:57, 2 February 2024
Ćwiczenie |
Temat |
---|
Tworzenie szablonu Rysunku Technicznego - makrodefinicja Python |
Poziom trudności |
Podstawowa znajomość środowiska Python i struktur svg jest przydatna |
Czas wykonania |
(Jeszcze nie wiem) |
Autorzy |
FBXL5 |
Wersja FreeCAD |
0.21 i nowszy |
Pliki z przykładami |
Nie dołączono |
Zobacz również |
Makro TemplateHelper |
Wprowadzenie
Ten poradnik opisuje jak z kilku linijek kodu Pythona wygenerować prosty szablon do wykorzystania w środowisku pracy Rysunek Techniczny.
Do kodowania można użyć dowolnego edytora tekstu. Mój wybór to Atom, ale wbudowany edytor FreeCAD też działa dobrze.
Poniższe przykłady kodu można skopiować i wkleić do pustego pliku tekstowego, a następnie zapisać pod wybraną nazwą jako plik typu *.py lub *.FCMacro.
Szablon stanowi tło dla zadań rysunkowych, a jego wymiary są wykorzystywane przez sterowniki drukarki do prawidłowego skalowania rysunku.
Szablony są plikami svg, więc makrodefinicja musi skomponować kilka linii kodu svg (który jest podzbiorem kodu xml).
Uwaga: Kiedy FreeCAD został przeniesiony z freecadweb.org do freecad.org, ta strona została odpowiednio zaktualizowana, a wynikowy kod SVG nie jest już kompatybilny z wersjami FreeCAD starszymi niż v0.21. W przypadku tych wersji należy ręcznie zmienić freecad.org
na freecadweb.org
w wierszu deklaracji przestrzeni nazw w wynikowym kodzie SVG, w przeciwnym razie edytowalne teksty nie zostaną rozpoznane.
Struktura zwykłej pustej strony
Format SVG jest podzbiorem formatu XML. Dlatego plik SVG, jak każdy plik XML, składa się z dwóch części:
- Nagłówka, czyli deklaracji formatu.
- Zawartości, zawierającej informacje co pokazać i gdzie to umieścić
- (nie wiem po co ma być nagłówek, plik svg jest nadal poprawnym plikiem szablonu bez niego ...)
Nagłówek
Nagłówek to tylko jedna linia deklarująca, której wersji języka XML powinien użyć interpreter do obsługi instrukcji w treści.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
Zawartość
Struktura zaczyna się od znacznika otwierającego, który zawiera informacje o przestrzeniach nazw oraz o wielkości szablonu i miejscu jego umieszczenia. A kończy się znacznikiem zamykającym.
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
width="420mm"
height="297mm"
viewBox="0 0 420 297">
</svg>
- xmlns=http://www.w3.org/2000/svg: Zewnętrzny link do przestrzeni nazw xml, aby wyszukać standardowe polecenia xml.
- version="1.1": Używana wersja xml to 1.1
- xmlns:freecad=...=Svg_Namespace": Zewnętrzny link do strony Wiki FreeCAD Svg Namespace. Link nie jest używany do pobierania informacji lub wartości w czasie wykonywania, ale jest kluczem do aktywacji niestandardowych atrybutów, np. tych dla edytowalnych tekstów.
- "freecad:" zostaną poprzedzone nazwami atrybutów niestandardowych.
- width= "420mm": Szerokość obszaru rysowania.
- height= "297mm": Wysokość obszaru rysowania.
- viewBox= "0 0 420 297": Położenie lewego górnego rogu (0;0) i prawego dolnego rogu (420;297) w przestrzeni konstrukcyjnej svg (w jednostkach svg).
- Szerokość, wysokość i viewBox w tej kombinacji ustawiają 1 jednostkę svg na 1 mm dla całego dokumentu. Jednostka wymiarowa może być od tej pory pomijana.
- W tym przypadku 420 i 297 dają stronę A3. Dostosuj te wartości, aby wygenerować inne rozmiary stron.
Dla pustej strony rozmiar DIN A3 w orientacji poziomej to wszystko.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
width="420mm"
height="297mm"
viewBox="0 0 420 297">
</svg>
Kod Python ...
Kodowanie rozpoczyna się od frameworka zawierającego oddzielną funkcję główną:
#! python
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL
def main():
"""Here is where the magic happens"""
return
if __name__ == '__main__':
# This will be true only if the file is "executed"
# but not if imported as module
main()
... aby stworzyć pustą stronę
Proces tworzenia szablonu składa się z następujących etapów
- Zlokalizowanie folderu szablonów.
- Otwarcie pliku do zapisu w celu utworzenia pliku svg od podstaw, napisanie linii nagłówka i zamknięcie pliku w pierwszym kroku.
- A następnie wielokrotne otwieranie pliku w celu dodania kolejnych segmentów, zamykając go ponownie za każdym razem.
Makrodefinicja składa się z kilku funkcji, które są wywoływane z sekcji głównej.
Dodatkowe funkcje można wstawić przed funkcją EndSvg, a potrzebne wywołania przed wywołaniem EndSvg().
Aby wcięcie było prawidłowe, musimy zwracać uwagę na liczbę spacji używanych podczas operacji zapisu.
pathToTemplate()
Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu i należy ustawić nazwę pliku.
Użytkownik powinien wybrać folder szablonu. Jego ścieżka jest następnie zapisywana w preferencjach środowiska pracy Rysunek Techniczny.
Nie jest konieczne, aby wiedzieć, gdzie przechowywane są preferencje, ponieważ FreeCAD posiada polecenia pozwalające na bezpośrednie adresowanie potrzebnych parametrów.
def pathToTemplate(template_name):
"""Link a given template name to the path of the template folder"""
#- Get the path to the template folder that is set in the FreeCAD parameters
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
#- Link template_path and template_name for any OS
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral
return path_to_file
parameter_path przyjmuje ścieżkę do "folderu" w ramach pliku konfiguracyjnego, w którym znajduje się parametr "TemplateDir".
template_path przyjmuje zawartość "TemplateDir", która jest ścieżką do katalogu z szablonem.
template_name przyjmuje nazwę szablonu, który ma zostać utworzony.
Teraz nazwa szablonu musi być połączona ze ścieżką szablonu w sposób, który jest kompatybilny z systemami operacyjnymi opartymi na Uniksie i Windows.
Robi się to za pomocą polecenia "os.path.join" i przechowuje w pliku szablonu. Aby włączyć to polecenie, wymagana jest instrukcja "import os".
createSvgFile()
Spowoduje to utworzenie nowego pliku szablonu i zapisanie nagłówka xml.
def createSvgFile(file_path):
# Create a file and insert a header line (with t as the space saving variant of template)
t = open(file_path, "w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
startSvg()
Powoduje to dołączenie pliku szablonu i utworzenie znacznika otwierającego svg wraz z jego atrybutami.
Każda instrukcja zapisu zawiera jedną linię kodu svg zakończoną "\n", znacznikiem CR/LF.
def startSvg(file_path, sheet_width, sheet_height):
# Create svg-tag including namespace and format definitions
t = open(file_path, "a", encoding="utf-8")
# a = append, new lines are added at the end of an existing file
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
t.write("\n" + "\n")
t.write("<svg\n")
#- Namespace declarations
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
#- Format definition
t.write(" width =\"" + sheet_width + "mm\"\n")
t.write(" height=\"" + sheet_height + "mm\"\n")
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
# identical values for width and height and Viewbox' width and height
# will synchronise mm and svg-units
t.close
endSvg()
To dalej dołącza plik szablonu i tworzy znacznik zamykający svg; To ostatecznie kończy kod szablonu.
def endSvg(file_path):
# Create closing svg-tag
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.close
main()
Funkcja main() wywołuje funkcje i przekazuje pewne parametry.
def main():
"""This one creates an empty A3 template"""
#- Set the name of the template file and get its location
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here
#- Here starts the compiling of the svg file
createSvgFile(template_file) # overwrites existing File
#- Set sheet format (DIN A3)
format_width = "420"
format_height = "297"
startSvg(template_file, format_width, format_height) # adds svg start tag
endSvg(template_file) # adds svg end tag
# At this point a new SVG-file is generated and saved
return
W tym przykładzie format_width
i format_height
są twardo zakodowanymi wymiarami, obie niepotrzebne linie oznaczają punkty, w których inne sposoby pobierania danych formatu mogłyby umieścić swoją zawartość.
Kompletna makrodefinicja pustej strony
To makro składa się z powyższych segmentów kodu, gotowych do uruchomienia.
#! python
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL
import os # to enable the use of os.path.join()
# - SVG creation -
def createSvgFile(file_path):
# Create a file and insert a header line (with t as the space saving variant of template)
t = open(file_path, "w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
def startSvg(file_path, sheet_width, sheet_height):
# Create svg-tag including namespace and format definitions
t = open(file_path, "a", encoding="utf-8")
# a = append, new lines are added at the end of an existing file
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
t.write("\n" + "\n")
t.write("<svg\n")
#- Namespace declarations
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
#- Format definition
t.write(" width =\"" + sheet_width + "mm\"\n")
t.write(" height=\"" + sheet_height + "mm\"\n")
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
# identical values for width and height and Viewbox' width and height
# will synchronise mm and svg-units
t.close
def endSvg(file_path):
# Create closing svg-tag
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.close
def pathToTemplate(template_name):
"""Link a given template name to the path of the template folder"""
#- Get the path to the template folder that is set in the FreeCAD parameters
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
#- Link template_path and template_name for any OS
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral
return path_to_file
def main():
"""This one creates an empty A3 template"""
#- Set the name of the template file and get its location
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here
#- Here starts the compiling of the svg file
createSvgFile(template_file) # overwrites existing File
#- Set sheet format (DIN A3)
format_width = "420"
format_height = "297"
startSvg(template_file, format_width, format_height) # adds svg start tag
endSvg(template_file) # adds svg end tag
# At this point a new SVG-file is generated and saved
return
if __name__ == '__main__':
# This will be true only if the file is "executed"
# but not if imported as module
main()
... aby stworzyć stronę w której jest trochę tuszu
Aby stworzyć rysunek z pustej kartki potrzebujemy:
- ramki czyli prostokąty utworzone za pomocą instrukcji rect,
bloku tytułowego i więcej linii utworzonych za pomocą instrukcji path,
- proste teksty do indeksów i etykietowania komórek bloku tytułowego,
- teksty edytowalne, takie jak numer lub nazwa części,
Zwykle te elementy graficzne są używane wielokrotnie, więc kod generujący jest umieszczony w czterech funkcjach:
- svgRect() dla prostokątnych elementów ramki.
- svgPath() dla elementów prostych.
- svgText() dla tekstów statycznych.
- ediText() dla tekstów edytowalnych.
Wszystkie funkcje są umieszczane wewnątrz powyższej makrodefinicji przed funkcją main(), a powiązane z nimi wywołania funkcji są wstawiane do funkcji main() między startSvg(...)
i endSvg(...)
.
funkcja svgRect()
Aby narysować prostokąt musimy tylko wywołać funkcję svgRect i przekazać wartości szerokości, wysokości i położenia lewego górnego rogu. Jest to lepiej czytelne w tym miejscu, niż gdyby svg_line zawierał całą linię kodu svg.
def svgRect(width, height, x, y):
# Generates an svg-instruction to draw a rectangle with the given values
svg_line = (
"<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x
+ "\" y=\"" + y + "\" />"
)
return svg_line
Instrukcja svg jest podzielona, aby pozostać w zalecanej długości linii Pythona, mimo to spowoduje to utworzenie pojedynczej linii svg.
funkcja svgPath()
Aby narysować linię wystarczy wywołać funkcję svgPath i przekazać współrzędne punktu początkowego i końcowego linii.
Zamiast współrzędnych punktu końcowego możemy przekazać znacznik do rysowania linii poziomej (h) lub pionowej (v), a następnie długość linii lub znacznik do rysowania linii poziomej (H) lub pionowej (V), a następnie rzędną x lub y punktu końcowego.
Każda ścieżka zaczyna się w punkcie początkowym i pierwszą czynnością jest ruch z "podniesionym piórem" (bez rysowania) do punktu początkowego. W tym przypadku ruch względny i ruch bezwzględny są takie same i dlatego nie ma znaczenia, czy znacznik m jest pisany wielką czy małą literą.
def svgPath(x1, y1, x2, y2):
# Generates an svg-instruction to draw a path element (line) with the given values
if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H":
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
else:
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
return svg_line
funkcja svgText()
Aby narysować fragment tekstu wystarczy wywołać funkcję svgText i przekazać współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu.
(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej).
def svgText(x, y, str_value, str_angle="0"):
"""
Generates an svg-instruction to place a text element with the given values.
Optional str_angle enables vertical and arbitrarily rotated texts
"""
if str_angle == "0":
svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>")
else:
svg_line = (
"<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle
+ "," + x + "," + y + ")\">" + str_value + "</text>"
)
return svg_line
funkcja ediText()
Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję ediText i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu.
FreeCAD tworzy obiekt słownika z każdym wstawionym szablonem, a każdy wpis ma nazwę (klucz) i wartość.
(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej).
def ediText(entry_name, x, y, str_value, str_angle="0"):
"""
Generates an svg-instruction to place an editable text element with the given values.
Optional str_angle enables vertical and arbitrarily rotated editable texts
"""
if str_angle == "0":
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\"> <tspan>" + str_value + "</tspan> </text>"
)
else:
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\"> <tspan>"
+ str_value + "</tspan> </text>"
)
return svg_line
Funkcja createXxxx
Funkcje te rozpoczynają się od kodu otwierającego plik w trybie dołączania i zapisującego znacznik otwierający grupę.
Następnie widzimy sekcję do ustawiania i obliczania wartości oraz z instrukcjami zapisu, które wywołują powyższe funkcje w celu wygenerowania kodu svg.
I wreszcie znacznik zamykający grupę, po którym następuje instrukcja zamknięcia pliku.
def createFrame(file_path, sheet_width, sheet_height):
# Creates rectangles for sheet frame and drawing area
t = open(file_path, "a", encoding="utf-8")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n")
#- upper left corner of inner Frame, drawing area
frame_x = str(20)
frame_y = str(10)
#- frame dimensions
frame_width = str(int(sheet_width) - 20 - 10)
frame_height = str(int(sheet_height) - 10 - 10)
#- frame rectangle
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
#- upper left corner outer frame, sheet frame
frame_x = str(15)
frame_y = str(5)
#- frame dimensions
frame_width = str(int(sheet_width)-20)
frame_height = str(int(sheet_height)-10)
#- frame rectangle
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
t.write(" </g>\n\n")
t.close
Rezultat
Makro to dodaje kilka podstawowych elementów graficznych potrzebnych do prawidłowego działania szablonów tj. elementy linii, etykiety oraz teksty do edycji.
#! python
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL
import os # to enable the use of os.path.join()
# - SVG creation -
def createSvgFile(file_path):
# Create a file and insert a header line (with t as the space saving variant of template)
t = open(file_path, "w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
def startSvg(file_path, sheet_width, sheet_height):
# Create svg-tag including namespace and format definitions
t = open(file_path, "a", encoding="utf-8")
# a = append, new lines are added at the end of an existing file
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
t.write("\n" + "\n")
t.write("<svg\n")
#- Namespace declarations
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
#- Format definition
t.write(" width =\"" + sheet_width + "mm\"\n")
t.write(" height=\"" + sheet_height + "mm\"\n")
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
# identical values for width and height and Viewbox' width and height
# will synchronise mm and svg-units
t.close
def endSvg(file_path):
# Create closing svg-tag
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.close
def svgRect(width, height, x, y):
# Ggenerates an svg-instruction to draw a rectangle with the given values
svg_line = (
"<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x
+ "\" y=\"" + y + "\" />"
)
return svg_line
def svgPath(x1, y1, x2, y2):
# Generates an svg-instruction to draw a path element (line) with the given values
if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H":
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
else:
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
return svg_line
def svgText(x, y, str_value, str_angle="0"):
"""
Generates an svg-instruction to place a text element with the given values.
Optional str_angle enables vertical and arbitrarily rotated texts
"""
if str_angle == "0":
svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>")
else:
svg_line = (
"<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle
+ "," + x + "," + y + ")\">" + str_value + "</text>"
)
return svg_line
def ediText(entry_name, x, y, str_value, str_angle="0"):
"""
Generates an svg-instruction to place an editable text element with the given values.
Optional str_angle enables vertical and arbitrarily rotated editable texts
"""
if str_angle == "0":
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\"> <tspan>" + str_value + "</tspan> </text>"
)
else:
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\"> <tspan>"
+ str_value + "</tspan> </text>"
)
return svg_line
def createFrame(file_path, sheet_width, sheet_height):
# Creates rectangles for sheet frame and drawing area
t = open(file_path, "a", encoding="utf-8")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n")
#- calculate upper left corner of inner Frame, drawing area
frame_x = str(20)
frame_y = str(10)
#- frame dimensions
frame_width = str(int(sheet_width) - 20 - 10)
frame_height = str(int(sheet_height) - 10 - 10)
#- frame rectangle
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
#- calculate upper left corner outer frame, sheet frame
frame_x = str(15)
frame_y = str(5)
#- frame dimensions
frame_width = str(int(sheet_width)-20)
frame_height = str(int(sheet_height)-10)
#- frame rectangle
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
t.write(" </g>\n\n")
t.close
def createTitleBlock(file_path, sheet_width, sheet_height):
"""Creates a title block and transfers it to the position according to the sheet dimensions"""
#- calculate title block origin (lower left corner), offset from page origin
tbX = str(int(sheet_width) - 10 - 180) # 180 according to DIN EN ISO 7200
tbY = str(int(sheet_height) - 10)
t = open(file_path, "a", encoding="utf-8")
#- group to transfer all included title block elements at once
t.write(" <g id=\"titleblock\"\n")
t.write(" transform=\"translate(" + tbX + "," + tbY + ")\">\n")
#- sub-group of title block line framework
t.write(" <g id=\"titleblock-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.35;\
stroke-linecap:miter;stroke-miterlimit:4\">\n")
t.write(" " + svgPath(" 0"," 0"," 0","-63") + "\n")
t.write(" " + svgPath(" 0","-63","180"," 0") + "\n")
t.write(" " + svgPath(" 0","-30","h","155") + "\n")
t.write(" " + svgPath("155"," 0","v","-63") + "\n")
t.write(" </g>\n")
#- sub-group of title block static texts (left-aligned by default)
t.write(" <g id=\"titleblock-text-non-editable\"\n")
t.write(" style=\"font-size:5.0;text-anchor:start;fill:#000000;\
font-family:osifont\">\n")
t.write(" " + svgText(" 4.5","-43.5 ","Some static text") + "\n")
t.write(" " + svgText(" 4.5","-13.5 ","More static text") + "\n")
t.write(" " + svgText("162.5","-3.5 ","Vertical static text","-90") + "\n")
t.write(" </g>\n")
t.write(" </g>\n\n")
t.close
def createEditableText(file_path, sheet_width, sheet_height):
"""Creates editable texts positioned according to the page origin"""
#- calculate offset to titleblock origin
edX = int(sheet_width) - 10 - 180 # 180 according to DIN EN ISO 7200
edY = int(sheet_height) - 10
t = open(file_path, "a", encoding="utf-8")
#- group editable texts using the same attributes
t.write(" <g id=\"titleblock-editable-texts\"\n")
t.write(" style=\"font-size:7.0;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
t.write(
" " + ediText("EdiText-1",str(edX + 60),str(edY - 43.5),"Some editable text") + "\n"
)
t.write(
" " + ediText("EdiText-2",str(edX + 60),str(edY - 13.5),"More editable text") + "\n"
)
t.write(
" " + ediText("EdiText-3",str(edX + 173),str(edY - 4.5),"90° editable text","-90")
+ "\n"
)
t.write(" </g>\n\n")
t.close
def pathToTemplate(template_name):
"""Link a given template name to the path of the template folder"""
#- Get the path to the template folder that is set in the FreeCAD parameters
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
#- Link template_path and template_name for any OS
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral
return path_to_file
def main():
"""This one creates an A3 template with simple frame and title block"""
#- Set the name of the template file and get its location
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here
#- Here starts the compiling of the svg file
createSvgFile(template_file) # overwrites existing File
#- Set sheet format (DIN A3)
format_width = "420"
format_height = "297"
startSvg(template_file, format_width, format_height) # adds svg start tag
createFrame(template_file, format_width, format_height)
createTitleBlock(template_file, format_width, format_height)
createEditableText(template_file, format_width, format_height)
endSvg(template_file) # adds svg end tag
# At this point a new SVG-file is generated and saved
return
if __name__ == '__main__':
# This will be true only if the file is "executed"
# but not if imported as module
main()
A to jest kod svg pochodzący z tej makrodefinicji:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
width ="420mm"
height="297mm"
viewBox="0 0 420 297">
<g id="drawing-frame"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round">
<rect width="390" height="277" x="20" y="10" />
<rect width="400" height="287" x="15" y="5" />
</g>
<g id="titleblock"
transform="translate(230,287)">
<g id="titleblock-frame"
style="fill:none;stroke:#000000;stroke-width:0.35;stroke-linecap:miter;stroke-miterlimit:4">
<path d="m 0, 0 l 0,-63" />
<path d="m 0,-63 l 180, 0" />
<path d="m 0,-30 h 155" />
<path d="m 155, 0 v -63" />
</g>
<g id="titleblock-text-non-editable"
style="font-size:5.0;text-anchor:start;fill:#000000;font-family:osifont">
<text x=" 4.5" y="-43.5 ">Some static text</text>
<text x=" 4.5" y="-13.5 ">More static text</text>
<text x="162.5" y="-3.5 " transform="rotate(-90,162.5,-3.5 )">Vertical static text</text>
</g>
</g>
<g id="titleblock-editable-texts"
style="font-size:7.0;text-anchor:start;fill:#0000d0;font-family:osifont">
<text freecad:editable="EdiText-1" x="290" y="243.5"> <tspan>Some editable text</tspan> </text>
<text freecad:editable="EdiText-2" x="290" y="273.5"> <tspan>More editable text</tspan> </text>
<text freecad:editable="EdiText-3" x="403" y="282.5" transform="rotate(-90,403,282.5)"> <tspan>90° editable text</tspan> </text>
</g>
</svg>
I jak to powinno wyglądać po wstawieniu (plus powiększony blok tytułowy):
Kolorowanie edytowalnych tekstów na niebiesko jest tylko osobistym wyborem, aby łatwiej odróżnić statyczne i edytowalne teksty na gotowym rysunku.