2
# ***************************************************************************
3
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
4
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
5
# * Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
7
# * This file is part of the FreeCAD CAx development system. *
9
# * This program is free software; you can redistribute it and/or modify *
10
# * it under the terms of the GNU Lesser General Public License (LGPL) *
11
# * as published by the Free Software Foundation; either version 2 of *
12
# * the License, or (at your option) any later version. *
13
# * for detail see the LICENCE text file. *
15
# * FreeCAD is distributed in the hope that it will be useful, *
16
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18
# * GNU Library General Public License for more details. *
20
# * You should have received a copy of the GNU Library General Public *
21
# * License along with FreeCAD; if not, write to the Free Software *
22
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
25
# ***************************************************************************
26
"""Provides functions to create Label objects."""
29
# \brief Provides functions to create Label objects.
31
## \addtogroup draftmake
34
from draftobjects import label
35
from draftutils import gui_utils
36
from draftutils import params
37
from draftutils import utils
38
from draftutils.messages import _wrn, _err
39
from draftutils.translate import translate
42
from draftviewproviders.view_label import ViewProviderLabel
45
def make_label(target_point=App.Vector(0, 0, 0),
46
placement=App.Vector(30, 30, 0),
47
target_object=None, subelements=None,
48
label_type="Custom", custom_text="Label",
49
direction="Horizontal", distance=-10,
51
"""Create a Label object containing different types of information.
53
The current color and text height and font specified in preferences
58
target_point: Base::Vector3, optional
59
It defaults to the origin `App.Vector(0, 0, 0)`.
60
This is the point which is pointed to by the label's leader line.
61
This point can be adorned with a marker like an arrow or circle.
63
placement: Base::Placement, Base::Vector3, or Base::Rotation, optional
64
It defaults to `App.Vector(30, 30, 0)`.
65
If it is provided, it defines the base point of the textual
67
The input could be a full placement, just a vector indicating
68
the translation, or just a rotation.
70
target_object: Part::Feature or str, optional
71
It defaults to `None`.
72
If it exists it should be an object which will be used to provide
73
information to the label, as long as `label_type` is different
76
If it is a string, it must be the `Label` of that object.
77
Since a `Label` is not guaranteed to be unique in a document,
78
it will use the first object found with this `Label`.
80
subelements: str, optional
81
It defaults to `None`.
82
If `subelements` is provided, `target_object` should be provided
83
as well, otherwise it is ignored.
85
It should be a string indicating a subelement name, either
86
`'VertexN'`, `'EdgeN'`, or `'FaceN'` which should exist
87
within `target_object`.
88
In this case `'N'` is an integer that indicates the specific number
89
of vertex, edge, or face in `target_object`.
91
Both `target_object` and `subelements` are used to link the label
92
to a particular object, or to the particular vertex, edge, or face,
93
and get information from them.
95
make_label(..., target_object=App.ActiveDocument.Box)
96
make_label(..., target_object="My box", subelements="Face3")
98
These two parameters can be can be obtained from the `Gui::Selection`
101
sel_object = Gui.Selection.getSelectionEx()[0]
102
target_object = sel_object.Object
103
subelements = sel_object.SubElementNames[0]
105
label_type: str, optional
106
It defaults to `'Custom'`.
107
It indicates the type of information that will be shown in the label.
108
See the get_label_types function in label.py for supported types.
110
Only `'Custom'` allows you to manually set the text
111
by defining `custom_text`. The other types take their information
112
from the object included in `target`.
114
- `'Position'` will show the base position of the target object,
115
or of the indicated `'VertexN'` in `target`.
116
- `'Length'` will show the `Length` of the target object's `Shape`,
117
or of the indicated `'EdgeN'` in `target`.
118
- `'Area'` will show the `Area` of the target object's `Shape`,
119
or of the indicated `'FaceN'` in `target`.
121
custom_text: str, or list of str, optional
122
It defaults to `'Label'`.
123
If it is a list, each element in the list represents a new text line.
125
It is the text that will be displayed by the label when
126
`label_type` is `'Custom'`.
128
direction: str, optional
129
It defaults to `'Horizontal'`.
130
It can be `'Horizontal'`, `'Vertical'`, or `'Custom'`.
131
It indicates the direction of the straight segment of the leader line
132
that ends up next to the textual label.
134
If `'Custom'` is selected, the leader line can be manually drawn
135
by specifying the value of `points`.
136
Normally, the leader line has only three points, but with `'Custom'`
137
you can specify as many points as needed.
139
distance: int, float, Base::Quantity, optional
141
It indicates the length of the horizontal or vertical segment
144
The leader line is composed of two segments, the first segment is
145
inclined, while the second segment is either horizontal or vertical
146
depending on the value of `direction`.
153
The `oL` segment's length is defined by `distance`
154
while the `oT` segment is automatically calculated depending
155
on the values of `placement` (L) and `distance` (o).
157
This `distance` is oriented, meaning that if it is positive
158
the segment will be to the right and above of the textual
159
label, depending on if `direction` is `'Horizontal'` or `'Vertical'`,
161
If it is negative, the segment will be to the left
162
and below of the text.
164
points: list of Base::Vector3, optional
165
It defaults to `None`.
166
It is a list of vectors defining the shape of the leader line;
167
the list must have at least two points.
168
This argument must be used together with `direction='Custom'`
169
to display this custom leader.
171
However, notice that if the Label's `StraightDirection` property
172
is later changed to `'Horizontal'` or `'Vertical'`,
173
the custom point list will be overwritten with a new,
174
automatically calculated three-point list.
176
For the object to use custom points, `StraightDirection`
177
must remain `'Custom'`, and then the `Points` property
178
can be overwritten by a suitable list of points.
183
A scripted object of type `'Label'`.
184
This object does not have a `Shape` attribute, as the text and lines
185
are created on screen by Coin (pivy).
188
If there is a problem it will return `None`.
192
found, doc = utils.find_doc(App.activeDocument())
194
_err(translate("draft","No active document. Aborting."))
198
target_point = App.Vector(0, 0, 0)
200
utils.type_check([(target_point, App.Vector)], name=_name)
202
_err(translate("draft","Wrong input: must be a vector."))
206
placement = App.Placement()
208
utils.type_check([(placement, (App.Placement,
210
App.Rotation))], name=_name)
212
_err(translate("draft","Wrong input: must be a placement, a vector, or a rotation."))
215
# Convert the vector or rotation to a full placement
216
if isinstance(placement, App.Vector):
217
placement = App.Placement(placement, App.Rotation())
218
elif isinstance(placement, App.Rotation):
219
placement = App.Placement(App.Vector(), placement)
222
if isinstance(target_object, (list, tuple)):
223
_err(translate("draft","Wrong input: target_object must not be a list."))
226
found, target_object = utils.find_object(target_object, doc)
228
_err(translate("draft","Wrong input: target_object not in document."))
231
if target_object and subelements:
234
if isinstance(subelements, str):
235
subelements = [subelements]
237
utils.type_check([(subelements, (list, tuple, str))],
240
_err(translate("draft","Wrong input: subelements must be a list or tuple of strings, or a single string."))
243
# The subelements list is used to build a special list
244
# called a LinkSub, which includes the target_object
245
# and the subelements.
246
# Single: (target_object, "Edge1")
247
# Multiple: (target_object, ("Edge1", "Edge2"))
248
for sub in subelements:
249
_sub = target_object.getSubObject(sub)
251
_err(translate("draft","Wrong input: subelement {} not in object.").format(sub))
255
label_type = "Custom"
257
utils.type_check([(label_type, str)], name=_name)
259
_err(translate("draft","Wrong input: label_type must be a string."))
262
types = label.get_label_types()
263
if label_type not in types:
264
_err(translate("draft", "Wrong input: label_type must be one of the following:") + " " + str(types).strip("[]"))
268
custom_text = "Label"
270
utils.type_check([(custom_text, (str, list))], name=_name)
272
_err(translate("draft","Wrong input: must be a list of strings or a single string."))
275
if (type(custom_text) is list
276
and not all(isinstance(element, str) for element in custom_text)):
277
_err(translate("draft","Wrong input: must be a list of strings or a single string."))
281
direction = "Horizontal"
283
utils.type_check([(direction, str)], name=_name)
285
_err(translate("draft","Wrong input: must be a string, 'Horizontal', 'Vertical', or 'Custom'."))
288
if direction not in ("Horizontal", "Vertical", "Custom"):
289
_err(translate("draft","Wrong input: must be a string, 'Horizontal', 'Vertical', or 'Custom'."))
295
utils.type_check([(distance, (int, float))], name=_name)
297
_err(translate("draft","Wrong input: must be a number."))
301
_err_msg = translate("draft","Wrong input: points {} must be a list of at least two vectors.").format(points)
303
utils.type_check([(points, (tuple, list))], name=_name)
312
if not all(isinstance(p, App.Vector) for p in points):
316
new_obj = doc.addObject("App::FeaturePython",
320
new_obj.TargetPoint = target_point
321
new_obj.Placement = placement
324
new_obj.Target = [target_object, subelements]
326
new_obj.Target = [target_object, []]
328
new_obj.LabelType = label_type
329
new_obj.CustomText = custom_text
331
new_obj.StraightDirection = direction
332
new_obj.StraightDistance = distance
334
if direction != "Custom":
335
_wrn(translate("draft","Direction is not 'Custom'; points won't be used."))
336
new_obj.Points = points
339
ViewProviderLabel(new_obj.ViewObject)
340
h = params.get_param("textheight")
341
new_obj.ViewObject.FontSize = h
343
gui_utils.format_object(new_obj)
344
gui_utils.select(new_obj)
349
def makeLabel(targetpoint=None, target=None, direction=None,
350
distance=None, labeltype=None, placement=None):
351
"""Create a Label. DEPRECATED. Use 'make_label'."""
352
utils.use_instead("make_label")
359
utils.type_check([(target, (tuple, list))],
362
_err(translate("draft","Wrong input: must be a list of two elements. For example, [object, 'Edge1']."))
365
# In the old function `target` is the original parameter,
366
# a list of two elements, the target object itself, and the subelement.
367
# If the list is a single element, it is expanded to two elements
368
# with the second being empty
370
# target = [object, ]
371
# target = [object, []]
372
# target = (object, )
373
# target = (object, ())
375
# Parentheses can be used as well except a single pair
377
target = list(target)
381
target_object = target[0]
382
subelements = target[1]
384
return make_label(target_point=targetpoint,
386
target_object=target_object,
387
subelements=subelements,
388
label_type=labeltype,