TechDraw TemplateGenerator/pl: Difference between revisions

From FreeCAD Documentation
No edit summary
(Updating to match new version of source page)
Line 1: Line 1:
<languages/>
<languages/>


<div class="mw-translate-fuzzy">
{{TutorialInfo/pl
{{TutorialInfo/pl
|Topic=Generator szablonów - makrodefinicja Python
|Topic=Generator szablonów - makrodefinicja Python
Line 9: Line 10:
|SeeAlso=[[Macro_TemplateHelper/pl|Makro TemplateHelper]]
|SeeAlso=[[Macro_TemplateHelper/pl|Makro TemplateHelper]]
}}
}}
</div>


<span id="Introduction"></span>
<span id="Introduction"></span>
Line 58: Line 60:
}}
}}


<div class="mw-translate-fuzzy">
: '''xmlns='''http://www.w3.org/2000/svg''': Zewnętrzny link do przestrzeni nazw xml, aby wyszukać standardowe polecenia xml.
: '''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
: '''version='''"1.1": Używana wersja xml to 1.1
Line 67: Line 70:
: 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.
: 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.
: W tym przypadku 420 i 297 dają stronę A3. Dostosuj te wartości, aby wygenerować inne rozmiary stron.
</div>


Dla pustej strony rozmiar DIN A3 w orientacji poziomej to wszystko.
Dla pustej strony rozmiar DIN A3 w orientacji poziomej to wszystko.
Line 84: Line 88:
==Kod Python ...==
==Kod Python ...==


Coding starts with a framework containing a separate main function:

{{Code| |code=
#! 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()
}}

<span id="..._to_create_a_blank_page"></span>
=== ... aby stworzyć pustą stronę ===

<div class="mw-translate-fuzzy">
: Główną zasadą jest:
:* otworzyć plik do zapisu, a więc rozpocząć plik svg od zera, napisać linię nagłówka i zamknąć plik w pierwszym kroku.
:* a następnie wielokrotne otwieranie pliku w celu dołączania kolejnych segmentów i ponowne zamykanie go po dołączeniu.
:
:Makro składa się z kilku funkcji, które są wywoływane z sekcji głównej.
:Dodatkowe funkcje można by wstawić przed funkcją EndSvg, a potrzebne wywołania umieszczamy przed wywołaniem EndSvg().
Musimy mieć oko na liczbę spacji używanych przy operacjach zapisu, aby uzyskać poprawne wcięcie.
</div>

The macro is made of several functions which are called from the main section.<br>
Additional functions could be inserted before the EndSvg function and the needed calls are put before the EndSvg() call.<br>
We need to have an eye on the number of spaces used with the write operations for a correct indentation.

==== pathToTemplate() ====

<div class="mw-translate-fuzzy">
Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu.
Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu.
</div>


Użytkownik powinien wybrać folder szablonu. Jego ścieżka jest następnie zapisywana w preferencjach środowiska pracy Rysunek Techniczny. </br>
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.
Nie jest konieczne, aby wiedzieć, gdzie przechowywane są preferencje, ponieważ FreeCAD posiada polecenia pozwalające na bezpośrednie adresowanie potrzebnych parametrów.


<div class="mw-translate-fuzzy">
{{Code| |code=
{{Code| |code=
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
Line 95: Line 139:
template_file = os.path.join(template_path, template_name)
template_file = os.path.join(template_path, template_name)
}}
}}
</div>


<div class="mw-translate-fuzzy">
'''parameter_path''' przyjmuje ścieżkę do "folderu" w ramach pliku konfiguracyjnego, w którym znajduje się parametr "TemplateDir". </br>
'''parameter_path''' przyjmuje ścieżkę do "folderu" w ramach pliku konfiguracyjnego, w którym znajduje się parametr "TemplateDir". </br>
'''template_path''' przyjmuje zawartość "TemplateDir", która jest ścieżką do katalogu z szablonem. </br>
'''template_path''' przyjmuje zawartość "TemplateDir", która jest ścieżką do katalogu z szablonem. </br>
'''template_name''' przyjmuje nazwę szablonu, który ma zostać utworzony.
'''template_name''' przyjmuje nazwę szablonu, który ma zostać utworzony.
</div>


<div class="mw-translate-fuzzy">
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>
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>
Robi się to za pomocą polecenia "os.path.join" i przechowuje w '''pliku szablonu'''.
Robi się to za pomocą polecenia "os.path.join" i przechowuje w '''pliku szablonu'''.
</div>


==== createSvgFile() ====
<span id="..._to_create_a_blank_page"></span>

=== ... aby stworzyć pustą stronę ===
This creates a new Template file and saves the xml head line.

{{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
}}

==== startSvg() ====

This appends the template file and creates the svg opening tag including its attributes.<br>
Each write instruction contains one single line of svg code ending with "\n", the marker for 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
}}

==== endSvg() ====

This further appends the template file and creates the svg closing tag; This eventually finishes the template code.

{{Code| |code=
def endSvg(file_path):
# Create closing svg-tag
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.close
}}

==== main() ====

The main() function calls the functions and hands over some parameters.

{{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
}}

In this example {{Incode|format_width}} and {{Incode|format_height}} are hard coded dimensions, both unnecessary lines mark the points where other means of retrieving format data could put their content.


<div class="mw-translate-fuzzy">
<div class="mw-collapsible mw-collapsed toccolours">
<div class="mw-collapsible mw-collapsed toccolours">
To makro pokazuje zasadę, jak można złożyć plik svg ''(Format to A3)''.
To makro pokazuje zasadę, jak można złożyć plik svg ''(Format to A3)''.
<div class="mw-collapsible-content">
</div>

This macro consists of the above code segments, ready to run.
<div class="mw-collapsible-content">
<div class="mw-collapsible-content">


Line 113: Line 235:
#! python
#! python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
# (c) 2024 Your name LGPL

#
import os # to enable the use of os.path.join()
#

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


# - SVG creation -
# - SVG creation -
def createSvgFile(file_path):

#- Create a file and insert a header line
# 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
# (with t as the space saving variant of template)
def CreateSvgFile(file_path):
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
def StartSvg(file_path):
t = open(file_path, "a", encoding="utf-8")
t=open(file_path, "a", encoding="utf-8")
# a = append, new lines are added at the end of an existing file
# 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
# 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.freecad.org/wiki/index.php?title=Svg_Namespace\"\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
t.close
# Sheet size section
def CreateSheet(file_path, shWidth, shHeight):
t=open(file_path, "a", encoding="utf-8")
t.write(" width =\""+shWidth+"mm\"\n")
t.write(" height=\""+shHeight+"mm\"\n")
t.write(" viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
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(file_path):
t=open(file_path, "a", encoding="utf-8")
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
}}
}}


Line 175: Line 303:
</div> <!-- Koniec elementu zwijanego dla ... sekcji. Nie usuwaj! -->
</div> <!-- Koniec elementu zwijanego dla ... sekcji. Nie usuwaj! -->


: Główną zasadą jest:
:* otworzyć plik do zapisu, a więc rozpocząć plik svg od zera, napisać linię nagłówka i zamknąć plik w pierwszym kroku.
:* a następnie wielokrotne otwieranie pliku w celu dołączania kolejnych segmentów i ponowne zamykanie go po dołączeniu.
:
:Makro składa się z kilku funkcji, które są wywoływane z sekcji głównej.
:Dodatkowe funkcje można by wstawić przed funkcją EndSvg, a potrzebne wywołania umieszczamy przed wywołaniem EndSvg().
Musimy mieć oko na liczbę spacji używanych przy operacjach zapisu, aby uzyskać poprawne wcięcie.


<span id="..._to_create_a_page_with_some_ink"></span>
<span id="..._to_create_a_page_with_some_ink"></span>
=== ... aby stworzyć stronę w której jest trochę tuszu ===
=== ... aby stworzyć stronę w której jest trochę tuszu ===


<div class="mw-translate-fuzzy">
Aby stworzyć rysunek z pustej kartki potrzebujemy:
Aby stworzyć rysunek z pustej kartki potrzebujemy:
:* ramki czyli prostokąty utworzone za pomocą instrukcji '''rect'''
:* ramki czyli prostokąty utworzone za pomocą instrukcji '''rect'''
Line 191: Line 313:
:* proste teksty do indeksów i etykietowania komórek bloku tytułowego,
:* proste teksty do indeksów i etykietowania komórek bloku tytułowego,
:* teksty edytowalne, takie jak numer lub nazwa części,
:* teksty edytowalne, takie jak numer lub nazwa części,
</div>


<div class="mw-translate-fuzzy">
Zwykle te elementy graficzne są używane wielokrotnie, dlatego kod generujący jest umieszczany w funkcjach.
Zwykle te elementy graficzne są używane wielokrotnie, dlatego kod generujący jest umieszczany w funkcjach.
</div>


All functions are placed inside the above macro before the main() function and their related funktion calls are inserted in the main() function between {{Incode|startSvg(...)}} and {{Incode|endSvg(...)}}.
<span id="svgrect_function"></span>

<span id="svgRect()"></span>
<div class="mw-translate-fuzzy">
===funkcja svgrect===
===funkcja svgrect===
</div>


<div class="mw-translate-fuzzy">
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 niż zawartość svgline, która musiała być użyta zamiennie.
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 niż zawartość svgline, która musiała być użyta zamiennie.
</div>


{{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
}}
}}


The svg instruction is split to stay within the recommended python line length, it will result in a single svg line nevertheless.
<span id="svgpath_function"></span>

<span id="svgPath()"></span>
<div class="mw-translate-fuzzy">
===funkcja svgpath===
===funkcja svgpath===
</div>


<div class="mw-translate-fuzzy">
Aby narysować linię wystarczy wywołać funkcję '''svgpath''' i przekazać współrzędne punktu początkowego i końcowego linii.
Aby narysować linię wystarczy wywołać funkcję '''svgpath''' i przekazać współrzędne punktu początkowego i końcowego linii.
</div>


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.
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.
Line 216: Line 356:


{{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":
svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
else:
else:
svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
return svgLine
return svg_line
}}
}}


<span id="svgtext_function"></span>
<span id="svgText()"></span>
<div class="mw-translate-fuzzy">
===funkcja svgtext===
===funkcja svgtext===
</div>


<div class="mw-translate-fuzzy">
Aby narysować fragment tekstu wystarczy wywołać funkcję '''svgtext''' i przekazać współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy.
Aby narysować fragment tekstu wystarczy wywołać funkcję '''svgtext''' i przekazać współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy.
</div>


''(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej)''.
''(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej)''.


{{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="FCeditext_function"></span>
<span id="ediText()"></span>
<div class="mw-translate-fuzzy">
===funkcja FCeditext===
===funkcja FCeditext===
</div>


<div class="mw-translate-fuzzy">
Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję '''FCeditext''' i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy.
Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję '''FCeditext''' i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy.
</div>


FreeCAD tworzy obiekt słownika z każdym wstawionym szablonem, a każdy wpis ma nazwę ''(klucz)'' i wartość.
FreeCAD tworzy obiekt słownika z każdym wstawionym szablonem, a każdy wpis ma nazwę ''(klucz)'' i wartość.
Line 249: Line 406:


{{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>")
return svgLine
"""
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>
<span id="createXxxx_functions"></span>
<div class="mw-translate-fuzzy">
===Funkcja CreateXxxx===
===Funkcja CreateXxxx===
</div>


Funkcje te rozpoczynają się od kodu otwierającego plik w trybie dołączania i zapisującego znacznik otwierający grupę.
Funkcje te rozpoczynają się od kodu otwierającego plik w trybie dołączania i zapisującego znacznik otwierający grupę.


<div class="mw-translate-fuzzy">
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.
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.
</div>


I wreszcie znacznik zamykający grupę, po którym następuje instrukcja zamknięcia pliku.
I wreszcie znacznik zamykający grupę, po którym następuje instrukcja zamknięcia pliku.


{{Code| |code=
{{Code| |code=
def createFrame(file_path, sheet_width, sheet_height):
#- Frame creation
# Creates rectangles for sheet frame and drawing area
def CreateFrame(file_path, shWidth, shHeight):
t=open(file_path, "a", encoding="utf-8")
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
#- upper left corner
frame_y = str(10)
drawingX=str(20)
drawingY=str(10)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20-10)
frame_width = str(int(sheet_width) - 20 - 10)
drawingHeight=str(int(shHeight)-10-10)
frame_height = str(int(sheet_height) - 10 - 10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
# outer frame
#- upper left corner outer frame, sheet frame
#- upper left corner
frame_x = str(15)
drawingX=str(15)
frame_y = str(5)
drawingY=str(5)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20)
frame_width = str(int(sheet_width)-20)
drawingHeight=str(int(shHeight)-10)
frame_height = str(int(sheet_height)-10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
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
Line 304: Line 474:
#! python
#! python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
# (c) 2024 Your name LGPL

#
import os # to enable the use of os.path.join()
#

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


# - SVG creation -
# - SVG creation -
def createSvgFile(file_path):

#- Create a file and insert a header line
# 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
# (with t as the space saving variant of template)
def CreateSvgFile(file_path):
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
def StartSvg(file_path):
t = open(file_path, "a", encoding="utf-8")
t=open(file_path, "a", encoding="utf-8")
# a = append, new lines are added at the end of an existing file
# 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
# 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.freecad.org/wiki/index.php?title=Svg_Namespace\"\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
t.close
# Sheet size section
def CreateSheet(file_path, shWidth, shHeight):
t=open(file_path, "a", encoding="utf-8")
t.write(" width =\""+shWidth+"mm\"\n")
t.write(" height=\""+shHeight+"mm\"\n")
t.write(" viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
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(file_path):
t=open(file_path, "a", encoding="utf-8")
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":
svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
else:
else:
svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
return svgLine
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>")
return svgLine
"""
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(file_path, shWidth, shHeight):
t=open(file_path, "a", encoding="utf-8")
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
#- upper left corner
frame_y = str(10)
drawingX=str(20)
drawingY=str(10)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20-10)
frame_width = str(int(sheet_width) - 20 - 10)
drawingHeight=str(int(shHeight)-10-10)
frame_height = str(int(sheet_height) - 10 - 10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
# outer frame
#- calculate upper left corner outer frame, sheet frame
#- upper left corner
frame_x = str(15)
drawingX=str(15)
frame_y = str(5)
drawingY=str(5)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20)
frame_width = str(int(sheet_width)-20)
drawingHeight=str(int(shHeight)-10)
frame_height = str(int(sheet_height)-10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
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(file_path, 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
tbX=str(int(shWidth)-10-180) # 180 according to DIN EN ISO 7200
tbY = str(int(sheet_height) - 10)
t = open(file_path, "a", encoding="utf-8")
tbY=str(int(shHeight)-10)
#- group to transform all elements at once
#- group to transfer all included title block elements at once
t=open(file_path, "a", encoding="utf-8")
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(" "+svgpath(" 0"," 0"," 0","-63")+"\n")
t.write(" " + svgPath(" 0"," 0"," 0","-63") + "\n")
t.write(" "+svgpath(" 0","-63","180"," 0")+"\n")
t.write(" " + svgPath(" 0","-63","180"," 0") + "\n")
t.write(" "+svgpath(" 0","-30","h","155")+"\n")
t.write(" " + svgPath(" 0","-30","h","155") + "\n")
t.write(" "+svgpath("155"," 0","v","-63")+"\n")
t.write(" " + svgPath("155"," 0","v","-63") + "\n")
t.write(" </g>\n")
t.write(" </g>\n")
#- small texts, left-aligned
#- 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(" "+svgtext(" 4.5","-43.5 ","Some static text")+"\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(" 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(file_path, 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(file_path, "a", encoding="utf-8")
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")
t.write(" "+FCeditext("EdiText-2",str(edX+60),str(edY-13.5),"More 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
}}
}}


Line 493: Line 696:
<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 500: Line 704:
<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 511: Line 716:


[[Image:TechDraw TemplateGenerator.png|600px]]
[[Image:TechDraw TemplateGenerator.png|600px]]

To color the editable texts in blue is just a personal choice to easier distinguish static and editable texts on the finished drawing.

Revision as of 08:36, 29 January 2024

Other languages:
Ćwiczenie
Temat
Generator szablonów - 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 rozszerzenia FreeCAD Svg Namespace, aby wyszukać specjalne polecenia, które są używane tylko w środowisku FreeCAD, np. dla tekstów edytowalnych.
"freecad:" będzie poprzedzone nazwami atrybutów, aby były one obsługiwane przez wspomniane specjalne polecenia.
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 ...

Coding starts with a framework containing a separate main function:

#! 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ę

Główną zasadą jest:
  • otworzyć plik do zapisu, a więc rozpocząć plik svg od zera, napisać linię nagłówka i zamknąć plik w pierwszym kroku.
  • a następnie wielokrotne otwieranie pliku w celu dołączania kolejnych segmentów i ponowne zamykanie go po dołączeniu.
Makro składa się z kilku funkcji, które są wywoływane z sekcji głównej.
Dodatkowe funkcje można by wstawić przed funkcją EndSvg, a potrzebne wywołania umieszczamy przed wywołaniem EndSvg().

Musimy mieć oko na liczbę spacji używanych przy operacjach zapisu, aby uzyskać poprawne wcięcie.

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.

pathToTemplate()

Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu.

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.

parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
template_name = "MyTemplate.svg"
template_file = os.path.join(template_path, template_name)

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.

createSvgFile()

This creates a new Template file and saves the xml head line.

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

This appends the template file and creates the svg opening tag including its attributes.
Each write instruction contains one single line of svg code ending with "\n", the marker for 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()

This further appends the template file and creates the svg closing tag; This eventually finishes the template code.

def endSvg(file_path):
    # Create closing svg-tag
    t = open(file_path, "a", encoding="utf-8")
    t.write("</svg>")
    t.close

main()

The main() function calls the functions and hands over some parameters.

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

In this example format_width and format_height are hard coded dimensions, both unnecessary lines mark the points where other means of retrieving format data could put their content.

To makro pokazuje zasadę, jak można złożyć plik svg (Format to A3).

This macro consists of the above code segments, ready to run.

#! 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, dlatego kod generujący jest umieszczany w funkcjach.

All functions are placed inside the above macro before the main() function and their related funktion calls are inserted in the main() function between startSvg(...) and 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 niż zawartość svgline, która musiała być użyta zamiennie.

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

The svg instruction is split to stay within the recommended python line length, it will result in a single svg line nevertheless.

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.

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

Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję FCeditext i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy.

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

To color the editable texts in blue is just a personal choice to easier distinguish static and editable texts on the finished drawing.