TechDraw Création de modèles
Cette documentation n'est pas terminée. Merci de nous aider et de contribuer à la documentation.
Modèle GuiCommand explique comment les commandes doivent être documentées. Parcourez Category:UnfinishedDocu pour voir d'autres pages incomplètes comme celle-ci. Voir Category:Command_Reference pour toutes les commandes.
Voir Écrire une page Wiki pour apprendre à éditer les pages du wiki, et aller à Contribuer à FreeCAD pour apprendre d'autres façons de contribuer.Tutoriel |
Thème |
---|
Création de modèles avec une macro Python |
Niveau |
Des connaissances de base de Python et de svg-structures sont utiles. |
Temps d'exécution estimé |
(ne sait pas) |
Auteurs |
User:FBXL5 |
Version de FreeCAD |
0.19.1 et plus |
Fichiers exemples |
None |
Voir aussi |
None |
Introduction
Ce tutoriel décrit comment générer un modèle simple à utiliser avec l'atelier TechDraw à partir de quelques lignes de code Python.
N'importe quel éditeur de texte peut être utilisé pour coder. Mon choix se porte sur Atom, mais l'éditeur intégré de FreeCAD fonctionne bien aussi.
Les exemples de code suivants peuvent être copiés et collés dans un fichier texte vide, puis enregistrés sous le nom de votre choix en tant que fichier *.py ou *.FCMacro.
Un modèle fournit un arrière-plan pour les tâches de dessin et ses dimensions sont utilisées par les pilotes d'imprimante pour mettre le dessin à l'échelle correctement.
Les modèles sont des fichiers svg et une macro doit donc composer quelques lignes de code svg (qui est un sous-ensemble du code xml).
Structure d'une simple page blanche
Le format SVG est un sous-ensemble du format XML. C'est pourquoi un fichier SVG, comme tout fichier XML, se compose de deux parties :
- L'en-tête, une déclaration de format
- Le corps, qui contient les informations à afficher et où les placer.
le titre : (je ne sais pas pourquoi il devrait y avoir un titre, le fichier svg est toujours un fichier modèle valide sans lui...)
L'en-tête
L'en-tête n'est qu'une ligne pour déclarer quelle version du langage XML un interpréteur doit utiliser pour traiter les instructions du corps.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
Le corps
Le corps commence par une balise d'ouverture qui contient des informations sur les espaces de nom et sur la taille du modèle et l'endroit où le placer. Et il se termine par une balise de fermeture.
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace"
width="420mm"
height="297mm"
viewBox="0 0 420 297">
</svg>
- xmlns= "http://www.w3.org/2000/svg" : Lien externe vers l'espace de noms xml pour rechercher les commandes xml standard
- version= "1.1" : La version de xml utilisée est 1.1
- xmlns:freecad= "Svg Namespace" : Lien externe vers l'extension de l'espace de nom de FreeCAD
- pour rechercher des commandes spéciales qui ne sont utilisées que dans un environnement FreeCAD, par exemple pour les textes éditables.
- width= "420mm" : Largeur de la zone de dessin
- height= "297mm" : Hauteur de la zone de dessin
- viewBox= "0 0 420 297" : Position du coin supérieur gauche (0;0) et du coin inférieur droit (420;297) dans l'espace de construction svg (en unités svg).
- La largeur, la hauteur et viewBox dans cette combinaison définissent 1 unité svg à 1 mm pour l'ensemble du document. Une unité dimensionnelle peut être omise à partir de maintenant.
- Dans ce cas, 420 et 297 donnent une page A3. Personnalisez ces valeurs pour générer d'autres tailles de page
Pour une page blanche de taille DIN A3 en orientation paysage, c'est tout.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace"
width="420mm"
height="297mm"
viewBox="0 0 420 297">
</svg>
Code Python...
Avant que le code ne soit généré, un dossier est nécessaire pour stocker le nouveau fichier modèle.
L'utilisateur doit avoir sélectionné un dossier de modèle. Son chemin est alors enregistré dans les préférences de TechDraw.
Il n'est pas nécessaire de savoir où sont stockées les préférences, car FreeCAD dispose de commandes permettant d'adresser directement les paramètres nécessaires.
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 reçoit le chemin vers le "dossier" dans le fichier de configuration où se trouve le paramètre "TemplateDir".
template_path reçoit le contenu de "TemplateDir" qui est le chemin vers le répertoire du modèle.
template_name reçoit le nom du modèle à créer.
Maintenant, le nom du modèle doit être associé au chemin du modèle d'une manière qui soit compatible avec les systèmes d'exploitation basés sur unix et Windows.
Ceci est fait avec la commande "os.path.join" et stocké dans le template_file.
... pour créer une page blanche
This macro shows the principle how an svg-file can be put together. (Format is A3)
#! python
# -*- coding: utf-8 -*-
# (c) 2021 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)
# - SVG creation -
#- Create a file and insert a header line
# (with t as the space saving variant of template)
def CreateSvgFile(filePath):
t=open(filePath,"w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
#- Create opening svg-tag
# Namespace section
def StartSvg(filePath):
t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
t.write("\n"+"\n")
t.write("<svg\n")
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace\"\n")
t.close
# Sheet size section
def CreateSheet(filePath,shWidth,shHeight):
t=open(filePath,"a")
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
#- Create closing svg-tag
def EndSvg(filePath):
t=open(filePath,"a")
t.write("</svg>")
t.close
# --- Main section ---
CreateSvgFile(template_file) # overwrites existing File
# Set sheet format (DIN A3)
formatWidth = "420"
formatHeight = "297"
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
- 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.
... to create a page with some ink
To make a drawing out of a blank page we need:
- Frames i.e. rectangles created with the rect instruction
- a title block and more made of lines created with the path instruction
- simple texts for indexes and labeling title block cells
- editable texts like part number or part name
Normally these graphical elements are used repeatedly and so the generating code is put into functions.
svgrect function
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.
#- Function to generate an svg-instruction to draw a rectangle with the given values
def svgrect(width,height,x,y):
svgLine=("<rect width=\""+width+"\" height=\""+height+"\" x=\""+x+"\" y=\""+y+"\" />")
return svgLine
svgpath function
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.
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.
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.
#- Function to generate 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":
svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
else:
svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
return svgLine
svgtext function
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.
(The text alignment is controlled by the surrounding group tag or left-aligned as default).
#- Function to generate an svg-instruction to place a text element with the given values
def svgtext(posX,posY,strValue):
svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
return svgLine
FCeditext function
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 creates a dictionary object with every inserted template, and each entry has a name (key) and a value.
(The text alignment is controlled by the surrounding group tag or left-aligned as default).
#- Function to generate an svg-instruction to place an editable text element with the given values
def FCeditext(entryName,posX,posY,strValue):
svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
+"\"> <tspan>"+strValue+"</tspan> </text>")
return svgLine
CreateXxxx functions
These functions start with code to open a file in append mode and to write the group opening tag.
Then follows a section to set and calculate values and with write instructions that call the above functions to generate svg-code.
And finally the group closing tag followed by an instruction to close the file.
#- Frame creation
def CreateFrame(filePath,shWidth,shHeight):
t=open(filePath,"a")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;\
stroke-linecap:round\">\n")
# inner Frame, drawing area
#- upper left corner
drawingX=str(20)
drawingY=str(10)
#- frame dimensions
drawingWidth=str(int(shWidth)-20-10)
drawingHeight=str(int(shHeight)-10-10)
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
# outer frame
#- upper left corner
drawingX=str(15)
drawingY=str(5)
#- frame dimensions
drawingWidth=str(int(shWidth)-20)
drawingHeight=str(int(shHeight)-10)
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" </g>\n\n")
t.close
Resulting macro
This macro adds some basic graphical elements needed for proper templates i.e. line elements, texts, and editable texts.
#! python
# -*- coding: utf-8 -*-
# (c) 2021 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)
# - SVG creation -
#- Create a file and insert a header line
# (with t as the space saving variant of template)
def CreateSvgFile(filePath):
t=open(filePath,"w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
#- Create opening svg-tag
# Namespace section
def StartSvg(filePath):
t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
t.write("\n"+"\n")
t.write("<svg\n")
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace\"\n")
t.close
# Sheet size section
def CreateSheet(filePath,shWidth,shHeight):
t=open(filePath,"a")
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
#- Create closing svg-tag
def EndSvg(filePath):
t=open(filePath,"a")
t.write("</svg>")
t.close
#- Function to generate an svg-instruction to draw a rectangle with the given values
def svgrect(width,height,x,y):
svgLine=("<rect width=\""+width+"\" height=\""+height+"\" x=\""+x+"\" y=\""+y+"\" />")
return svgLine
#- Function to generate 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":
svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
else:
svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
return svgLine
#- Function to generate an svg-instruction to place a text element with the given values
def svgtext(posX,posY,strValue):
svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
return svgLine
#- Function to generate an svg-instruction to place an editable text element with the given values
def FCeditext(entryName,posX,posY,strValue):
svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
+"\"> <tspan>"+strValue+"</tspan> </text>")
return svgLine
#- Frame creation
def CreateFrame(filePath,shWidth,shHeight):
t=open(filePath,"a")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;\
stroke-linecap:round\">\n")
# inner Frame, drawing area
#- upper left corner
drawingX=str(20)
drawingY=str(10)
#- frame dimensions
drawingWidth=str(int(shWidth)-20-10)
drawingHeight=str(int(shHeight)-10-10)
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
# outer frame
#- upper left corner
drawingX=str(15)
drawingY=str(5)
#- frame dimensions
drawingWidth=str(int(shWidth)-20)
drawingHeight=str(int(shHeight)-10)
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" </g>\n\n")
t.close
#- Title block movable
def CreateTitleBlock(filePath,shWidth,shHeight):
#- lower left corner
tbX=str(int(shWidth)-10-180) # 180 according to DIN EN ISO 7200
tbY=str(int(shHeight)-10)
#- group to transform all elements at once
t=open(filePath,"a")
t.write(" <g id=\"titleblock\"\n")
t.write(" transform=\"translate("+tbX+","+tbY+")\">\n")
#- title block
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")
#- small texts, left-aligned
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(" </g>\n")
t.write(" </g>\n\n")
t.close
#- Title block editable texts
def CreateEditableText(filePath,shWidth,shHeight):
#- offsets for editable texts
edX=int(shWidth)-10-180 # 180 according to DIN EN ISO 7200
edY=int(shHeight)-10
t=open(filePath,"a")
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(" "+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")
t.write(" </g>\n\n")
t.close
# --- Main section ---
CreateSvgFile(template_file) # overwrites existing File
# Set sheet format (A3)
formatWidth = "420"
formatHeight = "297"
StartSvg(template_file) # adds Start tag and namespaces
CreateSheet(template_file, formatWidth, formatHeight)
CreateFrame(template_file, formatWidth, formatHeight)
CreateTitleBlock(template_file, formatWidth, formatHeight)
CreateEditableText(template_file, formatWidth, formatHeight)
EndSvg(template_file)
# At this point a new SVG-file is generated and saved
And this is the svg-code coming out of this macro:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecadweb.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>
</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>
</g>
</svg>
And what it should look like when inserted (plus magnified title block):