Draft: Introduction of the PlaneGui class
Related issue: #5603. Previous PRs: #10766 #10838 #10931 Short explanation: * The PlaneGui class handles several GUI related aspects of the WP including a WP history. * The `get_working_plane` function should be used to get a WP. * Some of the code was taken from gui_selectplane.py. The intention is to remove almost all WP related code from that file. The icon, tooltip and label for the draftToolBar are also handled by PlaneGui. * The tooltip shows extended information about the WP (position and axes vectors). An asteriks is appended to the label if the origin does not match the global origin. * The `_update_all` function still updates FreeCAD.DraftWorkingPlane for compatibility. * Some confusing names from the Plane class have been changed: `weak` -> `auto`, `reset` -> `set_to_auto`, `setup` -> `auto_align`. Next step: Implementing PlaneGui in Draft and Arch. The gui_selectplane.py file will be updated first.
This commit is contained in:
@@ -36,6 +36,7 @@ YZ, and XZ planes.
|
||||
|
||||
import math
|
||||
import lazy_loader.lazy_loader as lz
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import FreeCAD
|
||||
import DraftVecUtils
|
||||
@@ -550,21 +551,21 @@ class PlaneBase:
|
||||
force_projection)
|
||||
|
||||
def set_to_top(self, offset=0):
|
||||
"""Sets the WP to the top position with an optional offset."""
|
||||
"""Set the WP to the top position with an optional offset."""
|
||||
self.u = Vector(1, 0, 0)
|
||||
self.v = Vector(0, 1, 0)
|
||||
self.axis = Vector(0, 0, 1)
|
||||
self.position = self.axis * offset
|
||||
|
||||
def set_to_front(self, offset=0):
|
||||
"""Sets the WP to the front position with an optional offset."""
|
||||
"""Set the WP to the front position with an optional offset."""
|
||||
self.u = Vector(1, 0, 0)
|
||||
self.v = Vector(0, 0, 1)
|
||||
self.axis = Vector(0, -1, 0)
|
||||
self.position = self.axis * offset
|
||||
|
||||
def set_to_side(self, offset=0):
|
||||
"""Sets the WP to the right side position with an optional offset."""
|
||||
"""Set the WP to the right side position with an optional offset."""
|
||||
self.u = Vector(0, 1, 0)
|
||||
self.v = Vector(0, 0, 1)
|
||||
self.axis = Vector(1, 0, 0)
|
||||
@@ -616,15 +617,16 @@ class Plane(PlaneBase):
|
||||
Defaults to Vector(0, 0, 0).
|
||||
Vector for the `position` attribute (origin).
|
||||
|
||||
weak: bool, optional
|
||||
Defaults to `True`.
|
||||
If `True` the WP is in "Auto" mode and will adapt to the current view.
|
||||
|
||||
Note that the u, v and w vectors are not checked for validity.
|
||||
|
||||
Other attributes
|
||||
----------------
|
||||
weak: bool
|
||||
A weak WP is in "Auto" mode and will adapt to the current view.
|
||||
|
||||
stored: None/list
|
||||
A placeholder for a stored state.
|
||||
Placeholder for a stored state.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
@@ -932,7 +934,7 @@ class Plane(PlaneBase):
|
||||
self.weak = True
|
||||
|
||||
def setTop(self):
|
||||
"""Sets the WP to the top position and updates the GUI."""
|
||||
"""Set the WP to the top position and updates the GUI."""
|
||||
super().set_to_top()
|
||||
self.weak = False
|
||||
if FreeCAD.GuiUp:
|
||||
@@ -943,7 +945,7 @@ class Plane(PlaneBase):
|
||||
FreeCADGui.draftToolBar.wplabel.setText(translate("draft", "Top"))
|
||||
|
||||
def setFront(self):
|
||||
"""Sets the WP to the front position and updates the GUI."""
|
||||
"""Set the WP to the front position and updates the GUI."""
|
||||
super().set_to_front()
|
||||
self.weak = False
|
||||
if FreeCAD.GuiUp:
|
||||
@@ -954,7 +956,7 @@ class Plane(PlaneBase):
|
||||
FreeCADGui.draftToolBar.wplabel.setText(translate("draft", "Front"))
|
||||
|
||||
def setSide(self):
|
||||
"""Sets the WP to the left side position and updates the GUI.
|
||||
"""Set the WP to the left side position and updates the GUI.
|
||||
|
||||
Note that set_to_side from the parent class sets the WP to the right side position.
|
||||
Which matches the Side option from Draft_SelectPlane.
|
||||
@@ -1132,6 +1134,623 @@ class Plane(PlaneBase):
|
||||
plane = Plane
|
||||
|
||||
|
||||
class PlaneGui(PlaneBase):
|
||||
"""The PlaneGui class.
|
||||
The class handles several GUI related aspects of the WP including a history.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
u: Base.Vector or WorkingPlane.Plane, optional
|
||||
Defaults to Vector(1, 0, 0).
|
||||
If a WP is provided:
|
||||
A copy of the WP is created, all other parameters are then ignored.
|
||||
If a vector is provided:
|
||||
Unit vector for the `u` attribute (+X axis).
|
||||
|
||||
v: Base.Vector, optional
|
||||
Defaults to Vector(0, 1, 0).
|
||||
Unit vector for the `v` attribute (+Y axis).
|
||||
|
||||
w: Base.Vector, optional
|
||||
Defaults to Vector(0, 0, 1).
|
||||
Unit vector for the `axis` attribute (+Z axis).
|
||||
|
||||
pos: Base.Vector, optional
|
||||
Defaults to Vector(0, 0, 0).
|
||||
Vector for the `position` attribute (origin).
|
||||
|
||||
auto: bool, optional
|
||||
Defaults to `True`.
|
||||
If `True` the WP is in "Auto" mode and will adapt to the current view.
|
||||
|
||||
icon: str, optional
|
||||
Defaults to ":/icons/view-axonometric.svg".
|
||||
Path to the icon for the draftToolBar.
|
||||
|
||||
label: str, optional
|
||||
Defaults to "Auto".
|
||||
Label for the draftToolBar.
|
||||
|
||||
tip: str, optional
|
||||
Defaults to "Current working plane: Auto".
|
||||
Tooltip for the draftToolBar.
|
||||
|
||||
Note that the u, v and w vectors are not checked for validity.
|
||||
|
||||
Other attributes
|
||||
----------------
|
||||
_view: Gui::View3DInventor
|
||||
Reference to a 3D view.
|
||||
|
||||
_stored: dict
|
||||
Dictionary for a temporary stored state.
|
||||
|
||||
_history: dict
|
||||
Dictionary that holds up to 10 stored states.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
u=Vector(1, 0, 0), v=Vector(0, 1, 0), w=Vector(0, 0, 1),
|
||||
pos=Vector(0, 0, 0),
|
||||
auto=True,
|
||||
icon=":/icons/view-axonometric.svg",
|
||||
label=QT_TRANSLATE_NOOP("draft", "Auto"),
|
||||
tip=QT_TRANSLATE_NOOP("draft", "Current working plane:") + " " + QT_TRANSLATE_NOOP("draft", "Auto")):
|
||||
|
||||
if isinstance(u, PlaneGui):
|
||||
self.match(u)
|
||||
else:
|
||||
super().__init__(u, v, w, pos)
|
||||
self.auto = auto
|
||||
self.icon = icon
|
||||
self.label = label
|
||||
self.tip = tip
|
||||
self._view = None
|
||||
self._stored = {}
|
||||
self._history = {}
|
||||
|
||||
def copy(self):
|
||||
"""Return a new plane that is a copy of the present object."""
|
||||
wp = PlaneGui()
|
||||
self.match(source=self, target=wp)
|
||||
return wp
|
||||
|
||||
def _save(self):
|
||||
"""Store the WP attributes."""
|
||||
self._stored = self.get_parameters()
|
||||
|
||||
def _restore(self):
|
||||
"""Restore the WP attributes that were saved."""
|
||||
if self._stored:
|
||||
self.set_parameters(self._stored)
|
||||
self._stored = {}
|
||||
self._update_all(hist_add=False)
|
||||
|
||||
def align_to_selection(self, offset=0, hist_add=True):
|
||||
"""Align the WP to a selection with an optional offset.
|
||||
|
||||
The selection must define a plane.
|
||||
|
||||
Parameter
|
||||
---------
|
||||
offset: float, optional
|
||||
Defaults to zero.
|
||||
Offset along the WP `axis`
|
||||
|
||||
Returns
|
||||
-------
|
||||
`True`/`False`
|
||||
`True` if successful.
|
||||
"""
|
||||
if not FreeCAD.GuiUp:
|
||||
return False
|
||||
|
||||
sels = FreeCADGui.Selection.getSelectionEx("", 0)
|
||||
if not sels:
|
||||
return False
|
||||
|
||||
objs = []
|
||||
for sel in sels:
|
||||
for sub in sel.SubElementNames if sel.SubElementNames else [""]:
|
||||
objs.append(Part.getShape(sel.Object, sub, needSubElement=True, retType=1))
|
||||
|
||||
if len(objs) != 1:
|
||||
if all([obj[0].isNull() is False and obj[0].ShapeType in ["Edge", "Vertex"] for obj in objs]):
|
||||
ret = self.align_to_edges_vertexes([obj[0] for obj in objs], offset, hist_add)
|
||||
else:
|
||||
ret = False
|
||||
|
||||
if ret is False:
|
||||
_wrn(translate("draft", "Selected shapes do not define a plane"))
|
||||
return ret
|
||||
|
||||
shape, mtx, obj = objs[0]
|
||||
place = FreeCAD.Placement(mtx)
|
||||
|
||||
if utils.get_type(obj) in ["App::Part",
|
||||
"PartDesign::Plane",
|
||||
"Axis",
|
||||
"SectionPlane"]:
|
||||
ret = self.align_to_obj_placement(obj, offset, place, hist_add)
|
||||
elif utils.get_type(obj) in ["WorkingPlaneProxy", "BuildingPart"]:
|
||||
ret = self.align_to_wp_proxy(obj, offset, place, hist_add)
|
||||
elif shape.isNull():
|
||||
ret = self.align_to_obj_placement(obj, offset, place, hist_add)
|
||||
elif shape.ShapeType == "Face":
|
||||
ret = self.align_to_face(shape, offset, hist_add)
|
||||
elif shape.ShapeType == "Edge":
|
||||
ret = self.align_to_edge_or_wire(shape, offset, hist_add)
|
||||
elif shape.Solids:
|
||||
ret = self.align_to_obj_placement(obj, offset, place, hist_add)
|
||||
else:
|
||||
ret = self.align_to_edges_vertexes(shape.Vertexes, offset, hist_add)
|
||||
|
||||
if ret is False:
|
||||
_wrn(translate("draft", "Selected shapes do not define a plane"))
|
||||
return ret
|
||||
|
||||
def _handle_custom(self, hist_add):
|
||||
self.auto = False
|
||||
self.icon = ":/icons/Draft_SelectPlane.svg"
|
||||
self.label = self._get_label(translate("draft", "Custom"))
|
||||
self.tip = self._get_tip(translate("draft", "Custom"))
|
||||
self._update_all(hist_add)
|
||||
|
||||
def align_to_3_points(self, p1, p2, p3, offset=0, hist_add=True):
|
||||
"""See PlaneBase.align_to_3_points."""
|
||||
if super().align_to_3_points(p1, p2, p3, offset) is False:
|
||||
return False
|
||||
self._handle_custom(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_edges_vertexes(self, shapes, offset=0, hist_add=True):
|
||||
"""See PlaneBase.align_to_edges_vertexes."""
|
||||
if super().align_to_edges_vertexes(shapes, offset) is False:
|
||||
return False
|
||||
self._handle_custom(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_edge_or_wire(self, shape, offset=0, hist_add=True):
|
||||
"""See PlaneBase.align_to_edge_or_wire."""
|
||||
if super().align_to_edge_or_wire(shape, offset) is False:
|
||||
return False
|
||||
self._handle_custom(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_face(self, shape, offset=0, hist_add=True):
|
||||
"""See PlaneBase.align_to_face."""
|
||||
if super().align_to_face(shape, offset) is False:
|
||||
return False
|
||||
self._handle_custom(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_placement(self, place, offset=0, hist_add=True):
|
||||
"""See PlaneBase.align_to_placement."""
|
||||
super().align_to_placement(place, offset)
|
||||
self._handle_custom(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_point_and_axis(self, point, axis, offset=0, upvec=Vector(1, 0, 0), hist_add=True):
|
||||
"""See PlaneBase.align_to_point_and_axis."""
|
||||
if super().align_to_point_and_axis(point, axis, offset, upvec) is False:
|
||||
return False
|
||||
self._handle_custom(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_obj_placement(self, obj, offset=0, place=None, hist_add=True):
|
||||
"""Align the WP to an object placement with an optional offset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj: App::DocumentObject
|
||||
Object to derive the Placement (if place is `None`), the icon and
|
||||
the label from.
|
||||
offset: float, optional
|
||||
Defaults to zero.
|
||||
Offset along the WP `axis`.
|
||||
place: Base.Placement, optional
|
||||
Defaults to `None`.
|
||||
If `None` the Placement from obj is used.
|
||||
Argument to be used if obj is inside a container.
|
||||
|
||||
Returns
|
||||
-------
|
||||
`True`/`False`
|
||||
`True` if successful.
|
||||
"""
|
||||
if hasattr(obj, "Placement") is False:
|
||||
return False
|
||||
if place is None:
|
||||
place = obj.Placement
|
||||
super().align_to_placement(place, offset)
|
||||
self.auto = False
|
||||
if utils.get_type(obj) == "WorkingPlaneProxy":
|
||||
self.icon = ":/icons/Draft_PlaneProxy.svg"
|
||||
elif FreeCAD.GuiUp \
|
||||
and hasattr(obj, "ViewObject") \
|
||||
and hasattr(obj.ViewObject, "Proxy") \
|
||||
and hasattr(obj.ViewObject.Proxy, "getIcon"):
|
||||
self.icon = obj.ViewObject.Proxy.getIcon()
|
||||
else:
|
||||
self.icon = ":/icons/Std_Placement.svg"
|
||||
self.label = self._get_label(obj.Label)
|
||||
self.tip = self._get_tip(obj.Label)
|
||||
self._update_all(hist_add)
|
||||
return True
|
||||
|
||||
def align_to_wp_proxy(self, obj, offset=0, place=None, hist_add=True):
|
||||
"""Align the WP to a WPProxy with an optional offset.
|
||||
|
||||
See align_to_obj_placement.
|
||||
|
||||
Also handles several WPProxy related features.
|
||||
"""
|
||||
if self.align_to_obj_placement(obj, offset, place, hist_add) is False:
|
||||
return False
|
||||
|
||||
if not FreeCAD.GuiUp:
|
||||
return True
|
||||
|
||||
vobj = obj.ViewObject
|
||||
|
||||
if hasattr(vobj, "AutoWorkingPlane") \
|
||||
and vobj.AutoWorkingPlane is True:
|
||||
self.auto = True
|
||||
|
||||
if hasattr(vobj, "CutView") \
|
||||
and hasattr(vobj, "AutoCutView") \
|
||||
and vobj.AutoCutView is True:
|
||||
vobj.CutView = True
|
||||
|
||||
if hasattr(vobj, "RestoreView") \
|
||||
and vobj.RestoreView is True \
|
||||
and hasattr(vobj, "ViewData") \
|
||||
and len(vobj.ViewData) >= 12:
|
||||
vdat = vobj.ViewData
|
||||
if self._view is not None:
|
||||
try:
|
||||
if len(vdat) == 13 and vdat[12] == 1:
|
||||
camtype = "Perspective"
|
||||
else:
|
||||
camtype = "Orthographic"
|
||||
if self._view.getCameraType() != camtype:
|
||||
self._view.setCameraType(camtype)
|
||||
|
||||
cam = self._view.getCameraNode()
|
||||
cam.position.setValue([vdat[0], vdat[1], vdat[2]])
|
||||
cam.orientation.setValue([vdat[3], vdat[4], vdat[5], vdat[6]])
|
||||
cam.nearDistance.setValue(vdat[7])
|
||||
cam.farDistance.setValue(vdat[8])
|
||||
cam.aspectRatio.setValue(vdat[9])
|
||||
cam.focalDistance.setValue(vdat[10])
|
||||
if camtype == "Orthographic":
|
||||
cam.height.setValue(vdat[11])
|
||||
else:
|
||||
cam.heightAngle.setValue(vdat[11])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if hasattr(vobj, "RestoreState") \
|
||||
and vobj.RestoreState is True \
|
||||
and hasattr(vobj, "VisibilityMap") \
|
||||
and vobj.VisibilityMap:
|
||||
for name, vis in vobj.VisibilityMap.items():
|
||||
obj = FreeCADGui.ActiveDocument.getObject(name)
|
||||
if obj:
|
||||
obj.Visibility = (vis == "True")
|
||||
|
||||
return True
|
||||
|
||||
def auto_align(self):
|
||||
"""Align the WP to the current view if self.auto is True."""
|
||||
if self.auto and self._view is not None:
|
||||
try:
|
||||
cam = self._view.getCameraNode()
|
||||
rot = FreeCAD.Rotation(*cam.getField("orientation").getValue().getValue())
|
||||
self.u, self.v, self.axis = self._axes_from_view_rotation(rot)
|
||||
self.position = Vector()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def set_to_default(self):
|
||||
"""Set the WP to the default from the preferences."""
|
||||
default_wp = utils.get_param("defaultWP", 0)
|
||||
if default_wp == 0:
|
||||
self.set_to_auto()
|
||||
elif default_wp == 1:
|
||||
self.set_to_top()
|
||||
elif default_wp == 2:
|
||||
self.set_to_front()
|
||||
elif default_wp == 3:
|
||||
self.set_to_side()
|
||||
|
||||
def set_to_auto(self): # Similar to Plane.reset.
|
||||
"""Set the WP to auto."""
|
||||
self.auto = True
|
||||
self.icon = ":/icons/view-axonometric.svg"
|
||||
self.label = self._get_label(translate("draft", "Auto"))
|
||||
self.tip = self._get_tip(translate("draft", "Auto"))
|
||||
self._update_all()
|
||||
|
||||
def set_to_top(self, offset=0, center_on_view=False):
|
||||
"""Set the WP to the top position with an optional offset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offset: float, optional
|
||||
Defaults to zero.
|
||||
Offset along the WP `axis`.
|
||||
center_on_view: bool, optional
|
||||
Defaults to `False`.
|
||||
If `True` the WP `position` is moved along the (offset) WP to the
|
||||
center of the view
|
||||
"""
|
||||
super().set_to_top(offset)
|
||||
if center_on_view:
|
||||
self._center_on_view()
|
||||
self.auto = False
|
||||
self.icon = ":/icons/view-top.svg"
|
||||
self.label = self._get_label(translate("draft", "Top"))
|
||||
self.tip = self._get_tip(translate("draft", "Top"))
|
||||
self._update_all()
|
||||
|
||||
def set_to_front(self, offset=0, center_on_view=False):
|
||||
"""Set the WP to the front position with an optional offset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offset: float, optional
|
||||
Defaults to zero.
|
||||
Offset along the WP `axis`.
|
||||
center_on_view: bool, optional
|
||||
Defaults to `False`.
|
||||
If `True` the WP `position` is moved along the (offset) WP to the
|
||||
center of the view
|
||||
"""
|
||||
super().set_to_front(offset)
|
||||
if center_on_view:
|
||||
self._center_on_view()
|
||||
self.auto = False
|
||||
self.icon = ":/icons/view-front.svg"
|
||||
self.label = self._get_label(translate("draft", "Front"))
|
||||
self.tip = self._get_tip(translate("draft", "Front"))
|
||||
self._update_all()
|
||||
|
||||
def set_to_side(self, offset=0, center_on_view=False):
|
||||
"""Set the WP to the right side position with an optional offset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offset: float, optional
|
||||
Defaults to zero.
|
||||
Offset along the WP `axis`.
|
||||
center_on_view: bool, optional
|
||||
Defaults to `False`.
|
||||
If `True` the WP `position` is moved along the (offset) WP to the
|
||||
center of the view
|
||||
"""
|
||||
super().set_to_side(offset)
|
||||
if center_on_view:
|
||||
self._center_on_view()
|
||||
self.auto = False
|
||||
self.icon = ":/icons/view-right.svg"
|
||||
self.label = self._get_label(translate("draft", "Side"))
|
||||
self.tip = self._get_tip(translate("draft", "Side"))
|
||||
self._update_all()
|
||||
|
||||
def set_to_view(self, offset=0, center_on_view=False):
|
||||
"""Align the WP to the view with an optional offset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offset: float, optional
|
||||
Defaults to zero.
|
||||
Offset along the WP `axis`.
|
||||
center_on_view: bool, optional
|
||||
Defaults to `False`.
|
||||
If `True` the WP `position` is moved along the (offset) WP to the
|
||||
center of the view
|
||||
"""
|
||||
if self._view is not None:
|
||||
try:
|
||||
cam = self._view.getCameraNode()
|
||||
rot = FreeCAD.Rotation(*cam.getField("orientation").getValue().getValue())
|
||||
self.u, self.v, self.axis = self._axes_from_view_rotation(rot)
|
||||
self.position = self.axis * offset
|
||||
if center_on_view:
|
||||
self._center_on_view()
|
||||
self.auto = False
|
||||
self.icon = ":/icons/Draft_SelectPlane.svg"
|
||||
self.label = self._get_label(translate("draft", "Custom"))
|
||||
self.tip = self._get_tip(translate("draft", "Custom"))
|
||||
self._update_all()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def set_to_position(self, pos):
|
||||
"""Set the `position` of the WP."""
|
||||
self.position = pos
|
||||
label = self.label.rstrip("*")
|
||||
self.label = self._get_label(label)
|
||||
self.tip = self._get_tip(label)
|
||||
self._update_all()
|
||||
|
||||
def center_on_view(self):
|
||||
"""Move the WP `position` along the WP to the center of the view."""
|
||||
self._center_on_view()
|
||||
label = self.label.rstrip("*")
|
||||
self.label = self._get_label(label)
|
||||
self.tip = self._get_tip(label)
|
||||
self._update_all()
|
||||
|
||||
def _center_on_view(self):
|
||||
if self._view is not None:
|
||||
try:
|
||||
cam = self._view.getCameraNode()
|
||||
pos = self.project_point(Vector(cam.position.getValue().getValue()),
|
||||
direction=self._view.getViewDirection(),
|
||||
force_projection=False)
|
||||
if pos is not None:
|
||||
self.position = pos
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def align_view(self):
|
||||
"""Align the view to the WP."""
|
||||
if self._view is not None:
|
||||
try:
|
||||
param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
|
||||
default_cam_dist = abs(param.GetFloat("NewDocumentCameraScale", 100.0))
|
||||
cam = self._view.getCameraNode()
|
||||
cur_cam_dist = abs(self.get_local_coords(Vector(cam.position.getValue().getValue())).z)
|
||||
cam_dist = max(default_cam_dist, cur_cam_dist)
|
||||
cam.position.setValue(self.position + DraftVecUtils.scaleTo(self.axis, cam_dist))
|
||||
cam.orientation.setValue(self.get_placement().Rotation.Q)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _previous(self):
|
||||
idx = self._history["idx"]
|
||||
if idx == 0:
|
||||
_wrn(translate("draft", "No previous working plane"))
|
||||
return
|
||||
idx -= 1
|
||||
self.set_parameters(self._history["data"][idx])
|
||||
self._history["idx"] = idx
|
||||
self._update_all(hist_add=False)
|
||||
|
||||
def _next(self):
|
||||
idx = self._history["idx"]
|
||||
if idx == len(self._history["data"]) - 1:
|
||||
_wrn(translate("draft", "No next working plane"))
|
||||
return
|
||||
idx += 1
|
||||
self.set_parameters(self._history["data"][idx])
|
||||
self._history["idx"] = idx
|
||||
self._update_all(hist_add=False)
|
||||
|
||||
def _has_previous(self):
|
||||
return bool(self._history) and self._history["idx"] != 0
|
||||
|
||||
def _has_next(self):
|
||||
return bool(self._history) and self._history["idx"] != len(self._history["data"]) - 1
|
||||
|
||||
def _get_prop_list(self):
|
||||
return ["u",
|
||||
"v",
|
||||
"axis",
|
||||
"position",
|
||||
"auto",
|
||||
"icon",
|
||||
"label",
|
||||
"tip"]
|
||||
|
||||
def _get_label(self, label):
|
||||
if self.auto or self.position.isEqual(Vector(), 0):
|
||||
return label
|
||||
else:
|
||||
return label + "*"
|
||||
|
||||
def _get_tip(self, label):
|
||||
tip = translate("draft", "Current working plane:")
|
||||
tip += " " + label
|
||||
if self.auto:
|
||||
return tip
|
||||
tip += "\n" + translate("draft", "Axes:")
|
||||
tip += "\n X = "
|
||||
tip += self._format_vector(self.u)
|
||||
tip += "\n Y = "
|
||||
tip += self._format_vector(self.v)
|
||||
tip += "\n Z = "
|
||||
tip += self._format_vector(self.axis)
|
||||
tip += "\n" + translate("draft", "Position:")
|
||||
tip += "\n X = "
|
||||
tip += self._format_coord(self.position.x)
|
||||
tip += "\n Y = "
|
||||
tip += self._format_coord(self.position.y)
|
||||
tip += "\n Z = "
|
||||
tip += self._format_coord(self.position.z)
|
||||
return tip
|
||||
|
||||
def _format_coord(self, coord):
|
||||
return FreeCAD.Units.Quantity(coord, FreeCAD.Units.Length).UserString
|
||||
|
||||
def _format_vector(self, vec):
|
||||
dec = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals", 2)
|
||||
return f"({vec.x:.{dec}f} {vec.y:.{dec}f} {vec.z:.{dec}f})"
|
||||
|
||||
def _update_all(self, hist_add=True):
|
||||
# Update the old DraftWorkingPlane for compatiblity:
|
||||
if not hasattr(FreeCAD, "DraftWorkingPlane"):
|
||||
FreeCAD.DraftWorkingPlane = Plane()
|
||||
for prop in ["u", "v", "axis", "position"]:
|
||||
setattr(FreeCAD.DraftWorkingPlane,
|
||||
prop,
|
||||
self._copy_value(getattr(self, prop)))
|
||||
FreeCAD.DraftWorkingPlane.weak = self.auto
|
||||
|
||||
if hist_add is True and self.auto is False:
|
||||
self._update_history()
|
||||
self._update_grid()
|
||||
self._update_gui()
|
||||
|
||||
def _update_history(self):
|
||||
if not self._history:
|
||||
self._history = {"idx": 0,
|
||||
"data": [self.get_parameters()]}
|
||||
return
|
||||
|
||||
max_len = 10 # Max. length of data.
|
||||
idx = self._history["idx"]
|
||||
self._history["data"] = self._history["data"][(idx - (max_len - 2)):(idx + 1)]
|
||||
self._history["data"].append(self.get_parameters())
|
||||
self._history["idx"] = len(self._history["data"]) - 1
|
||||
|
||||
def _update_grid(self):
|
||||
if FreeCAD.GuiUp:
|
||||
if hasattr(FreeCADGui, "Snapper"):
|
||||
FreeCADGui.Snapper.setGrid()
|
||||
FreeCADGui.Snapper.restack() # Required??
|
||||
|
||||
def _update_gui(self):
|
||||
if FreeCAD.GuiUp:
|
||||
if hasattr(FreeCADGui, "draftToolBar"):
|
||||
from PySide import QtGui
|
||||
button = FreeCADGui.draftToolBar.wplabel
|
||||
button.setIcon(QtGui.QIcon(self.icon))
|
||||
button.setText(self.label)
|
||||
button.setToolTip(self.tip)
|
||||
|
||||
|
||||
def get_working_plane(update=True):
|
||||
|
||||
if not hasattr(FreeCAD, "draft_working_planes"):
|
||||
FreeCAD.draft_working_planes = [[], []]
|
||||
|
||||
view = gui_utils.get_3d_view()
|
||||
|
||||
if view is not None and view in FreeCAD.draft_working_planes[0]:
|
||||
i = FreeCAD.draft_working_planes[0].index(view)
|
||||
wp = FreeCAD.draft_working_planes[1][i]
|
||||
if update is False:
|
||||
return wp
|
||||
wp.auto_align()
|
||||
wp._update_all(hist_add=False)
|
||||
return wp
|
||||
elif update is False:
|
||||
return None
|
||||
|
||||
wp = PlaneGui()
|
||||
wp._view = view
|
||||
wp.set_to_default()
|
||||
|
||||
if view is not None:
|
||||
FreeCAD.draft_working_planes[0].append(view)
|
||||
FreeCAD.draft_working_planes[1].append(wp)
|
||||
|
||||
return wp
|
||||
|
||||
|
||||
# Compatibility function (V0.22, 2023):
|
||||
def getPlacementFromPoints(points):
|
||||
"""Return a placement from a list of 3 or 4 points. The 4th point is no longer used.
|
||||
|
||||
Reference in New Issue
Block a user