Draft: move DraftTools utilities to another module
These small functions and utilities are placed in another module, so they can be imported and called by the individual Gui Command classes as necessary. When these Gui Command classes are moved to their respective modules, they will be able to import this auxiliary module as well.
This commit is contained in:
@@ -86,6 +86,7 @@ SET(Draft_view_providers
|
||||
SET(Draft_GUI_tools
|
||||
draftguitools/__init__.py
|
||||
draftguitools/gui_base.py
|
||||
draftguitools/gui_tool_utils.py
|
||||
draftguitools/gui_circulararray.py
|
||||
draftguitools/gui_orthoarray.py
|
||||
draftguitools/gui_polararray.py
|
||||
|
||||
@@ -114,144 +114,29 @@ elif defaultWP == 3: plane.alignToPointAndAxis(Vector(0,0,0), Vector(1,0,0), 0)
|
||||
# last snapped objects, for quick intersection calculation
|
||||
lastObj = [0,0]
|
||||
|
||||
# set modifier keys
|
||||
MODS = ["shift","ctrl","alt"]
|
||||
MODCONSTRAIN = MODS[Draft.getParam("modconstrain",0)]
|
||||
MODSNAP = MODS[Draft.getParam("modsnap",1)]
|
||||
MODALT = MODS[Draft.getParam("modalt",2)]
|
||||
# Set modifier keys
|
||||
from draftguitools.gui_tool_utils import MODCONSTRAIN
|
||||
from draftguitools.gui_tool_utils import MODSNAP
|
||||
from draftguitools.gui_tool_utils import MODALT
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# General functions
|
||||
# ---------------------------------------------------------------------------
|
||||
def formatUnit(exp,unit="mm"):
|
||||
'''returns a formatting string to set a number to the correct unit'''
|
||||
return FreeCAD.Units.Quantity(exp,FreeCAD.Units.Length).UserString
|
||||
from draftguitools.gui_tool_utils import formatUnit
|
||||
|
||||
def selectObject(arg):
|
||||
'''this is a scene even handler, to be called from the Draft tools
|
||||
when they need to select an object'''
|
||||
if (arg["Type"] == "SoKeyboardEvent"):
|
||||
if (arg["Key"] == "ESCAPE"):
|
||||
FreeCAD.activeDraftCommand.finish()
|
||||
# TODO : this part raises a coin3D warning about scene traversal, to be fixed.
|
||||
elif (arg["Type"] == "SoMouseButtonEvent"):
|
||||
if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
|
||||
cursor = arg["Position"]
|
||||
snapped = Draft.get3DView().getObjectInfo((cursor[0],cursor[1]))
|
||||
if snapped:
|
||||
obj = FreeCAD.ActiveDocument.getObject(snapped['Object'])
|
||||
FreeCADGui.Selection.addSelection(obj)
|
||||
FreeCAD.activeDraftCommand.component=snapped['Component']
|
||||
FreeCAD.activeDraftCommand.proceed()
|
||||
from draftguitools.gui_tool_utils import selectObject
|
||||
|
||||
def getPoint(target,args,mobile=False,sym=False,workingplane=True,noTracker=False):
|
||||
"""Function used by the Draft Tools.
|
||||
returns a constrained 3d point and its original point.
|
||||
if mobile=True, the constraining occurs from the location of
|
||||
mouse cursor when Shift is pressed, otherwise from last entered
|
||||
point. If sym=True, x and y values stay always equal. If workingplane=False,
|
||||
the point won't be projected on the Working Plane. if noTracker is True, the
|
||||
tracking line will not be displayed
|
||||
"""
|
||||
from draftguitools.gui_tool_utils import getPoint
|
||||
|
||||
ui = FreeCADGui.draftToolBar
|
||||
from draftguitools.gui_tool_utils import getSupport
|
||||
|
||||
if target.node:
|
||||
last = target.node[-1]
|
||||
else:
|
||||
last = None
|
||||
from draftguitools.gui_tool_utils import setWorkingPlaneToObjectUnderCursor
|
||||
|
||||
amod = hasMod(args, MODSNAP)
|
||||
cmod = hasMod(args, MODCONSTRAIN)
|
||||
point = None
|
||||
from draftguitools.gui_tool_utils import setWorkingPlaneToSelectedObject
|
||||
|
||||
if hasattr(FreeCADGui, "Snapper"):
|
||||
point = FreeCADGui.Snapper.snap(args["Position"],lastpoint=last,active=amod,constrain=cmod,noTracker=noTracker)
|
||||
info = FreeCADGui.Snapper.snapInfo
|
||||
mask = FreeCADGui.Snapper.affinity
|
||||
if not point:
|
||||
p = FreeCADGui.ActiveDocument.ActiveView.getCursorPos()
|
||||
point = FreeCADGui.ActiveDocument.ActiveView.getPoint(p)
|
||||
info = FreeCADGui.ActiveDocument.ActiveView.getObjectInfo(p)
|
||||
mask = None
|
||||
from draftguitools.gui_tool_utils import hasMod
|
||||
|
||||
ctrlPoint = Vector(point)
|
||||
if target.node:
|
||||
if target.featureName == "Rectangle":
|
||||
ui.displayPoint(point, target.node[0], plane=plane, mask=mask)
|
||||
else:
|
||||
ui.displayPoint(point, target.node[-1], plane=plane, mask=mask)
|
||||
else:
|
||||
ui.displayPoint(point, plane=plane, mask=mask)
|
||||
return point,ctrlPoint,info
|
||||
|
||||
def getSupport(mouseEvent=None):
|
||||
"""returns the supporting object and sets the working plane"""
|
||||
plane.save()
|
||||
if mouseEvent:
|
||||
return setWorkingPlaneToObjectUnderCursor(mouseEvent)
|
||||
return setWorkingPlaneToSelectedObject()
|
||||
|
||||
def setWorkingPlaneToObjectUnderCursor(mouseEvent):
|
||||
objectUnderCursor = Draft.get3DView().getObjectInfo((
|
||||
mouseEvent["Position"][0],
|
||||
mouseEvent["Position"][1]))
|
||||
|
||||
if not objectUnderCursor:
|
||||
return None
|
||||
|
||||
try:
|
||||
componentUnderCursor = getattr(
|
||||
FreeCAD.ActiveDocument.getObject(
|
||||
objectUnderCursor['Object']
|
||||
).Shape,
|
||||
objectUnderCursor["Component"])
|
||||
|
||||
if not plane.weak:
|
||||
return None
|
||||
|
||||
if "Face" in objectUnderCursor["Component"]:
|
||||
plane.alignToFace(componentUnderCursor)
|
||||
else:
|
||||
plane.alignToCurve(componentUnderCursor)
|
||||
plane.weak = True
|
||||
return objectUnderCursor
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def setWorkingPlaneToSelectedObject():
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if len(sel) != 1:
|
||||
return None
|
||||
sel = sel[0]
|
||||
if sel.HasSubObjects \
|
||||
and len(sel.SubElementNames) == 1 \
|
||||
and "Face" in sel.SubElementNames[0]:
|
||||
if plane.weak:
|
||||
plane.alignToFace(sel.SubObjects[0])
|
||||
plane.weak = True
|
||||
return sel.Object
|
||||
return None
|
||||
|
||||
def hasMod(args,mod):
|
||||
"""checks if args has a specific modifier"""
|
||||
if mod == "shift":
|
||||
return args["ShiftDown"]
|
||||
elif mod == "ctrl":
|
||||
return args["CtrlDown"]
|
||||
elif mod == "alt":
|
||||
return args["AltDown"]
|
||||
|
||||
def setMod(args,mod,state):
|
||||
"""sets a specific modifier state in args"""
|
||||
if mod == "shift":
|
||||
args["ShiftDown"] = state
|
||||
elif mod == "ctrl":
|
||||
args["CtrlDown"] = state
|
||||
elif mod == "alt":
|
||||
args["AltDown"] = state
|
||||
from draftguitools.gui_tool_utils import setMod
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -367,12 +252,8 @@ class DraftTool:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Geometry constructors
|
||||
# ---------------------------------------------------------------------------
|
||||
def redraw3DView():
|
||||
"""redraw3DView(): forces a redraw of 3d view."""
|
||||
try:
|
||||
FreeCADGui.ActiveDocument.ActiveView.redraw()
|
||||
except AttributeError as err:
|
||||
pass
|
||||
from draftguitools.gui_tool_utils import redraw3DView
|
||||
|
||||
|
||||
class Creator(DraftTool):
|
||||
"""A generic Draft Creator Tool used by creation tools such as line or arc"""
|
||||
|
||||
389
src/Mod/Draft/draftguitools/gui_tool_utils.py
Normal file
389
src/Mod/Draft/draftguitools/gui_tool_utils.py
Normal file
@@ -0,0 +1,389 @@
|
||||
# ***************************************************************************
|
||||
# * (c) 2009 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * (c) 2010 Ken Cline <cline@frii.com> *
|
||||
# * (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 utility functions for Draft Gui Commands.
|
||||
|
||||
These functions are used by different command classes in the `DraftTools`
|
||||
module. We assume that the graphical interface was already loaded
|
||||
as they operate on selections and graphical properties.
|
||||
"""
|
||||
## @package gui_tool_utils
|
||||
# \ingroup DRAFT
|
||||
# \brief Provides the utility functions for Draft Gui Commands.
|
||||
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
import draftutils.gui_utils as gui_utils
|
||||
import draftutils.utils as utils
|
||||
from draftutils.messages import _wrn
|
||||
|
||||
# Set modifier keys from the parameter database
|
||||
MODS = ["shift", "ctrl", "alt"]
|
||||
MODCONSTRAIN = MODS[utils.get_param("modconstrain", 0)]
|
||||
MODSNAP = MODS[utils.get_param("modsnap", 1)]
|
||||
MODALT = MODS[utils.get_param("modalt", 2)]
|
||||
|
||||
|
||||
def format_unit(exp, unit="mm"):
|
||||
"""Return a formatting string to set a number to the correct unit."""
|
||||
return App.Units.Quantity(exp, App.Units.Length).UserString
|
||||
|
||||
|
||||
formatUnit = format_unit
|
||||
|
||||
|
||||
def select_object(arg):
|
||||
"""Handle the selection of objects depending on buttons pressed.
|
||||
|
||||
This is a scene event handler, to be called from the Draft tools
|
||||
when they need to select an object.
|
||||
::
|
||||
self.call = self.view.addEventCallback("SoEvent", select_object)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg: Coin event
|
||||
The Coin event received from the 3D view.
|
||||
|
||||
If it is of type Keyboard and the `ESCAPE` key, it runs the `finish`
|
||||
method of the active command.
|
||||
|
||||
If it is of type Mouse button and `BUTTON1` press,
|
||||
it captures the position of the cursor (x, y)
|
||||
and the object below that cursor to add it to the active selection;
|
||||
then it runs the `proceed` method of the active command
|
||||
to continue with the command's logic.
|
||||
"""
|
||||
if arg["Type"] == "SoKeyboardEvent":
|
||||
if arg["Key"] == "ESCAPE":
|
||||
App.activeDraftCommand.finish()
|
||||
# TODO: this part raises a coin3D warning about scene traversal.
|
||||
# It needs to be fixed.
|
||||
elif arg["Type"] == "SoMouseButtonEvent":
|
||||
if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1":
|
||||
cursor = arg["Position"]
|
||||
snapped = gui_utils.get_3d_view().getObjectInfo((cursor[0],
|
||||
cursor[1]))
|
||||
if snapped:
|
||||
obj = App.ActiveDocument.getObject(snapped['Object'])
|
||||
Gui.Selection.addSelection(obj)
|
||||
App.activeDraftCommand.component = snapped['Component']
|
||||
App.activeDraftCommand.proceed()
|
||||
|
||||
|
||||
selectObject = select_object
|
||||
|
||||
|
||||
def has_mod(args, mod):
|
||||
"""Check if args has a specific modifier.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args: Coin event
|
||||
The Coin event received from the 3D view.
|
||||
|
||||
mod: str
|
||||
A string indicating the modifier, either `'shift'`, `'ctrl'`,
|
||||
or `'alt'`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
It returns `args["ShiftDown"]`, `args["CtrlDown"]`,
|
||||
or `args["AltDown"]`, depending on the passed value of `mod`.
|
||||
"""
|
||||
if mod == "shift":
|
||||
return args["ShiftDown"]
|
||||
elif mod == "ctrl":
|
||||
return args["CtrlDown"]
|
||||
elif mod == "alt":
|
||||
return args["AltDown"]
|
||||
|
||||
|
||||
hasMod = has_mod
|
||||
|
||||
|
||||
def set_mod(args, mod, state):
|
||||
"""Set a specific modifier state in args.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args: Coin event
|
||||
The Coin event received from the 3D view.
|
||||
|
||||
mod: str
|
||||
A string indicating the modifier, either `'shift'`, `'ctrl'`,
|
||||
or `'alt'`.
|
||||
|
||||
state: bool
|
||||
The boolean value of `state` is assigned to `args["ShiftDown"]`,
|
||||
`args["CtrlDown"]`, or `args["AltDown"]`
|
||||
depending on `mod`.
|
||||
"""
|
||||
if mod == "shift":
|
||||
args["ShiftDown"] = state
|
||||
elif mod == "ctrl":
|
||||
args["CtrlDown"] = state
|
||||
elif mod == "alt":
|
||||
args["AltDown"] = state
|
||||
|
||||
|
||||
setMod = set_mod
|
||||
|
||||
|
||||
def get_point(target, args,
|
||||
mobile=False, sym=False, workingplane=True, noTracker=False):
|
||||
"""Return a constrained 3D point and its original point.
|
||||
|
||||
It is used by the Draft tools.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
target: object (class)
|
||||
The target object with a `node` attribute. If this is present,
|
||||
return the last node, otherwise return `None`.
|
||||
|
||||
In the Draft tools, `target` is essentially the same class
|
||||
of the Gui command, that is, `self`. Therefore, this method
|
||||
probably makes more sense as a class method.
|
||||
|
||||
args: Coin event
|
||||
The Coin event received from the 3D view.
|
||||
|
||||
mobile: bool, optional
|
||||
It defaults to `False`.
|
||||
If it is `True` the constraining occurs from the location of
|
||||
the mouse cursor when `Shift` is pressed; otherwise from the last
|
||||
entered point.
|
||||
|
||||
sym: bool, optional
|
||||
It defaults to `False`.
|
||||
If it is `True`, the x and y values always stay equal.
|
||||
|
||||
workingplane: bool, optional
|
||||
It defaults to `True`.
|
||||
If it is `False`, the point won't be projected on the currently
|
||||
active working plane.
|
||||
|
||||
noTracker: bool, optional
|
||||
It defaults to `False`.
|
||||
If it is `True`, the tracking line will not be displayed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
CoinPoint, Base::Vector3, str
|
||||
It returns a tuple with some information.
|
||||
|
||||
The first is the Coin point returned by `Snapper.snap`
|
||||
or by the `ActiveView`; the second is that same point
|
||||
turned into an `App.Vector`,
|
||||
and the third is some information of the point
|
||||
returned by the `Snapper` or by the `ActiveView`.
|
||||
"""
|
||||
ui = Gui.draftToolBar
|
||||
|
||||
if target.node:
|
||||
last = target.node[-1]
|
||||
else:
|
||||
last = None
|
||||
|
||||
amod = hasMod(args, MODSNAP)
|
||||
cmod = hasMod(args, MODCONSTRAIN)
|
||||
point = None
|
||||
|
||||
if hasattr(Gui, "Snapper"):
|
||||
point = Gui.Snapper.snap(args["Position"],
|
||||
lastpoint=last,
|
||||
active=amod,
|
||||
constrain=cmod,
|
||||
noTracker=noTracker)
|
||||
info = Gui.Snapper.snapInfo
|
||||
mask = Gui.Snapper.affinity
|
||||
if not point:
|
||||
p = Gui.ActiveDocument.ActiveView.getCursorPos()
|
||||
point = Gui.ActiveDocument.ActiveView.getPoint(p)
|
||||
info = Gui.ActiveDocument.ActiveView.getObjectInfo(p)
|
||||
mask = None
|
||||
|
||||
ctrlPoint = App.Vector(point)
|
||||
if target.node:
|
||||
if target.featureName == "Rectangle":
|
||||
ui.displayPoint(point, target.node[0],
|
||||
plane=App.DraftWorkingPlane, mask=mask)
|
||||
else:
|
||||
ui.displayPoint(point, target.node[-1],
|
||||
plane=App.DraftWorkingPlane, mask=mask)
|
||||
else:
|
||||
ui.displayPoint(point, plane=App.DraftWorkingPlane, mask=mask)
|
||||
return point, ctrlPoint, info
|
||||
|
||||
|
||||
getPoint = get_point
|
||||
|
||||
|
||||
def set_working_plane_to_object_under_cursor(mouseEvent):
|
||||
"""Set the working plane to the object under the cursor.
|
||||
|
||||
It tests for an object under the cursor.
|
||||
If it is found, it checks whether a `'face'` or `'curve'` component
|
||||
is selected in the object's `Shape`.
|
||||
Then it tries to align the working plane to that face or curve.
|
||||
|
||||
The working plane is only aligned to the face if
|
||||
the working plane is not `'weak'`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mouseEvent: Coin event
|
||||
Coin event with the mouse, that is, a click.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
If no object was found in the 3D view under the cursor.
|
||||
Or if the working plane is not `weak`.
|
||||
Or if there was an exception with aligning the working plane
|
||||
to the component under the cursor.
|
||||
|
||||
Coin info
|
||||
The `getObjectInfo` of the object under the cursor.
|
||||
"""
|
||||
objectUnderCursor = gui_utils.get_3d_view().getObjectInfo((
|
||||
mouseEvent["Position"][0],
|
||||
mouseEvent["Position"][1]))
|
||||
|
||||
if not objectUnderCursor:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Get the component "face" or "curve" under the "Shape"
|
||||
# of the selected object
|
||||
componentUnderCursor = getattr(
|
||||
App.ActiveDocument.getObject(objectUnderCursor['Object']).Shape,
|
||||
objectUnderCursor["Component"])
|
||||
|
||||
if not App.DraftWorkingPlane.weak:
|
||||
return None
|
||||
|
||||
if "Face" in objectUnderCursor["Component"]:
|
||||
App.DraftWorkingPlane.alignToFace(componentUnderCursor)
|
||||
else:
|
||||
App.DraftWorkingPlane.alignToCurve(componentUnderCursor)
|
||||
App.DraftWorkingPlane.weak = True
|
||||
return objectUnderCursor
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
setWorkingPlaneToObjectUnderCursor = set_working_plane_to_object_under_cursor
|
||||
|
||||
|
||||
def set_working_plane_to_selected_object():
|
||||
"""Set the working plane to the selected object's face.
|
||||
|
||||
The working plane is only aligned to the face if
|
||||
the working plane is `'weak'`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
If more than one object was selected.
|
||||
Or if the selected object has many subelements.
|
||||
Or if the single subelement is not a `'Face'`.
|
||||
|
||||
App::DocumentObject
|
||||
The single object that contains a single selected face.
|
||||
"""
|
||||
sel = Gui.Selection.getSelectionEx()
|
||||
if len(sel) != 1:
|
||||
return None
|
||||
sel = sel[0]
|
||||
if (sel.HasSubObjects
|
||||
and len(sel.SubElementNames) == 1
|
||||
and "Face" in sel.SubElementNames[0]):
|
||||
if App.DraftWorkingPlane.weak:
|
||||
App.DraftWorkingPlane.alignToFace(sel.SubObjects[0])
|
||||
App.DraftWorkingPlane.weak = True
|
||||
return sel.Object
|
||||
return None
|
||||
|
||||
|
||||
setWorkingPlaneToSelectedObject = set_working_plane_to_selected_object
|
||||
|
||||
|
||||
def get_support(mouseEvent=None):
|
||||
"""Return the supporting object and set the working plane.
|
||||
|
||||
It saves the current working plane, then sets it to the selected object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mouseEvent: Coin event, optional
|
||||
It defaults to `None`.
|
||||
Coin event with the mouse, that is, a click.
|
||||
|
||||
If there is a mouse event it calls
|
||||
`set_working_plane_to_object_under_cursor`.
|
||||
Otherwise, it calls `set_working_plane_to_selected_object`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
If there was a mouse event, but there was nothing under the cursor.
|
||||
Or if the working plane is not `weak`.
|
||||
Or if there was an exception with aligning the working plane
|
||||
to the component under the cursor.
|
||||
Or if more than one object was selected.
|
||||
Or if the selected object has many subelements.
|
||||
Or if the single subelement is not a `'Face'`.
|
||||
|
||||
Coin info
|
||||
If there was a mouse event, the `getObjectInfo`
|
||||
of the object under the cursor.
|
||||
|
||||
App::DocumentObject
|
||||
If there was no mouse event, the single selected object
|
||||
that contains the single selected face that was used
|
||||
to align the working plane.
|
||||
"""
|
||||
App.DraftWorkingPlane.save()
|
||||
if mouseEvent:
|
||||
return setWorkingPlaneToObjectUnderCursor(mouseEvent)
|
||||
return setWorkingPlaneToSelectedObject()
|
||||
|
||||
|
||||
getSupport = get_support
|
||||
|
||||
|
||||
def redraw_3d_view():
|
||||
"""Force a redraw of 3D view or do nothing if it fails."""
|
||||
try:
|
||||
Gui.ActiveDocument.ActiveView.redraw()
|
||||
except AttributeError as err:
|
||||
_wrn(err)
|
||||
|
||||
|
||||
redraw3DView = redraw_3d_view
|
||||
Reference in New Issue
Block a user