Draft: migrate Layer object and function to the new structure

Move `make_layer` to `draftmake`; `Layer` and `LayerContainer`
to `draftobjects`; `ViewProviderLayer` and `ViewProviderLayerContainer`
to `draftviewproviders`.

The make function and the classes are imported in `Draft.py`
to support the usage of the older `VisGroup`.
This commit is contained in:
vocx-fc
2020-07-02 18:00:24 -05:00
committed by Yorik van Havre
parent ff4cb41860
commit ee11678a58
8 changed files with 892 additions and 429 deletions

View File

@@ -117,6 +117,7 @@ SET(Draft_make_functions
draftmake/make_facebinder.py
draftmake/make_fillet.py
draftmake/make_label.py
draftmake/make_layer.py
draftmake/make_line.py
draftmake/make_orthoarray.py
draftmake/make_patharray.py
@@ -150,6 +151,7 @@ SET(Draft_objects
draftobjects/fillet.py
draftobjects/draftlink.py
draftobjects/label.py
draftobjects/layer.py
draftobjects/dimension.py
draftobjects/patharray.py
draftobjects/point.py
@@ -179,6 +181,7 @@ SET(Draft_view_providers
draftviewproviders/view_fillet.py
draftviewproviders/view_draftlink.py
draftviewproviders/view_label.py
draftviewproviders/view_layer.py
draftviewproviders/view_dimension.py
draftviewproviders/view_point.py
draftviewproviders/view_rectangle.py

View File

@@ -368,10 +368,15 @@ from draftmake.make_fillet import make_fillet
if App.GuiUp:
from draftviewproviders.view_fillet import ViewProviderFillet
# Layers
from DraftLayer import Layer as _VisGroup
from DraftLayer import ViewProviderLayer as _ViewProviderVisGroup
from DraftLayer import makeLayer
from draftobjects.layer import (Layer,
_VisGroup)
from draftmake.make_layer import (make_layer,
makeLayer)
if App.GuiUp:
from draftviewproviders.view_layer import (ViewProviderLayer,
_ViewProviderVisGroup)
# Annotation objects
from draftobjects.dimension import (LinearDimension,

View File

@@ -45,8 +45,10 @@ is only required to migrate old objects created in that time
with the 0.19 development version.
Since this module is only used to migrate older objects, it is only temporary,
and will be removed after one year of the original introduction of the tool,
that is, in August 2020.
and will be removed after one year, that is, in January 2021.
The explanation of the migration methods is in the wiki page:
https://wiki.freecadweb.org/Scripted_objects_migration
"""
## @package DraftFillet
# \ingroup DRAFT
@@ -54,7 +56,7 @@ that is, in August 2020.
#
# This module is only required to migrate old objects created
# from August 2019 to February 2020. It will be removed definitely
# in August 2020, as the new Fillet object should be available.
# in January 2021, as the new Fillet object should be available.
import FreeCAD as App
import draftobjects.fillet

View File

@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
# * 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) *
@@ -20,423 +21,42 @@
# * USA *
# * *
# ***************************************************************************
"""Provides the Layer object.
"""Provides the Layer object. This module is deprecated.
The original Layer object was a VisGroup, but it was renamed to Layer
in the development cycle of 0.19.
In 6f896d8f22 (April 2014) the `Layer` object was created,
but in 4e595bd7bb (June 2014) it was renamed to `VisGroup`.
However, it was not used a lot, so in commit 5ee99ca4e (June 2019)
it was renamed again to `Layer`, but this time it was improved to behave
more like a proper layer system to control the visual properties
of the contained objects. All new code was moved to this module.
With the reorganization of the entire Draft workbench, the Layer object
and associated viewprovider, make function, and Gui Command
have been moved to the appropriate directories `draftobjects`,
`draftviewproviders`, `draftmake`, and `draftguitools`.
Therefore, this module is only required to migrate old objects
created with v0.18 and earlier, and certain development version of v0.19.
Since this module is only used to migrate older objects, it is only temporary,
and will be removed after one year, that is, in July 2021.
The explanation of the migration methods is in the wiki page:
https://wiki.freecadweb.org/Scripted_objects_migration
"""
## @package DraftLayer
# \ingroup DRAFT
# \brief Provides the Layer object
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
def translate(ctx, txt):
return txt
def QT_TRANSLATE_NOOP(ctx, txt):
return txt
"""This module contains everything related to Draft Layers"""
def makeLayer(name=None, linecolor=None, drawstyle=None, shapecolor=None, transparency=None):
"""makeLayer([name,linecolor,drawstyle,shapecolor,transparency]):
creates a Layer object in the active document
"""
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError(translate("draft", "No active document. Aborting") + "\n")
return
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Layer")
Layer(obj)
if name:
obj.Label = name
else:
obj.Label = translate("draft", "Layer")
if FreeCAD.GuiUp:
ViewProviderLayer(obj.ViewObject)
if linecolor:
obj.ViewObject.LineColor = linecolor
if drawstyle:
obj.ViewObject.DrawStyle = drawstyle
if shapecolor:
obj.ViewObject.ShapeColor = shapecolor
if transparency:
obj.ViewObject.Transparency = transparency
getLayerContainer().addObject(obj)
return obj
def getLayerContainer():
"""getLayerContainer(): returns a group object to put layers in"""
for obj in FreeCAD.ActiveDocument.Objects:
if obj.Name == "LayerContainer":
return obj
obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython", "LayerContainer")
obj.Label = translate("draft", "Layers")
LayerContainer(obj)
if FreeCAD.GuiUp:
ViewProviderLayerContainer(obj.ViewObject)
return obj
class Layer:
"""The Draft Layer object"""
def __init__(self, obj):
self.Type = "Layer"
obj.Proxy = self
self.Object = obj
self.setProperties(obj)
def onDocumentRestored(self, obj):
self.setProperties(obj)
def setProperties(self, obj):
if "Group" not in obj.PropertiesList:
obj.addProperty(
"App::PropertyLinkList",
"Group",
"Layer",
QT_TRANSLATE_NOOP("App::Property", "The objects that are part of this layer")
)
def __getstate__(self):
return self.Type
def __setstate__(self, state):
if state:
self.Type = state
def execute(self, obj):
pass
def addObject(self, obj, child):
g = obj.Group
if child not in g:
g.append(child)
obj.Group = g
class ViewProviderLayer:
"""A View Provider for the Layer object"""
def __init__(self, vobj):
vobj.addProperty(
"App::PropertyBool",
"OverrideLineColorChildren",
"Layer",
QT_TRANSLATE_NOOP(
"App::Property",
"If on, the child objects of this layer will match its visual aspects"
)
)
vobj.addProperty(
"App::PropertyBool",
"OverrideShapeColorChildren",
"Layer",
QT_TRANSLATE_NOOP(
"App::Property",
"If on, the child objects of this layer will match its visual aspects"
)
)
vobj.addProperty(
"App::PropertyColor",
"LineColor",
"Layer",
QT_TRANSLATE_NOOP("App::Property", "The line color of the children of this layer")
)
vobj.addProperty(
"App::PropertyColor",
"ShapeColor",
"Layer",
QT_TRANSLATE_NOOP("App::Property", "The shape color of the children of this layer")
)
vobj.addProperty(
"App::PropertyFloat",
"LineWidth",
"Layer",
QT_TRANSLATE_NOOP("App::Property", "The line width of the children of this layer")
)
vobj.addProperty(
"App::PropertyEnumeration",
"DrawStyle",
"Layer",
QT_TRANSLATE_NOOP("App::Property", "The draw style of the children of this layer")
)
vobj.addProperty(
"App::PropertyInteger",
"Transparency",
"Layer",
QT_TRANSLATE_NOOP("App::Property", "The transparency of the children of this layer")
)
vobj.DrawStyle = ["Solid", "Dashed", "Dotted", "Dashdot"]
vobj.OverrideLineColorChildren = True
vobj.OverrideShapeColorChildren = True
c = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned("DefaultShapeLineColor", 255)
vobj.LineColor = (((c >> 24) & 0xFF) / 255, ((c >> 16) & 0xFF) / 255, ((c >> 8) & 0xFF) / 255)
w = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetInt("DefaultShapeLineWidth", 2)
vobj.LineWidth = w
c = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned("DefaultShapeColor", 4294967295)
vobj.ShapeColor = (((c >> 24) & 0xFF) / 255, ((c >> 16) & 0xFF) / 255, ((c >> 8) & 0xFF) / 255)
vobj.DrawStyle = "Solid"
vobj.Proxy = self
def getIcon(self):
if hasattr(self, "icondata"):
return self.icondata
import Draft_rc
return ":/icons/Draft_Layer.svg"
def attach(self, vobj):
self.Object = vobj.Object
from pivy import coin
sep = coin.SoGroup()
vobj.addDisplayMode(sep, "Default")
return
def claimChildren(self):
if hasattr(self, "Object") and hasattr(self.Object, "Group"):
return self.Object.Group
def getDisplayModes(self, vobj):
return ["Default"]
def getDefaultDisplayMode(self):
return "Default"
def setDisplayMode(self, mode):
return mode
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def updateData(self, obj, prop):
if prop == "Group":
self.onChanged(obj.ViewObject, "LineColor")
def onChanged(self, vobj, prop):
if hasattr(vobj, "OverrideLineColorChildren") and vobj.OverrideLineColorChildren:
if hasattr(vobj, "Object") and hasattr(vobj.Object, "Group"):
for o in vobj.Object.Group:
if o.ViewObject:
for p in ["LineColor", "ShapeColor", "LineWidth", "DrawStyle", "Transparency"]:
if p == "ShapeColor":
if hasattr(vobj, "OverrideShapeColorChildren") and vobj.OverrideShapeColorChildren:
if hasattr(vobj, p):
# see forum topic https://forum.freecadweb.org/viewtopic.php?f=23&t=42197
setattr(o.ViewObject, p, getattr(vobj, p))
else:
if hasattr(vobj, p) and hasattr(o.ViewObject, p):
setattr(o.ViewObject, p, getattr(vobj, p))
# give line color to texts
if hasattr(vobj, "LineColor") and hasattr(o.ViewObject, "TextColor"):
o.ViewObject.TextColor = vobj.LineColor
if (prop == "Visibility") and hasattr(vobj, "Visibility"):
if hasattr(vobj, "Object") and hasattr(vobj.Object, "Group"):
for o in vobj.Object.Group:
if o.ViewObject and hasattr(o.ViewObject, "Visibility"):
o.ViewObject.Visibility = vobj.Visibility
if (prop in ["LineColor", "ShapeColor"]) and hasattr(vobj, "LineColor") and hasattr(vobj, "ShapeColor"):
from PySide import QtCore, QtGui
lc = vobj.LineColor
sc = vobj.ShapeColor
lc = QtGui.QColor(int(lc[0] * 255), int(lc[1] * 255), int(lc[2] * 255))
sc = QtGui.QColor(int(sc[0] * 255), int(sc[1] * 255), int(sc[2] * 255))
p1 = QtCore.QPointF(2, 17)
p2 = QtCore.QPointF(13, 8)
p3 = QtCore.QPointF(30, 15)
p4 = QtCore.QPointF(20, 25)
im = QtGui.QImage(32, 32, QtGui.QImage.Format_ARGB32)
im.fill(QtCore.Qt.transparent)
pt = QtGui.QPainter(im)
pt.setBrush(QtGui.QBrush(sc, QtCore.Qt.SolidPattern))
pt.drawPolygon([p1, p2, p3, p4])
pt.setPen(QtGui.QPen(lc, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap))
pt.drawPolygon([p1, p2, p3, p4])
pt.end()
ba = QtCore.QByteArray()
b = QtCore.QBuffer(ba)
b.open(QtCore.QIODevice.WriteOnly)
im.save(b, "XPM")
self.icondata = ba.data().decode("latin1")
vobj.signalChangeIcon()
def canDragObject(self, obj):
return True
def canDragObjects(self):
return True
def dragObject(self, vobj, otherobj):
if hasattr(vobj.Object, "Group"):
if otherobj in vobj.Object.Group:
g = vobj.Object.Group
g.remove(otherobj)
vobj.Object.Group = g
FreeCAD.ActiveDocument.recompute()
def canDropObject(self, obj):
if hasattr(obj, "Proxy") and isinstance(obj.Proxy, Layer):
# for now, prevent stacking layers
return False
return True
def canDropObjects(self):
return True
def dropObject(self, vobj, otherobj):
if hasattr(vobj.Object, "Group"):
if otherobj not in vobj.Object.Group:
if not (hasattr(otherobj, "Proxy") and isinstance(otherobj.Proxy, Layer)):
# for now, prevent stacking layers
g = vobj.Object.Group
g.append(otherobj)
vobj.Object.Group = g
# remove from all other layers (not automatic)
for parent in otherobj.InList:
if hasattr(parent, "Proxy") and isinstance(parent.Proxy, Layer):
if otherobj in parent.Group:
if parent != vobj.Object:
g = parent.Group
g.remove(otherobj)
parent.Group = g
FreeCAD.ActiveDocument.recompute()
def setupContextMenu(self, vobj, menu):
from PySide import QtCore, QtGui
import Draft_rc
action1 = QtGui.QAction(
QtGui.QIcon(":/icons/button_right.svg"),
translate("draft", "Activate this layer"),
menu
)
action1.triggered.connect(self.activate)
menu.addAction(action1)
action2 = QtGui.QAction(
QtGui.QIcon(":/icons/Draft_SelectGroup.svg"),
translate("draft", "Select contents"),
menu
)
action2.triggered.connect(self.selectcontents)
menu.addAction(action2)
def activate(self):
if hasattr(self, "Object"):
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(self.Object)
FreeCADGui.runCommand("Draft_AutoGroup")
def selectcontents(self):
if hasattr(self, "Object"):
FreeCADGui.Selection.clearSelection()
for o in self.Object.Group:
FreeCADGui.Selection.addSelection(o)
class LayerContainer:
"""The Layer Container"""
def __init__(self, obj):
self.Type = "LayerContainer"
obj.Proxy = self
def execute(self, obj):
g = obj.Group
g.sort(key=lambda o: o.Label)
obj.Group = g
def __getstate__(self):
if hasattr(self, "Type"):
return self.Type
def __setstate__(self, state):
if state:
self.Type = state
class ViewProviderLayerContainer:
"""A View Provider for the Layer Container"""
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
import Draft_rc
return ":/icons/Draft_Layer.svg"
def attach(self, vobj):
self.Object = vobj.Object
def setupContextMenu(self, vobj, menu):
import Draft_rc
from PySide import QtCore, QtGui
action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Layer.svg"), "Merge duplicates", menu)
action1.triggered.connect(self.mergeByName)
menu.addAction(action1)
def mergeByName(self):
if hasattr(self, "Object") and hasattr(self.Object, "Group"):
layers = [o for o in self.Object.Group if (hasattr(o, "Proxy") and isinstance(o.Proxy, Layer))]
todelete = []
for layer in layers:
if layer.Label[-1].isdigit() and layer.Label[-2].isdigit() and layer.Label[-3].isdigit():
orig = None
for ol in layer:
if ol.Label == layer.Label[:-3].strip():
orig = ol
break
if orig:
for par in layer.InList:
for prop in par.PropertiesList:
if getattr(par, prop) == layer:
FreeCAD.Console.PrintMessage(
"Changed property '" + prop + "' of object " + par.Label + " from " + layer.Label + " to " + orig.Label + "\n")
setattr(par, prop, orig)
todelete.append(layer)
for tod in todelete:
if not tod.InList:
FreeCAD.Console.PrintMessage("Merging duplicate layer " + tod.Label + "\n")
FreeCAD.ActiveDocument.removeObject(tod.Name)
elif (len(tod.InList) == 1) and (tod.InList[0].isDerivedFrom("App::DocumentObjectGroup")):
FreeCAD.Console.PrintMessage("Merging duplicate layer " + tod.Label + "\n")
FreeCAD.ActiveDocument.removeObject(tod.Name)
else:
FreeCAD.Console.PrintMessage("Unable to delete layer " + tod.Label + ": InList not empty\n")
def __getstate__(self):
return None
def __setstate__(self, state):
return None
# \brief Provides the Layer object. This module is deprecated.
#
# This module is only required to migrate old objects created
# with v0.18 and earlier and with certain development version of v0.19.
# It will be removed definitely in January 2021.
import FreeCAD as App
from draftobjects.layer import (Layer,
_VisGroup,
LayerContainer)
if App.GuiUp:
from draftviewproviders.view_layer import (ViewProviderLayer,
_ViewProviderVisGroup,
ViewProviderLayerContainer)

View File

@@ -21,10 +21,10 @@
# * USA *
# * *
# ***************************************************************************
"""Provides tools for creating Layers with the Draft Workbench."""
"""Provides GUI tools to create Layer objects."""
## @package gui_layers
# \ingroup draftguitools
# \brief Provides tools for creating Layers with the Draft Workbench.
# \brief Provides GUI tools to create Layer objects.
## \addtogroup draftguitools
# @{
@@ -66,7 +66,7 @@ class Layer(gui_base.GuiCommandSimplest):
self.doc.openTransaction("Create Layer")
Gui.addModule("Draft")
Gui.doCommand('_layer_ = Draft.makeLayer()')
Gui.doCommand('_layer_ = Draft.make_layer()')
Gui.doCommand('FreeCAD.ActiveDocument.recompute()')
self.doc.commitTransaction()

View File

@@ -0,0 +1,256 @@
# ***************************************************************************
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Provides functions to create Layer objects."""
## @package make_layer
# \ingroup draftmake
# \brief Provides functions to create Layer objects.
## \addtogroup draftmake
# @{
import FreeCAD as App
import draftutils.utils as utils
from draftutils.messages import _msg, _err
from draftutils.translate import _tr, translate
from draftobjects.layer import (Layer,
LayerContainer)
if App.GuiUp:
from draftviewproviders.view_layer import (ViewProviderLayer,
ViewProviderLayerContainer)
view_group = App.ParamGet("User parameter:BaseApp/Preferences/View")
def get_layer_container():
"""Return a group object to put layers in.
Returns
-------
App::DocumentObjectGroupPython
The existing group object named `'LayerContainer'`
of type `LayerContainer`.
If it doesn't exist it will create it with this default Name.
"""
found, doc = utils.find_doc(App.activeDocument())
if not found:
_err(_tr("No active document. Aborting."))
return None
for obj in doc.Objects:
if obj.Name == "LayerContainer":
return obj
new_obj = doc.addObject("App::DocumentObjectGroupPython",
"LayerContainer")
new_obj.Label = translate("draft", "Layers")
LayerContainer(new_obj)
if App.GuiUp:
ViewProviderLayerContainer(new_obj.ViewObject)
return new_obj
def getLayerContainer():
"""Get the Layer container. DEPRECATED. Use 'get_layer_container'."""
utils.use_instead("get_layer_container")
return get_layer_container()
def make_layer(name=None,
line_color=None, shape_color=None,
line_width=2.0,
draw_style="Solid", transparency=0):
"""Create a Layer object in the active document.
If a layer container named `'LayerContainer'` does not exist,
it is created with this name.
A layer controls the view properties of the objects inside the layer,
so all parameters except for `name` only apply if the graphical interface
is up.
Parameters
----------
name: str, optional
It is used to set the layer's `Label` (user editable).
It defaults to `None`, in which case the `Label`
is set to `'Layer'` or to its translation in the current language.
line_color: tuple, optional
It defaults to `None`, in which case it uses the value of the parameter
`User parameter:BaseApp/Preferences/View/DefaultShapeLineColor`.
If it is given, it should be a tuple of three
floating point values from 0.0 to 1.0.
shape_color: tuple, optional
It defaults to `None`, in which case it uses the value of the parameter
`User parameter:BaseApp/Preferences/View/DefaultShapeColor`.
If it is given, it should be a tuple of three
floating point values from 0.0 to 1.0.
line_width: float, optional
It defaults to 2.0.
It determines the width of the edges of the objects contained
in the layer.
draw_style: str, optional
It defaults to `'Solid'`.
It determines the style of the edges of the objects contained
in the layer.
If it is given, it should be 'Solid', 'Dashed', 'Dotted',
or 'Dashdot'.
transparency: int, optional
It defaults to 0.
It should be an integer value from 0 (completely opaque)
to 100 (completely transparent).
Return
------
App::FeaturePython
A scripted object of type `'Layer'`.
This object does not have a `Shape` attribute.
Modifying the view properties of this object will affect the objects
inside of it.
None
If there is a problem it will return `None`.
"""
_name = "make_layer"
utils.print_header(_name, _tr("Layer"))
found, doc = utils.find_doc(App.activeDocument())
if not found:
_err(_tr("No active document. Aborting."))
return None
if name:
_msg("name: {}".format(name))
try:
utils.type_check([(name, str)], name=_name)
except TypeError:
_err(_tr("Wrong input: it must be a string."))
return None
else:
name = translate("draft", "Layer")
_info_color = ("Wrong input: "
"must be a tuple of three floats 0.0 to 1.0.")
if line_color:
_msg("line_color: {}".format(line_color))
try:
utils.type_check([(line_color, tuple)], name=_name)
except TypeError:
_err(_tr(_info_color))
return None
if not all(isinstance(color, (int, float)) for color in line_color):
_err(_tr(_info_color))
return None
else:
c = view_group.GetUnsigned("DefaultShapeLineColor", 255)
line_color = (((c >> 24) & 0xFF) / 255,
((c >> 16) & 0xFF) / 255,
((c >> 8) & 0xFF) / 255)
if shape_color:
_msg("shape_color: {}".format(shape_color))
try:
utils.type_check([(shape_color, tuple)], name=_name)
except TypeError:
_err(_tr(_info_color))
return None
if not all(isinstance(color, (int, float)) for color in shape_color):
_err(_tr(_info_color))
return None
else:
c = view_group.GetUnsigned("DefaultShapeColor", 4294967295)
shape_color = (((c >> 24) & 0xFF) / 255,
((c >> 16) & 0xFF) / 255,
((c >> 8) & 0xFF) / 255)
_msg("line_width: {}".format(line_width))
try:
utils.type_check([(line_width, (int, float))], name=_name)
line_width = float(abs(line_width))
except TypeError:
_err(_tr("Wrong input: must be a number."))
return None
_info_style = ("Wrong input: "
"must be 'Solid', 'Dashed', 'Dotted', or 'Dashdot'.")
_msg("draw_style: {}".format(draw_style))
try:
utils.type_check([(draw_style, str)], name=_name)
except TypeError:
_err(_tr(_info_style))
return None
if draw_style not in ('Solid', 'Dashed', 'Dotted', 'Dashdot'):
_err(_tr(_info_style))
return None
_msg("transparency: {}".format(transparency))
try:
utils.type_check([(transparency, (int, float))], name=_name)
transparency = int(abs(transparency))
except TypeError:
_err(_tr("Wrong input: must be a number between 0 and 100."))
return None
new_obj = doc.addObject("App::FeaturePython", "Layer")
Layer(new_obj)
new_obj.Label = name
if App.GuiUp:
ViewProviderLayer(new_obj.ViewObject)
new_obj.ViewObject.LineColor = line_color
new_obj.ViewObject.ShapeColor = shape_color
new_obj.ViewObject.LineWidth = line_width
new_obj.ViewObject.DrawStyle = draw_style
new_obj.ViewObject.Transparency = transparency
container = get_layer_container()
container.addObject(new_obj)
return new_obj
def makeLayer(name=None, linecolor=None, drawstyle=None,
shapecolor=None, transparency=None):
"""Create a Layer. DEPRECATED. Use 'make_layer'."""
utils.use_instead("make_layer")
return make_layer(name,
linecolor, shapecolor,
draw_style=drawstyle, transparency=transparency)
## @}

View File

@@ -0,0 +1,120 @@
# ***************************************************************************
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Provides the object code for the Layer object."""
## @package layer
# \ingroup draftobjects
# \brief Provides the object code for the Layer object.
## \addtogroup draftobjects
# @{
from PySide.QtCore import QT_TRANSLATE_NOOP
class Layer:
"""The Layer object.
This class is normally used to extend a base `App::FeaturePython` object.
"""
def __init__(self, obj):
self.Type = "Layer"
self.Object = obj
self.set_properties(obj)
obj.Proxy = self
def onDocumentRestored(self, obj):
"""Execute code when the document is restored.
Add properties that don't exist.
"""
self.set_properties(obj)
def set_properties(self, obj):
"""Set properties only if they don't exist."""
if "Group" not in obj.PropertiesList:
_tip = QT_TRANSLATE_NOOP("App::Property",
"The objects that are part of this layer")
obj.addProperty("App::PropertyLinkList",
"Group",
"Layer",
_tip)
def __getstate__(self):
"""Return a tuple of objects to save or None."""
return self.Type
def __setstate__(self, state):
"""Set the internal properties from the restored state."""
if state:
self.Type = state
def execute(self, obj):
"""Execute when the object is created or recomputed. Do nothing."""
pass
def addObject(self, obj, child):
"""Add an object to this object if not in the Group property."""
group = obj.Group
if child not in group:
group.append(child)
obj.Group = group
# Alias for compatibility with v0.18 and earlier
_VisGroup = Layer
class LayerContainer:
"""The container object for layers.
This class is normally used to extend
a base `App::DocumentObjectGroupPython` object.
"""
def __init__(self, obj):
self.Type = "LayerContainer"
obj.Proxy = self
def execute(self, obj):
"""Execute when the object is created or recomputed.
Update the value of `Group` by sorting the contained layers
by `Label`.
"""
group = obj.Group
group.sort(key=lambda layer: layer.Label)
obj.Group = group
def __getstate__(self):
"""Return a tuple of objects to save or None."""
if hasattr(self, "Type"):
return self.Type
def __setstate__(self, state):
"""Set the internal properties from the restored state."""
if state:
self.Type = state
## @}

View File

@@ -0,0 +1,457 @@
# ***************************************************************************
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Provides the viewprovider code for the Layer object."""
## @package view_layer
# \ingroup draftviewproviders
# \brief Provides the viewprovider code for the Layer object.
## \addtogroup draftviewproviders
# @{
import pivy.coin as coin
import PySide.QtCore as QtCore
import PySide.QtGui as QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCAD as App
import FreeCADGui as Gui
from draftutils.messages import _msg
from draftutils.translate import translate
from draftobjects.layer import Layer
class ViewProviderLayer:
"""The viewprovider for the Layer object."""
def __init__(self, vobj):
self.Object = vobj.Object
self.set_properties(vobj)
vobj.Proxy = self
def set_properties(self, vobj):
"""Set the properties only if they don't already exist."""
properties = vobj.PropertiesList
self.set_override_options(vobj, properties)
self.set_visual_properties(vobj, properties)
def set_override_options(self, vobj, properties):
"""Set property options only if they don't already exist."""
if "OverrideLineColorChildren" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"If it is true, the objects contained "
"within this layer will adopt "
"the line color of the layer")
vobj.addProperty("App::PropertyBool",
"OverrideLineColorChildren",
"Layer",
_tip)
vobj.OverrideLineColorChildren = True
if "OverrideShapeColorChildren" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"If it is true, the objects contained "
"within this layer will adopt "
"the line color of the layer")
vobj.addProperty("App::PropertyBool",
"OverrideShapeColorChildren",
"Layer",
_tip)
vobj.OverrideShapeColorChildren = True
def set_visual_properties(self, vobj, properties):
"""Set visual properties only if they don't already exist."""
view_group = App.ParamGet("User parameter:BaseApp/Preferences/View")
if "LineColor" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"The line color of the objects "
"contained within this layer")
vobj.addProperty("App::PropertyColor",
"LineColor",
"Layer",
_tip)
c = view_group.GetUnsigned("DefaultShapeLineColor", 255)
vobj.LineColor = (((c >> 24) & 0xFF) / 255,
((c >> 16) & 0xFF) / 255,
((c >> 8) & 0xFF) / 255)
if "ShapeColor" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"The shape color of the objects "
"contained within this layer")
vobj.addProperty("App::PropertyColor",
"ShapeColor",
"Layer",
_tip)
c = view_group.GetUnsigned("DefaultShapeColor", 4294967295)
vobj.ShapeColor = (((c >> 24) & 0xFF) / 255,
((c >> 16) & 0xFF) / 255,
((c >> 8) & 0xFF) / 255)
if "LineWidth" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"The line width of the objects contained "
"within this layer")
vobj.addProperty("App::PropertyFloat",
"LineWidth",
"Layer",
_tip)
width = view_group.GetInt("DefaultShapeLineWidth", 2)
vobj.LineWidth = width
if "DrawStyle" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"The draw style of the objects contained "
"within this layer")
vobj.addProperty("App::PropertyEnumeration",
"DrawStyle",
"Layer",
_tip)
vobj.DrawStyle = ["Solid", "Dashed", "Dotted", "Dashdot"]
vobj.DrawStyle = "Solid"
if "Transparency" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"The transparency of the objects "
"contained within this layer")
vobj.addProperty("App::PropertyInteger",
"Transparency",
"Layer",
_tip)
vobj.Transparency = 0
def getIcon(self):
"""Return the path to the icon used by the viewprovider.
Normally it returns the basic Layer icon, but if `icondata` exists
it is the modified icon with the line and shape colors of the layer.
"""
if hasattr(self, "icondata"):
return self.icondata
return ":/icons/Draft_Layer.svg"
def attach(self, vobj):
"""Set up the scene sub-graph of the viewprovider."""
self.Object = vobj.Object
sep = coin.SoGroup()
vobj.addDisplayMode(sep, "Default")
def claimChildren(self):
"""Return objects that will be placed under it in the tree view.
These are the elements of the `Group` property of the Proxy object.
"""
if hasattr(self, "Object") and hasattr(self.Object, "Group"):
return self.Object.Group
def getDisplayModes(self, vobj):
"""Return the display modes that this viewprovider supports."""
return ["Default"]
def getDefaultDisplayMode(self):
"""Return the default display mode."""
return "Default"
def setDisplayMode(self, mode):
"""Return the saved display mode."""
return mode
def __getstate__(self):
"""Return a tuple of objects to save or None."""
return None
def __setstate__(self, state):
"""Set the internal properties from the restored state."""
return None
def updateData(self, obj, prop):
"""Execute when a property from the Proxy class is changed."""
if prop == "Group":
for _prop in ("LineColor", "ShapeColor", "LineWidth",
"DrawStyle", "Transparency", "Visibility"):
self.onChanged(obj.ViewObject, _prop)
def change_view_properties(self, vobj, prop):
"""Iterate over the contents and change the properties."""
obj = vobj.Object
# Return if the property does not exist
if not hasattr(vobj, prop):
return
for target_obj in obj.Group:
target_vobj = target_obj.ViewObject
# If the override properties are not set return without change
if prop == "LineColor" and not vobj.OverrideLineColorChildren:
return
elif prop == "ShapeColor" and not vobj.OverrideShapeColorChildren:
return
# This checks that the property exists in the target object,
# and then sets the target property accordingly
if hasattr(target_vobj, prop):
setattr(target_vobj, prop, getattr(vobj, prop))
else:
continue
# Use the line color for the text color if it exists
if prop == "LineColor":
if hasattr(target_vobj, "TextColor"):
target_vobj.TextColor = vobj.LineColor
if hasattr(target_vobj, "FontColor"):
target_vobj.FontColor = vobj.LineColor
def onChanged(self, vobj, prop):
"""Execute when a view property is changed."""
if prop in ("LineColor", "ShapeColor", "LineWidth",
"DrawStyle", "Transparency", "Visibility"):
self.change_view_properties(vobj, prop)
if (prop in ("LineColor", "ShapeColor")
and hasattr(vobj, "LineColor")
and hasattr(vobj, "ShapeColor")):
# This doesn't do anything to the objects inside the layer,
# it just uses the defined Line and Shape colors
# to paint the layer icon accordingly in the tree view
l_color = vobj.LineColor
s_color = vobj.ShapeColor
l_color = QtGui.QColor(int(l_color[0] * 255),
int(l_color[1] * 255),
int(l_color[2] * 255))
s_color = QtGui.QColor(int(s_color[0] * 255),
int(s_color[1] * 255),
int(s_color[2] * 255))
p1 = QtCore.QPointF(2, 17)
p2 = QtCore.QPointF(13, 8)
p3 = QtCore.QPointF(30, 15)
p4 = QtCore.QPointF(20, 25)
image = QtGui.QImage(32, 32, QtGui.QImage.Format_ARGB32)
image.fill(QtCore.Qt.transparent)
pt = QtGui.QPainter(image)
pt.setBrush(QtGui.QBrush(s_color, QtCore.Qt.SolidPattern))
pt.drawPolygon([p1, p2, p3, p4])
pt.setPen(QtGui.QPen(l_color, 2,
QtCore.Qt.SolidLine, QtCore.Qt.FlatCap))
pt.drawPolygon([p1, p2, p3, p4])
pt.end()
byte_array = QtCore.QByteArray()
buffer = QtCore.QBuffer(byte_array)
buffer.open(QtCore.QIODevice.WriteOnly)
image.save(buffer, "XPM")
self.icondata = byte_array.data().decode("latin1")
vobj.signalChangeIcon()
def canDragObject(self, obj):
"""Return True to allow dragging one object from the Layer."""
return True
def canDragObjects(self):
"""Return True to allow dragging many objects from the Layer."""
return True
def dragObject(self, vobj, otherobj):
"""Remove the object that was dragged from the layer."""
if hasattr(vobj.Object, "Group") and otherobj in vobj.Object.Group:
group = vobj.Object.Group
group.remove(otherobj)
vobj.Object.Group = group
App.ActiveDocument.recompute()
def canDropObject(self, obj):
"""Return true to allow dropping one object.
If the object being dropped is itself a `'Layer'`, return `False`
to prevent dropping a layer inside a layer, at least for now.
"""
if hasattr(obj, "Proxy") and isinstance(obj.Proxy, Layer):
return False
return True
def canDropObjects(self):
"""Return true to allow dropping many objects."""
return True
def dropObject(self, vobj, otherobj):
"""Add object that was dropped into the Layer to the group.
If the object being dropped is itself a `'Layer'`,
return immediately to prevent dropping a layer inside a layer,
at least for now.
"""
if hasattr(otherobj, "Proxy") and isinstance(otherobj.Proxy, Layer):
return
obj = vobj.Object
if hasattr(obj, "Group") and otherobj not in obj.Group:
group = obj.Group
group.append(otherobj)
obj.Group = group
# Remove from all other layers (not automatic)
for parent in otherobj.InList:
if (hasattr(parent, "Proxy")
and isinstance(parent.Proxy, Layer)
and otherobj in parent.Group
and parent != obj):
p_group = parent.Group
p_group.remove(otherobj)
parent.Group = p_group
App.ActiveDocument.recompute()
def setupContextMenu(self, vobj, menu):
"""Set up actions to perform in the context menu."""
action1 = QtGui.QAction(QtGui.QIcon(":/icons/button_right.svg"),
translate("draft", "Activate this layer"),
menu)
action1.triggered.connect(self.activate)
menu.addAction(action1)
action2 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_SelectGroup.svg"),
translate("draft", "Select layer contents"),
menu)
action2.triggered.connect(self.select_contents)
menu.addAction(action2)
def activate(self):
"""Activate the selected layer, it becomes the Autogroup."""
if hasattr(self, "Object"):
Gui.Selection.clearSelection()
Gui.Selection.addSelection(self.Object)
Gui.runCommand("Draft_AutoGroup")
def select_contents(self):
"""Select the contents of the layer."""
if hasattr(self, "Object"):
Gui.Selection.clearSelection()
for layer_obj in self.Object.Group:
Gui.Selection.addSelection(layer_obj)
class ViewProviderLayerContainer:
"""The viewprovider for the LayerContainer object."""
def __init__(self, vobj):
self.Object = vobj.Object
vobj.Proxy = self
def getIcon(self):
"""Return the path to the icon used by the viewprovider."""
return ":/icons/Draft_Layer.svg"
def attach(self, vobj):
"""Set up the scene sub-graph of the viewprovider."""
self.Object = vobj.Object
def setupContextMenu(self, vobj, menu):
"""Set up actions to perform in the context menu."""
action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Layer.svg"),
translate("Draft", "Merge layer duplicates"),
menu)
action1.triggered.connect(self.merge_by_name)
menu.addAction(action1)
def merge_by_name(self):
"""Merge the layers that have the same name."""
if not hasattr(self, "Object") or not hasattr(self.Object, "Group"):
return
obj = self.Object
layers = list()
for iobj in obj.Group:
if hasattr(iobj, "Proxy") and isinstance(iobj.Proxy, Layer):
layers.append(iobj)
to_delete = list()
for layer in layers:
# Test the last three characters of the layer's Label to see
# if it's a number, like `'Layer017'`
if (layer.Label[-1].isdigit()
and layer.Label[-2].isdigit()
and layer.Label[-3].isdigit()):
# If the object inside the layer has the same Label
# as the layer, save this object
orig = None
for ol in layer.OutList:
if ol.Label == layer.Label[:-3].strip():
orig = ol
break
# Go into the objects that reference this layer object
# and set the layer property with the previous `orig`
# object found
# Editor: when is this possible? Maybe if a layer is inside
# another layer? Currently the code doesn't allow this
# so maybe this was a previous behavior that was disabled
# in `ViewProviderLayer`.
if orig:
for par in layer.InList:
for prop in par.PropertiesList:
if getattr(par, prop) == layer:
_msg("Changed property '" + prop
+ "' of object " + par.Label
+ " from " + layer.Label
+ " to " + orig.Label)
setattr(par, prop, orig)
to_delete.append(layer)
for layer in to_delete:
if not layer.InList:
_msg("Merging duplicate layer: " + layer.Label)
App.ActiveDocument.removeObject(layer.Name)
elif len(layer.InList) == 1:
first = layer.InList[0]
if first.isDerivedFrom("App::DocumentObjectGroup"):
_msg("Merging duplicate layer: " + layer.Label)
App.ActiveDocument.removeObject(layer.Name)
else:
_msg("InList not empty. "
"Unable to delete layer: " + layer.Label)
def __getstate__(self):
"""Return a tuple of objects to save or None."""
return None
def __setstate__(self, state):
"""Set the internal properties from the restored state."""
return None
# Alias for compatibility with v0.18 and earlier
_ViewProviderVisGroup = ViewProviderLayer
## @}