995 lines
42 KiB
Python
995 lines
42 KiB
Python
# -*- coding: utf8 -*-
|
|
# ***************************************************************************
|
|
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
|
|
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
|
|
# * *
|
|
# * 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 *
|
|
# * *
|
|
# ***************************************************************************
|
|
"""Provide GUI commands of the Draft Workbench.
|
|
|
|
This module loads all graphical commands of the Draft Workbench,
|
|
that is, those actions that can be called from menus and buttons.
|
|
This module must be imported only when the graphical user interface
|
|
is available, for example, during the workbench definition in `IntiGui.py`.
|
|
"""
|
|
## @package DraftTools
|
|
# \ingroup DRAFT
|
|
# \brief Provide GUI commands of the Draft workbench.
|
|
#
|
|
# This module contains all the graphical commands of the Draft workbench,
|
|
# that is, those actions that can be called from menus and buttons.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Generic stuff
|
|
# ---------------------------------------------------------------------------
|
|
import math
|
|
import sys
|
|
from PySide import QtCore, QtGui
|
|
from pivy import coin
|
|
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
from FreeCAD import Vector
|
|
|
|
import Draft
|
|
import Draft_rc
|
|
import DraftGui # Initializes the DraftToolBar class
|
|
import DraftVecUtils
|
|
import WorkingPlane
|
|
from draftutils.todo import ToDo
|
|
from draftutils.translate import translate
|
|
import draftguitools.gui_snapper as gui_snapper
|
|
import draftguitools.gui_trackers as trackers
|
|
|
|
# The module is used to prevent complaints from code checkers (flake8)
|
|
True if Draft_rc.__name__ else False
|
|
True if DraftGui.__name__ else False
|
|
|
|
__title__ = "FreeCAD Draft Workbench GUI Tools"
|
|
__author__ = ("Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline, "
|
|
"Dmitry Chigrin")
|
|
__url__ = "https://www.freecadweb.org"
|
|
|
|
if not hasattr(FreeCADGui, "Snapper"):
|
|
FreeCADGui.Snapper = gui_snapper.Snapper()
|
|
|
|
if not hasattr(FreeCAD, "DraftWorkingPlane"):
|
|
FreeCAD.DraftWorkingPlane = WorkingPlane.plane()
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Commands that have been migrated to their own modules
|
|
# ---------------------------------------------------------------------------
|
|
import draftguitools.gui_edit
|
|
import draftguitools.gui_selectplane
|
|
import draftguitools.gui_planeproxy
|
|
from draftguitools.gui_lineops import FinishLine
|
|
from draftguitools.gui_lineops import CloseLine
|
|
from draftguitools.gui_lineops import UndoLine
|
|
from draftguitools.gui_togglemodes import ToggleConstructionMode
|
|
from draftguitools.gui_togglemodes import ToggleContinueMode
|
|
from draftguitools.gui_togglemodes import ToggleDisplayMode
|
|
from draftguitools.gui_groups import AddToGroup
|
|
from draftguitools.gui_groups import SelectGroup
|
|
from draftguitools.gui_groups import SetAutoGroup
|
|
from draftguitools.gui_groups import Draft_AddConstruction
|
|
from draftguitools.gui_grid import ToggleGrid
|
|
from draftguitools.gui_heal import Heal
|
|
from draftguitools.gui_dimension_ops import Draft_FlipDimension
|
|
from draftguitools.gui_lineslope import Draft_Slope
|
|
import draftguitools.gui_arrays
|
|
import draftguitools.gui_annotationstyleeditor
|
|
# import DraftFillet
|
|
import drafttaskpanels.task_scale as task_scale
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Preflight stuff
|
|
# ---------------------------------------------------------------------------
|
|
# update the translation engine
|
|
FreeCADGui.updateLocale()
|
|
|
|
# sets the default working plane
|
|
plane = WorkingPlane.plane()
|
|
FreeCAD.DraftWorkingPlane = plane
|
|
defaultWP = Draft.getParam("defaultWP",1)
|
|
if defaultWP == 1: plane.alignToPointAndAxis(Vector(0,0,0), Vector(0,0,1), 0)
|
|
elif defaultWP == 2: plane.alignToPointAndAxis(Vector(0,0,0), Vector(0,1,0), 0)
|
|
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
|
|
from draftguitools.gui_tool_utils import MODCONSTRAIN
|
|
from draftguitools.gui_tool_utils import MODSNAP
|
|
from draftguitools.gui_tool_utils import MODALT
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# General functions
|
|
# ---------------------------------------------------------------------------
|
|
from draftguitools.gui_tool_utils import formatUnit
|
|
|
|
from draftguitools.gui_tool_utils import selectObject
|
|
|
|
from draftguitools.gui_tool_utils import getPoint
|
|
|
|
from draftguitools.gui_tool_utils import getSupport
|
|
|
|
from draftguitools.gui_tool_utils import setWorkingPlaneToObjectUnderCursor
|
|
|
|
from draftguitools.gui_tool_utils import setWorkingPlaneToSelectedObject
|
|
|
|
from draftguitools.gui_tool_utils import hasMod
|
|
|
|
from draftguitools.gui_tool_utils import setMod
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Base Class
|
|
# ---------------------------------------------------------------------------
|
|
from draftguitools.gui_base_original import DraftTool
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Geometry constructors
|
|
# ---------------------------------------------------------------------------
|
|
from draftguitools.gui_tool_utils import redraw3DView
|
|
|
|
from draftguitools.gui_base_original import Creator
|
|
|
|
from draftguitools.gui_lines import Line
|
|
from draftguitools.gui_lines import Wire
|
|
from draftguitools.gui_splines import BSpline
|
|
from draftguitools.gui_beziers import BezCurve
|
|
from draftguitools.gui_beziers import CubicBezCurve
|
|
from draftguitools.gui_beziers import BezierGroup
|
|
from draftguitools.gui_rectangles import Rectangle
|
|
from draftguitools.gui_arcs import Arc
|
|
from draftguitools.gui_arcs import Draft_Arc_3Points
|
|
from draftguitools.gui_circles import Circle
|
|
from draftguitools.gui_polygons import Polygon
|
|
from draftguitools.gui_ellipses import Ellipse
|
|
from draftguitools.gui_texts import Text
|
|
from draftguitools.gui_dimensions import Dimension
|
|
from draftguitools.gui_shapestrings import ShapeString
|
|
from draftguitools.gui_points import Point
|
|
from draftguitools.gui_facebinders import Draft_Facebinder
|
|
from draftguitools.gui_labels import Draft_Label
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Modifier functions
|
|
# ---------------------------------------------------------------------------
|
|
from draftguitools.gui_base_original import Modifier
|
|
|
|
from draftguitools.gui_subelements import SubelementHighlight
|
|
from draftguitools.gui_move import Move
|
|
from draftguitools.gui_styles import ApplyStyle
|
|
from draftguitools.gui_rotate import Rotate
|
|
from draftguitools.gui_offset import Offset
|
|
from draftguitools.gui_stretch import Stretch
|
|
from draftguitools.gui_join import Join
|
|
from draftguitools.gui_split import Split
|
|
from draftguitools.gui_upgrade import Upgrade
|
|
from draftguitools.gui_downgrade import Downgrade
|
|
from draftguitools.gui_trimex import Trimex
|
|
|
|
|
|
class Scale(Modifier):
|
|
'''The Draft_Scale FreeCAD command definition.
|
|
This tool scales the selected objects from a base point.'''
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_Scale',
|
|
'Accel' : "S, C",
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Scale", "Scale"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Scale", "Scales the selected objects from a base point. CTRL to snap, SHIFT to constrain, ALT to copy")}
|
|
|
|
def Activated(self):
|
|
self.name = translate("draft","Scale", utf8_decode=True)
|
|
Modifier.Activated(self,self.name)
|
|
if not self.ui:
|
|
return
|
|
self.ghosts = []
|
|
self.get_object_selection()
|
|
|
|
def get_object_selection(self):
|
|
if FreeCADGui.Selection.getSelection():
|
|
return self.proceed()
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to scale")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent", self.call)
|
|
self.selected_objects = FreeCADGui.Selection.getSelection()
|
|
self.selected_objects = Draft.getGroupContents(self.selected_objects)
|
|
self.selected_subelements = FreeCADGui.Selection.getSelectionEx()
|
|
self.refs = []
|
|
self.ui.pointUi(self.name)
|
|
self.ui.modUi()
|
|
self.ui.xValue.setFocus()
|
|
self.ui.xValue.selectAll()
|
|
self.pickmode = False
|
|
self.task = None
|
|
self.call = self.view.addEventCallback("SoEvent", self.action)
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Pick base point")+"\n")
|
|
|
|
def set_ghosts(self):
|
|
if self.ui.isSubelementMode.isChecked():
|
|
return self.set_subelement_ghosts()
|
|
self.ghosts = [trackers.ghostTracker(self.selected_objects)]
|
|
|
|
def set_subelement_ghosts(self):
|
|
import Part
|
|
for object in self.selected_subelements:
|
|
for subelement in object.SubObjects:
|
|
if isinstance(subelement, Part.Vertex) \
|
|
or isinstance(subelement, Part.Edge):
|
|
self.ghosts.append(trackers.ghostTracker(subelement))
|
|
|
|
def pickRef(self):
|
|
self.pickmode = True
|
|
if self.node:
|
|
self.node = self.node[:1] # remove previous picks
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Pick reference distance from base point")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",self.action)
|
|
|
|
def action(self,arg):
|
|
"""scene event handler"""
|
|
if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
|
|
self.finish()
|
|
elif arg["Type"] == "SoLocation2Event":
|
|
self.handle_mouse_move_event(arg)
|
|
elif arg["Type"] == "SoMouseButtonEvent" \
|
|
and arg["State"] == "DOWN" \
|
|
and (arg["Button"] == "BUTTON1") \
|
|
and self.point:
|
|
self.handle_mouse_click_event()
|
|
|
|
def handle_mouse_move_event(self, arg):
|
|
for ghost in self.ghosts:
|
|
ghost.off()
|
|
self.point, ctrlPoint, info = getPoint(self, arg, sym=True)
|
|
|
|
def handle_mouse_click_event(self):
|
|
if not self.ghosts:
|
|
self.set_ghosts()
|
|
self.numericInput(self.point.x, self.point.y, self.point.z)
|
|
|
|
def scale(self):
|
|
self.delta = Vector(self.task.xValue.value(), self.task.yValue.value(), self.task.zValue.value())
|
|
self.center = self.node[0]
|
|
if self.task.isSubelementMode.isChecked():
|
|
self.scale_subelements()
|
|
elif self.task.isClone.isChecked():
|
|
self.scale_with_clone()
|
|
else:
|
|
self.scale_object()
|
|
self.finish()
|
|
|
|
def scale_subelements(self):
|
|
try:
|
|
if self.task.isCopy.isChecked():
|
|
self.commit(translate("draft", "Copy"), self.build_copy_subelements_command())
|
|
else:
|
|
self.commit(translate("draft", "Scale"), self.build_scale_subelements_command())
|
|
except:
|
|
FreeCAD.Console.PrintError(translate("draft", "Some subelements could not be scaled."))
|
|
|
|
def scale_with_clone(self):
|
|
if self.task.relative.isChecked():
|
|
self.delta = FreeCAD.DraftWorkingPlane.getGlobalCoords(self.delta)
|
|
objects = '[' + ','.join(['FreeCAD.ActiveDocument.' + object.Name for object in self.selected_objects]) + ']'
|
|
FreeCADGui.addModule("Draft")
|
|
self.commit(translate("draft","Copy") if self.task.isCopy.isChecked() else translate("draft","Scale"),
|
|
['clone = Draft.clone('+objects+',forcedraft=True)',
|
|
'clone.Scale = '+DraftVecUtils.toString(self.delta),
|
|
'FreeCAD.ActiveDocument.recompute()'])
|
|
|
|
def build_copy_subelements_command(self):
|
|
import Part
|
|
command = []
|
|
arguments = []
|
|
for object in self.selected_subelements:
|
|
for index, subelement in enumerate(object.SubObjects):
|
|
if not isinstance(subelement, Part.Edge):
|
|
continue
|
|
arguments.append('[FreeCAD.ActiveDocument.{}, {}, {}, {}]'.format(
|
|
object.ObjectName,
|
|
int(object.SubElementNames[index][len("Edge"):])-1,
|
|
DraftVecUtils.toString(self.delta),
|
|
DraftVecUtils.toString(self.center)))
|
|
command.append('Draft.copyScaledEdges([{}])'.format(','.join(arguments)))
|
|
command.append('FreeCAD.ActiveDocument.recompute()')
|
|
return command
|
|
|
|
def build_scale_subelements_command(self):
|
|
import Part
|
|
command = []
|
|
for object in self.selected_subelements:
|
|
for index, subelement in enumerate(object.SubObjects):
|
|
if isinstance(subelement, Part.Vertex):
|
|
command.append('Draft.scaleVertex(FreeCAD.ActiveDocument.{}, {}, {}, {})'.format(
|
|
object.ObjectName,
|
|
int(object.SubElementNames[index][len("Vertex"):])-1,
|
|
DraftVecUtils.toString(self.delta),
|
|
DraftVecUtils.toString(self.center)))
|
|
elif isinstance(subelement, Part.Edge):
|
|
command.append('Draft.scaleEdge(FreeCAD.ActiveDocument.{}, {}, {}, {})'.format(
|
|
object.ObjectName,
|
|
int(object.SubElementNames[index][len("Edge"):])-1,
|
|
DraftVecUtils.toString(self.delta),
|
|
DraftVecUtils.toString(self.center)))
|
|
command.append('FreeCAD.ActiveDocument.recompute()')
|
|
return command
|
|
|
|
def is_scalable(self,obj):
|
|
t = Draft.getType(obj)
|
|
if t in ["Rectangle","Wire","Annotation","BSpline"]:
|
|
# TODO - support more types in Draft.scale
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def scale_object(self):
|
|
if self.task.relative.isChecked():
|
|
self.delta = FreeCAD.DraftWorkingPlane.getGlobalCoords(self.delta)
|
|
goods = []
|
|
bads = []
|
|
for obj in self.selected_objects:
|
|
if self.is_scalable(obj):
|
|
goods.append(obj)
|
|
else:
|
|
bads.append(obj)
|
|
if bads:
|
|
if len(bads) == 1:
|
|
m = translate("draft","Unable to scale object")+": "+bads[0].Label
|
|
else:
|
|
m = translate("draft","Unable to scale objects")+": "+",".join([o.Label for o in bads])
|
|
m += " - "+translate("draft","This object type cannot be scaled directly. Please use the clone method.")+"\n"
|
|
FreeCAD.Console.PrintError(m)
|
|
if goods:
|
|
objects = '[' + ','.join(['FreeCAD.ActiveDocument.' + obj.Name for obj in goods]) + ']'
|
|
FreeCADGui.addModule("Draft")
|
|
self.commit(translate("draft","Copy" if self.task.isCopy.isChecked() else "Scale"),
|
|
['Draft.scale('+objects+',scale='+DraftVecUtils.toString(self.delta)+',center='+DraftVecUtils.toString(self.center)+',copy='+str(self.task.isCopy.isChecked())+')',
|
|
'FreeCAD.ActiveDocument.recompute()'])
|
|
|
|
def scaleGhost(self,x,y,z,rel):
|
|
delta = Vector(x,y,z)
|
|
if rel:
|
|
delta = FreeCAD.DraftWorkingPlane.getGlobalCoords(delta)
|
|
for ghost in self.ghosts:
|
|
ghost.scale(delta)
|
|
# calculate a correction factor depending on the scaling center
|
|
corr = Vector(self.node[0].x,self.node[0].y,self.node[0].z)
|
|
corr.scale(delta.x,delta.y,delta.z)
|
|
corr = (corr.sub(self.node[0])).negative()
|
|
for ghost in self.ghosts:
|
|
ghost.move(corr)
|
|
ghost.on()
|
|
|
|
def numericInput(self,numx,numy,numz):
|
|
"""this function gets called by the toolbar when a valid base point has been entered"""
|
|
self.point = Vector(numx,numy,numz)
|
|
self.node.append(self.point)
|
|
if not self.pickmode:
|
|
if not self.ghosts:
|
|
self.set_ghosts()
|
|
self.ui.offUi()
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
self.task = task_scale.ScaleTaskPanel()
|
|
self.task.sourceCmd = self
|
|
ToDo.delay(FreeCADGui.Control.showDialog, self.task)
|
|
ToDo.delay(self.task.xValue.selectAll, None)
|
|
ToDo.delay(self.task.xValue.setFocus, None)
|
|
for ghost in self.ghosts:
|
|
ghost.on()
|
|
elif len(self.node) == 2:
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Pick new distance from base point")+"\n")
|
|
elif len(self.node) == 3:
|
|
if hasattr(FreeCADGui,"Snapper"):
|
|
FreeCADGui.Snapper.off()
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
d1 = (self.node[1].sub(self.node[0])).Length
|
|
d2 = (self.node[2].sub(self.node[0])).Length
|
|
#print d2,"/",d1,"=",d2/d1
|
|
if hasattr(self,"task"):
|
|
if self.task:
|
|
self.task.lock.setChecked(True)
|
|
self.task.setValue(d2/d1)
|
|
|
|
def finish(self,closed=False,cont=False):
|
|
Modifier.finish(self)
|
|
for ghost in self.ghosts:
|
|
ghost.finalize()
|
|
|
|
|
|
class Drawing(Modifier):
|
|
"""The Draft Drawing command definition"""
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_Drawing',
|
|
'Accel' : "D, D",
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Drawing", "Drawing"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Drawing", "Puts the selected objects on a Drawing sheet")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self,"Drawing")
|
|
if not FreeCADGui.Selection.getSelection():
|
|
self.ghost = None
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to project")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
sel = FreeCADGui.Selection.getSelection()
|
|
if not sel:
|
|
self.page = self.createDefaultPage()
|
|
else:
|
|
self.page = None
|
|
# if the user selected a page, put the objects on that page
|
|
for obj in sel:
|
|
if obj.isDerivedFrom("Drawing::FeaturePage"):
|
|
self.page = obj
|
|
break
|
|
if not self.page:
|
|
# no page selected, default to the first page in the document
|
|
for obj in self.doc.Objects:
|
|
if obj.isDerivedFrom("Drawing::FeaturePage"):
|
|
self.page = obj
|
|
break
|
|
if not self.page:
|
|
# no page in the document, create a default page.
|
|
self.page = self.createDefaultPage()
|
|
otherProjection = None
|
|
# if an existing projection is selected, reuse its projection properties
|
|
for obj in sel:
|
|
if obj.isDerivedFrom("Drawing::FeatureView"):
|
|
otherProjection = obj
|
|
break
|
|
sel.reverse()
|
|
for obj in sel:
|
|
if ( obj.ViewObject.isVisible() and not obj.isDerivedFrom("Drawing::FeatureView")
|
|
and not obj.isDerivedFrom("Drawing::FeaturePage") ):
|
|
name = 'View'+obj.Name
|
|
# no reason to remove the old one...
|
|
#oldobj = self.page.getObject(name)
|
|
#if oldobj:
|
|
# self.doc.removeObject(oldobj.Name)
|
|
Draft.makeDrawingView(obj,self.page,otherProjection=otherProjection)
|
|
self.doc.recompute()
|
|
|
|
def createDefaultPage(self):
|
|
"""created a default page"""
|
|
template = Draft.getParam("template",FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg')
|
|
page = self.doc.addObject('Drawing::FeaturePage','Page')
|
|
page.ViewObject.HintOffsetX = 200
|
|
page.ViewObject.HintOffsetY = 100
|
|
page.ViewObject.HintScale = 20
|
|
page.Template = template
|
|
self.doc.recompute()
|
|
return page
|
|
|
|
|
|
class WireToBSpline(Modifier):
|
|
"""The Draft_Wire2BSpline FreeCAD command definition"""
|
|
|
|
def __init__(self):
|
|
self.running = False
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_WireToBSpline',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_WireToBSpline", "Wire to B-spline"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_WireToBSpline", "Converts between Wire and B-spline")}
|
|
|
|
def IsActive(self):
|
|
if FreeCADGui.Selection.getSelection():
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def Activated(self):
|
|
if self.running:
|
|
self.finish()
|
|
else:
|
|
selection = FreeCADGui.Selection.getSelection()
|
|
if selection:
|
|
if (Draft.getType(selection[0]) in ['Wire','BSpline']):
|
|
Modifier.Activated(self,"Convert Curve Type")
|
|
if self.doc:
|
|
self.obj = FreeCADGui.Selection.getSelection()
|
|
if self.obj:
|
|
self.obj = self.obj[0]
|
|
self.pl = None
|
|
if "Placement" in self.obj.PropertiesList:
|
|
self.pl = self.obj.Placement
|
|
self.Points = self.obj.Points
|
|
self.closed = self.obj.Closed
|
|
n = None
|
|
if (Draft.getType(self.obj) == 'Wire'):
|
|
n = Draft.makeBSpline(self.Points, self.closed, self.pl)
|
|
elif (Draft.getType(self.obj) == 'BSpline'):
|
|
self.bs2wire = True
|
|
n = Draft.makeWire(self.Points, self.closed, self.pl, None, None, self.bs2wire)
|
|
if n:
|
|
Draft.formatObject(n,self.obj)
|
|
self.doc.recompute()
|
|
else:
|
|
self.finish()
|
|
|
|
|
|
class Shape2DView(Modifier):
|
|
"""The Shape2DView FreeCAD command definition"""
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_2DShapeView',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Shape2DView", "Shape 2D view"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Shape2DView", "Creates Shape 2D views of selected objects")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self)
|
|
if not FreeCADGui.Selection.getSelection():
|
|
if self.ui:
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to project")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
faces = []
|
|
objs = []
|
|
vec = FreeCADGui.ActiveDocument.ActiveView.getViewDirection().negative()
|
|
sel = FreeCADGui.Selection.getSelectionEx()
|
|
for s in sel:
|
|
objs.append(s.Object)
|
|
for e in s.SubElementNames:
|
|
if "Face" in e:
|
|
faces.append(int(e[4:])-1)
|
|
#print(objs,faces)
|
|
commitlist = []
|
|
FreeCADGui.addModule("Draft")
|
|
if (len(objs) == 1) and faces:
|
|
commitlist.append("Draft.makeShape2DView(FreeCAD.ActiveDocument."+objs[0].Name+",FreeCAD.Vector"+str(tuple(vec))+",facenumbers="+str(faces)+")")
|
|
else:
|
|
for o in objs:
|
|
commitlist.append("Draft.makeShape2DView(FreeCAD.ActiveDocument."+o.Name+",FreeCAD.Vector"+str(tuple(vec))+")")
|
|
if commitlist:
|
|
commitlist.append("FreeCAD.ActiveDocument.recompute()")
|
|
self.commit(translate("draft","Create 2D view"),commitlist)
|
|
self.finish()
|
|
|
|
|
|
class Draft2Sketch(Modifier):
|
|
"""The Draft2Sketch FreeCAD command definition"""
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_Draft2Sketch',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Draft2Sketch", "Draft to Sketch"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Draft2Sketch", "Convert bidirectionally between Draft and Sketch objects")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self)
|
|
if not FreeCADGui.Selection.getSelection():
|
|
if self.ui:
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to convert")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
sel = FreeCADGui.Selection.getSelection()
|
|
allSketches = True
|
|
allDraft = True
|
|
FreeCADGui.addModule("Draft")
|
|
for obj in sel:
|
|
if obj.isDerivedFrom("Sketcher::SketchObject"):
|
|
allDraft = False
|
|
elif obj.isDerivedFrom("Part::Part2DObjectPython"):
|
|
allSketches = False
|
|
else:
|
|
allDraft = False
|
|
allSketches = False
|
|
if not sel:
|
|
return
|
|
elif allDraft:
|
|
lines = ["Draft.makeSketch(FreeCADGui.Selection.getSelection(),autoconstraints=True)"]
|
|
self.commit(translate("draft","Convert to Sketch"),
|
|
lines + ['FreeCAD.ActiveDocument.recompute()'])
|
|
elif allSketches:
|
|
lines = ["Draft.draftify(FreeCAD.ActiveDocument."+o.Name+",delete=False)" for o in sel]
|
|
self.commit(translate("draft","Convert to Draft"),
|
|
lines + ['FreeCAD.ActiveDocument.recompute()'])
|
|
else:
|
|
lines = []
|
|
for obj in sel:
|
|
if obj.isDerivedFrom("Sketcher::SketchObject"):
|
|
lines.append("Draft.draftify(FreeCAD.ActiveDocument."+obj.Name+",delete=False)")
|
|
elif obj.isDerivedFrom("Part::Part2DObjectPython"):
|
|
lines.append("Draft.makeSketch(FreeCAD.ActiveDocument."+obj.Name+",autoconstraints=True)")
|
|
elif obj.isDerivedFrom("Part::Feature"):
|
|
#if (len(obj.Shape.Wires) == 1) or (len(obj.Shape.Edges) == 1):
|
|
lines.append("Draft.makeSketch(FreeCAD.ActiveDocument."+obj.Name+",autoconstraints=True)")
|
|
self.commit(translate("draft","Convert"),
|
|
lines + ['FreeCAD.ActiveDocument.recompute()'])
|
|
self.finish()
|
|
|
|
|
|
class Array(Modifier):
|
|
"""GuiCommand for the Draft_Array tool.
|
|
|
|
Parameters
|
|
----------
|
|
use_link: bool, optional
|
|
It defaults to `False`. If it is `True`, the created object
|
|
will be a `Link array`.
|
|
"""
|
|
|
|
def __init__(self, use_link=False):
|
|
Modifier.__init__(self)
|
|
self.use_link = use_link
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_Array',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Array", "Array"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Array", "Creates a polar or rectangular array from a selected object")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self)
|
|
if not FreeCADGui.Selection.getSelection():
|
|
if self.ui:
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to array")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
if FreeCADGui.Selection.getSelection():
|
|
obj = FreeCADGui.Selection.getSelection()[0]
|
|
FreeCADGui.addModule("Draft")
|
|
self.commit(translate("draft","Array"),
|
|
['obj = Draft.makeArray(FreeCAD.ActiveDocument.{},FreeCAD.Vector(1,0,0),FreeCAD.Vector(0,1,0),2,2,use_link={})'.format(obj.Name,self.use_link),
|
|
'Draft.autogroup(obj)',
|
|
'FreeCAD.ActiveDocument.recompute()'])
|
|
self.finish()
|
|
|
|
|
|
class LinkArray(Array):
|
|
"""GuiCommand for the Draft_LinkArray tool."""
|
|
|
|
def __init__(self):
|
|
Array.__init__(self, use_link=True)
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_LinkArray',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_LinkArray", "LinkArray"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_LinkArray", "Creates a polar or rectangular link array from a selected object")}
|
|
|
|
class PathArray(Modifier):
|
|
"""The PathArray FreeCAD command definition"""
|
|
|
|
def __init__(self,use_link=False):
|
|
Modifier.__init__(self)
|
|
self.use_link = use_link
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_PathArray',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_PathArray", "PathArray"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_PathArray", "Creates copies of a selected object along a selected path.")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self)
|
|
if not FreeCADGui.Selection.getSelectionEx():
|
|
if self.ui:
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Please select base and path objects")+"\n")
|
|
# print("Please select base and path objects")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
sel = FreeCADGui.Selection.getSelectionEx()
|
|
if sel:
|
|
base = sel[0].Object
|
|
path = sel[1].Object
|
|
pathsubs = list(sel[1].SubElementNames)
|
|
defXlate = FreeCAD.Vector(0,0,0)
|
|
defCount = 4
|
|
defAlign = False
|
|
FreeCAD.ActiveDocument.openTransaction("PathArray")
|
|
Draft.makePathArray(base,path,defCount,defXlate,defAlign,pathsubs,use_link=self.use_link)
|
|
FreeCAD.ActiveDocument.commitTransaction()
|
|
FreeCAD.ActiveDocument.recompute() # feature won't appear until recompute.
|
|
self.finish()
|
|
|
|
class PathLinkArray(PathArray):
|
|
"The PathLinkArray FreeCAD command definition"
|
|
|
|
def __init__(self):
|
|
PathArray.__init__(self,True)
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_PathLinkArray',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_PathLinkArray", "PathLinkArray"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_PathLinkArray", "Creates links of a selected object along a selected path.")}
|
|
|
|
class PointArray(Modifier):
|
|
"""The PointArray FreeCAD command definition"""
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_PointArray',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_PointArray", "PointArray"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_PointArray", "Creates copies of a selected object on the position of points.")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self)
|
|
if not FreeCADGui.Selection.getSelectionEx():
|
|
if self.ui:
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Please select base and pointlist objects\n"))
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
sel = FreeCADGui.Selection.getSelectionEx()
|
|
if sel:
|
|
base = sel[0].Object
|
|
ptlst = sel[1].Object
|
|
FreeCAD.ActiveDocument.openTransaction("PointArray")
|
|
Draft.makePointArray(base, ptlst)
|
|
FreeCAD.ActiveDocument.commitTransaction()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
self.finish()
|
|
|
|
|
|
class Draft_Clone(Modifier):
|
|
"""The Draft Clone command definition"""
|
|
|
|
def __init__(self):
|
|
Modifier.__init__(self)
|
|
self.moveAfterCloning = False
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_Clone',
|
|
'Accel' : "C,L",
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Clone", "Clone"),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Clone", "Clones the selected object(s)")}
|
|
|
|
def Activated(self):
|
|
Modifier.Activated(self)
|
|
if not FreeCADGui.Selection.getSelection():
|
|
if self.ui:
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to clone")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call:
|
|
self.view.removeEventCallback("SoEvent",self.call)
|
|
if FreeCADGui.Selection.getSelection():
|
|
l = len(FreeCADGui.Selection.getSelection())
|
|
FreeCADGui.addModule("Draft")
|
|
FreeCAD.ActiveDocument.openTransaction("Clone")
|
|
nonRepeatList = []
|
|
for obj in FreeCADGui.Selection.getSelection():
|
|
if obj not in nonRepeatList:
|
|
FreeCADGui.doCommand("Draft.clone(FreeCAD.ActiveDocument.getObject(\""+obj.Name+"\"))")
|
|
nonRepeatList.append(obj)
|
|
FreeCAD.ActiveDocument.commitTransaction()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
FreeCADGui.Selection.clearSelection()
|
|
for i in range(l):
|
|
FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.Objects[-(1+i)])
|
|
self.finish()
|
|
|
|
def finish(self,close=False):
|
|
Modifier.finish(self,close=False)
|
|
if self.moveAfterCloning:
|
|
ToDo.delay(FreeCADGui.runCommand, "Draft_Move")
|
|
|
|
|
|
class Mirror(Modifier):
|
|
"""The Draft_Mirror FreeCAD command definition"""
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'Draft_Mirror',
|
|
'Accel' : "M, I",
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Mirror", "Mirror"),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Mirror", "Mirrors the selected objects along a line defined by two points")}
|
|
|
|
def Activated(self):
|
|
self.name = translate("draft","Mirror", utf8_decode=True)
|
|
Modifier.Activated(self,self.name)
|
|
self.ghost = None
|
|
if self.ui:
|
|
if not FreeCADGui.Selection.getSelection():
|
|
self.ui.selectUi()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Select an object to mirror")+"\n")
|
|
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
|
else:
|
|
self.proceed()
|
|
|
|
def proceed(self):
|
|
if self.call: self.view.removeEventCallback("SoEvent",self.call)
|
|
self.sel = FreeCADGui.Selection.getSelection()
|
|
self.ui.pointUi(self.name)
|
|
self.ui.modUi()
|
|
self.ui.xValue.setFocus()
|
|
self.ui.xValue.selectAll()
|
|
# self.ghost = trackers.ghostTracker(self.sel) TODO: solve this (see below)
|
|
self.call = self.view.addEventCallback("SoEvent",self.action)
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Pick start point of mirror line")+"\n")
|
|
self.ui.isCopy.hide()
|
|
|
|
def finish(self,closed=False,cont=False):
|
|
if self.ghost:
|
|
self.ghost.finalize()
|
|
Modifier.finish(self)
|
|
if cont and self.ui:
|
|
if self.ui.continueMode:
|
|
FreeCADGui.Selection.clearSelection()
|
|
self.Activated()
|
|
|
|
def mirror(self,p1,p2,copy=False):
|
|
"""mirroring the real shapes"""
|
|
sel = '['
|
|
for o in self.sel:
|
|
if len(sel) > 1:
|
|
sel += ','
|
|
sel += 'FreeCAD.ActiveDocument.'+o.Name
|
|
sel += ']'
|
|
FreeCADGui.addModule("Draft")
|
|
self.commit(translate("draft","Mirror"),
|
|
['Draft.mirror('+sel+','+DraftVecUtils.toString(p1)+','+DraftVecUtils.toString(p2)+')',
|
|
'FreeCAD.ActiveDocument.recompute()'])
|
|
|
|
def action(self,arg):
|
|
"""scene event handler"""
|
|
if arg["Type"] == "SoKeyboardEvent":
|
|
if arg["Key"] == "ESCAPE":
|
|
self.finish()
|
|
elif arg["Type"] == "SoLocation2Event": #mouse movement detection
|
|
self.point,ctrlPoint,info = getPoint(self,arg)
|
|
if (len(self.node) > 0):
|
|
last = self.node[-1]
|
|
if self.ghost:
|
|
if self.point != last:
|
|
# TODO : the following doesn't work at the moment
|
|
mu = self.point.sub(last).normalize()
|
|
if FreeCAD.GuiUp:
|
|
mv = FreeCADGui.ActiveDocument.ActiveView.getViewDirection().negative()
|
|
else:
|
|
mv = FreeCAD.Vector(0,0,1)
|
|
mw = mv.cross(mu)
|
|
import WorkingPlane
|
|
tm = WorkingPlane.plane(u=mu,v=mv,w=mw,pos=last).getPlacement().toMatrix()
|
|
m = self.ghost.getMatrix()
|
|
m = m.multiply(tm.inverse())
|
|
m.scale(FreeCAD.Vector(1,1,-1))
|
|
m = m.multiply(tm)
|
|
m.scale(FreeCAD.Vector(-1,1,1))
|
|
self.ghost.setMatrix(m)
|
|
if self.extendedCopy:
|
|
if not hasMod(arg,MODALT): self.finish()
|
|
redraw3DView()
|
|
elif arg["Type"] == "SoMouseButtonEvent":
|
|
if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
|
|
if self.point:
|
|
self.ui.redraw()
|
|
if (self.node == []):
|
|
self.node.append(self.point)
|
|
self.ui.isRelative.show()
|
|
if self.ghost:
|
|
self.ghost.on()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Pick end point of mirror line")+"\n")
|
|
if self.planetrack:
|
|
self.planetrack.set(self.point)
|
|
else:
|
|
last = self.node[0]
|
|
if self.ui.isCopy.isChecked() or hasMod(arg,MODALT):
|
|
self.mirror(last,self.point,True)
|
|
else:
|
|
self.mirror(last,self.point)
|
|
if hasMod(arg,MODALT):
|
|
self.extendedCopy = True
|
|
else:
|
|
self.finish(cont=True)
|
|
|
|
def numericInput(self,numx,numy,numz):
|
|
"""this function gets called by the toolbar when valid x, y, and z have been entered there"""
|
|
self.point = Vector(numx,numy,numz)
|
|
if not self.node:
|
|
self.node.append(self.point)
|
|
if self.ghost:
|
|
self.ghost.on()
|
|
FreeCAD.Console.PrintMessage(translate("draft", "Pick end point of mirror line")+"\n")
|
|
else:
|
|
last = self.node[-1]
|
|
if self.ui.isCopy.isChecked():
|
|
self.mirror(last,self.point,True)
|
|
else:
|
|
self.mirror(last,self.point)
|
|
self.finish()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Snap tools
|
|
# ---------------------------------------------------------------------------
|
|
from draftguitools.gui_snaps import Draft_Snap_Lock
|
|
from draftguitools.gui_snaps import Draft_Snap_Midpoint
|
|
from draftguitools.gui_snaps import Draft_Snap_Perpendicular
|
|
from draftguitools.gui_snaps import Draft_Snap_Grid
|
|
from draftguitools.gui_snaps import Draft_Snap_Intersection
|
|
from draftguitools.gui_snaps import Draft_Snap_Parallel
|
|
from draftguitools.gui_snaps import Draft_Snap_Endpoint
|
|
from draftguitools.gui_snaps import Draft_Snap_Angle
|
|
from draftguitools.gui_snaps import Draft_Snap_Center
|
|
from draftguitools.gui_snaps import Draft_Snap_Extension
|
|
from draftguitools.gui_snaps import Draft_Snap_Near
|
|
from draftguitools.gui_snaps import Draft_Snap_Ortho
|
|
from draftguitools.gui_snaps import Draft_Snap_Special
|
|
from draftguitools.gui_snaps import Draft_Snap_Dimensions
|
|
from draftguitools.gui_snaps import Draft_Snap_WorkingPlane
|
|
from draftguitools.gui_snaps import ShowSnapBar
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Adds the icons & commands to the FreeCAD command manager, and sets defaults
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# drawing commands
|
|
|
|
# modification commands
|
|
FreeCADGui.addCommand('Draft_Scale',Scale())
|
|
FreeCADGui.addCommand('Draft_Drawing',Drawing())
|
|
FreeCADGui.addCommand('Draft_WireToBSpline',WireToBSpline())
|
|
FreeCADGui.addCommand('Draft_Draft2Sketch',Draft2Sketch())
|
|
FreeCADGui.addCommand('Draft_Array',Array())
|
|
FreeCADGui.addCommand('Draft_LinkArray',LinkArray())
|
|
FreeCADGui.addCommand('Draft_Clone',Draft_Clone())
|
|
FreeCADGui.addCommand('Draft_PathArray',PathArray())
|
|
FreeCADGui.addCommand('Draft_PathLinkArray',PathLinkArray())
|
|
FreeCADGui.addCommand('Draft_PointArray',PointArray())
|
|
FreeCADGui.addCommand('Draft_Mirror',Mirror())
|
|
|
|
# context commands
|
|
FreeCADGui.addCommand('Draft_Shape2DView',Shape2DView())
|
|
|
|
# a global place to look for active draft Command
|
|
FreeCAD.activeDraftCommand = None
|