Draft: move make_label function to its own module

Also perform several improvements such as PEP8 cleanup,
writing complete docstrings, type checking the input arguments,
and deprecating the older call.

Update `Draft.py`, the Gui Command, the unit test, and test script
as well.
This commit is contained in:
vocx-fc
2020-06-04 20:59:55 -05:00
committed by Yorik van Havre
parent f7e53eaab3
commit fa092ae3df
7 changed files with 394 additions and 102 deletions

View File

@@ -113,6 +113,7 @@ SET(Draft_make_functions
draftmake/make_ellipse.py
draftmake/make_facebinder.py
draftmake/make_fillet.py
draftmake/make_label.py
draftmake/make_line.py
draftmake/make_orthoarray.py
draftmake/make_patharray.py

View File

@@ -398,17 +398,16 @@ if gui:
_ViewProviderAngularDimension = ViewProviderAngularDimension
from draftobjects.label import make_label
from draftobjects.label import Label
from draftobjects.label import (Label,
DraftLabel)
makeLabel = make_label
DraftLabel = Label
from draftmake.make_label import (make_label,
makeLabel)
if gui:
from draftviewproviders.view_label import ViewProviderLabel
ViewProviderDraftLabel = ViewProviderLabel
from draftobjects.text import (Text,
DraftText)

View File

@@ -43,6 +43,7 @@ import draftguitools.gui_base_original as gui_base_original
import draftguitools.gui_tool_utils as gui_tool_utils
import draftguitools.gui_trackers as trackers
import draftutils.utils as utils
from draftutils.messages import _msg
from draftutils.translate import translate
@@ -56,7 +57,19 @@ class Label(gui_base_original.Creator):
def GetResources(self):
"""Set icon, menu and tooltip."""
_tip = ("Creates a label, "
"optionally attached to a selected object or element.")
"optionally attached to a selected object or subelement.\n"
"\n"
"First select a vertex, an edge, or a face of an object, "
"then call this command,\n"
"and then set the position of the leader line "
"and the textual label.\n"
"The label will be able to display information "
"about this object, and about the selected subelement,\n"
"if any.\n"
"\n"
"If many objects or many subelements are selected, "
"only the first one in each case\n"
"will be used to provide information to the label.")
return {'Pixmap': 'Draft_Label',
'Accel': "D, L",
@@ -107,6 +120,7 @@ class Label(gui_base_original.Creator):
h = App.Vector(1, 0, 0)
n = App.Vector(0, 0, 1)
r = App.Rotation()
if abs(DraftVecUtils.angle(v, h, n)) <= math.pi/4:
direction = "Horizontal"
dist = -dist
@@ -117,38 +131,45 @@ class Label(gui_base_original.Creator):
else:
direction = "Vertical"
dist = -dist
tp = "targetpoint=FreeCAD." + str(targetpoint) + ", "
tp = DraftVecUtils.toString(targetpoint)
sel = ""
if self.sel:
if self.sel.SubElementNames:
sub = "'" + self.sel.SubElementNames[0] + "'"
else:
sub = "()"
sel = "target="
sel += "("
sub = "[]"
sel = "["
sel += "FreeCAD.ActiveDocument." + self.sel.Object.Name + ", "
sel += sub
sel += "),"
pl = "placement=FreeCAD.Placement"
sel += "]"
pl = "FreeCAD.Placement"
pl += "("
pl += "FreeCAD." + str(basepoint) + ", "
pl += DraftVecUtils.toString(basepoint) + ", "
pl += "FreeCAD.Rotation" + str(r.Q)
pl += ")"
App.ActiveDocument.openTransaction("Create Label")
Gui.addModule("Draft")
_cmd = "Draft.makeLabel"
_cmd = "Draft.make_label"
_cmd += "("
_cmd += tp
_cmd += sel
_cmd += "direction='" + direction + "', "
_cmd += "distance=" + str(dist) + ", "
_cmd += "labeltype='" + self.labeltype + "', "
_cmd += pl
_cmd += "target_point=" + tp + ", "
_cmd += "placement=" + pl + ", "
if sel:
_cmd += "target=" + sel + ", "
_cmd += "label_type=" + "'" + self.labeltype + "'" + ", "
# _cmd += "custom_text=" + "'Label'" + ", "
_cmd += "direction=" + "'" + direction + "'" + ", "
_cmd += "distance=" + str(dist)
_cmd += ")"
Gui.doCommand("l = " + _cmd)
Gui.doCommand("Draft.autogroup(l)")
App.ActiveDocument.recompute()
App.ActiveDocument.commitTransaction()
# Commit the creation instructions through the parent class,
# the Creator class
_cmd_list = ['_label_ = ' + _cmd,
'Draft.autogroup(_label_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Label"),
_cmd_list)
self.finish()
def action(self, arg):

View File

@@ -0,0 +1,336 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with FreeCAD; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Provides the make function to create Draft Label objects."""
## @package make_label
# \ingroup DRAFT
# \brief Provides the make function to create Draft Label objects.
import FreeCAD as App
import draftutils.gui_utils as gui_utils
import draftutils.utils as utils
from draftutils.messages import _msg, _wrn, _err
from draftutils.translate import _tr
from draftobjects.label import Label
if App.GuiUp:
from draftviewproviders.view_label import ViewProviderLabel
def make_label(target_point=App.Vector(0, 0, 0),
placement=App.Vector(30, 30, 0),
target=None,
label_type="Custom", custom_text="Label",
direction="Horizontal", distance=-10,
points=None):
"""Create a Label object containing different types of information.
The current color and text height and font specified in preferences
are used.
Parameters
----------
target_point: Base::Vector3, optional
It defaults to the origin `App.Vector(0, 0, 0)`.
This is the point which is pointed to by the label's leader line.
This point can be adorned with a marker like an arrow or circle.
placement: Base::Placement, Base::Vector3, or Base::Rotation, optional
It defaults to `App.Vector(30, 30, 0)`.
If it is provided, it defines the base point of the textual
label.
The input could be a full placement, just a vector indicating
the translation, or just a rotation.
target: list, optional
It defaults to `None`.
The list should be a `LinkSubList`, that is, it should contain
two elements; the first element should be an object which will be used
to provide information to the label; the second element should be
a string indicating a subelement name, either `'VertexN'`, `'EdgeN'`,
or `'FaceN'` which exists within the first element.
In this case `'N'` is a number that starts with `1`
and goes up to the maximum number of vertices, edges, or faces.
::
target = [Part::Feature, 'Edge1']
The target may not need a subelement, in which case the second
element of the list may be empty.
::
target = [Part::Feature, ]
This `LinkSubList` can be obtained from the `Gui::Selection`
module.
::
sel_object = Gui.Selection.getSelectionEx()[0]
object = sel_object.Object
subelement = sel_object.SubElementNames[0]
target = [object, subelement]
label_type: str, optional
It defaults to `'Custom'`.
It can be `'Custom'`, `'Name'`, `'Label'`, `'Position'`,
`'Length'`, `'Area'`, `'Volume'`, `'Tag'`, or `'Material'`.
It indicates the type of information that will be shown in the label.
Only `'Custom'` allows you to manually set the text
by defining `custom_text`. The other types take their information
from the object included in `target`.
- `'Position'` will show the base position of the target object,
or of the indicated `'VertexN'` in `target`.
- `'Length'` will show the `Length` of the target object's `Shape`,
or of the indicated `'EdgeN'` in `target`.
- `'Area'` will show the `Area` of the target object's `Shape`,
or of the indicated `'FaceN'` in `target`.
custom_text: str, optional
It defaults to `'Label'`.
It is the text that will be displayed by the label when
`label_type` is `'Custom'`.
direction: str, optional
It defaults to `'Horizontal'`.
It can be `'Horizontal'`, `'Vertical'`, or `'Custom'`.
It indicates the direction of the straight segment of the leader line
that ends up next to the textual label.
If `'Custom'` is selected, the leader line can be manually drawn
by specifying the value of `points`.
Normally, the leader line has only three points, but with `'Custom'`
you can specify as many points as needed.
distance: int, float, Base::Quantity, optional
It defaults to -10.
It indicates the length of the horizontal or vertical segment
of the leader line.
The leader line is composed of two segments, the first segment is
inclined, while the second segment is either horizontal or vertical
depending on the value of `direction`.
::
T
|
|
o------- L text
The `oL` segment's length is defined by `distance`
while the `oT` segment is automatically calculated depending
on the values of `placement` (L) and `distance` (o).
This `distance` is oriented, meaning that if it is positive
the segment will be to the right and above of the textual
label, depending on if `direction` is `'Horizontal'` or `'Vertical'`,
respectively.
If it is negative, the segment will be to the left
and below of the text.
points: list of Base::Vector3, optional
It defaults to `None`.
It is a list of vectors defining the shape of the leader line;
the list must have at least two points.
This argument must be used together with `direction='Custom'`
to display this custom leader.
However, notice that if the Label's `StraightDirection` property
is later changed to `'Horizontal'` or `'Vertical'`,
the custom point list will be overwritten with a new,
automatically calculated three-point list.
For the object to use custom points, `StraightDirection`
must remain `'Custom'`, and then the `Points` property
can be overwritten by a suitable list of points.
Returns
-------
App::FeaturePython
A scripted object of type `'Label'`.
This object does not have a `Shape` attribute, as the text and lines
are created on screen by Coin (pivy).
None
If there is a problem it will return `None`.
"""
_name = "make_label"
utils.print_header(_name, "Label")
found, doc = utils.find_doc(App.activeDocument())
if not found:
_err(_tr("No active document. Aborting."))
return None
_msg("target_point: {}".format(target_point))
if not target_point:
target_point = App.Vector(0, 0, 0)
try:
utils.type_check([(target_point, App.Vector)], name=_name)
except TypeError:
_err(_tr("Wrong input: must be a vector."))
return None
_msg("placement: {}".format(placement))
if not placement:
placement = App.Placement()
try:
utils.type_check([(placement, (App.Placement,
App.Vector,
App.Rotation))], name=_name)
except TypeError:
_err(_tr("Wrong input: must be a placement, a vector, "
"or a rotation."))
return None
# Convert the vector or rotation to a full placement
if isinstance(placement, App.Vector):
placement = App.Placement(placement, App.Rotation())
elif isinstance(placement, App.Rotation):
placement = App.Placement(App.Vector(), placement)
_msg("target: {}".format(target))
if target:
try:
utils.type_check([(target, (tuple, list))],
name=_name)
except TypeError:
_err(_tr("Wrong input: must be a LinkSubList of two elements. "
"For example, [object, 'Edge1']"))
return None
target = list(target)
if len(target) == 1:
target.append([])
_msg("label_type: {}".format(label_type))
if not label_type:
label_type = "Custom"
try:
utils.type_check([(label_type, str)], name=_name)
except TypeError:
_err(_tr("Wrong input: must be a string, "
"'Custom', 'Name', 'Label', 'Position', "
"'Length', 'Area', 'Volume', 'Tag', or 'Material'."))
return None
if label_type not in ("Custom", "Name", "Label", "Position",
"Length", "Area", "Volume", "Tag", "Material"):
_err(_tr("Wrong input: must be a string, "
"'Custom', 'Name', 'Label', 'Position', "
"'Length', 'Area', 'Volume', 'Tag', or 'Material'."))
return None
_msg("custom_text: {}".format(custom_text))
if not custom_text:
custom_text = "Label"
try:
utils.type_check([(custom_text, str)], name=_name)
except TypeError:
_err(_tr("Wrong input: must be a string."))
return None
_msg("direction: {}".format(direction))
if not direction:
direction = "Horizontal"
try:
utils.type_check([(direction, str)], name=_name)
except TypeError:
_err(_tr("Wrong input: must be a string, "
"'Horizontal', 'Vertical', or 'Custom'."))
return None
if direction not in ("Horizontal", "Vertical", "Custom"):
_err(_tr("Wrong input: must be a string, "
"'Horizontal', 'Vertical', or 'Custom'."))
return None
_msg("distance: {}".format(distance))
if not distance:
distance = 1
try:
utils.type_check([(distance, (int, float))], name=_name)
except TypeError:
_err(_tr("Wrong input: must be a number."))
return None
if points:
_msg("points: {}".format(points))
_err_msg = _tr("Wrong input: must be a list of at least two vectors.")
try:
utils.type_check([(points, (tuple, list))], name=_name)
except TypeError:
_err(_err_msg)
return None
if len(points) < 2:
_err(_err_msg)
return None
if not all(isinstance(p, App.Vector) for p in points):
_err(_err_msg)
return None
new_obj = doc.addObject("App::FeaturePython",
"dLabel")
Label(new_obj)
new_obj.TargetPoint = target_point
new_obj.Placement = placement
if target:
new_obj.Target = target
new_obj.LabelType = label_type
new_obj.CustomText = custom_text
new_obj.StraightDirection = direction
new_obj.StraightDistance = distance
if points:
if direction != "Custom":
_wrn(_tr("Direction is not 'Custom'; "
"points won't be used."))
new_obj.Points = points
if App.GuiUp:
ViewProviderLabel(new_obj.ViewObject)
h = utils.get_param("textheight", 0.20)
new_obj.ViewObject.TextSize = h
gui_utils.format_object(new_obj)
gui_utils.select(new_obj)
return new_obj
def makeLabel(targetpoint=None, target=None, direction=None,
distance=None, labeltype=None, placement=None):
"""Create a Label. DEPRECATED. Use 'make_label'."""
utils.use_instead("make_label")
return make_label(target_point=targetpoint,
placement=placement,
target=target,
label_type=labeltype,
direction=direction,
distance=distance)

View File

@@ -29,95 +29,25 @@
# \ingroup DRAFT
# \brief This module provides the object code for Draft Label.
import FreeCAD as App
import math
from PySide.QtCore import QT_TRANSLATE_NOOP
import DraftGeomUtils
import draftutils.gui_utils as gui_utils
import draftutils.utils as utils
import FreeCAD as App
from draftobjects.draft_annotation import DraftAnnotation
if App.GuiUp:
from draftviewproviders.view_label import ViewProviderLabel
def make_label(targetpoint=None, target=None, direction=None,
distance=None, labeltype=None, placement=None):
"""
make_label(targetpoint, target, direction, distance, labeltype, placement)
Function to create a Draft Label annotation object
Parameters
----------
targetpoint : App::Vector
To be completed
target : LinkSub
To be completed
direction : String
Straight direction of the label
["Horizontal","Vertical","Custom"]
distance : Quantity
Length of the straight segment of label leader line
labeltype : String
Label type in
["Custom","Name","Label","Position",
"Length","Area","Volume","Tag","Material"]
placement : Base::Placement
To be completed
Returns
-------
obj : App::DocumentObject
Newly created label object
"""
obj = App.ActiveDocument.addObject("App::FeaturePython",
"dLabel")
Label(obj)
if App.GuiUp:
ViewProviderLabel(obj.ViewObject)
if targetpoint:
obj.TargetPoint = targetpoint
if target:
obj.Target = target
if direction:
obj.StraightDirection = direction
if distance:
obj.StraightDistance = distance
if labeltype:
obj.LabelType = labeltype
if placement:
obj.Placement = placement
if App.GuiUp:
gui_utils.format_object(obj)
gui_utils.select(obj)
return obj
class Label(DraftAnnotation):
"""The Draft Label object"""
def __init__(self, obj):
super(Label, self).__init__(obj, "Label")
self.init_properties(obj)
obj.Proxy = self
def init_properties(self, obj):
"""Add properties to the object and set them"""
_tip = QT_TRANSLATE_NOOP("App::Property",
"The placement of this object")
obj.addProperty("App::PropertyPlacement", "Placement", "Base", _tip)
@@ -213,3 +143,7 @@ class Label(DraftAnnotation):
'''Do something when a property has changed'''
return
# Alias for compatibility with v0.18 and earlier
DraftLabel = Label

View File

@@ -354,9 +354,10 @@ def _create_objects(doc=None,
_msg(16 * "-")
_msg("Label")
place = App.Placement(Vector(18500, 500, 0), App.Rotation())
label = Draft.make_label(targetpoint=Vector(18000, 0, 0),
distance=-250,
placement=place)
label = Draft.make_label(target_point=Vector(18000, 0, 0),
placement=place,
custom_text="Example label",
distance=-250)
label.Text = "Testing"
if App.GuiUp:
label.ViewObject.ArrowSize = 15

View File

@@ -321,7 +321,7 @@ class DraftCreation(unittest.TestCase):
_msg(" target_point={0}, "
"distance={1}".format(target_point, distance))
_msg(" placement={}".format(placement))
obj = Draft.make_label(targetpoint=target_point,
obj = Draft.make_label(target_point=target_point,
distance=distance,
placement=placement)
self.doc.recompute()