Merge branch 'master' into Draft_addgroup

This commit is contained in:
JeromeL
2020-04-21 14:53:12 +02:00
committed by GitHub
49 changed files with 4097 additions and 1968 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
liberapay: FreeCAD
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://www.patreon.com/yorikvanhavre', 'https://www.patreon.com/kkremitzki', 'https://www.patreon.com/thundereal']
+6 -6
View File
@@ -974,8 +974,8 @@ void NaviCubeImplementation::drawNaviCube(bool pickMode) {
static GLubyte xbmp[] = { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11 };
glColor3f(1, 0, 0);
glBegin(GL_LINES);
glVertex3f(-1.1 , -1.1, -1.1);
glVertex3f(+0.5 , -1.1, -1.1);
glVertex3f(-1.1f, -1.1f, -1.1f);
glVertex3f(+0.5f, -1.1f, -1.1f);
glEnd();
glRasterPos3d(a, -a, -a);
glBitmap(8, 7, 0, 0, 0, 0, xbmp);
@@ -983,8 +983,8 @@ void NaviCubeImplementation::drawNaviCube(bool pickMode) {
static GLubyte ybmp[] = { 0x04,0x04,0x04,0x04,0x0a,0x11,0x11 };
glColor3f(0, 1, 0);
glBegin(GL_LINES);
glVertex3f(-1.1 , -1.1, -1.1);
glVertex3f(-1.1 , +0.5, -1.1);
glVertex3f(-1.1f, -1.1f, -1.1f);
glVertex3f(-1.1f, +0.5f, -1.1f);
glEnd();
glRasterPos3d( -a, a, -a);
glBitmap(8, 7, 0, 0, 0, 0, ybmp);
@@ -992,8 +992,8 @@ void NaviCubeImplementation::drawNaviCube(bool pickMode) {
static GLubyte zbmp[] = { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f };
glColor3f(0, 0, 1);
glBegin(GL_LINES);
glVertex3f(-1.1 , -1.1, -1.1);
glVertex3f(-1.1 , -1.1, +0.5);
glVertex3f(-1.1f, -1.1f, -1.1f);
glVertex3f(-1.1f, -1.1f, +0.5f);
glEnd();
glRasterPos3d( -a, -a, a);
glBitmap(8, 7, 0, 0, 0, 0, zbmp);
@@ -7,26 +7,17 @@
<x>0</x>
<y>0</y>
<width>463</width>
<height>421</height>
<height>466</height>
</rect>
</property>
<property name="windowTitle">
<string>IFC-Export</string>
<string>IFC export</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>9</number>
</property>
<item>
+19 -12
View File
@@ -7,26 +7,17 @@
<x>0</x>
<y>0</y>
<width>463</width>
<height>495</height>
<height>577</height>
</rect>
</property>
<property name="windowTitle">
<string>IFC</string>
<string>IFC import</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>9</number>
</property>
<item>
@@ -394,6 +385,22 @@ FreeCAD object properties</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox">
<property name="toolTip">
<string>If this option is checked, the default Project, Site, Building and Storeys objects that are usually found in an IFC file are not imported, and all objects are placed in a Group. Buildins ans storeys are still imported if there is more than one.</string>
</property>
<property name="text">
<string>Replace Project, Site, Buiding and Storey by Group</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>ifcReplaceProject</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Arch</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
+34 -9
View File
@@ -161,7 +161,8 @@ def getPreferences():
'IMPORT_PROPERTIES': p.GetBool("ifcImportProperties",False),
'SPLIT_LAYERS': p.GetBool("ifcSplitLayers",False),
'FITVIEW_ONIMPORT': p.GetBool("ifcFitViewOnImport",False),
'ALLOW_INVALID': p.GetBool("ifcAllowInvalid",False)
'ALLOW_INVALID': p.GetBool("ifcAllowInvalid",False),
'REPLACE_PROJECT': p.GetBool("ifcReplaceProject",False)
}
if preferences['MERGE_MODE_ARCH'] > 0:
@@ -299,23 +300,24 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None):
FreeCADGui.ActiveDocument.activeView().viewAxonometric()
# Create the base project object
if len(ifcfile.by_type("IfcProject")) > 0:
projectImporter = importIFCHelper.ProjectImporter(ifcfile, objects)
projectImporter.execute()
else:
# https://forum.freecadweb.org/viewtopic.php?f=39&t=40624
print("No IfcProject found in the ifc file. Nothing imported")
return doc
if not preferences['REPLACE_PROJECT']:
if len(ifcfile.by_type("IfcProject")) > 0:
projectImporter = importIFCHelper.ProjectImporter(ifcfile, objects)
projectImporter.execute()
else:
# https://forum.freecadweb.org/viewtopic.php?f=39&t=40624
print("No IfcProject found in the ifc file. Nothing imported")
return doc
# handle IFC products
for product in products:
count += 1
pid = product.id()
guid = product.GlobalId
ptype = product.is_a()
if preferences['DEBUG']: print(count,"/",len(products),"object #"+str(pid),":",ptype,end="")
# build list of related property sets
@@ -382,6 +384,20 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None):
if ptype in preferences['SKIP']: # preferences-set type skip list
if preferences['DEBUG']: print(" skipped.")
continue
if preferences['REPLACE_PROJECT']: # options-enabled project/site/building skip
if ptype in ['IfcProject','IfcSite']:
if preferences['DEBUG']: print(" skipped.")
continue
elif ptype in ['IfcBuilding']:
if len(ifcfile.by_type("IfcBuilding")) == 1:
# let multiple buildings through...
if preferences['DEBUG']: print(" skipped.")
continue
elif ptype in ['IfcBuildingStorey']:
if len(ifcfile.by_type("IfcBuildingStorey")) == 1:
# let multiple storeys through...
if preferences['DEBUG']: print(" skipped.")
continue
# check if this object is sharing its shape (mapped representation)
clone = None
@@ -1220,6 +1236,15 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None):
if l:
setattr(p[0],p[1],l)
# Grouping everything if required
if preferences['REPLACE_PROJECT']:
rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group")
rootgroup.Label = os.path.basename(filename)
for key,obj in objects.items():
# only add top-level objects
if not obj.InList:
rootgroup.addObject(obj)
FreeCAD.ActiveDocument.recompute()
if ZOOMOUT and FreeCAD.GuiUp:
+14 -5
View File
@@ -72,14 +72,23 @@ def getIndices(obj,shape,offsetv,offsetvn):
try:
if not isinstance(e.Curve,Part.LineSegment):
if not curves:
myshape = obj.Shape.copy(False)
myshape.Placement=obj.getGlobalPlacement()
if obj.isDerivedFrom("App::Link"):
myshape = obj.LinkedObject.Shape.copy(False)
myshape.Placement=obj.LinkPlacement
else:
myshape = obj.Shape.copy(False)
myshape.Placement=obj.getGlobalPlacement()
mesh=MeshPart.meshFromShape(Shape=myshape, LinearDeflection=0.1, AngularDeflection=0.7, Relative=True)
FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n")
break
except: # unimplemented curve type
myshape = obj.Shape.copy(False)
myshape.Placement=obj.getGlobalPlacement()
if obj.isDerivedFrom("App::Link"):
if obj.Shape:
myshape = obj.Shape.copy(False)
myshape.Placement=obj.LinkPlacement
else:
myshape = obj.Shape.copy(False)
myshape.Placement=obj.getGlobalPlacement()
mesh=MeshPart.meshFromShape(Shape=myshape, LinearDeflection=0.1, AngularDeflection=0.7, Relative=True)
FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n")
break
@@ -157,7 +166,7 @@ def export(exportList,filename,colors=None):
materials = []
outfile.write("mtllib " + os.path.basename(filenamemtl) + "\n")
for obj in objectslist:
if obj.isDerivedFrom("Part::Feature") or obj.isDerivedFrom("Mesh::Feature"):
if obj.isDerivedFrom("Part::Feature") or obj.isDerivedFrom("Mesh::Feature") or obj.isDerivedFrom("App::Link"):
hires = None
if FreeCAD.GuiUp:
visible = obj.ViewObject.isVisible()
+2
View File
@@ -86,6 +86,8 @@ SET(Draft_view_providers
SET(Draft_GUI_tools
draftguitools/__init__.py
draftguitools/gui_base.py
draftguitools/gui_base_original.py
draftguitools/gui_tool_utils.py
draftguitools/gui_circulararray.py
draftguitools/gui_orthoarray.py
draftguitools/gui_polararray.py
+3 -3
View File
@@ -514,7 +514,7 @@ def makeCopy(obj,force=None,reparent=False):
_Point(newobj)
if gui:
_ViewProviderPoint(newobj.ViewObject)
elif (getType(obj) == "Dimension") or (force == "Dimension"):
elif (getType(obj) in ["Dimension","LinearDimension"]) or (force == "Dimension"):
newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name))
_Dimension(newobj)
if gui:
@@ -1025,7 +1025,7 @@ def move(objectslist,vector,copy=False):
else:
newobj = obj
newobj.Placement.Base = obj.Placement.Base.add(real_vector)
elif getType(obj) == "Dimension":
elif getType(obj) in ["Dimension","LinearDimension"]:
if copy:
newobj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",getRealName(obj.Name))
_Dimension(newobj)
@@ -1589,7 +1589,7 @@ def getDXF(obj,direction=None):
ny = DraftVecUtils.project(vec,plane.v)
return Vector(nx.Length,ny.Length,0)
if getType(obj) == "Dimension":
if getType(obj) in ["Dimension","LinearDimension"]:
p1 = getProj(obj.Start)
p2 = getProj(obj.End)
p3 = getProj(obj.Dimline)
+1 -2
View File
@@ -1893,8 +1893,7 @@ class DraftToolBar:
return str(a)
def togglesnap(self):
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.toggle()
FreeCADGui.doCommand('FreeCADGui.runCommand("Draft_Snap_Lock")')
def togglenearsnap(self):
if hasattr(FreeCADGui,"Snapper"):
+32 -256
View File
@@ -114,276 +114,42 @@ 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
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 hasMod
from draftguitools.gui_tool_utils import setMod
# ---------------------------------------------------------------------------
# Base Class
# ---------------------------------------------------------------------------
class DraftTool:
"""The base class of all Draft Tools"""
def __init__(self):
self.commitList = []
def IsActive(self):
if FreeCADGui.ActiveDocument:
return True
else:
return False
def Activated(self, name="None", noplanesetup=False, is_subtool=False):
if FreeCAD.activeDraftCommand and not is_subtool:
FreeCAD.activeDraftCommand.finish()
global Part, DraftGeomUtils
import Part, DraftGeomUtils
self.ui = None
self.call = None
self.support = None
self.point = None
self.commitList = []
self.doc = FreeCAD.ActiveDocument
if not self.doc:
self.finish()
return
FreeCAD.activeDraftCommand = self
self.view = Draft.get3DView()
self.ui = FreeCADGui.draftToolBar
self.featureName = name
self.ui.sourceCmd = self
self.ui.setTitle(name)
self.ui.show()
if not noplanesetup:
plane.setup()
self.node = []
self.pos = []
self.constrain = None
self.obj = None
self.extendedCopy = False
self.ui.setTitle(name)
self.planetrack = None
if Draft.getParam("showPlaneTracker",False):
self.planetrack = trackers.PlaneTracker()
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.setTrackers()
def finish(self,close=False):
self.node = []
FreeCAD.activeDraftCommand = None
if self.ui:
self.ui.offUi()
self.ui.sourceCmd = None
if self.planetrack:
self.planetrack.finalize()
plane.restore()
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.off()
if self.call:
try:
self.view.removeEventCallback("SoEvent",self.call)
except RuntimeError:
# the view has been deleted already
pass
self.call = None
if self.commitList:
ToDo.delayCommit(self.commitList)
self.commitList = []
def commit(self,name,func):
"""stores actions to be committed to the FreeCAD document"""
self.commitList.append((name,func))
def getStrings(self,addrot=None):
"""returns a couple of useful strings for building python commands"""
# current plane rotation
p = plane.getRotation()
qr = p.Rotation.Q
qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')'
# support object
if self.support and FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("useSupport",False):
sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")'
else:
sup = 'None'
# contents of self.node
points='['
for n in self.node:
if len(points) > 1:
points += ','
points += DraftVecUtils.toString(n)
points += ']'
# fill mode
if self.ui:
fil = str(bool(self.ui.fillmode))
else:
fil = "True"
return qr,sup,points,fil
from draftguitools.gui_base_original import 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"""
from draftguitools.gui_base_original import Creator
def __init__(self):
DraftTool.__init__(self)
def Activated(self,name="None",noplanesetup=False):
DraftTool.Activated(self,name,noplanesetup)
if not noplanesetup:
self.support = getSupport()
class Line(Creator):
"""The Line FreeCAD command definition"""
@@ -497,6 +263,7 @@ class Line(Creator):
def undolast(self):
"""undoes last line segment"""
import Part
if (len(self.node) > 1):
self.node.pop()
last = self.node[-1]
@@ -513,6 +280,7 @@ class Line(Creator):
def drawSegment(self,point):
"""draws a new segment"""
import Part
if self.planetrack and self.node:
self.planetrack.set(self.node[-1])
if (len(self.node) == 1):
@@ -665,6 +433,7 @@ class BSpline(Line):
def undolast(self):
"""undoes last line segment"""
import Part
if (len(self.node) > 1):
self.node.pop()
self.bsplinetrack.update(self.node)
@@ -674,6 +443,7 @@ class BSpline(Line):
FreeCAD.Console.PrintMessage(translate("draft", "Last point has been removed")+"\n")
def drawUpdate(self,point):
import Part
if (len(self.node) == 1):
self.bsplinetrack.on()
if self.planetrack:
@@ -785,6 +555,7 @@ class BezCurve(Line):
def updateShape(self, pts):
'''creates shape for display during creation process.'''
import Part
edges = []
if len(pts) >= 2: #allow lower degree segment
poles=pts[1:]
@@ -939,7 +710,8 @@ class CubicBezCurve(Line):
def updateShape(self, pts):
'''creates shape for display during creation process.'''
# not quite right. draws 1 big bez. sb segmented
import Part
# not quite right. draws 1 big bez. sb segmented
edges = []
if len(pts) >= 2: #allow lower degree segment
@@ -1182,6 +954,7 @@ class Arc(Creator):
def action(self,arg):
"""scene event handler"""
import DraftGeomUtils
if arg["Type"] == "SoKeyboardEvent":
if arg["Key"] == "ESCAPE":
self.finish()
@@ -1397,6 +1170,7 @@ class Arc(Creator):
FreeCAD.Console.PrintMessage(translate("draft", "Pick radius")+"\n")
def numericRadius(self,rad):
import DraftGeomUtils
"""this function gets called by the toolbar when valid radius have been entered there"""
if (self.step == 1):
self.rad = rad
@@ -1496,6 +1270,7 @@ class Polygon(Creator):
def action(self,arg):
"""scene event handler"""
import DraftGeomUtils
if arg["Type"] == "SoKeyboardEvent":
if arg["Key"] == "ESCAPE":
self.finish()
@@ -1626,6 +1401,7 @@ class Polygon(Creator):
def numericRadius(self,rad):
"""this function gets called by the toolbar when valid radius have been entered there"""
import DraftGeomUtils
self.rad = rad
if len(self.tangents) == 2:
cir = DraftGeomUtils.circleFrom2tan1rad(self.tangents[0], self.tangents[1], rad)
@@ -2329,13 +2105,8 @@ class ShapeString(Creator):
#---------------------------------------------------------------------------
# Modifier functions
#---------------------------------------------------------------------------
from draftguitools.gui_base_original import Modifier
class Modifier(DraftTool):
"""A generic Modifier Tool, used by modification tools such as move"""
def __init__(self):
DraftTool.__init__(self)
self.copymode = False
class Move(Modifier):
"""The Draft_Move FreeCAD command definition"""
@@ -2911,6 +2682,7 @@ class Offset(Modifier):
def action(self,arg):
"""scene event handler"""
import DraftGeomUtils
if arg["Type"] == "SoKeyboardEvent":
if arg["Key"] == "ESCAPE":
self.finish()
@@ -3522,6 +3294,7 @@ class Trimex(Modifier):
self.linetrack = trackers.lineTracker()
import DraftGeomUtils
import Part
if not "Shape" in self.obj.PropertiesList: return
if "Placement" in self.obj.PropertiesList:
@@ -3640,6 +3413,7 @@ class Trimex(Modifier):
if real: newedges = []
import DraftGeomUtils
import Part
# finding the active point
vlist = []
@@ -3754,6 +3528,7 @@ class Trimex(Modifier):
def trimObject(self):
"""trims the actual object"""
import Part
if self.extrudeMode:
delta = self.extrude(self.shift,real=True)
#print("delta",delta)
@@ -3816,6 +3591,7 @@ class Trimex(Modifier):
def trimObjects(self,objectslist):
"""attempts to trim two objects together"""
import Part
import DraftGeomUtils
wires = []
for obj in objectslist:
if not Draft.getType(obj) in ["Wire","Circle"]:
+2 -1
View File
@@ -116,6 +116,7 @@ class DraftWorkbench(FreeCADGui.Workbench):
if hasattr(FreeCADGui, "draftToolBar"):
if not hasattr(FreeCADGui.draftToolBar, "loadedPreferences"):
FreeCADGui.addPreferencePage(":/ui/preferences-draft.ui", QT_TRANSLATE_NOOP("Draft", "Draft"))
FreeCADGui.addPreferencePage(":/ui/preferences-draftinterface.ui", QT_TRANSLATE_NOOP("Draft", "Draft"))
FreeCADGui.addPreferencePage(":/ui/preferences-draftsnap.ui", QT_TRANSLATE_NOOP("Draft", "Draft"))
FreeCADGui.addPreferencePage(":/ui/preferences-draftvisual.ui", QT_TRANSLATE_NOOP("Draft", "Draft"))
FreeCADGui.addPreferencePage(":/ui/preferences-drafttexts.ui", QT_TRANSLATE_NOOP("Draft", "Draft"))
@@ -140,7 +141,7 @@ class DraftWorkbench(FreeCADGui.Workbench):
if hasattr(FreeCADGui, "Snapper"):
FreeCADGui.Snapper.hide()
import draftutils.init_draft_statusbar as dsb
dsb.hide_draft_statusbar()
dsb.hide_draft_statusbar()
FreeCAD.Console.PrintLog("Draft workbench deactivated.\n")
def ContextMenu(self, recipient):
+1
View File
@@ -154,6 +154,7 @@
<file>translations/Draft_zh-CN.qm</file>
<file>translations/Draft_zh-TW.qm</file>
<file>ui/preferences-draft.ui</file>
<file>ui/preferences-draftinterface.ui</file>
<file>ui/preferences-draftsnap.ui</file>
<file>ui/preferences-drafttexts.ui</file>
<file>ui/preferences-draftvisual.ui</file>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,975 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DlgSettingsDraft</class>
<widget class="QWidget" name="Gui::Dialog::DlgSettingsDraft">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>456</width>
<height>338</height>
</rect>
</property>
<property name="windowTitle">
<string>User interface settings</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>In-Command Shortcuts</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
<item>
<layout class="QGridLayout" name="gridLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item row="4" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_8">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>`</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutCycleSnap</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_9">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>S</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutSnap</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Relative</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>R</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutRelative</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="Gui::PrefLineEdit" name="lineEdit_4">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>O</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutClose</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="Gui::PrefLineEdit" name="lineEdit_3">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>T</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutContinue</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Continue</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Increase Radius</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Cycle Snap</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="Gui::PrefLineEdit" name="lineEdit_19">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>[</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutIncreaseRadius</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="5" column="5">
<widget class="Gui::PrefLineEdit" name="lineEdit_17">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>]</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutDecreaseRadius</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Snap</string>
</property>
</widget>
</item>
<item row="5" column="4">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Decrease Radius</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Length</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Wipe</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="Gui::PrefLineEdit" name="lineEdit_11">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>D</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutSubelementMode</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Add Hold</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="Gui::PrefLineEdit" name="lineEdit_12">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>L</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutFill</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_5">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>P</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutCopy</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Fill</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Subelement Mode</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="Gui::PrefLineEdit" name="lineEdit_13">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>E</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutSelectEdge</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_7">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>H</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutLength</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Select Edge</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="Gui::PrefLineEdit" name="lineEdit_14">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>W</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutWipe</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_6">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>A</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutExit</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="Gui::PrefLineEdit" name="lineEdit_15">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Q</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutAddHold</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Set WP</string>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="Gui::PrefLineEdit" name="lineEdit_16">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>U</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutSetWP</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Restrict X</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="Gui::PrefLineEdit" name="lineEdit_10">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>X</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutRestrictX</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Restrict Y</string>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="Gui::PrefLineEdit" name="lineEdit_20">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Y</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>inCommandShortcutRestrictY</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="6" column="4">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Restrict Z</string>
</property>
</widget>
</item>
<item row="6" column="5">
<widget class="Gui::PrefLineEdit" name="lineEdit_18">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Z</string>
</property>
<property name="maxLength">
<number>1</number>
</property>
<property name="placeholderText">
<string/>
</property>
<property name="clearButtonEnabled" stdset="0">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>RestrictZ</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Enable draft statusbar customization</string>
</property>
<property name="title">
<string>Draft Statusbar</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<string notr="true">DisplayStatusbar</string>
</property>
<property name="prefPath" stdset="0">
<string notr="true">Mod/Draft</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="Gui::PrefCheckBox" name="gui::prefcheckbox_3">
<property name="toolTip">
<string>Enable snap statusbar widget</string>
</property>
<property name="text">
<string>Draft snap widget</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>DisplayStatusbarSnapWidget</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::PrefCheckBox" name="gui::prefcheckbox_13">
<property name="toolTip">
<string>Enable draft statusbar annotation scale widget</string>
</property>
<property name="text">
<string>Annotation scale widget</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DisplayStatusbarScaleWidget</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<customwidgets>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefLineEdit</class>
<extends>QLineEdit</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+1 -1
View File
@@ -139,7 +139,7 @@ class Arc_3Points(gui_base.GuiCommandSimplest):
Parameters
----------
point: Base::Vector
The dynamic point pased by the callback
The dynamic point passed by the callback
as we move the pointer in the 3D view.
info: str
-93
View File
@@ -33,99 +33,6 @@ import draftutils.todo as todo
from draftutils.messages import _msg, _log
class GuiCommandSimplest:
"""Simplest base class for GuiCommands.
This class only sets up the command name and the document object
to use for the command.
When it is executed, it logs the command name to the log file,
and prints the command name to the console.
It implements the `IsActive` method, which must return `True`
when the command should be available.
It should return `True` when there is an active document,
otherwise the command (button or menu) should be disabled.
This class is meant to be inherited by other GuiCommand classes
to quickly log the command name, and set the correct document object.
Parameter
---------
name: str, optional
It defaults to `'None'`.
The name of the action that is being run,
for example, `'Heal'`, `'Flip dimensions'`,
`'Line'`, `'Circle'`, etc.
doc: App::Document, optional
It defaults to the value of `App.activeDocument()`.
The document object itself, which indicates where the actions
of the command will be executed.
Attributes
----------
command_name: str
This is the command name, which is assigned by `name`.
doc: App::Document
This is the document object itself, which is assigned by `doc`.
This attribute should be used by functions to make sure
that the operations are performed in the correct document
and not in other documents.
To set the active document we can use
>>> App.setActiveDocument(self.doc.Name)
"""
def __init__(self, name="None", doc=App.activeDocument()):
self.command_name = name
self.doc = doc
def IsActive(self):
"""Return True when this command should be available.
It is `True` when there is a document.
"""
if App.activeDocument():
return True
else:
return False
def Activated(self):
"""Execute when the command is called.
Log the command name to the log file and console.
Also update the `doc` attribute.
"""
self.doc = App.activeDocument()
_log("Document: {}".format(self.doc.Label))
_log("GuiCommand: {}".format(self.command_name))
_msg("{}".format(16*"-"))
_msg("GuiCommand: {}".format(self.command_name))
class GuiCommandNeedsSelection(GuiCommandSimplest):
"""Base class for GuiCommands that need a selection to be available.
It re-implements the `IsActive` method to return `True`
when there is both an active document and an active selection.
It inherits `GuiCommandSimplest` to set up the document
and other behavior. See this class for more information.
"""
def IsActive(self):
"""Return True when this command should be available.
It is `True` when there is a selection.
"""
if App.activeDocument() and Gui.Selection.getSelection():
return True
else:
return False
class GuiCommandSimplest:
"""Simplest base class for GuiCommands.
@@ -0,0 +1,302 @@
# ***************************************************************************
# * (c) 2009 Yorik van Havre <[email protected]> *
# * (c) 2010 Ken Cline <[email protected]> *
# * (c) 2020 Eliud Cabrera Castillo <[email protected]> *
# * *
# * 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 Base object for most old Draft Gui Commands.
This class is used by Gui Commands to set up some properties
of the DraftToolBar, the Snapper, and the working plane.
"""
## @package gui_base_original
# \ingroup DRAFT
# \brief Provides the Base object for most old Draft Gui Commands.
import FreeCAD as App
import FreeCADGui as Gui
import DraftVecUtils
import draftutils.utils as utils
import draftutils.gui_utils as gui_utils
import draftutils.todo as todo
import draftguitools.gui_trackers as trackers
import draftguitools.gui_tool_utils as gui_tool_utils
from draftutils.messages import _msg, _log
class DraftTool:
"""The base class of all Draft Tools.
This is the original class that was defined in `DraftTools.py`
before any re-organization of the code.
It must be preserved exactly like this to keep the original tools
running without problems.
This class is subclassed by `Creator` and `Modifier`
to set up a few additional properties of these two types.
This class connects with the `draftToolBar` and `Snapper` classes
that are installed in the `FreeCADGui` namespace in order to set up some
properties of the running tools such as the task panel, the snapping
functions, and the grid trackers.
It also connects with the `DraftWorkingPlane` class
that is installed in the `FreeCAD` namespace in order to set up
the working plane if it doesn't exist.
This class is intended to be replaced by newer classes inside the
`gui_base` module, in particular, `GuiCommandBase`.
"""
def __init__(self):
self.commitList = []
def IsActive(self):
"""Return True when this command should be available.
It is `True` when there is a document.
"""
if Gui.ActiveDocument:
return True
else:
return False
def Activated(self, name="None", noplanesetup=False, is_subtool=False):
"""Execute when the command is called.
If an active Gui Command exists, it will call the `finish` method
of it to terminate it.
If no active Gui Command exists, it will proceed with configuration
of the tool. The child class that subclasses this class
then should continue with its own Activated method.
Parameters
----------
name: str, optional
It defaults to `'None'`.
It is the `featureName` of the object, to know what is being run.
noplanesetup: bool, optional
It defaults to `False`.
If it is `False` it will set up the working plane
by running `App.DraftWorkingPlane.setup()`.
is_subtool: bool, optional
It defaults to `False`.
This is set to `True` when we want to modify an object
by using the mechanism of a `subtool`, introduced
through the `Draft_SubelementHighlight` command.
That is, first we run `Draft_SubelementHighlight`
then we can use `Draft_Move` while setting `is_subtool` to `True`.
"""
if App.activeDraftCommand and not is_subtool:
App.activeDraftCommand.finish()
# The Part module is first initialized when using any Gui Command
# for the first time.
global Part, DraftGeomUtils
import Part
import DraftGeomUtils
self.ui = None
self.call = None
self.support = None
self.point = None
self.commitList = []
self.doc = App.ActiveDocument
if not self.doc:
self.finish()
return
App.activeDraftCommand = self
self.view = gui_utils.get_3d_view()
self.ui = Gui.draftToolBar
self.featureName = name
self.ui.sourceCmd = self
self.ui.setTitle(name)
self.ui.show()
if not noplanesetup:
App.DraftWorkingPlane.setup()
self.node = []
self.pos = []
self.constrain = None
self.obj = None
self.extendedCopy = False
self.ui.setTitle(name)
self.planetrack = None
if utils.get_param("showPlaneTracker", False):
self.planetrack = trackers.PlaneTracker()
if hasattr(Gui, "Snapper"):
Gui.Snapper.setTrackers()
_log("GuiCommand: {}".format(self.featureName))
_msg("{}".format(16*"-"))
_msg("GuiCommand: {}".format(self.featureName))
def finish(self, close=False):
"""Finish the current command.
These are general cleaning tasks that are performed
when terminating all commands.
These include setting the node list to empty,
setting to `None` the active command,
turning off the graphical interface (task panel),
finishing the plane tracker, restoring the working plane,
turning off the snapper.
If a callback is installed in the 3D view, the callback is removed,
and set to `None`.
If the commit list is non-empty it will commit the instructions on
the list with `draftutils.todo.ToDo.delayCommit`,
and the list will be set to empty.
"""
self.node = []
App.activeDraftCommand = None
if self.ui:
self.ui.offUi()
self.ui.sourceCmd = None
if self.planetrack:
self.planetrack.finalize()
App.DraftWorkingPlane.restore()
if hasattr(Gui, "Snapper"):
Gui.Snapper.off()
if self.call:
try:
self.view.removeEventCallback("SoEvent", self.call)
except RuntimeError:
# the view has been deleted already
pass
self.call = None
if self.commitList:
todo.ToDo.delayCommit(self.commitList)
self.commitList = []
def commit(self, name, func):
"""Store actions in the commit list to be run later.
Parameters
----------
name: str
An arbitraty string that indicates the name of the operation
to run.
func: list of str
Each element of the list is a string that will be run by
`Gui.doCommand`.
See the complete information in the `draftutils.todo.ToDo` class.
"""
self.commitList.append((name, func))
def getStrings(self, addrot=None):
"""Return useful strings that will be used to build commands.
Returns
-------
str, str, str, str
A tuple of four strings that represent useful information.
* the current working plane rotation quaternion as a string
* the support object if available as a string
* the list of nodes inside the `node` attribute as a string
* the string `'True'` or `'False'` depending on the fill mode
of the current tool
"""
# Current plane rotation as a string
p = App.DraftWorkingPlane.getRotation()
qr = p.Rotation.Q
qr = "({0}, {1}, {2}, {3})".format(qr[0], qr[1], qr[2], qr[3])
# Support object
_params = "User parameter:BaseApp/Preferences/Mod/Draft"
_params_group = App.ParamGet(_params)
if self.support and _params_group.GetBool("useSupport", False):
sup = 'FreeCAD.ActiveDocument.getObject'
sup += '("{}")'.format(self.support.Name)
else:
sup = 'None'
# Contents of self.node
points = '['
for n in self.node:
if len(points) > 1:
points += ', '
points += DraftVecUtils.toString(n)
points += ']'
# Fill mode
if self.ui:
fil = str(bool(self.ui.fillmode))
else:
fil = "True"
return qr, sup, points, fil
class Creator(DraftTool):
"""A generic Creator tool, used by creation tools such as line or arc.
It runs the Activated method from the parent class.
If `noplanesetup` is `False`, it sets the appropriate `support` attribute
and sets the working plane with `gui_tool_utils.get_support`.
It inherits `DraftTool`, which sets up the majority of the behavior
of this class.
"""
def __init__(self):
super().__init__()
def Activated(self, name="None", noplanesetup=False):
"""Execute when the command is called.
Parameters
----------
name: str, optional
It defaults to `'None'`.
It is the `featureName` of the object, to know what is being run.
noplanesetup: bool, optional
It defaults to `False`.
If it is `False` it will set up the working plane
by running `App.DraftWorkingPlane.setup()`.
"""
super().Activated(name, noplanesetup)
if not noplanesetup:
self.support = gui_tool_utils.get_support()
class Modifier(DraftTool):
"""A generic Modifier tool, used by modification tools such as move.
After initializing the parent class, it sets the `copymode` attribute
to `False`.
It inherits `DraftTool`, which sets up the majority of the behavior
of this class.
"""
def __init__(self):
super().__init__()
self.copymode = False
+2 -2
View File
@@ -53,7 +53,7 @@ class AddToGroup(gui_base.GuiCommandNeedsSelection):
It adds selected objects to a group, or removes them from any group.
It inherits `GuiCommandNeedsSelection` to only be availbale
It inherits `GuiCommandNeedsSelection` to only be available
when there is a document and a selection.
See this class for more information.
"""
@@ -169,7 +169,7 @@ class SelectGroup(gui_base.GuiCommandNeedsSelection):
in this case it works in an intuitive manner, selecting
only the objects under the group.
It inherits `GuiCommandNeedsSelection` to only be availbale
It inherits `GuiCommandNeedsSelection` to only be available
when there is a document and a selection.
See this class for more information.
"""
File diff suppressed because it is too large Load Diff
+154 -91
View File
@@ -28,13 +28,74 @@
# \brief Provide the Draft_Snap commands used by the snapping mechanism
# in Draft.
from PySide import QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCADGui as Gui
import draftguitools.gui_base as gui_base
from draftutils.translate import _tr
# UTILITIES -----------------------------------------------------------------
def get_snap_statusbar_widget():
"""Return snap statusbar button."""
mw = Gui.getMainWindow()
if mw:
sb = mw.statusBar()
if sb:
return sb.findChild(QtGui.QToolBar,"draft_snap_widget")
return None
def sync_snap_toolbar_button(button, status):
"""Set snap toolbar button to given state."""
snap_toolbar = Gui.Snapper.get_snap_toolbar()
if not snap_toolbar:
return
for a in snap_toolbar.actions():
if a.objectName() == button:
if button == "Draft_Snap_Lock_Button":
# for lock button
snap_toolbar.actions()[0].setChecked(status)
for a in snap_toolbar.actions()[1:]:
a.setEnabled(status)
else:
# for every other button
a.setChecked(status)
if a.isChecked():
a.setToolTip(a.toolTip().replace("OFF","ON"))
else:
a.setToolTip(a.toolTip().replace("ON","OFF"))
def sync_snap_statusbar_button(button, status):
"""Set snap statusbar button to given state."""
ssw = get_snap_statusbar_widget()
if not ssw:
return
for child in ssw.children():
if child.objectName() == "Snap_Statusbutton":
ssb = child
actions = []
for a in ssb.menu().actions() + ssw.children()[-6:]:
actions.append(a)
if button == "Draft_Snap_Lock_Statusbutton":
ssb.setChecked(status)
for a in actions[1:]:
a.setEnabled(status)
else:
for a in actions:
if a.objectName() == button:
a.setChecked(status)
# SNAP GUI TOOLS ------------------------------------------------------------
class Draft_Snap_Lock(gui_base.GuiCommandSimplest):
"""GuiCommand for the Draft_Snap_Lock tool.
@@ -42,7 +103,7 @@ class Draft_Snap_Lock(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Main toggle snap"))
super(Draft_Snap_Lock, self).__init__(name=_tr("Main toggle snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -57,11 +118,13 @@ class Draft_Snap_Lock(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Lock, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "masterbutton"):
Gui.Snapper.masterbutton.toggle()
status = Gui.Snapper.toggle_snap('Lock')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Lock"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Lock"+"_Statusbutton", status)
Gui.addCommand('Draft_Snap_Lock', Draft_Snap_Lock())
@@ -74,7 +137,7 @@ class Draft_Snap_Midpoint(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Midpoint snap"))
super(Draft_Snap_Midpoint, self).__init__(name=_tr("Midpoint snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -87,13 +150,13 @@ class Draft_Snap_Midpoint(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Midpoint, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonmidpoint":
b.toggle()
status = Gui.Snapper.toggle_snap('Midpoint')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Midpoint"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Midpoint_Statusbutton", status)
Gui.addCommand('Draft_Snap_Midpoint', Draft_Snap_Midpoint())
@@ -106,7 +169,7 @@ class Draft_Snap_Perpendicular(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Perpendicular snap"))
super(Draft_Snap_Perpendicular, self).__init__(name=_tr("Perpendicular snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -121,13 +184,13 @@ class Draft_Snap_Perpendicular(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Perpendicular, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonperpendicular":
b.toggle()
status = Gui.Snapper.toggle_snap('Perpendicular')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Perpendicular"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Perpendicular_Statusbutton", status)
Gui.addCommand('Draft_Snap_Perpendicular', Draft_Snap_Perpendicular())
@@ -140,7 +203,7 @@ class Draft_Snap_Grid(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Grid snap"))
super(Draft_Snap_Grid, self).__init__(name=_tr("Grid snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -152,13 +215,13 @@ class Draft_Snap_Grid(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Grid, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtongrid":
b.toggle()
status = Gui.Snapper.toggle_snap('Grid')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Grid"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Grid_Statusbutton", status)
Gui.addCommand('Draft_Snap_Grid', Draft_Snap_Grid())
@@ -171,7 +234,7 @@ class Draft_Snap_Intersection(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Intersection snap"))
super(Draft_Snap_Intersection, self).__init__(name=_tr("Intersection snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -186,13 +249,13 @@ class Draft_Snap_Intersection(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Intersection, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonintersection":
b.toggle()
status = Gui.Snapper.toggle_snap('Intersection')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Intersection"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Intersection_Statusbutton", status)
Gui.addCommand('Draft_Snap_Intersection', Draft_Snap_Intersection())
@@ -205,7 +268,7 @@ class Draft_Snap_Parallel(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Parallel snap"))
super(Draft_Snap_Parallel, self).__init__(name=_tr("Parallel snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -218,13 +281,13 @@ class Draft_Snap_Parallel(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Parallel, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonparallel":
b.toggle()
status = Gui.Snapper.toggle_snap('Parallel')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Parallel"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Parallel_Statusbutton", status)
Gui.addCommand('Draft_Snap_Parallel', Draft_Snap_Parallel())
@@ -237,7 +300,7 @@ class Draft_Snap_Endpoint(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Endpoint snap"))
super(Draft_Snap_Endpoint, self).__init__(name=_tr("Endpoint snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -250,13 +313,13 @@ class Draft_Snap_Endpoint(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Endpoint, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonendpoint":
b.toggle()
status = Gui.Snapper.toggle_snap('Endpoint')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Endpoint"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Endpoint_Statusbutton", status)
Gui.addCommand('Draft_Snap_Endpoint', Draft_Snap_Endpoint())
@@ -270,7 +333,7 @@ class Draft_Snap_Angle(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Angle snap (30 and 45 degrees)"))
super(Draft_Snap_Angle, self).__init__(name=_tr("Angle snap (30 and 45 degrees)"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -284,13 +347,13 @@ class Draft_Snap_Angle(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Angle, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonangle":
b.toggle()
status = Gui.Snapper.toggle_snap('Angle')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Angle"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Angle_Statusbutton", status)
Gui.addCommand('Draft_Snap_Angle', Draft_Snap_Angle())
@@ -303,7 +366,7 @@ class Draft_Snap_Center(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Arc center snap"))
super(Draft_Snap_Center, self).__init__(name=_tr("Arc center snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -315,13 +378,13 @@ class Draft_Snap_Center(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Center, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtoncenter":
b.toggle()
status = Gui.Snapper.toggle_snap('Center')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Center"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Center_Statusbutton", status)
Gui.addCommand('Draft_Snap_Center', Draft_Snap_Center())
@@ -334,7 +397,7 @@ class Draft_Snap_Extension(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Edge extension snap"))
super(Draft_Snap_Extension, self).__init__(name=_tr("Edge extension snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -347,13 +410,13 @@ class Draft_Snap_Extension(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Extension, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonextension":
b.toggle()
status = Gui.Snapper.toggle_snap('Extension')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Extension"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Extension_Statusbutton", status)
Gui.addCommand('Draft_Snap_Extension', Draft_Snap_Extension())
@@ -366,7 +429,7 @@ class Draft_Snap_Near(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Near snap"))
super(Draft_Snap_Near, self).__init__(name=_tr("Near snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -378,13 +441,13 @@ class Draft_Snap_Near(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Near, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonpassive":
b.toggle()
status = Gui.Snapper.toggle_snap('Near')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Near"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Near_Statusbutton", status)
Gui.addCommand('Draft_Snap_Near', Draft_Snap_Near())
@@ -398,7 +461,7 @@ class Draft_Snap_Ortho(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Orthogonal snap"))
super(Draft_Snap_Ortho, self).__init__(name=_tr("Orthogonal snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -412,13 +475,13 @@ class Draft_Snap_Ortho(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Ortho, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonortho":
b.toggle()
status = Gui.Snapper.toggle_snap('Ortho')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Ortho"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Ortho"+"_Statusbutton", status)
Gui.addCommand('Draft_Snap_Ortho', Draft_Snap_Ortho())
@@ -431,7 +494,7 @@ class Draft_Snap_Special(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Special point snap"))
super(Draft_Snap_Special, self).__init__(name=_tr("Special point snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -444,13 +507,13 @@ class Draft_Snap_Special(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Special, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonspecial":
b.toggle()
status = Gui.Snapper.toggle_snap('Special')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Special"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Special_Statusbutton", status)
Gui.addCommand('Draft_Snap_Special', Draft_Snap_Special())
@@ -464,7 +527,7 @@ class Draft_Snap_Dimensions(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Dimension display"))
super(Draft_Snap_Dimensions, self).__init__(name=_tr("Dimension display"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -478,13 +541,13 @@ class Draft_Snap_Dimensions(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_Dimensions, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonDimensions":
b.toggle()
status = Gui.Snapper.toggle_snap('Dimensions')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_Dimensions"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_Dimensions"+"_Statusbutton", status)
Gui.addCommand('Draft_Snap_Dimensions', Draft_Snap_Dimensions())
@@ -500,7 +563,7 @@ class Draft_Snap_WorkingPlane(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Working plane snap"))
super(Draft_Snap_WorkingPlane, self).__init__(name=_tr("Working plane snap"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -520,13 +583,13 @@ class Draft_Snap_WorkingPlane(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(Draft_Snap_WorkingPlane, self).Activated()
if hasattr(Gui, "Snapper"):
if hasattr(Gui.Snapper, "toolbarButtons"):
for b in Gui.Snapper.toolbarButtons:
if b.objectName() == "SnapButtonWorkingPlane":
b.toggle()
status = Gui.Snapper.toggle_snap('WorkingPlane')
# change interface consistently
sync_snap_toolbar_button("Draft_Snap_WorkingPlane"+"_Button", status)
sync_snap_statusbar_button("Draft_Snap_WorkingPlane_Statusbutton", status)
Gui.addCommand('Draft_Snap_WorkingPlane', Draft_Snap_WorkingPlane())
@@ -539,7 +602,7 @@ class ShowSnapBar(gui_base.GuiCommandSimplest):
"""
def __init__(self):
super().__init__(name=_tr("Show snap toolbar"))
super(ShowSnapBar, self).__init__(name=_tr("Show snap toolbar"))
def GetResources(self):
"""Set icon, menu and tooltip."""
@@ -553,7 +616,7 @@ class ShowSnapBar(gui_base.GuiCommandSimplest):
def Activated(self):
"""Execute when the command is called."""
super().Activated()
super(ShowSnapBar, self).Activated()
if hasattr(Gui, "Snapper"):
Gui.Snapper.show()
@@ -160,7 +160,7 @@ class ToggleDisplayMode(gui_base.GuiCommandNeedsSelection):
Switches the display mode of selected objects from flatlines
to wireframe and back.
It inherits `GuiCommandNeedsSelection` to only be availbale
It inherits `GuiCommandNeedsSelection` to only be available
when there is a document and a selection.
See this class for more information.
"""
@@ -0,0 +1,389 @@
# ***************************************************************************
# * (c) 2009 Yorik van Havre <[email protected]> *
# * (c) 2010 Ken Cline <[email protected]> *
# * (c) 2020 Eliud Cabrera Castillo <[email protected]> *
# * *
# * 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
+1 -1
View File
@@ -62,7 +62,7 @@ def make_label(targetpoint=None, target=None, direction=None,
["Horizontal","Vertical","Custom"]
distance : Quantity
Lenght of the straight segment of label leader line
Length of the straight segment of label leader line
labeltype : String
Label type in
+189 -42
View File
@@ -1,11 +1,3 @@
"""Draft Statusbar commands.
This module provide the code for the Draft Statusbar, activated by initGui
"""
## @package init_draft_statusbar
# \ingroup DRAFT
# \brief This module provides the code for the Draft Statusbar.
# ***************************************************************************
# * *
# * Copyright (c) 2020 Carlo Pavan <[email protected]> *
@@ -29,12 +21,22 @@ This module provide the code for the Draft Statusbar, activated by initGui
# * USA *
# * *
# ***************************************************************************
"""Draft Statusbar commands.
import FreeCAD as App
import FreeCADGui as Gui
This module provide the code for the Draft Statusbar, activated by initGui
"""
## @package init_draft_statusbar
# \ingroup DRAFT
# \brief This module provides the code for the Draft Statusbar.
from PySide import QtCore
from PySide import QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCAD as App
import FreeCADGui as Gui
from draftutils.init_tools import get_draft_snap_commands
#----------------------------------------------------------------------------
# SCALE WIDGET FUNCTIONS
@@ -124,7 +126,8 @@ def label_to_scale(label):
return scale
except:
err = QT_TRANSLATE_NOOP("draft",
"Unable to convert input into a scale factor")
"Unable to convert input into a "
"scale factor")
App.Console.PrintWarning(err)
return None
@@ -137,22 +140,25 @@ def _set_scale(action):
mw = Gui.getMainWindow()
sb = mw.statusBar()
statuswidget = sb.findChild(QtGui.QToolBar,"draft_status_widget")
scale_widget = sb.findChild(QtGui.QToolBar,"draft_status_scale_widget")
if action.text() == QT_TRANSLATE_NOOP("draft","custom"):
dialog_text = QT_TRANSLATE_NOOP("draft",
"Set custom annotation scale in format x:x, x=x"
"Set custom annotation scale in "
"format x:x, x=x"
)
custom_scale = QtGui.QInputDialog.getText(None, "Set custom scale", dialog_text)
custom_scale = QtGui.QInputDialog.getText(None, "Set custom scale",
dialog_text)
if custom_scale[1]:
print(custom_scale[0])
scale = label_to_scale(custom_scale[0])
if scale is None: return
if scale is None:
return
param.SetFloat("DraftAnnotationScale", scale)
cs = scale_to_label(scale)
statuswidget.scaleLabel.setText(cs)
scale_widget.scaleLabel.setText(cs)
else:
text_scale = action.text()
statuswidget.scaleLabel.setText(text_scale)
scale_widget.scaleLabel.setText(text_scale)
scale = label_to_scale(text_scale)
param.SetFloat("DraftAnnotationScale", scale)
@@ -164,21 +170,131 @@ def init_draft_statusbar(sb):
"""
this function initializes draft statusbar
"""
param = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
# SNAP WIDGET - init ----------------------------------------------------
statuswidget = QtGui.QToolBar()
statuswidget.setObjectName("draft_status_widget")
snap_widget = QtGui.QToolBar()
snap_widget.setObjectName("draft_snap_widget")
snap_widget.setIconSize(QtCore.QSize(16,16))
# GRID BUTTON - init
gridbutton = QtGui.QPushButton(snap_widget)
gridbutton.setIcon(QtGui.QIcon.fromTheme("Draft",
QtGui.QIcon(":/icons/"
"Draft_Grid.svg")))
gridbutton.setToolTip(QT_TRANSLATE_NOOP("Draft",
"Toggles Grid On/Off"))
gridbutton.setObjectName("Grid_Statusbutton")
gridbutton.setWhatsThis("Draft_ToggleGrid")
gridbutton.setFlat(True)
QtCore.QObject.connect(gridbutton,QtCore.SIGNAL("clicked()"),
lambda f=Gui.doCommand,
arg='Gui.runCommand("Draft_ToggleGrid")':f(arg))
snap_widget.addWidget(gridbutton)
# SNAP BUTTON - init
snappref = param.GetString("snapModes","111111111101111")[0]
snapbutton = QtGui.QPushButton(snap_widget)
snapbutton.setIcon(QtGui.QIcon.fromTheme("Draft",
QtGui.QIcon(":/icons/"
"Snap_Lock.svg")))
snapbutton.setObjectName("Snap_Statusbutton")
snapbutton.setWhatsThis("Draft_ToggleLockSnap")
snapbutton.setToolTip(QT_TRANSLATE_NOOP("Draft",
"Object snapping"))
snapbutton.setCheckable(True)
snapbutton.setChecked(bool(int(snappref)))
snapbutton.setFlat(True)
snaps_menu = QtGui.QMenu(snapbutton)
snaps_menu.setObjectName("draft_statusbar_snap_toolbar")
snap_gui_commands = get_draft_snap_commands()
if 'Draft_Snap_Ortho' in snap_gui_commands:
snap_gui_commands.remove('Draft_Snap_Ortho')
if 'Draft_Snap_WorkingPlane' in snap_gui_commands:
snap_gui_commands.remove('Draft_Snap_WorkingPlane')
if 'Draft_Snap_Dimensions' in snap_gui_commands:
snap_gui_commands.remove('Draft_Snap_Dimensions')
Gui.Snapper.init_draft_snap_buttons(snap_gui_commands,snaps_menu, "_Statusbutton")
Gui.Snapper.restore_snap_buttons_state(snaps_menu, "_Statusbutton")
snapbutton.setMenu(snaps_menu)
snap_widget.addWidget(snapbutton)
# DIMENSION BUTTON - init
dimpref = param.GetString("snapModes","111111111101111")[13]
dimbutton = QtGui.QPushButton(snap_widget)
dimbutton.setIcon(QtGui.QIcon.fromTheme("Draft",
QtGui.QIcon(":/icons/"
"Snap_Dimensions.svg")))
dimbutton.setToolTip(QT_TRANSLATE_NOOP("Draft",
"Toggles Visual Aid Dimensions On/Off"))
dimbutton.setObjectName("Draft_Snap_Dimensions_Statusbutton")
dimbutton.setWhatsThis("Draft_ToggleDimensions")
dimbutton.setFlat(True)
dimbutton.setCheckable(True)
dimbutton.setChecked(bool(int(dimpref)))
QtCore.QObject.connect(dimbutton,QtCore.SIGNAL("clicked()"),
lambda f=Gui.doCommand,
arg='Gui.runCommand("Draft_Snap_Dimensions")':f(arg))
snap_widget.addWidget(dimbutton)
# ORTHO BUTTON - init
ortopref = param.GetString("snapModes","111111111101111")[10]
orthobutton = QtGui.QPushButton(snap_widget)
orthobutton.setIcon(QtGui.QIcon.fromTheme("Draft",
QtGui.QIcon(":/icons/"
"Snap_Ortho.svg")))
orthobutton.setObjectName("Draft_Snap_Ortho"+"_Statusbutton")
orthobutton.setWhatsThis("Draft_ToggleOrtho")
orthobutton.setToolTip(QT_TRANSLATE_NOOP("Draft",
"Toggles Ortho On/Off"))
orthobutton.setFlat(True)
orthobutton.setCheckable(True)
orthobutton.setChecked(bool(int(ortopref)))
QtCore.QObject.connect(orthobutton,QtCore.SIGNAL("clicked()"),
lambda f=Gui.doCommand,
arg='Gui.runCommand("Draft_Snap_Ortho")':f(arg))
snap_widget.addWidget(orthobutton)
# WORKINGPLANE BUTTON - init
wppref = param.GetString("snapModes","111111111101111")[14]
wpbutton = QtGui.QPushButton(snap_widget)
wpbutton.setIcon(QtGui.QIcon.fromTheme("Draft",
QtGui.QIcon(":/icons/"
"Snap_WorkingPlane.svg")))
wpbutton.setObjectName("Draft_Snap_WorkingPlane_Statusbutton")
wpbutton.setWhatsThis("Draft_ToggleWorkingPlaneSnap")
wpbutton.setToolTip(QT_TRANSLATE_NOOP("Draft",
"Toggles Constrain to Working Plane On/Off"))
wpbutton.setFlat(True)
wpbutton.setCheckable(True)
wpbutton.setChecked(bool(int(wppref)))
QtCore.QObject.connect(wpbutton,QtCore.SIGNAL("clicked()"),
lambda f=Gui.doCommand,
arg='Gui.runCommand("Draft_Snap_WorkingPlane")':f(arg))
snap_widget.addWidget(wpbutton)
# add snap widget to the statusbar
sb.insertPermanentWidget(2, snap_widget)
snap_widget.show()
# SCALE TOOL -------------------------------------------------------------
# SCALE WIDGET ----------------------------------------------------------
scale_widget = QtGui.QToolBar()
scale_widget.setObjectName("draft_status_scale_widget")
# get scales list according to system units
draft_scales = get_scales()
# get draft annotation scale
param = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
draft_annotation_scale = param.GetFloat("DraftAnnotationScale", 1.0)
# initializes scale widget
statuswidget.draft_scales = draft_scales
scale_widget.draft_scales = draft_scales
scaleLabel = QtGui.QPushButton("Scale")
scaleLabel.setObjectName("ScaleLabel")
scaleLabel.setFlat(True)
@@ -194,39 +310,70 @@ def init_draft_statusbar(sb):
scaleLabel.setText(scale_label)
tooltip = "Set the scale used by draft annotation tools"
scaleLabel.setToolTip(QT_TRANSLATE_NOOP("draft",tooltip))
statuswidget.addWidget(scaleLabel)
statuswidget.scaleLabel = scaleLabel
scale_widget.addWidget(scaleLabel)
scale_widget.scaleLabel = scaleLabel
# ADD TOOLS TO STATUS BAR ------------------------------------------------
sb.addPermanentWidget(statuswidget)
statuswidget.show()
# add scale widget to the statusbar
sb.insertPermanentWidget(3, scale_widget)
scale_widget.show()
def show_draft_statusbar():
"""
shows draft statusbar if present or initializes it
"""
params = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
display_statusbar = params.GetBool("DisplayStatusbar", True)
if not display_statusbar:
return
mw = Gui.getMainWindow()
if mw:
sb = mw.statusBar()
statuswidget = sb.findChild(QtGui.QToolBar,"draft_status_widget")
if statuswidget:
statuswidget.show()
else:
scale_widget = sb.findChild(QtGui.QToolBar,
"draft_status_scale_widget")
if scale_widget:
scale_widget.show()
elif params.GetBool("DisplayStatusbarScaleWidget", True):
init_draft_statusbar(sb)
snap_widget = sb.findChild(QtGui.QToolBar,"draft_snap_widget")
if snap_widget:
snap_widget.show()
elif params.GetBool("DisplayStatusbarSnapWidget", True):
init_draft_statusbar(sb)
def hide_draft_statusbar():
"""
hides draft statusbar if present
"""
mw = Gui.getMainWindow()
if mw:
sb = mw.statusBar()
statuswidget = sb.findChild(QtGui.QToolBar,"draft_status_widget")
if statuswidget:
statuswidget.hide()
else:
# when switching workbenches, the toolbar sometimes "jumps"
# out of the status bar to any other dock area...
statuswidget = mw.findChild(QtGui.QToolBar,"draft_status_widget")
if statuswidget:
statuswidget.hide()
if not mw:
return
sb = mw.statusBar()
# hide scale widget
scale_widget = sb.findChild(QtGui.QToolBar,
"draft_status_scale_widget")
if scale_widget:
scale_widget.hide()
else:
# when switching workbenches, the toolbar sometimes "jumps"
# out of the status bar to any other dock area...
scale_widget = mw.findChild(QtGui.QToolBar,
"draft_status_scale_widget")
if scale_widget:
scale_widget.hide()
# hide snap widget
snap_widget = sb.findChild(QtGui.QToolBar,"draft_snap_widget")
if snap_widget:
snap_widget.hide()
else:
# when switching workbenches, the toolbar sometimes "jumps"
# out of the status bar to any other dock area...
snap_widget = mw.findChild(QtGui.QToolBar,"draft_snap_widget")
if snap_widget:
snap_widget.hide()
+9 -9
View File
@@ -116,15 +116,15 @@ def get_draft_utility_commands():
def get_draft_snap_commands():
"""Return the snapping commands list."""
return ['Draft_Snap_Lock', 'Draft_Snap_Midpoint',
'Draft_Snap_Perpendicular',
'Draft_Snap_Grid', 'Draft_Snap_Intersection',
'Draft_Snap_Parallel',
'Draft_Snap_Endpoint', 'Draft_Snap_Angle',
'Draft_Snap_Center',
'Draft_Snap_Extension', 'Draft_Snap_Near',
'Draft_Snap_Ortho', 'Draft_Snap_Special',
'Draft_Snap_Dimensions', 'Draft_Snap_WorkingPlane']
return ['Draft_Snap_Lock',
'Draft_Snap_Endpoint', 'Draft_Snap_Midpoint',
'Draft_Snap_Center', 'Draft_Snap_Angle',
'Draft_Snap_Intersection', 'Draft_Snap_Perpendicular',
'Draft_Snap_Extension', 'Draft_Snap_Parallel',
'Draft_Snap_Special', 'Draft_Snap_Near',
'Draft_Snap_Ortho', 'Draft_Snap_Grid',
'Draft_Snap_WorkingPlane', 'Draft_Snap_Dimensions',
]
def init_draft_toolbars(workbench):
@@ -225,6 +225,10 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
def attach(self, vobj):
'''Setup the scene sub-graph of the view provider'''
# backwards compatibility
self.ScaleMultiplier = 1.00
self.Object = vobj.Object
self.color = coin.SoBaseColor()
self.font = coin.SoFont()
@@ -341,8 +345,8 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
self.p2 = self.p1
self.p3 = self.p4
if proj:
if hasattr(obj.ViewObject,"ExtLines") and hasattr(obj.ViewObject,"ScaleMultiplier"):
dmax = obj.ViewObject.ExtLines.Value * obj.ViewObject.ScaleMultiplier
if hasattr(obj.ViewObject,"ExtLines"):
dmax = obj.ViewObject.ExtLines.Value * self.ScaleMultiplier
if dmax and (proj.Length > dmax):
if (dmax > 0):
self.p1 = self.p2.add(DraftVecUtils.scaleTo(proj,dmax))
@@ -402,8 +406,8 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
rot3 = App.Placement(DraftVecUtils.getPlaneRotation(u3,v3,norm)).Rotation.Q
self.transExtOvershoot1.rotation.setValue((rot3[0],rot3[1],rot3[2],rot3[3]))
self.transExtOvershoot2.rotation.setValue((rot3[0],rot3[1],rot3[2],rot3[3]))
if hasattr(obj.ViewObject,"TextSpacing") and hasattr(obj.ViewObject,"ScaleMultiplier"):
ts = obj.ViewObject.TextSpacing.Value * obj.ViewObject.ScaleMultiplier
if hasattr(obj.ViewObject,"TextSpacing"):
ts = obj.ViewObject.TextSpacing.Value * self.ScaleMultiplier
offset = DraftVecUtils.scaleTo(v1,ts)
else:
offset = DraftVecUtils.scaleTo(v1,0.05)
@@ -474,11 +478,13 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
def onChanged(self, vobj, prop):
"""called when a view property has changed"""
if prop == "ScaleMultiplier" and hasattr(vobj,"ScaleMultiplier"):
if vobj.ScaleMultiplier: # don't set if zero
self.ScaleMultiplier = vobj.ScaleMultiplier
# update all dimension values
if hasattr(self,"font"):
self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier
self.font.size = vobj.FontSize.Value * self.ScaleMultiplier
if hasattr(self,"font3d"):
self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier
self.font3d.size = vobj.FontSize.Value * 100 * self.ScaleMultiplier
if hasattr(self,"node") and hasattr(self,"p2") and hasattr(vobj,"ArrowSize"):
self.remove_dim_arrows()
self.draw_dim_arrows(vobj)
@@ -492,10 +498,10 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
vobj.Object.touch()
elif (prop == "FontSize") and hasattr(vobj,"FontSize"):
if hasattr(self,"font") and hasattr(vobj,"ScaleMultiplier"):
self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier
if hasattr(self,"font3d") and hasattr(vobj,"ScaleMultiplier"):
self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier
if hasattr(self,"font"):
self.font.size = vobj.FontSize.Value * self.ScaleMultiplier
if hasattr(self,"font3d"):
self.font3d.size = vobj.FontSize.Value * 100 * self.ScaleMultiplier
vobj.Object.touch()
elif (prop == "FontName") and hasattr(vobj,"FontName"):
@@ -514,22 +520,19 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
elif (prop in ["ArrowSize","ArrowType"]) and hasattr(vobj,"ArrowSize"):
if hasattr(self,"node") and hasattr(self,"p2"):
if hasattr(vobj,"ScaleMultiplier"):
self.remove_dim_arrows()
self.draw_dim_arrows(vobj)
vobj.Object.touch()
self.remove_dim_arrows()
self.draw_dim_arrows(vobj)
vobj.Object.touch()
elif (prop == "DimOvershoot") and hasattr(vobj,"DimOvershoot"):
if hasattr(vobj,"ScaleMultiplier"):
self.remove_dim_overshoot()
self.draw_dim_overshoot(vobj)
vobj.Object.touch()
self.remove_dim_overshoot()
self.draw_dim_overshoot(vobj)
vobj.Object.touch()
elif (prop == "ExtOvershoot") and hasattr(vobj,"ExtOvershoot"):
if hasattr(vobj,"ScaleMultiplier"):
self.remove_ext_overshoot()
self.draw_ext_overshoot(vobj)
vobj.Object.touch()
self.remove_ext_overshoot()
self.draw_ext_overshoot(vobj)
vobj.Object.touch()
elif (prop == "ShowLine") and hasattr(vobj,"ShowLine"):
if vobj.ShowLine:
@@ -559,7 +562,7 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
# set scale
symbol = utils.ARROW_TYPES.index(vobj.ArrowType)
s = vobj.ArrowSize.Value * vobj.ScaleMultiplier
s = vobj.ArrowSize.Value * self.ScaleMultiplier
self.trans1.scaleFactor.setValue((s,s,s))
self.trans2.scaleFactor.setValue((s,s,s))
@@ -593,7 +596,7 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
from pivy import coin
# set scale
s = vobj.DimOvershoot.Value * vobj.ScaleMultiplier
s = vobj.DimOvershoot.Value * self.ScaleMultiplier
self.transDimOvershoot1.scaleFactor.setValue((s,s,s))
self.transDimOvershoot2.scaleFactor.setValue((s,s,s))
@@ -624,7 +627,7 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase):
from pivy import coin
# set scale
s = vobj.ExtOvershoot.Value * vobj.ScaleMultiplier
s = vobj.ExtOvershoot.Value * self.ScaleMultiplier
self.transExtOvershoot1.scaleFactor.setValue((s,s,s))
self.transExtOvershoot2.scaleFactor.setValue((s,s,s))
@@ -728,6 +731,8 @@ class ViewProviderAngularDimension(ViewProviderDimensionBase):
self.onChanged(vobj,"FontName")
self.onChanged(vobj,"ArrowType")
self.onChanged(vobj,"LineColor")
# backwards compatibility
self.ScaleMultiplier = 1.00
def updateData(self, obj, prop):
if hasattr(self,"arc"):
@@ -853,21 +858,23 @@ class ViewProviderAngularDimension(ViewProviderDimensionBase):
def onChanged(self, vobj, prop):
if prop == "ScaleMultiplier" and hasattr(vobj,"ScaleMultiplier"):
if vobj.ScaleMultiplier:
self.ScaleMultiplier = vobj.ScaleMultiplier
# update all dimension values
if hasattr(self,"font"):
self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier
self.font.size = vobj.FontSize.Value * self.ScaleMultiplier
if hasattr(self,"font3d"):
self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier
self.font3d.size = vobj.FontSize.Value * 100 * self.ScaleMultiplier
if hasattr(self,"node") and hasattr(self,"p2") and hasattr(vobj,"ArrowSize"):
self.remove_dim_arrows()
self.draw_dim_arrows(vobj)
self.updateData(vobj.Object,"Start")
vobj.Object.touch()
elif prop == "FontSize" and hasattr(vobj,"ScaleMultiplier"):
elif prop == "FontSize":
if hasattr(self,"font"):
self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier
self.font.size = vobj.FontSize.Value * self.ScaleMultiplier
if hasattr(self,"font3d"):
self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier
self.font3d.size = vobj.FontSize.Value * 100 * self.ScaleMultiplier
vobj.Object.touch()
elif prop == "FontName":
if hasattr(self,"font") and hasattr(self,"font3d"):
@@ -880,7 +887,7 @@ class ViewProviderAngularDimension(ViewProviderDimensionBase):
elif prop == "LineWidth":
if hasattr(self,"drawstyle"):
self.drawstyle.lineWidth = vobj.LineWidth
elif prop in ["ArrowSize","ArrowType"] and hasattr(vobj,"ScaleMultiplier"):
elif prop in ["ArrowSize","ArrowType"]:
if hasattr(self,"node") and hasattr(self,"p2"):
self.remove_dim_arrows()
self.draw_dim_arrows(vobj)
@@ -901,7 +908,7 @@ class ViewProviderAngularDimension(ViewProviderDimensionBase):
# set scale
symbol = utils.ARROW_TYPES.index(vobj.ArrowType)
s = vobj.ArrowSize.Value * vobj.ScaleMultiplier
s = vobj.ArrowSize.Value * self.ScaleMultiplier
self.trans1.scaleFactor.setValue((s,s,s))
self.trans2.scaleFactor.setValue((s,s,s))
@@ -926,4 +933,4 @@ class ViewProviderAngularDimension(ViewProviderDimensionBase):
self.node3d.insertChild(self.marks,2)
def getIcon(self):
return ":/icons/Draft_DimensionAngular.svg"
return ":/icons/Draft_DimensionAngular.svg"
+12 -10
View File
@@ -54,10 +54,6 @@ class ViewProviderText(ViewProviderDraftAnnotation):
def set_properties(self, vobj):
vobj.addProperty("App::PropertyFloat","ScaleMultiplier",
"Annotation",QT_TRANSLATE_NOOP("App::Property",
"Dimension size overall multiplier"))
vobj.addProperty("App::PropertyLength","FontSize",
"Text",QT_TRANSLATE_NOOP("App::Property",
"The size of the text"))
@@ -93,6 +89,10 @@ class ViewProviderText(ViewProviderDraftAnnotation):
def attach(self,vobj):
'''Setup the scene sub-graph of the view provider'''
# backwards compatibility
self.ScaleMultiplier = 1.00
self.mattext = coin.SoMaterial()
textdrawstyle = coin.SoDrawStyle()
textdrawstyle.style = coin.SoDrawStyle.FILLED
@@ -123,7 +123,6 @@ class ViewProviderText(ViewProviderDraftAnnotation):
self.onChanged(vobj,"Justification")
self.onChanged(vobj,"LineSpacing")
def getDisplayModes(self,vobj):
return ["2D text","3D text"]
@@ -148,8 +147,11 @@ class ViewProviderText(ViewProviderDraftAnnotation):
def onChanged(self,vobj,prop):
if prop == "ScaleMultiplier":
if "ScaleMultiplier" in vobj.PropertiesList and "FontSize" in vobj.PropertiesList:
self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier
if "ScaleMultiplier" in vobj.PropertiesList:
if vobj.ScaleMultiplier:
self.ScaleMultiplier = vobj.ScaleMultiplier
if "FontSize" in vobj.PropertiesList:
self.font.size = vobj.FontSize.Value * self.ScaleMultiplier
elif prop == "TextColor":
if "TextColor" in vobj.PropertiesList:
l = vobj.TextColor
@@ -158,8 +160,8 @@ class ViewProviderText(ViewProviderDraftAnnotation):
if "FontName" in vobj.PropertiesList:
self.font.name = vobj.FontName.encode("utf8")
elif prop == "FontSize":
if "FontSize" in vobj.PropertiesList and "ScaleMultiplier" in vobj.PropertiesList:
self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier
if "FontSize" in vobj.PropertiesList:
self.font.size = vobj.FontSize.Value * self.ScaleMultiplier
elif prop == "Justification":
try:
if getattr(vobj, "Justification", None) is not None:
@@ -177,4 +179,4 @@ class ViewProviderText(ViewProviderDraftAnnotation):
elif prop == "LineSpacing":
if "LineSpacing" in vobj.PropertiesList:
self.text2d.spacing = vobj.LineSpacing
self.text3d.spacing = vobj.LineSpacing
self.text3d.spacing = vobj.LineSpacing
+1 -1
View File
@@ -485,7 +485,7 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct
svg += getPath(obj.Edges,pathname="")
elif getType(obj) == "Dimension":
elif getType(obj) in ["Dimension","LinearDimension"]:
if gui:
if not obj.ViewObject:
print ("export of dimensions to SVG is only available in GUI mode")
+1 -1
View File
@@ -3817,7 +3817,7 @@ def export(objectslist, filename, nospline=False, lwPoly=False):
style='STANDARD',
layer=getStrGroup(ob)))
elif Draft.getType(ob) == "Dimension":
elif Draft.getType(ob) in ["Dimension","LinearDimension"]:
p1 = DraftVecUtils.tup(ob.Start)
p2 = DraftVecUtils.tup(ob.End)
base = Part.LineSegment(ob.Start, ob.End).toShape()
+1 -1
View File
@@ -1526,7 +1526,7 @@ class svgHandler(xml.sax.ContentHandler):
# see issue #2062
sh = sh.transformGeometry(transform)
return sh
elif Draft.getType(sh) == "Dimension":
elif Draft.getType(sh) in ["Dimension","LinearDimension"]:
pts = []
for p in [sh.Start, sh.End, sh.Dimline]:
cp = Vector(p)
@@ -703,6 +703,8 @@ def get_displacement_scale_factor(res_obj):
z_span = abs(p_z_max - p_z_min)
span = max(x_span, y_span, z_span)
max_disp = max(x_max, y_max, z_max)
if max_disp == 0.0:
return 0.0 # avoid float division by zero
# FIXME - add max_allowed_disp to Preferences
max_allowed_disp = 0.01 * span
scale = max_allowed_disp / max_disp
+5 -4
View File
@@ -1426,6 +1426,7 @@ def build_mesh_faces_of_volume_elements(
FreeCAD.Console.PrintLog("VolElement: {}\n".format(veID))
vol_node_ct = len(femelement_table[veID])
face_node_indexs = sorted(face_nodenumber_table[veID])
node_numbers = ()
if vol_node_ct == 10:
FreeCAD.Console.PrintLog(" --> tetra10 --> tria6 face\n")
# node order of face in tetra10 volume element
@@ -1441,7 +1442,7 @@ def build_mesh_faces_of_volume_elements(
else:
FreeCAD.Console.PrintError(
"Error in build_mesh_faces_of_volume_elements(): "
"hexa20: face not found! {}\n"
"tetra10: face not found! {}\n"
.format(face_node_indexs)
)
elif vol_node_ct == 4:
@@ -1459,7 +1460,7 @@ def build_mesh_faces_of_volume_elements(
else:
FreeCAD.Console.PrintError(
"Error in build_mesh_faces_of_volume_elements(): "
"hexa20: face not found! {}\n"
"tetra4: face not found! {}\n"
.format(face_node_indexs)
)
elif vol_node_ct == 20:
@@ -1503,7 +1504,7 @@ def build_mesh_faces_of_volume_elements(
else:
FreeCAD.Console.PrintError(
"Error in build_mesh_faces_of_volume_elements(): "
"hexa20: face not found! {}\n"
"hexa8: face not found! {}\n"
.format(face_node_indexs)
)
elif vol_node_ct == 15:
@@ -1543,7 +1544,7 @@ def build_mesh_faces_of_volume_elements(
else:
FreeCAD.Console.PrintError(
"Error in build_mesh_faces_of_volume_elements(): "
"pent6: face not found! {}\n"
"penta6: face not found! {}\n"
.format(face_node_indexs)
)
else:
+1 -1
View File
@@ -469,7 +469,7 @@ an empty dictionary if there is no intersection.
<UserDocu>getPlanarSegments(dev,[min faces=0]) -> list
Get all planes of the mesh as segment.
In the worst case each triangle can be regarded as single
plane if none of its neighours is coplanar.</UserDocu>
plane if none of its neighbors are coplanar.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getSegmentsOfType" Const="true">
+1 -1
View File
@@ -750,7 +750,7 @@ private:
p1.Transform(loc.Transformation());
p2.Transform(loc.Transformation());
p3.Transform(loc.Transformation());
// TODO: verify if tolerence should be hard coded
// TODO: verify if tolerance should be hard coded
if (!p1.IsEqual(p2, 0.01) && !p2.IsEqual(p3, 0.01) && !p3.IsEqual(p1, 0.01)) {
PyObject *t1 = PyTuple_Pack(3, PyFloat_FromDouble(p1.X()), PyFloat_FromDouble(p1.Y()), PyFloat_FromDouble(p1.Z()));
PyObject *t2 = PyTuple_Pack(3, PyFloat_FromDouble(p2.X()), PyFloat_FromDouble(p2.Y()), PyFloat_FromDouble(p2.Z()));
+13 -4
View File
@@ -40,7 +40,7 @@
<property name="toolTip">
<string>The default color for new shapes</string>
</property>
<property name="color" stdset="0">
<property name="color">
<color>
<red>204</red>
<green>204</green>
@@ -89,7 +89,7 @@
<property name="toolTip">
<string>The default line color for new shapes</string>
</property>
<property name="color" stdset="0">
<property name="color">
<color>
<red>25</red>
<green>25</green>
@@ -157,7 +157,7 @@
<property name="toolTip">
<string>The default color for new vertices</string>
</property>
<property name="color" stdset="0">
<property name="color">
<color>
<red>25</red>
<green>25</green>
@@ -225,7 +225,7 @@
<property name="toolTip">
<string>The color of bounding boxes in the 3D view</string>
</property>
<property name="color" stdset="0">
<property name="color">
<color>
<red>255</red>
<green>255</green>
@@ -248,6 +248,12 @@
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Bottom side of surface will be rendered the same way than top.
If not checked, it depends on the option &quot;Backlight color&quot;
(preferences section Display -&gt; 3D View); either the backlight color
will be used or black.</string>
</property>
<property name="text">
<string>Two-side rendering</string>
</property>
@@ -303,6 +309,9 @@
</item>
<item row="0" column="1">
<widget class="Gui::PrefColorButton" name="AnnotationTextColor">
<property name="toolTip">
<string>Text color for document annotations</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>AnnotationTextColor</cstring>
</property>
+18 -1
View File
@@ -16,6 +16,8 @@ if(BUILD_GUI)
TestPartDesignGui.py
InvoluteGearFeature.py
InvoluteGearFeature.ui
SprocketFeature.py
SprocketFeature.ui
)
endif(BUILD_GUI)
@@ -60,6 +62,13 @@ set(PartDesign_GearScripts
fcgear/svggear.py
)
set(PartDesign_SprocketScripts
fcsprocket/__init__.py
fcsprocket/fcsprocket.py
fcsprocket/fcsprocketdialog.py
fcsprocket/sprocket.py
)
set(PartDesign_WizardShaft
WizardShaft/__init__.py
WizardShaft/WizardShaft.svg
@@ -76,6 +85,7 @@ add_custom_target(PartDesignScripts ALL SOURCES
${PartDesign_OtherScripts}
${PartDesign_TestScripts}
${PartDesign_GearScripts}
${PartDesign_SprocketScripts}
)
fc_target_copy_resource(PartDesignScripts
@@ -85,6 +95,7 @@ fc_target_copy_resource(PartDesignScripts
${PartDesign_OtherScripts}
${PartDesign_TestScripts}
${PartDesign_GearScripts}
${PartDesign_SprocketScripts}
)
INSTALL(
@@ -113,7 +124,13 @@ INSTALL(
${PartDesign_GearScripts}
DESTINATION
Mod/PartDesign/fcgear
)
INSTALL(
FILES
${PartDesign_SprocketScripts}
DESTINATION
Mod/PartDesign/fcsprocket
)
if(BUILD_FEM)
@@ -37,6 +37,7 @@
<file>icons/PartDesign_Revolution.svg</file>
<file>icons/PartDesign_Scaled.svg</file>
<file>icons/PartDesign_ShapeBinder.svg</file>
<file>icons/PartDesign_Sprocket.svg</file>
<file>icons/PartDesign_SubShapeBinder.svg</file>
<file>icons/PartDesign_Subtractive_Box.svg</file>
<file>icons/PartDesign_Subtractive_Cone.svg</file>
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 89 KiB

+4 -3
View File
@@ -509,9 +509,10 @@ Gui::MenuItem* Workbench::setupMenuBar() const
<< "PartDesign_Boolean"
<< "Separator"
//<< "PartDesign_Hole"
<< "PartDesign_InvoluteGear"
<< "Separator"
<< "PartDesign_Migrate";
<< "PartDesign_Migrate"
<< "PartDesign_Sprocket"
<< "PartDesign_InvoluteGear";
// For 0.13 a couple of python packages like numpy, matplotlib and others
// are not deployed with the installer on Windows. Thus, the WizardShaft is
+1
View File
@@ -51,6 +51,7 @@ class PartDesignWorkbench ( Workbench ):
import PartDesign
try:
from PartDesign import InvoluteGearFeature
from PartDesign import SprocketFeature
except ImportError:
print("Involute gear module cannot be loaded")
#try:
+248
View File
@@ -0,0 +1,248 @@
#***************************************************************************
#* Copyright (c) 2020 Adam Spontarelli <[email protected]> *
#* *
#* 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 *
#* *
#***************************************************************************
import FreeCAD, Part
from fcsprocket import fcsprocket
from fcsprocket import sprocket
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from FreeCADGui import PySideUic as uic
__title__="PartDesign SprocketObject management"
__author__ = "Adam Spontarelli"
__url__ = "http://www.freecadweb.org"
def makeSprocket(name):
"""
makeSprocket(name): makes a Sprocket
"""
obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",name)
Sprocket(obj)
if FreeCAD.GuiUp:
ViewProviderSprocket(obj.ViewObject)
#FreeCAD.ActiveDocument.recompute()
if FreeCAD.GuiUp:
body=FreeCADGui.ActiveDocument.ActiveView.getActiveObject("pdbody")
part=FreeCADGui.ActiveDocument.ActiveView.getActiveObject("part")
if body:
body.Group=body.Group+[obj]
elif part:
part.Group=part.Group+[obj]
return obj
class CommandSprocket:
"""
the Fem Sprocket command definition
"""
def GetResources(self):
return {'Pixmap' : 'PartDesign_Sprocket',
'MenuText': QtCore.QT_TRANSLATE_NOOP("PartDesign_Sprocket","Sprocket..."),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PartDesign_Sprocket","Creates or edit the sprocket definition.")}
def Activated(self):
FreeCAD.ActiveDocument.openTransaction("Create Sprocket")
FreeCADGui.addModule("SprocketFeature")
FreeCADGui.doCommand("SprocketFeature.makeSprocket('Sprocket')")
FreeCADGui.doCommand("Gui.activeDocument().setEdit(App.ActiveDocument.ActiveObject.Name,0)")
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class Sprocket:
"""
The Sprocket object
"""
def __init__(self,obj):
self.Type = "Sprocket"
obj.addProperty("App::PropertyInteger","NumberOfTeeth","Sprocket","Number of gear teeth")
obj.addProperty("App::PropertyLength","Pitch","Sprocket","Chain Pitch")
obj.addProperty("App::PropertyLength","RollerDiameter","Sprocket","Roller Diameter")
obj.addProperty("App::PropertyString","ANSISize","Sprocket","ANSI Size")
obj.NumberOfTeeth = 50
obj.Pitch = "0.375 in"
obj.RollerDiameter = "0.20 in"
obj.ANSISize = "35"
obj.Proxy = self
def execute(self,obj):
w = fcsprocket.FCWireBuilder()
sprocket.CreateSprocket(w, obj.Pitch.Value, obj.NumberOfTeeth, obj.RollerDiameter.Value)
sprocketw = Part.Wire([o.toShape() for o in w.wire])
obj.Shape = sprocketw
obj.positionBySupport();
return
class ViewProviderSprocket:
"""
A View Provider for the Sprocket object
"""
def __init__(self,vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/PartDesign_Sprocket.svg"
def attach(self, vobj):
self.ViewObject = vobj
self.Object = vobj.Object
def setEdit(self,vobj,mode):
taskd = SprocketTaskPanel(self.Object,mode)
taskd.obj = vobj.Object
taskd.update()
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self,vobj,mode):
FreeCADGui.Control.closeDialog()
return
def __getstate__(self):
return None
def __setstate__(self,state):
return None
class SprocketTaskPanel:
"""
The editmode TaskPanel for Sprocket objects
"""
def __init__(self,obj,mode):
self.obj = obj
self.form=FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/PartDesign/SprocketFeature.ui")
self.form.setWindowIcon(QtGui.QIcon(":/icons/PartDesign_Sprocket.svg"))
QtCore.QObject.connect(self.form.Quantity_Pitch, QtCore.SIGNAL("valueChanged(double)"), self.pitchChanged)
QtCore.QObject.connect(self.form.Quantity_RollerDiameter, QtCore.SIGNAL("valueChanged(double)"), self.rollerDiameterChanged)
QtCore.QObject.connect(self.form.spinBox_NumberOfTeeth, QtCore.SIGNAL("valueChanged(int)"), self.numTeethChanged)
QtCore.QObject.connect(self.form.comboBox_ANSISize, QtCore.SIGNAL("currentTextChanged(const QString)"), self.ANSISizeChanged)
self.update()
if mode == 0: # fresh created
self.obj.Proxy.execute(self.obj) # calculate once
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def transferTo(self):
"""
Transfer from the dialog to the object
"""
self.obj.NumberOfTeeth = self.form.spinBox_NumberOfTeeth.value()
self.obj.Pitch = self.form.Quantity_Pitch.text()
self.obj.RollerDiameter = self.form.Quantity_RollerDiameter.text()
self.obj.ANSISize = self.form.comboBox_ANSISize.currentText()
def transferFrom(self):
"""
Transfer from the object to the dialog
"""
self.form.spinBox_NumberOfTeeth.setValue(self.obj.NumberOfTeeth)
self.form.Quantity_Pitch.setText(self.obj.Pitch.UserString)
self.form.Quantity_RollerDiameter.setText(self.obj.RollerDiameter.UserString)
self.form.comboBox_ANSISize.setCurrentText(self.obj.ANSISize)
def pitchChanged(self, value):
self.obj.Pitch = value
self.obj.Proxy.execute(self.obj)
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def ANSISizeChanged(self, size):
"""
ANSI B29.1-2011 standard roller chain sizes in USCS units (inches)
{size: [Pitch, Roller Diameter]}
"""
ANSIRollerTable = {"25": [0.250, 0.130],
"35": [0.375, 0.200],
"41": [0.500, 0.306],
"40": [0.500, 0.312],
"50": [0.625, 0.400],
"60": [0.750, 0.469],
"80": [1.000, 0.625],
"100":[1.250, 0.750],
"120":[1.500, 0.875],
"140":[1.750, 1.000],
"160":[2.000, 1.125],
"180":[2.250, 1.460],
"200":[2.500, 1.562],
"240":[3.000, 1.875]}
self.obj.Pitch = str(ANSIRollerTable[size][0]) + " in"
self.obj.RollerDiameter = str(ANSIRollerTable[size][1]) + " in"
self.form.Quantity_Pitch.setText(self.obj.Pitch.UserString)
self.form.Quantity_RollerDiameter.setText(self.obj.RollerDiameter.UserString)
self.obj.Proxy.execute(self.obj)
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def rollerDiameterChanged(self, value):
self.obj.RollerDiameter = value
self.obj.Proxy.execute(self.obj)
def numTeethChanged(self, value):
self.obj.NumberOfTeeth = value
self.obj.Proxy.execute(self.obj)
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Ok) | int(QtGui.QDialogButtonBox.Cancel)| int(QtGui.QDialogButtonBox.Apply)
def clicked(self,button):
if button == QtGui.QDialogButtonBox.Apply:
self.transferTo()
self.obj.Proxy.execute(self.obj)
def update(self):
self.transferFrom()
def accept(self):
self.transferTo()
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.resetEdit()
def reject(self):
FreeCADGui.ActiveDocument.resetEdit()
FreeCAD.ActiveDocument.abortTransaction()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PartDesign_Sprocket', CommandSprocket())
+232
View File
@@ -0,0 +1,232 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SprocketParameter</class>
<widget class="QWidget" name="SprocketParameter">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>195</width>
<height>142</height>
</rect>
</property>
<property name="windowTitle">
<string>Sprocket parameter</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Number of teeth:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBox_NumberOfTeeth">
<property name="minimum">
<number>3</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>ANSI Size:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBox_ANSISize">
<item>
<property name="text">
<string>25</string>
</property>
</item>
<item>
<property name="text">
<string>35</string>
</property>
</item>
<item>
<property name="text">
<string>41</string>
</property>
</item>
<item>
<property name="text">
<string>40</string>
</property>
</item>
<item>
<property name="text">
<string>50</string>
</property>
</item>
<item>
<property name="text">
<string>60</string>
</property>
</item>
<item>
<property name="text">
<string>80</string>
</property>
</item>
<item>
<property name="text">
<string>100</string>
</property>
</item>
<item>
<property name="text">
<string>120</string>
</property>
</item>
<item>
<property name="text">
<string>140</string>
</property>
</item>
<item>
<property name="text">
<string>160</string>
</property>
</item>
<item>
<property name="text">
<string>180</string>
</property>
</item>
<item>
<property name="text">
<string>200</string>
</property>
</item>
<item>
<property name="text">
<string>240</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Chain Pitch:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::InputField" name="Quantity_Pitch">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="unit" stdset="0">
<string notr="true">in</string>
</property>
<property name="decimals" stdset="0">
<number>3</number>
</property>
<property name="maximum" stdset="0">
<double>2000.000000000000000</double>
</property>
<property name="minimum" stdset="0">
<double>0.01</double>
</property>
<property name="singleStep" stdset="0">
<double>0.001</double>
</property>
<property name="value" stdset="0">
<double>0.375</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Roller Diameter:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::InputField" name="Quantity_RollerDiameter">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="unit" stdset="0">
<string notr="true">in</string>
</property>
<property name="decimals" stdset="0">
<number>3</number>
</property>
<property name="maximum" stdset="0">
<double>50.000000000000000</double>
</property>
<property name="minimum" stdset="0">
<double>0.01</double>
</property>
<property name="singleStep" stdset="0">
<double>0.01</double>
</property>
<property name="value" stdset="0">
<double>0.20</double>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+24
View File
@@ -0,0 +1,24 @@
================================================
FCSprocket: a Sprocket Generator for FreeCAD
================================================
This is a simple sprocket generation tool. Sprockets are used in combination
with roller chain to transmit power. The tooth profiles are drawn according
to ANSI standards from the methods outlined in:
Standard handbook of chains : chains for power transmission and material
handling. Boca Raton: Taylor & Francis, 2006. Print.
AND
Oberg, Erik, et al. Machinery's handbook : a reference book for the
mechanical engineer, designer, manufacturing engineer, draftsman,
toolmaker, and machinist. New York: Industrial Press, 2016. Print.
This code is based on the work of David Douard and his implementation of the
gear generator (fcgear) found in FreeCAD.
Copyright 2020 Adam Spontarelli <adam@vector-space.org>.
Distributed under the LGPL licence.
@@ -0,0 +1 @@
+105
View File
@@ -0,0 +1,105 @@
# (c) 2014 David Douard <[email protected]>
#
# 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.
#
# FCGear 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 FCGear; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
from math import cos, sin, pi, acos, asin, atan, sqrt
import FreeCAD, Part
from FreeCAD import Base, Console
from . import sprocket
rotate = sprocket.rotate
def makeSprocket(P, N, Dr):
if FreeCAD.ActiveDocument is None:
FreeCAD.newDocument("Sprocket")
doc = FreeCAD.ActiveDocument
w = FCWireBuilder()
sprocket.CreateSprocket(w, P, N, Dr)
sprocketw = Part.Wire([o.toShape() for o in w.wire])
sprocket = doc.addObject("Part::Feature", "Sprocket")
sprocket.Shape = sprocketw
return sprocket
class FCWireBuilder(object):
"""A helper class to prepare a Part.Wire object"""
def __init__(self):
self.pos = None
self.theta = 0.0
self.wire = []
def move(self, p):
"""set current position"""
self.pos = Base.Vector(*p)
def line(self, p):
"""Add a segment between self.pos and p"""
p = rotate(p, self.theta)
end = Base.Vector(*p)
self.wire.append(Part.LineSegment(self.pos, end))
self.pos = end
def arc(self, p, r, sweep):
""""Add an arc from self.pos to p which radius is r
sweep (0 or 1) determine the orientation of the arc
"""
p = rotate(p, self.theta)
end = Base.Vector(*p)
mid = Base.Vector(*(midpoints(p, self.pos, r)[sweep]))
self.wire.append(Part.Arc(self.pos, mid, end))
self.pos = end
def curve(self, *points):
"""Add a Bezier curve from self.pos to points[-1]
every other points are the control points of the Bezier curve (which
will thus be of degree len(points) )
"""
points = [Base.Vector(*rotate(p, self.theta)) for p in points]
bz = Part.BezierCurve()
bz.setPoles([self.pos] + points)
self.wire.append(bz)
self.pos = points[-1]
def close(self):
pass
def midpoints(p1, p2, r):
"""A very ugly function that returns the midpoint of a p1 and p2
on the circle which radius is r and which pass through p1 and
p2
Return the 2 possible solutions
"""
vx, vy = p2[0]-p1[0], p2[1]-p1[1]
b = (vx**2 + vy**2)**.5
v = (vx/b, vy/b)
cosA = b**2 / (2*b*r)
A = acos(cosA)
vx, vy = rotate(v, A)
c1 = (p1[0]+r*vx, p1[1]+r*vy)
m1x, m1y = ((p1[0]+p2[0])/2 - c1[0], (p1[1]+p2[1])/2 - c1[1])
dm1 = (m1x**2+m1y**2)**.5
m1x, m1y = (c1[0] + r*m1x/dm1, c1[1] + r*m1y/dm1)
m1 = (m1x, m1y)
vx, vy = rotate(v, -A)
c2 = (p1[0]+r*vx, p1[1]+r*vy)
m2x, m2y = ((p1[0]+p2[0])/2 - c2[0], (p1[1]+p2[1])/2 - c2[1])
dm2 = (m2x**2+m2y**2)**.5
m2x, m2y = (c2[0] + r*m2x/dm2, c2[1] + r*m2y/dm2)
m2 = (m2x, m2y)
return m1, m2
@@ -0,0 +1,66 @@
# (c) 2020 Adam Spontarelli <[email protected]>
#
# 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.
#
# FCGear 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 FCGear; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
from PySide import QtGui as qt
import fcsprocket
import FreeCAD, FreeCADGui
class SprocketCreationFrame(qt.QFrame):
def __init__(self, parent=None):
super(SprocketCreationFrame, self).__init__(parent)
self.P = qt.QSpinBox(value=0.375)
self.N = qt.QDoubleSpinBox(value=45)
self.Dr = qt.QDoubleSpinBox(value=0.20)
l = qt.QFormLayout(self)
l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
l.addRow('Number of teeth:', self.N)
l.addRow('Chain Pitch (in):', self.P)
l.addRow('Roller Diameter (in):', self.Dr)
class SprocketDialog(qt.QDialog):
def __init__(self, parent=None):
super(SprocketDialog, self).__init__(parent)
self.gc = SprocketCreationFrame()
btns = qt.QDialogButtonBox.Ok | qt.QDialogButtonBox.Cancel
buttonBox = qt.QDialogButtonBox(btns,
accepted=self.accept,
rejected=self.reject)
l = qt.QVBoxLayout(self)
l.addWidget(self.gc)
l.addWidget(buttonBox)
self.setWindowTitle('Sprocket creation dialog')
def accept(self):
if FreeCAD.ActiveDocument is None:
FreeCAD.newDocument("Sprocket")
gear = fcgear.makeSprocket(self.gc.m.value(),
self.gc.Z.value(),
self.gc.angle.value(),
not self.gc.split.currentIndex())
FreeCADGui.SendMsgToActiveView("ViewFit")
return super(SprocketDialog, self).accept()
if __name__ == '__main__':
a = qt.QApplication([])
w = SprocketDialog()
w.show()
a.exec_()
+139
View File
@@ -0,0 +1,139 @@
# (c) 2020 Adam Spontarelli <[email protected]>
#
# 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.
#
# FCGear 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 FCGear; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
from math import cos, sin, tan, sqrt, radians, acos, atan, asin, degrees
def CreateSprocket(w, P, N, Dr):
"""
Create a sprocket
w is the wirebuilder object (in which the sprocket will be constructed)
P is the chain pitch
N is the number of teeth
Dr is the roller diameter
Remaining variables can be found in Standard Handbook of Chains
"""
Ds = 1.005 * Dr + (0.003 * 25.4)
R = Ds / 2
alpha = 35 + 60/N
beta = 18 - 56 / N
M = 0.8 * Dr * cos(radians(35) + radians(60/N))
T = 0.8 * Dr * sin(radians(35) + radians(60/N))
E = 1.3025 * Dr + (0.0015 * 25.4)
W = 1.4 * Dr * cos(radians(180/N))
V = 1.4 * Dr * sin(radians(180/N))
F = Dr * (0.8 * cos(radians(18) - radians(56)/N) + 1.4 *
cos(radians(17) - radians(64) / N) - 1.3025) - (0.0015 * 25.4)
PD = P / (sin(radians(180)/N))
H = sqrt(F**2 - (1.4 * Dr - P/2)**2)
OD = P * (0.6 + 1/tan(radians(180/N)))
# The sprocket tooth gullet consists of four segments
x0 = 0
y0 = PD/2 - R
# ---- Segment 1 -----
alpha = 35 + 60/N
x1 = -R * cos(radians(alpha))
y1 = PD/2 - R * sin(radians(alpha))
arc_end = [x1, y1]
# ---- Segment 2 -----
alpha = 35 + 60/N
beta = 18 - 56 / N
x2 = M - E * cos(radians(alpha-beta))
y2 = T - E * sin(radians(alpha-beta)) + PD/2
# # ---- Segment 3 -----
y2o = y2 - PD/2
hyp = sqrt((-W-x2)**2 + (-V-y2o)**2)
AP = sqrt(hyp**2 - F**2)
gamma = atan((y2o + V)/(x2 + W))
alpha = asin(AP / hyp)
beta = 180 - (90 - degrees(alpha)) - (90 - degrees(gamma))
x3o = AP * sin(radians(beta))
y3o = AP * cos(radians(beta))
x3 = x2 - x3o
y3 = y2 + y3o
# ---- Segment 4 -----
alpha = 180/N
m = -1/tan(radians(alpha))
yf = PD/2 - V
A = 1 + m**2
B = 2*m*yf - 2*W
C = W**2 + yf**2 - F**2
# x4a = (-B - sqrt(B**2 - 4 * A * C)) / (2*A)
x4b = (-B + sqrt(B**2 - 4 * A * C)) / (2*A)
x4 = -x4b
y4 = m * x4
p0 = [x0,y0]
p1 = [x1,y1]
p2 = [x2,y2]
p3 = [x3,y3]
p4 = [x4,y4]
p5 = [-x1,y1]
p6 = [-x2,y2]
p7 = [-x3,y3]
p8 = [-x4,y4]
w.move(p4) # vectors are lists [x,y]
w.arc(p3, F, 0)
w.line(p2)
w.arc(p1, E, 1)
w.arc(p0, R, 1)
# ---- Mirror -----
w.arc(p5, R, 1)
w.arc(p6, E, 1)
w.line(p7)
w.arc(p8, F, 0)
# ---- Polar Array ----
alpha = -radians(360/N)
for n in range(1,N):
# falling gullet slope
w.arc(rotate(p3, alpha*n), F, 0)
w.line(rotate(p2, alpha*n))
w.arc(rotate(p1, alpha*n), E, 1)
w.arc(rotate(p0, alpha*n), R, 1)
# rising gullet slope
w.arc(rotate(p5, alpha*n), R, 1)
w.line(rotate(p6, alpha*n))
w.arc(rotate(p7, alpha*n), E, 0)
w.arc(rotate(p8, alpha*n), F, 0)
w.close()
return w
def rotate(pt, rads):
"""
rotate pt by rads radians about origin
"""
sinA = sin(rads)
cosA = cos(rads)
return (pt[0] * cosA - pt[1] * sinA,
pt[0] * sinA + pt[1] * cosA)
+39 -24
View File
@@ -255,9 +255,9 @@ void DrawViewDetail::detailExec(TopoDS_Shape shape,
double scale = getScale();
BRepBuilderAPI_Copy BuilderCopy(shape);
TopoDS_Shape copyShape = BuilderCopy.Shape();
TopoDS_Shape myShape = BuilderCopy.Shape();
gp_Pnt gpCenter = TechDraw::findCentroid(copyShape,
gp_Pnt gpCenter = TechDraw::findCentroid(myShape,
dirDetail);
Base::Vector3d shapeCenter = Base::Vector3d(gpCenter.X(),gpCenter.Y(),gpCenter.Z());
m_saveCentroid = shapeCenter; //centroid of original shape
@@ -265,7 +265,7 @@ void DrawViewDetail::detailExec(TopoDS_Shape shape,
if (dvs != nullptr) {
//section cutShape should already be on origin
} else {
copyShape = TechDraw::moveShape(copyShape, //centre shape on origin
myShape = TechDraw::moveShape(myShape, //centre shape on origin
-shapeCenter);
}
@@ -280,7 +280,7 @@ void DrawViewDetail::detailExec(TopoDS_Shape shape,
Bnd_Box bbxSource;
bbxSource.SetGap(0.0);
BRepBndLib::Add(copyShape, bbxSource);
BRepBndLib::Add(myShape, bbxSource);
double diag = sqrt(bbxSource.SquareExtent());
Base::Vector3d toolPlaneOrigin = anchorOffset3d + dirDetail * diag * -1.0; //center tool about anchor
@@ -301,33 +301,47 @@ void DrawViewDetail::detailExec(TopoDS_Shape shape,
gp_Vec extrudeDir(extrudeVec.x,extrudeVec.y,extrudeVec.z);
TopoDS_Shape tool = BRepPrimAPI_MakePrism(aProjFace, extrudeDir, false, true).Shape();
BRepAlgoAPI_Common mkCommon(copyShape,tool);
if (!mkCommon.IsDone()) {
Base::Console().Warning("DVD::execute - %s - detail cut operation failed (1)\n", getNameInDocument());
return;
}
if (mkCommon.Shape().IsNull()) {
Base::Console().Warning("DVD::execute - %s - detail cut operation failed (2)\n", getNameInDocument());
return;
}
//Did we get a solid?
TopExp_Explorer xp;
xp.Init(mkCommon.Shape(),TopAbs_SOLID);
if (!(xp.More() == Standard_True)) {
Base::Console().Warning("DVD::execute - mkCommon.Shape is not a solid!\n");
BRep_Builder builder;
TopoDS_Compound pieces;
builder.MakeCompound(pieces);
TopExp_Explorer expl(myShape, TopAbs_SOLID);
int indb = 0;
int outdb = 0;
for (; expl.More(); expl.Next()) {
indb++;
const TopoDS_Solid& s = TopoDS::Solid(expl.Current());
BRepAlgoAPI_Common mkCommon(s,tool);
if (!mkCommon.IsDone()) {
// Base::Console().Warning("DVD::execute - %s - detail cut operation failed (1)\n", getNameInDocument());
continue;
}
if (mkCommon.Shape().IsNull()) {
// Base::Console().Warning("DVD::execute - %s - detail cut operation failed (2)\n", getNameInDocument());
continue;
}
//this might be overkill for piecewise algo
//Did we get at least 1 solid?
TopExp_Explorer xp;
xp.Init(mkCommon.Shape(),TopAbs_SOLID);
if (!(xp.More() == Standard_True)) {
// Base::Console().Warning("DVD::execute - mkCommon.Shape is not a solid!\n");
continue;
}
builder.Add(pieces, mkCommon.Shape());
outdb++;
}
TopoDS_Shape detail = mkCommon.Shape();
if (debugDetail()) {
BRepTools::Write(tool, "DVDTool.brep"); //debug
BRepTools::Write(copyShape, "DVDCopy.brep"); //debug
BRepTools::Write(detail, "DVDCommon.brep"); //debug
BRepTools::Write(myShape, "DVDCopy.brep"); //debug
BRepTools::Write(pieces, "DVDCommon.brep"); //debug
}
Bnd_Box testBox;
testBox.SetGap(0.0);
BRepBndLib::Add(detail, testBox);
BRepBndLib::Add(pieces, testBox);
if (testBox.IsVoid()) {
TechDraw::GeometryObject* go = getGeometryObject();
if (go != nullptr) {
@@ -343,7 +357,7 @@ void DrawViewDetail::detailExec(TopoDS_Shape shape,
// TopoDS_Compound Comp;
// builder.MakeCompound(Comp);
// builder.Add(Comp, tool);
// builder.Add(Comp, copyShape);
// builder.Add(Comp, myShape);
gp_Pnt inputCenter;
try {
@@ -358,7 +372,8 @@ void DrawViewDetail::detailExec(TopoDS_Shape shape,
gp_Ax2 viewAxis = dvp->getProjectionCS(stdOrg); //sb same CS as base view.
//center shape on origin
TopoDS_Shape centeredShape = TechDraw::moveShape(detail,
// TopoDS_Shape centeredShape = TechDraw::moveShape(detail,
TopoDS_Shape centeredShape = TechDraw::moveShape(pieces,
centroid * -1.0);
TopoDS_Shape scaledShape = TechDraw::scaleShape(centeredShape,
+18 -6
View File
@@ -347,7 +347,6 @@ App::DocumentObjectExecReturn *DrawViewSection::execute(void)
}
}
dvp->requestPaint(); //to refresh section line
return DrawView::execute();
}
@@ -382,13 +381,26 @@ void DrawViewSection::sectionExec(TopoDS_Shape baseShape)
BRepBuilderAPI_Copy BuilderCopy(baseShape);
TopoDS_Shape myShape = BuilderCopy.Shape();
BRepAlgoAPI_Cut mkCut(myShape, prism);
if (!mkCut.IsDone()) {
Base::Console().Warning("DVS: Section cut has failed in %s\n",getNameInDocument());
return;
BRep_Builder builder;
TopoDS_Compound pieces;
builder.MakeCompound(pieces);
TopExp_Explorer expl(myShape, TopAbs_SOLID);
int indb = 0;
int outdb = 0;
for (; expl.More(); expl.Next()) {
indb++;
const TopoDS_Solid& s = TopoDS::Solid(expl.Current());
BRepAlgoAPI_Cut mkCut(s, prism);
if (!mkCut.IsDone()) {
Base::Console().Warning("DVS: Section cut has failed in %s\n",getNameInDocument());
continue;
}
TopoDS_Shape cut = mkCut.Shape();
builder.Add(pieces, cut);
outdb++;
}
TopoDS_Shape rawShape = mkCut.Shape();
TopoDS_Shape rawShape = pieces;
if (debugSection()) {
BRepTools::Write(myShape, "DVSCopy.brep"); //debug
BRepTools::Write(aProjFace, "DVSFace.brep"); //debug