From c419371b5deb5b23a9540d32e29cd4a167007c1f Mon Sep 17 00:00:00 2001 From: travisapple Date: Sat, 7 Nov 2020 09:56:12 -0800 Subject: [PATCH 01/32] New WebGL Exporter This is a complete rewrite of this file. --- src/Mod/Arch/importWebGL.py | 965 ++++++++++++++++++++++++++++-------- 1 file changed, 757 insertions(+), 208 deletions(-) diff --git a/src/Mod/Arch/importWebGL.py b/src/Mod/Arch/importWebGL.py index 09e70e57aa..798a786f9e 100644 --- a/src/Mod/Arch/importWebGL.py +++ b/src/Mod/Arch/importWebGL.py @@ -1,5 +1,5 @@ #*************************************************************************** -#* Copyright (c) 2013 Yorik van Havre * +#* Copyright (c) 2020 Travis Apple * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Lesser General Public License (LGPL) * @@ -18,233 +18,782 @@ #* USA * #* * #*************************************************************************** +# +# REFS: +# https://github.com/mrdoob/three.js/blob/master/examples/webgl_interactive_buffergeometry.html +# https://threejs.org/examples/#webgl_buffergeometry_lines +# https://forum.freecadweb.org/viewtopic.php?t=51245 +# https://forum.freecadweb.org/viewtopic.php?t=29487 +# https://threejs.org/examples/#webgl_raycast_sprite +# +# Params for export() +# 'colors' is of the form: {'Body': [1,0,0], 'Body001': [1,1,0], 'Body002': [1,0,1] } +# 'camera' is of the form: "PerspectiveCamera {\n viewportMapping ADJUST_CAMERA\n position 30.242626 -51.772324 85.63475\n orientation -0.4146691 0.088459305 -0.90566254 4.7065201\nnearDistance 53.126431\n farDistance 123.09125\n aspectRatio 1\n focalDistance 104.53851\n heightAngle 0.78539819\n\n}" +# The 'camera' string for the active document may be generated from: import OfflineRenderingUtils; OfflineRenderingUtils.getCamera(FreeCAD.ActiveDocument.FileName); +# +# Development reload oneliner: +# def re(): from importlib import reload;import importWebGL;reload(importWebGL);o=FreeCAD.getDocument("curve");importWebGL.export([o.getObject("Body")],u"C:/Users/Travis/Desktop/test.htm"); -"""FreeCAD webgl exporter +"""FreeCAD WebGL Exporter""" -options: importWebGL.wireframeStyle = "faceloop" (can also be "multimaterial" or None) -importWebGL.template = a complete html file, where $CameraData is a placeholder for the -FreeCAD camera, and $ObjectsData a placeholder for the FreeCAD objects. -importWebGL.linewidth = an integer, specifying the width of lines in "faceloop" mode""" - -import FreeCAD,Draft,Part,DraftGeomUtils +import FreeCAD,Mesh,Draft,Part,DraftGeomUtils,Arch,OfflineRenderingUtils,json,six if FreeCAD.GuiUp: import FreeCADGui from DraftTools import translate else: FreeCADGui = None - # \cond - def translate(ctxt,txt,utf8_decode=True): - return txt - # \endcond + def translate(ctxt, txt): return txt -## @package importWebGL -# \ingroup ARCH -# \brief WebGL file format exporter -# -# This module provides tools to export HTML files containing the -# exported objects in WebGL format and a simple three.js-based viewer. +if open.__module__ in ['__builtin__','io']: pythonopen = open -tab = " " # the tab size -wireframeStyle = "faceloop" # this can be "faceloop", "multimaterial", or None -cameraPosition = None # set this to a tuple to change, for ex. (0,0,0) -linewidth = 1 -template = """ - - - FreeCAD model - +disableCompression = False # Compress object data before sending to JS +base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%&()*+-:;/=>?@[]^_,.{|}~`' # safe str chars for js in all cases +baseFloat = ',.-0123456789' - + + +""" - var camera, controls, scene, renderer; - - window.onload = function() { - - var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight; - var VIEW_ANGLE = 35, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 200000; - - renderer = new THREE.WebGLRenderer(); - renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT ); - document.body.appendChild( renderer.domElement ); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera( - VIEW_ANGLE, // Field of view - ASPECT, // Aspect ratio - NEAR, // Near plane - FAR // Far plane - ); - $CameraData // placeholder for the FreeCAD camera - - controls = new THREE.TrackballControls( camera ); - controls.rotateSpeed = 1.0; - controls.zoomSpeed = 1.2; - controls.panSpeed = 0.8; - controls.noZoom = false; - controls.noPan = false; - controls.staticMoving = true; - controls.dynamicDampingFactor = 0.3; - controls.keys = [ 65, 83, 68 ]; - - $ObjectsData // placeholder for the FreeCAD objects - - var light = new THREE.PointLight( 0xFFFF00 ); - light.position.set( -10000, -10000, 10000 ); - scene.add( light ); - - renderer.render( scene, camera ); - - animate(); - }; - - function animate(){ - requestAnimationFrame( animate ); - render(); - }; - - function render(){ - controls.update(); - renderer.render( scene, camera ); - }; - - - -""" - - -if open.__module__ in ['__builtin__','io']: - pythonopen = open +def export( exportList, filename, colors = None, camera = None ): + """Exports objects to an html file""" -def export(exportList,filename,colors=None,camera=None): - "exports the given objects to an .html file" - - html = getHTML(exportList,colors,camera) - outfile = pythonopen(filename,"w") - outfile.write(html) - outfile.close() - FreeCAD.Console.PrintMessage(translate("Arch", "Successfully written", utf8_decode=True) + ' ' + filename + "\n") + global html, disableCompression, base, baseFloat -def getHTML(objectsList,colors=None,camera=None): - "returns the complete HTML code of a viewer for the given objects" + data = { 'camera':{}, 'file':{}, 'objects':[] } - # get objects data - objectsData = '' - for obj in objectsList: - colordata = None - if colors: - if obj.Name in colors: - colordata = colors[obj.Name] - objectsData += getObjectData(obj,color=colordata) - t = template.replace("$CameraData",getCameraData(camera)) - t = t.replace("$ObjectsData",objectsData) - return t + if not FreeCADGui and not camera: + camera = OfflineRenderingUtils.getCamera(FreeCAD.ActiveDocument.FileName) -def getCameraData(camera=None): - "returns the position and direction of the camera as three.js snippet" - - result = "" if camera: - global cameraPosition - if isinstance(camera,str): - import OfflineRenderingUtils - camnode = OfflineRenderingUtils.getCoinCamera(camera) - cameraPosition = camnode.position.getValue().getValue() - elif hasattr(camera,"position"): - cameraPosition = camera.position.getValue().getValue() - if cameraPosition: - result += "camera.position.set("+str(cameraPosition[0])+","+str(cameraPosition[1])+","+str(cameraPosition[2])+");\n" - elif FreeCADGui: - # getting camera position - pos = FreeCADGui.ActiveDocument.ActiveView.viewPosition().Base - result += "camera.position.set( " - result += str(pos.x) + ", " - result += str(pos.y) + ", " - result += str(pos.z) + " );\n" + # REF: https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/Arch/OfflineRenderingUtils.py + camnode = OfflineRenderingUtils.getCoinCamera(camera) + cameraPosition = camnode.position.getValue().getValue() + data['camera']['type'] = 'Orthographic' + if 'PerspectiveCamera' in camera: data['camera']['type'] = 'Perspective' + data['camera']['focalDistance'] = camnode.focalDistance.getValue() + data['camera']['position_x'] = cameraPosition[0] + data['camera']['position_y'] = cameraPosition[1] + data['camera']['position_z'] = cameraPosition[2] else: - result += "camera.position.set(0,0,1000);\n" - result += tab+"camera.lookAt( scene.position );\n"+tab - # print(result) - return result + v = FreeCADGui.ActiveDocument.ActiveView + data['camera']['type'] = v.getCameraType() + data['camera']['focalDistance'] = v.getCameraNode().focalDistance.getValue() + data['camera']['position_x'] = v.viewPosition().Base.x + data['camera']['position_y'] = v.viewPosition().Base.y + data['camera']['position_z'] = v.viewPosition().Base.z -def getObjectData(obj,wireframeMode=wireframeStyle,color=None): - """returns the geometry data of an object as three.js snippet. - wireframeMode can be multimaterial, faceloop, or None""" + # Take the objects out of groups + objectslist = Draft.get_group_contents(exportList, walls=True, addgroups=False) + objectslist = Arch.pruneIncluded(objectslist) - result = "" - wires = [] - - if hasattr(obj,'Shape'): - fcmesh = obj.Shape.tessellate(0.1) - result = "var geom = new THREE.Geometry();\n" - # adding vertices data - for i in range(len(fcmesh[0])): - v = fcmesh[0][i] - result += tab+"var v"+str(i)+" = new THREE.Vector3("+str(v.x)+","+str(v.y)+","+str(v.z)+");\n" - result += tab+"console.log(geom.vertices)\n" - for i in range(len(fcmesh[0])): - result += tab+"geom.vertices.push(v"+str(i)+");\n" - # adding facets data - for f in fcmesh[1]: - result += tab+"geom.faces.push( new THREE.Face3"+str(f).replace("L","")+" );\n" - for f in obj.Shape.Faces: - for w in f.Wires: - wo = Part.Wire(Part.__sortEdges__(w.Edges)) - wires.append(wo.discretize(QuasiDeflection=0.1)) - - elif obj.isDerivedFrom("Mesh::Feature"): - mesh = obj.Mesh - result = "var geom = new THREE.Geometry();\n" - # adding vertices data - for p in mesh.Points: - v = p.Vector - i = p.Index - result += tab+"var v"+str(i)+" = new THREE.Vector3("+str(v.x)+","+str(v.y)+","+str(v.z)+");\n" - result += tab+"console.log(geom.vertices)\n" - for p in mesh.Points: - result += tab+"geom.vertices.push(v"+str(p.Index)+");\n" - # adding facets data + for obj in objectslist: + + # Pull all obj data before we dig down the links + label = obj.Label + + color = '#cccccc'; + opacity = 1.0 + if FreeCADGui: + color = Draft.getrgb(obj.ViewObject.ShapeColor, testbw = False) + opacity = int((100 - obj.ViewObject.Transparency)/5) / 20 # 0>>1 with step of 0.05 + elif colors: + if label in colors: + color = Draft.getrgb(colors[label], testbw = False) + + validObject = False + if obj.isDerivedFrom('Mesh::Feature'): + mesh = obj.Mesh + validObject = True + if obj.isDerivedFrom('Part::Feature'): + objShape = obj.Shape + validObject = True + if obj.isDerivedFrom('App::Link'): + linkPlacement = obj.LinkPlacement + while True: # drill down to get to the actual obj + if obj.isDerivedFrom("App::Link"): + if obj.ViewObject.OverrideMaterial: color = Draft.getrgb(obj.ViewObject.ShapeMaterial.DiffuseColor, testbw = False) + obj = obj.LinkedObject + if hasattr(obj, "__len__"): + FreeCAD.Console.PrintMessage(label + ": Sub-Links are Unsupported.\n") + break + elif obj.isDerivedFrom('Part::Feature'): + objShape = obj.Shape.copy(False) + objShape.Placement = linkPlacement + validObject = True + break + elif obj.isDerivedFrom("Mesh::Feature"): + mesh = obj.Mesh.copy() + mesh.Placement = linkPlacement + validObject = True + break + + if not validObject: continue + + objdata = { 'name': label, 'color': color, 'opacity': opacity, 'verts':'', 'facets':'', 'wires':[], 'faceColors':[], 'facesToFacets':[], 'floats':[] } + + if obj.isDerivedFrom('Part::Feature'): + + deviation = 0.5 + if FreeCADGui: + deviation = obj.ViewObject.Deviation + + # obj.ViewObject.DiffuseColor is length=1 when all faces are the same color, length=len(faces) for when they're not + if len(obj.ViewObject.DiffuseColor) == len(objShape.Faces): + for fc in obj.ViewObject.DiffuseColor: + objdata['faceColors'].append( Draft.getrgb(fc, testbw = False) ) + + # get verts and facets for ENTIRE object + shapeData = objShape.tessellate( deviation ) + mesh = Mesh.Mesh(shapeData) + + if len(objShape.Faces) > 1: + # Map each Facet created by tessellate() to a Face so that it can be colored correctly using faceColors + # This is done by matching the results of a tessellate() on EACH FACE to the overall tessellate stored in shapeData + # if there is any error in matching these two then we display the whole object as one face and forgo the face colors + for f in objShape.Faces: + faceData = f.tessellate( deviation ) + found = True + for fv in range( len(faceData[0]) ): # face verts. List of type Vector() + found = False + for sv in range( len(shapeData[0]) ): #shape verts + if faceData[0][fv] == shapeData[0][sv]: # do not use isEqual() here + faceData[0][fv] = sv # replace with the index of shapeData[0] + found = True + break + if not found: break + if not found: + FreeCAD.Console.PrintMessage("Facet to Face Mismach.\n") + objdata['facesToFacets'] = [] + break + + # map each of the face facets to the shape facets and make a list of shape facet indices that belong to this face + facetList = [] + for ff in faceData[1]: # face facets + found = False + for sf in range( len(shapeData[1]) ): #shape facets + if faceData[0][ff[0]] in shapeData[1][sf] and faceData[0][ff[1]] in shapeData[1][sf] and faceData[0][ff[2]] in shapeData[1][sf]: + facetList.append(sf) + found = True + break + if not found: break + if not found: + FreeCAD.Console.PrintMessage("Facet List Mismach.\n") + objdata['facesToFacets'] = [] + break + + objdata['facesToFacets'].append( baseEncode(facetList) ) + + wires = [] # Add wires + for f in objShape.Faces: + for w in f.Wires: + wo = Part.Wire(Part.__sortEdges__(w.Edges)) + wire = [] + for v in wo.discretize(QuasiDeflection = 0.005): + wire.append( '{:.5f}'.format(v.x) ) # use strings to avoid 0.00001 written as 1e-05 + wire.append( '{:.5f}'.format(v.y) ) + wire.append( '{:.5f}'.format(v.z) ) + wires.append( wire ) + + if not disableCompression: + for w in range( len(wires) ): + for wv in range( len(wires[w]) ): + found = False + for f in range( len(objdata['floats']) ): + if objdata['floats'][f] == wires[w][wv]: + wires[w][wv] = f + found = True + break + if not found: + objdata['floats'].append( wires[w][wv] ) + wires[w][wv] = len(objdata['floats'])-1 + wires[w] = baseEncode(wires[w]) + objdata['wires'] = wires + + vIndex = {} + verts = [] + for p in range( len(mesh.Points) ): + vIndex[ mesh.Points[p].Index ] = p + verts.append( '{:.5f}'.format(mesh.Points[p].Vector.x) ) + verts.append( '{:.5f}'.format(mesh.Points[p].Vector.y) ) + verts.append( '{:.5f}'.format(mesh.Points[p].Vector.z) ) + + # create floats list to compress verts and wires being written into the JS + if not disableCompression: + for v in range( len(verts) ): + found = False + for f in range( len(objdata['floats']) ): + if objdata['floats'][f] == verts[v]: + verts[v] = f + found = True + break + if not found: + objdata['floats'].append( verts[v] ) + verts[v] = len(objdata['floats'])-1 + objdata['verts'] = baseEncode(verts) + + facets = [] for f in mesh.Facets: - pointIndices = tuple([ int(i) for i in f.PointIndices ]) - result += tab+"geom.faces.push( new THREE.Face3"+str(pointIndices).replace("L","")+" );\n" - - if result: - # adding a base material - if color: - rgb = Draft.getrgb(color,testbw=False) - elif FreeCADGui: - col = obj.ViewObject.ShapeColor - rgb = Draft.getrgb(col,testbw=False) - else: - rgb = "#888888" # test color - result += tab+"var basematerial = new THREE.MeshBasicMaterial( { color: 0x"+str(rgb)[1:]+" } );\n" - #result += tab+"var basematerial = new THREE.MeshLambertMaterial( { color: 0x"+str(rgb)[1:]+" } );\n" + for i in f.PointIndices: + facets.append( vIndex[i] ) + objdata['facets'] = baseEncode(facets) - if wireframeMode == "faceloop": - # adding the mesh to the scene with a wireframe copy - result += tab+"var mesh = new THREE.Mesh( geom, basematerial );\n" - result += tab+"scene.add( mesh );\n" - result += tab+"var linematerial = new THREE.LineBasicMaterial({linewidth: %d, color: 0x000000,});\n" % linewidth - for w in wires: - result += tab+"var wire = new THREE.Geometry();\n" - for p in w: - result += tab+"wire.vertices.push(new THREE.Vector3(" - result += str(p.x)+", "+str(p.y)+", "+str(p.z)+"));\n" - result += tab+"var line = new THREE.Line(wire, linematerial);\n" - result += tab+"scene.add(line);\n" - - elif wireframeMode == "multimaterial": - # adding a wireframe material - result += tab+"var wireframe = new THREE.MeshBasicMaterial( { color: " - result += "0x000000, wireframe: true, transparent: true } );\n" - result += tab+"var material = [ basematerial, wireframe ];\n" - result += tab+"var mesh = new THREE.SceneUtils.createMultiMaterialObject( geom, material );\n" - result += tab+"scene.add( mesh );\n"+tab - - else: - # adding the mesh to the scene with simple material - result += tab+"var mesh = new THREE.Mesh( geom, basematerial );\n" - result += tab+"scene.add( mesh );\n"+tab + # compress floats + if not disableCompression: + # use ratio of 7x base13 to 4x base90 because 13^7 ~ 90^4 + fullstr = json.dumps(objdata['floats'], separators=(',', ':')) + fullstr = fullstr.replace('[', '').replace(']', '').replace('"', '') + floatStr = '' + baseFloatCt = len(baseFloat) + baseCt = len(base) + for fs in range( 0, len(fullstr), 7 ): # chunks of 7 chars, skip the first one + str7 = fullstr[fs:(fs+7)] + quotient = 0 + for s in range( len(str7) ): + quotient += baseFloat.find(str7[s]) * pow(baseFloatCt, (6-s)) + for v in range(4): + floatStr += base[ quotient % baseCt ] + quotient = int(quotient / baseCt) + objdata['floats'] = floatStr - return result + data['objects'].append( objdata ) + + html = html.replace('$pagetitle',FreeCAD.ActiveDocument.Label) + version = FreeCAD.Version() + html = html.replace('$version',version[0] + '.' + version[1] + '.' + version[2]) + + # Remove data compression in JS + if disableCompression: html = html.replace('$disableCompression','true') + else: html = html.replace('$disableCompression','false') + + html = html.replace('$base', base) + html = html.replace('$float', baseFloat) + html = html.replace('$data', json.dumps(data, separators=(',', ':')) ) # Shape Data + + if six.PY2: + outfile = pythonopen(filename, "wb") + else: + outfile = pythonopen(filename, "w") + outfile.write( html ) + outfile.close() + FreeCAD.Console.PrintMessage( translate("Arch", "Successfully written") + ' ' + filename + "\n" ) + +def baseEncode( arr ): + """Compresses an array of ints into a base90 string""" + + global disableCompression, base + if disableCompression: return arr + if len(arr) == 0: return '' + + longest = 0 + output = [] + baseCt = len(base) + for v in range( len(arr) ): + buffer = '' + quotient = arr[v] + while True: + buffer += base[ quotient % baseCt ] + quotient = int(quotient / baseCt) + if quotient == 0: break + output.append( buffer ) + if len(buffer) > longest: longest = len(buffer) + output = [('{:>'+str(longest)+'}').format(x) for x in output] # pad each element + return str(longest) + ('').join(output) From 4633e6e039451a07ea9c4fc21504ed1591499f56 Mon Sep 17 00:00:00 2001 From: travisapple Date: Sat, 7 Nov 2020 13:42:16 -0800 Subject: [PATCH 02/32] Update importWebGL.py --- src/Mod/Arch/importWebGL.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Arch/importWebGL.py b/src/Mod/Arch/importWebGL.py index 798a786f9e..4c29483dfe 100644 --- a/src/Mod/Arch/importWebGL.py +++ b/src/Mod/Arch/importWebGL.py @@ -1,4 +1,5 @@ #*************************************************************************** +#* Copyright (c) 2013 Yorik van Havre * #* Copyright (c) 2020 Travis Apple * #* * #* This program is free software; you can redistribute it and/or modify * From 1f70e1b441c5adeaab22b791fc56b041b7609bd6 Mon Sep 17 00:00:00 2001 From: travisapple Date: Sun, 8 Nov 2020 10:25:30 -0800 Subject: [PATCH 03/32] Update importWebGL.py --- src/Mod/Arch/importWebGL.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Mod/Arch/importWebGL.py b/src/Mod/Arch/importWebGL.py index 4c29483dfe..4992d14e7b 100644 --- a/src/Mod/Arch/importWebGL.py +++ b/src/Mod/Arch/importWebGL.py @@ -48,6 +48,13 @@ else: if open.__module__ in ['__builtin__','io']: pythonopen = open +## @package importWebGL +# \ingroup ARCH +# \brief FreeCAD WebGL Exporter +# +# This module provides tools to export HTML files containing the +# exported objects in WebGL format and a simple three.js-based viewer. + disableCompression = False # Compress object data before sending to JS base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%&()*+-:;/=>?@[]^_,.{|}~`' # safe str chars for js in all cases baseFloat = ',.-0123456789' From 061c3f8fb7464b1a9fe2abb2c80cdb5c75eee3df Mon Sep 17 00:00:00 2001 From: travisapple Date: Sun, 8 Nov 2020 10:29:15 -0800 Subject: [PATCH 04/32] Update importWebGL.py --- src/Mod/Arch/importWebGL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/importWebGL.py b/src/Mod/Arch/importWebGL.py index 4992d14e7b..7eed983803 100644 --- a/src/Mod/Arch/importWebGL.py +++ b/src/Mod/Arch/importWebGL.py @@ -33,7 +33,7 @@ # The 'camera' string for the active document may be generated from: import OfflineRenderingUtils; OfflineRenderingUtils.getCamera(FreeCAD.ActiveDocument.FileName); # # Development reload oneliner: -# def re(): from importlib import reload;import importWebGL;reload(importWebGL);o=FreeCAD.getDocument("curve");importWebGL.export([o.getObject("Body")],u"C:/Users/Travis/Desktop/test.htm"); +# def re(): from importlib import reload;import importWebGL;reload(importWebGL);o=FreeCAD.getDocument("YourDocName");importWebGL.export([o.getObject("YourBodyName")],u"C:/path/to/your/file.htm"); """FreeCAD WebGL Exporter""" From 9b1a8d6d6989e46263c3213f6e9fddfea3407a6a Mon Sep 17 00:00:00 2001 From: travisapple Date: Tue, 17 Nov 2020 12:24:11 -0800 Subject: [PATCH 05/32] Update importWebGL.py --- src/Mod/Arch/importWebGL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/importWebGL.py b/src/Mod/Arch/importWebGL.py index 7eed983803..c07a123fc9 100644 --- a/src/Mod/Arch/importWebGL.py +++ b/src/Mod/Arch/importWebGL.py @@ -591,7 +591,7 @@ def export( exportList, filename, colors = None, camera = None ): # Take the objects out of groups objectslist = Draft.get_group_contents(exportList, walls=True, addgroups=False) - objectslist = Arch.pruneIncluded(objectslist) + # objectslist = Arch.pruneIncluded(objectslist) for obj in objectslist: From 65595c1b26171b5a58c819d70ddf34368b74942e Mon Sep 17 00:00:00 2001 From: travisapple Date: Mon, 7 Dec 2020 12:57:53 -0800 Subject: [PATCH 06/32] Update importWebGL.py --- src/Mod/Arch/importWebGL.py | 973 ++++++++++++++++++------------------ 1 file changed, 488 insertions(+), 485 deletions(-) diff --git a/src/Mod/Arch/importWebGL.py b/src/Mod/Arch/importWebGL.py index c07a123fc9..046d78d11d 100644 --- a/src/Mod/Arch/importWebGL.py +++ b/src/Mod/Arch/importWebGL.py @@ -59,512 +59,513 @@ disableCompression = False # Compress object data before sending to JS base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%&()*+-:;/=>?@[]^_,.{|}~`' # safe str chars for js in all cases baseFloat = ',.-0123456789' -html = """ - - - $pagetitle - - - - - - - - - -""" + data = false; // free up some ram + + + + """ def export( exportList, filename, colors = None, camera = None ): """Exports objects to an html file""" - global html, disableCompression, base, baseFloat + global disableCompression, base, baseFloat data = { 'camera':{}, 'file':{}, 'objects':[] } @@ -764,6 +765,8 @@ def export( exportList, filename, colors = None, camera = None ): data['objects'].append( objdata ) + html = getHTMLTemplate() + html = html.replace('$pagetitle',FreeCAD.ActiveDocument.Label) version = FreeCAD.Version() html = html.replace('$version',version[0] + '.' + version[1] + '.' + version[2]) From cefd6b2ce0ccfedd7abda1079a71438ada44d49e Mon Sep 17 00:00:00 2001 From: Mitch Roote Date: Tue, 15 Dec 2020 11:10:40 -0500 Subject: [PATCH 07/32] Update fcinfo script for py3 compatibility --- src/Tools/fcinfo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tools/fcinfo b/src/Tools/fcinfo index 0b71f0b76f..9fb2834768 100755 --- a/src/Tools/fcinfo +++ b/src/Tools/fcinfo @@ -174,7 +174,7 @@ class FreeCADFileHandler(xml.sax.ContentHandler): # Print all the contents of the document properties items = self.contents.items() - items.sort() + items = sorted(items) for key,value in items: key = self.clean(key) value = self.clean(value) @@ -186,7 +186,7 @@ class FreeCADFileHandler(xml.sax.ContentHandler): if (tag == "Document") and (self.short != 2): items = self.contents.items() - items.sort() + items = sorted(items) for key,value in items: key = self.clean(key) if "00000000::" in key: From fc9be21345a4e19d5b374fd0c526c41cec5442af Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 16 Dec 2020 10:34:24 +0100 Subject: [PATCH 08/32] Path: [skip ci] fix TooltablePy::getTools to avoid possible double destruction of a Tool instance --- src/Mod/Path/App/TooltablePyImp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/App/TooltablePyImp.cpp b/src/Mod/Path/App/TooltablePyImp.cpp index a48223a516..b20fb539c3 100644 --- a/src/Mod/Path/App/TooltablePyImp.cpp +++ b/src/Mod/Path/App/TooltablePyImp.cpp @@ -105,7 +105,7 @@ Py::Dict TooltablePy::getTools(void) const { Py::Dict dict; for(std::map::iterator i = getTooltablePtr()->Tools.begin(); i != getTooltablePtr()->Tools.end(); ++i) { - PyObject *tool = new Path::ToolPy(i->second); + PyObject *tool = new Path::ToolPy(new Tool(*i->second)); dict.setItem(Py::Long(i->first), Py::asObject(tool)); } return dict; From e8674fc13d6305fc015511efafa34a89bced4408 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Wed, 16 Dec 2020 14:20:02 +0100 Subject: [PATCH 09/32] Sketcher: Fix crash on applying angle constraint on arc ======================================================= The GeoId passed was Constraint::GeoUndef (-2000). Fixes: https://forum.freecadweb.org/viewtopic.php?f=10&t=51716&p=458202#p458160 --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 970341f1ea..34dacb6ca6 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -5987,7 +5987,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg) std::swap(PosId1,PosId2); } - if(isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) { + if(isBsplinePole(Obj, GeoId1) || (GeoId2 != Constraint::GeoUndef && isBsplinePole(Obj, GeoId2))) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select an edge that is not a B-spline weight")); return; From 51189caba41a97fe5cf6b65c5828341b23aefff3 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Wed, 16 Dec 2020 15:21:21 +0100 Subject: [PATCH 10/32] Sketcher: Fix equality constraint command ========================================= For select constraint then click elements mode. Fixes: https://forum.freecadweb.org/viewtopic.php?f=10&t=51716&p=458207#p457974 --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 34dacb6ca6..acf6ec759b 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -6538,14 +6538,14 @@ void CmdSketcherConstrainEqual::applyConstraint(std::vector &selSeq, const Part::Geometry *geo1 = Obj->getGeometry(GeoId1); const Part::Geometry *geo2 = Obj->getGeometry(GeoId2); - if ( (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) || - (geo1->getTypeId() == Part::GeomHyperbola::getClassTypeId() && geo2->getTypeId() != Part::GeomHyperbola::getClassTypeId()) || - (geo1->getTypeId() == Part::GeomParabola::getClassTypeId() && geo2->getTypeId() != Part::GeomParabola::getClassTypeId()) || - (isBsplinePole(geo1) && !isBsplinePole(geo1)) || + if ( (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) || + (geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() && geo2->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId()) || + (geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() && geo2->getTypeId() != Part::GeomArcOfParabola::getClassTypeId()) || + (isBsplinePole(geo1) && !isBsplinePole(geo2)) || ( (geo1->getTypeId() == Part::GeomCircle::getClassTypeId() || geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) && - (geo2->getTypeId() != Part::GeomCircle::getClassTypeId() || geo2->getTypeId() != Part::GeomArcOfCircle::getClassTypeId())) || + !(geo2->getTypeId() == Part::GeomCircle::getClassTypeId() || geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())) || ( (geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) && - (geo2->getTypeId() != Part::GeomEllipse::getClassTypeId() || geo2->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId())) ){ + !(geo2->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())) ){ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select two or more edges of similar type")); From 6c6f5e7fbffe5bd2c389839cfe1c2b08ffa5f194 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Wed, 16 Dec 2020 19:30:48 +0100 Subject: [PATCH 11/32] Sketch: Fix exception on redraw =============================== Fixes: https://forum.freecadweb.org/viewtopic.php?p=458293#p458293 Rationale: In order to fix B-Spline pole dragging, the order was inverted. This fixed the B-Spline pole dragging issue, but introduced a draw before solve approach that is not consistent with the rest of the Sketcher. In my parallel development I had already identified this inconsistency, switched the order, and provided a new mechanism to fix the issue with the B-Spline pole dragging. This will be merged as part of another PR. In the meantime, this PR restores the intended behaviour, and let us identify if the particular reported exception also happens in other situations. --- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index b62fd15bc6..9f378b3247 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -6106,9 +6106,9 @@ bool ViewProviderSketch::setEdit(int ModNum) // is loaded into the solver, which ensures that any prospective draw using temporal // geometry (draw with first parameter true) has the right ViewProvider geometry extensions // set - This fixes Weight constraint dragging on a just opened sketch. - draw(false,true); getSketchObject()->solve(false); UpdateSolverInformation(); + draw(false,true); connectUndoDocument = getDocument() ->signalUndoDocument.connect(boost::bind(&ViewProviderSketch::slotUndoDocument, this, bp::_1)); From 93736cca48285a205cdd3527e953218492d92653 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 17 Dec 2020 16:27:36 +0100 Subject: [PATCH 12/32] Gui: [skip ci] add option to use software OpenGL --- src/Gui/Application.cpp | 9 +++++++++ src/Gui/DlgSettings3DView.ui | 18 ++++++++++++++++++ src/Gui/DlgSettings3DViewImp.cpp | 2 ++ 3 files changed, 29 insertions(+) diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 0f24076cb5..ea4c6a76d9 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1923,6 +1923,15 @@ void Application::runApplication(void) QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif + // Use software rendering for OpenGL +#if QT_VERSION >= 0x050400 + ParameterGrp::handle hOpenGL = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OpenGL"); + bool useSoftwareOpenGL = hOpenGL->GetBool("UseSoftwareOpenGL", false); + if (useSoftwareOpenGL) { + QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + } +#endif // QT_VERSION >= 0x050400 + // A new QApplication Base::Console().Log("Init: Creating Gui::Application and QApplication\n"); diff --git a/src/Gui/DlgSettings3DView.ui b/src/Gui/DlgSettings3DView.ui index 65cc2d9fe6..9df215caba 100644 --- a/src/Gui/DlgSettings3DView.ui +++ b/src/Gui/DlgSettings3DView.ui @@ -108,6 +108,24 @@ will be shown at the lower left corner in opened files Rendering + + + + This option is useful for troubleshooting graphics card and driver problems. + +Changing this option requires a restart of the application. + + + Use software OpenGL + + + UseSoftwareOpenGL + + + OpenGL + + + diff --git a/src/Gui/DlgSettings3DViewImp.cpp b/src/Gui/DlgSettings3DViewImp.cpp index 134aae1336..b1fe1d71aa 100644 --- a/src/Gui/DlgSettings3DViewImp.cpp +++ b/src/Gui/DlgSettings3DViewImp.cpp @@ -93,6 +93,7 @@ void DlgSettings3DViewImp::saveSettings() ui->CheckBox_WbByTab->onSave(); ui->CheckBox_ShowFPS->onSave(); ui->spinPickRadius->onSave(); + ui->CheckBox_use_SW_OpenGL->onSave(); ui->CheckBox_useVBO->onSave(); ui->FloatSpinBox_EyeDistance->onSave(); ui->checkBoxBacklight->onSave(); @@ -109,6 +110,7 @@ void DlgSettings3DViewImp::loadSettings() ui->CheckBox_WbByTab->onRestore(); ui->CheckBox_ShowFPS->onRestore(); ui->spinPickRadius->onRestore(); + ui->CheckBox_use_SW_OpenGL->onRestore(); ui->CheckBox_useVBO->onRestore(); ui->FloatSpinBox_EyeDistance->onRestore(); ui->checkBoxBacklight->onRestore(); From 249b3c3a1b60afdcd1f6b9a8b23037ea03c498c2 Mon Sep 17 00:00:00 2001 From: M G Berberich Date: Thu, 17 Dec 2020 15:15:24 +0100 Subject: [PATCH 13/32] make the hole-cut-type dropdown-box wider This drop-down box contains long names and did not use the space of the dialog. --- src/Mod/PartDesign/Gui/TaskHoleParameters.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui index 33def9abea..123eb607ab 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui @@ -509,7 +509,7 @@ Only available for holes without thread - + @@ -519,7 +519,7 @@ Only available for holes without thread - 140 + 16777215 16777215 From 16c565725523ca004a2221725f77a78439aa4b9a Mon Sep 17 00:00:00 2001 From: Przemo Firszt Date: Tue, 15 Dec 2020 16:20:18 +0000 Subject: [PATCH 14/32] Switch off mac builds - temporary travis fix Signed-off-by: Przemo Firszt --- .travis.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index e58c8fee13..d59f4398dd 100755 --- a/.travis.yml +++ b/.travis.yml @@ -102,19 +102,19 @@ jobs: - CMAKE_ARGS="-DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc" - CACHE_NAME=JOB3 - - os: osx - osx_image: xcode11.6 - language: cpp - cache: - - ccache: true - - directories: - - $HOME/.ccache - - $HOME/Library/Caches/Homebrew - - /usr/local/Homebrew - env: - - CMAKE_OPTS="-DBUILD_QT5=ON -DBUILD_ENABLE_CXX_STD='C++17' -DUSE_PYTHON3=1 -DCMAKE_CXX_FLAGS='-Wno-deprecated-declarations' -DBUILD_FEM_NETGEN=1 -DBUILD_FEM=1 -DBUILD_TECHDRAW=0 -DCMAKE_PREFIX_PATH='/usr/local/opt/qt/lib/cmake;/usr/local/opt/nglib/Contents/Resources' -DBUILD_FEM_NETGEN:BOOL=ON -DFREECAD_USE_EXTERNAL_KDL=ON -DCMAKE_BUILD_TYPE=Release" - - PATH=/usr/local/bin:$PATH - - CACHE_NAME=OSX1 + # - os: osx + # osx_image: xcode11.6 + # language: cpp + # cache: + # - ccache: true + # - directories: + # - $HOME/.ccache + # - $HOME/Library/Caches/Homebrew + # - /usr/local/Homebrew + # env: + # - CMAKE_OPTS="-DBUILD_QT5=ON -DBUILD_ENABLE_CXX_STD='C++17' -DUSE_PYTHON3=1 -DCMAKE_CXX_FLAGS='-Wno-deprecated-declarations' -DBUILD_FEM_NETGEN=1 -DBUILD_FEM=1 -DBUILD_TECHDRAW=0 -DCMAKE_PREFIX_PATH='/usr/local/opt/qt/lib/cmake;/usr/local/opt/nglib/Contents/Resources' -DBUILD_FEM_NETGEN:BOOL=ON -DFREECAD_USE_EXTERNAL_KDL=ON -DCMAKE_BUILD_TYPE=Release" + # - PATH=/usr/local/bin:$PATH + # - CACHE_NAME=OSX1 - os: windows language: cpp From 627fea4e22bc92bbad06577660aea869262d0b45 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 17 Dec 2020 17:10:42 +0100 Subject: [PATCH 15/32] PartDesign: [skip ci] make the spin box for the angle of the drill point wider --- src/Mod/PartDesign/Gui/TaskHoleParameters.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui index 123eb607ab..7d0a694760 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui @@ -267,7 +267,7 @@ Only available for holes without thread - + @@ -277,7 +277,7 @@ Only available for holes without thread - 140 + 16777215 16777215 From 97e39479481e7a73126ce846ae98256f381d5892 Mon Sep 17 00:00:00 2001 From: Aapo Date: Wed, 16 Dec 2020 00:54:05 +0200 Subject: [PATCH 16/32] [TD] Make Dimension Tolerances respect the Prefs formatSpec when creating a Dimension. --- src/Mod/TechDraw/App/DrawViewDimension.cpp | 10 +++++++--- src/Mod/TechDraw/App/DrawViewDimension.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index 8849cedead..cea5225be9 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -106,8 +106,8 @@ DrawViewDimension::DrawViewDimension(void) References3D.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(FormatSpec,(getDefaultFormatSpec()) , "Format", App::Prop_Output,"Dimension Format"); - ADD_PROPERTY_TYPE(FormatSpecOverTolerance,("%+g") , "Format", App::Prop_Output,"Dimension Overtolerance Format"); - ADD_PROPERTY_TYPE(FormatSpecUnderTolerance,("%+g") , "Format", App::Prop_Output,"Dimension Undertolerance Format"); + ADD_PROPERTY_TYPE(FormatSpecOverTolerance,(getDefaultFormatSpec(true)) , "Format", App::Prop_Output,"Dimension Overtolerance Format"); + ADD_PROPERTY_TYPE(FormatSpecUnderTolerance,(getDefaultFormatSpec(true)) , "Format", App::Prop_Output,"Dimension Undertolerance Format"); ADD_PROPERTY_TYPE(Arbitrary,(false) ,"Format", App::Prop_Output,"Value overridden by user"); ADD_PROPERTY_TYPE(ArbitraryTolerances,(false) ,"Format", App::Prop_Output,"Tolerance values overridden by user"); @@ -1300,7 +1300,7 @@ std::string DrawViewDimension::getPrefix() const return result; } -std::string DrawViewDimension::getDefaultFormatSpec() const +std::string DrawViewDimension::getDefaultFormatSpec(bool isToleranceFormat) const { Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions"); @@ -1333,6 +1333,10 @@ std::string DrawViewDimension::getDefaultFormatSpec() const } + if (isToleranceFormat) { + formatSpec.replace(QString::fromUtf8("%"), QString::fromUtf8("%+")); + } + return Base::Tools::toStdString(formatSpec); } diff --git a/src/Mod/TechDraw/App/DrawViewDimension.h b/src/Mod/TechDraw/App/DrawViewDimension.h index 865c2601a4..20cad696d2 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.h +++ b/src/Mod/TechDraw/App/DrawViewDimension.h @@ -171,7 +171,7 @@ protected: virtual void onChanged(const App::Property* prop) override; virtual void onDocumentRestored() override; std::string getPrefix() const; - std::string getDefaultFormatSpec() const; + std::string getDefaultFormatSpec(bool isToleranceFormat = false) const; virtual pointPair getPointsOneEdge(); virtual pointPair getPointsTwoEdges(); virtual pointPair getPointsTwoVerts(); From 7049c09448a615d1e82dd505d1dee716d9920562 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 19:14:44 -0800 Subject: [PATCH 17/32] Only update the shape if it has changed. --- src/Mod/Path/PathScripts/PathToolBitEdit.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathToolBitEdit.py b/src/Mod/Path/PathScripts/PathToolBitEdit.py index 0ee54747bb..c9cf238ebd 100644 --- a/src/Mod/Path/PathScripts/PathToolBitEdit.py +++ b/src/Mod/Path/PathScripts/PathToolBitEdit.py @@ -194,12 +194,14 @@ class ToolBitEditor(object): def updateShape(self): PathLog.track() - self.tool.BitShape = str(self.form.shapePath.text()) - self.setupTool(self.tool) - self.form.toolName.setText(self.tool.Label) + shapePath = str(self.form.shapePath.text()) + if self.tool.BitShape != shapePath: + self.tool.BitShape = shapePath + self.setupTool(self.tool) + self.form.toolName.setText(self.tool.Label) - for editor in self.bitEditor: - self.bitEditor[editor].updateSpinBox() + for editor in self.bitEditor: + self.bitEditor[editor].updateSpinBox() def updateTool(self): PathLog.track() From 3da82fa6afc29d845334a58cd102336d5f5fbdca Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 19:55:59 -0800 Subject: [PATCH 18/32] Hide document when loading a ToolBit. --- src/Mod/Path/PathScripts/PathToolBit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index 9cd6f488de..4c8929b26f 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -252,7 +252,7 @@ class ToolBit(object): p = findShape(p) if not path and p != obj.BitShape: obj.BitShape = p - doc = FreeCAD.open(p) + doc = FreeCAD.openDocument(p, True) obj.ShapeName = doc.Name docOpened = True return (doc, docOpened) From a5e992f700a07f4544fc7618154261fb0475ec07 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 19:59:44 -0800 Subject: [PATCH 19/32] Make BitShape read/writeable, so the tool can be edited when shared to a different system --- src/Mod/Path/PathScripts/PathToolBit.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index 4c8929b26f..4c502c528a 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -200,7 +200,9 @@ class ToolBit(object): return [prop for prop in obj.PropertiesList if obj.getGroupOfProperty(prop) == PropertyGroupAttribute] def onDocumentRestored(self, obj): - obj.setEditorMode('BitShape', 1) + # when files are shared it is essential to be able to change/set the shape file, + # otherwise the file is hard to use + # obj.setEditorMode('BitShape', 1) obj.setEditorMode('BitBody', 2) obj.setEditorMode('File', 1) obj.setEditorMode('Shape', 2) From d1363943761587664a58886ac1b52949969370d4 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 20:27:42 -0800 Subject: [PATCH 20/32] Rearranged BitTool shape update so dependent ops don't execute with invalid tool. --- src/Mod/Path/PathScripts/PathToolBit.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index 4c502c528a..5ffe7503dd 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -288,21 +288,22 @@ class ToolBit(object): self._removeBitBody(obj) def _setupBitShape(self, obj, path=None): + PathLog.track(obj.Label) + activeDoc = FreeCAD.ActiveDocument (doc, docOpened) = self._loadBitBody(obj, path) obj.Label = doc.RootObjects[0].Label self._deleteBitSetup(obj) - obj.BitBody = obj.Document.copyObject(doc.RootObjects[0], True) + bitBody = obj.Document.copyObject(doc.RootObjects[0], True) if docOpened: FreeCAD.setActiveDocument(activeDoc.Name) FreeCAD.closeDocument(doc.Name) - if obj.BitBody.ViewObject: - obj.BitBody.ViewObject.Visibility = False - self._copyBitShape(obj) + if bitBody.ViewObject: + bitBody.ViewObject.Visibility = False - for sketch in [o for o in obj.BitBody.Group if o.TypeId == 'Sketcher::SketchObject']: + for sketch in [o for o in bitBody.Group if o.TypeId == 'Sketcher::SketchObject']: for constraint in [c for c in sketch.Constraints if c.Name != '']: typ = ParameterTypeConstraint.get(constraint.Type) PathLog.track(constraint, typ) @@ -318,6 +319,9 @@ class ToolBit(object): if constraint.Type == 'Angle': value = value * 180 / math.pi PathUtil.setProperty(obj, prop, value) + # has to happen last because it could trigger op.execute evaluations + obj.BitBody = bitBody + self._copyBitShape(obj) def getBitThumbnail(self, obj): if obj.BitShape: From c1549ba3a88327044621467c953d6fa49ecd70bf Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 22:56:42 -0800 Subject: [PATCH 21/32] Allowing QuantitySpinBox to be reused for different attribute; using properties to get values to work around build differences. --- src/Mod/Path/PathScripts/PathGui.py | 41 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathGui.py b/src/Mod/Path/PathScripts/PathGui.py index d7fdd6be32..c9fee42062 100644 --- a/src/Mod/Path/PathScripts/PathGui.py +++ b/src/Mod/Path/PathScripts/PathGui.py @@ -35,13 +35,8 @@ __doc__ = "A collection of helper and utility functions for the Path GUI." def translate(context, text, disambig=None): return PySide.QtCore.QCoreApplication.translate(context, text, disambig) -LOGLEVEL = False - -if LOGLEVEL: - PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) - PathLog.trackModule(PathLog.thisModule()) -else: - PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) def updateInputField(obj, prop, widget, onBeforeChange=None): @@ -53,7 +48,7 @@ def updateInputField(obj, prop, widget, onBeforeChange=None): If onBeforeChange is specified it is called before a new value is assigned to the property. Returns True if a new value was assigned, False otherwise (new value is the same as the current). ''' - value = FreeCAD.Units.Quantity(widget.text()).Value + value = widget.property('rawValue') attr = PathUtil.getProperty(obj, prop) attrValue = attr.Value if hasattr(attr, 'Value') else attr @@ -72,10 +67,10 @@ def updateInputField(obj, prop, widget, onBeforeChange=None): isDiff = True break if noExpr: - widget.setReadOnly(False) + widget.setProperty('readonly', False) widget.setStyleSheet("color: black") else: - widget.setReadOnly(True) + widget.setProperty('readonly', True) widget.setStyleSheet("color: gray") widget.update() @@ -100,19 +95,26 @@ class QuantitySpinBox: ''' def __init__(self, widget, obj, prop, onBeforeChange=None): - self.obj = obj + PathLog.track(widget) self.widget = widget - self.prop = prop self.onBeforeChange = onBeforeChange + self.attachTo(obj, prop) - attr = PathUtil.getProperty(self.obj, self.prop) - if attr is not None: - if hasattr(attr, 'Value'): - widget.setProperty('unit', attr.getUserPreferred()[2]) - widget.setProperty('binding', "%s.%s" % (obj.Name, prop)) - self.valid = True + def attachTo(self, obj, prop = None): + '''attachTo(obj, prop=None) ... use an existing editor for the given object and property''' + self.obj = obj + self.prop = prop + if obj and prop: + attr = PathUtil.getProperty(obj, prop) + if attr is not None: + if hasattr(attr, 'Value'): + self.widget.setProperty('unit', attr.getUserPreferred()[2]) + self.widget.setProperty('binding', "%s.%s" % (obj.Name, prop)) + self.valid = True + else: + PathLog.warning(translate('PathGui', "Cannot find property %s of %s") % (prop, obj.Label)) + self.valid = False else: - PathLog.warning(translate('PathGui', "Cannot find property %s of %s") % (prop, obj.Label)) self.valid = False def expression(self): @@ -122,6 +124,7 @@ class QuantitySpinBox: return '' def setMinimum(self, quantity): + '''setMinimum(quantity) ... set the minimum''' if self.valid: value = quantity.Value if hasattr(quantity, 'Value') else quantity self.widget.setProperty('setMinimum', value) From 3b69d23571ae073ded725ad03d029cf2ddb8750c Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 23:01:55 -0800 Subject: [PATCH 22/32] Reuse existing QuantitySpinBox'es in order to avoid segfault on focus change. --- src/Mod/Path/PathScripts/PathToolBitEdit.py | 67 ++++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathToolBitEdit.py b/src/Mod/Path/PathScripts/PathToolBitEdit.py index c9cf238ebd..3488bcc95d 100644 --- a/src/Mod/Path/PathScripts/PathToolBitEdit.py +++ b/src/Mod/Path/PathScripts/PathToolBitEdit.py @@ -62,24 +62,60 @@ class ToolBitEditor(object): if self.loadbitbody: self.tool.Proxy.loadBitBody(self.tool) + # remove example widgets + layout = self.form.bitParams.layout() + for i in range(layout.rowCount() - 1, -1, -1): + layout.removeRow(i) + # used to track property widgets and editors + self.widgets = [] + self.setupTool(self.tool) self.setupAttributes(self.tool) def setupTool(self, tool): PathLog.track() + # Can't delete and add fields to the form because of dangling references in case of + # a focus change. see https://forum.freecadweb.org/viewtopic.php?f=10&t=52246#p458583 + # Instead we keep widgets once created and use them for new properties, and hide all + # which aren't being needed anymore. + + def labelText(name): + return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', name)) + layout = self.form.bitParams.layout() - for i in range(layout.rowCount() - 1, -1, -1): - layout.removeRow(i) - editor = {} ui = FreeCADGui.UiLoader() + nr = 0 + + # for all properties either assign them to existing labels and editors + # or create additional ones for them if not enough have already been + # created. for name in tool.PropertiesList: if tool.getGroupOfProperty(name) == PathToolBit.PropertyGroupBit: - qsb = ui.createWidget('Gui::QuantitySpinBox') - editor[name] = PathGui.QuantitySpinBox(qsb, tool, name) - label = QtGui.QLabel(re.sub('([A-Z][a-z]+)', r' \1', - re.sub('([A-Z]+)', r' \1', name))) - layout.addRow(label, qsb) - self.bitEditor = editor + if nr < len(self.widgets): + PathLog.debug("re-use row: {} [{}]".format(nr, name)) + label, qsb, editor = self.widgets[nr] + label.setText(labelText(name)) + editor.attachTo(tool, name) + label.show() + qsb.show() + else: + qsb = ui.createWidget('Gui::QuantitySpinBox') + editor = PathGui.QuantitySpinBox(qsb, tool, name) + label = QtGui.QLabel(labelText(name)) + self.widgets.append((label, qsb, editor)) + PathLog.debug("create row: {} [{}]".format(nr, name)) + if nr >= layout.rowCount(): + layout.addRow(label, qsb) + nr = nr + 1 + + # hide all rows which aren't being used + for i in range(nr, len(self.widgets)): + label, qsb, editor = self.widgets[i] + label.hide() + qsb.hide() + editor.attachTo(None) + PathLog.debug(" hide row: {}".format(i)) + img = tool.Proxy.getBitThumbnail(tool) if img: self.form.image.setPixmap(QtGui.QPixmap(QtGui.QImage.fromData(img))) @@ -189,27 +225,28 @@ class ToolBitEditor(object): self.form.toolName.setText(self.tool.Label) self.form.shapePath.setText(self.tool.BitShape) - for editor in self.bitEditor: - self.bitEditor[editor].updateSpinBox() + for lbl, qsb, editor in self.widgets: + editor.updateSpinBox() def updateShape(self): PathLog.track() shapePath = str(self.form.shapePath.text()) + # Only need to go through this exercise if the shape actually changed. if self.tool.BitShape != shapePath: self.tool.BitShape = shapePath self.setupTool(self.tool) self.form.toolName.setText(self.tool.Label) - for editor in self.bitEditor: - self.bitEditor[editor].updateSpinBox() + for lbl, qsb, editor in self.widgets: + editor.updateSpinBox() def updateTool(self): PathLog.track() self.tool.Label = str(self.form.toolName.text()) self.tool.BitShape = str(self.form.shapePath.text()) - for editor in self.bitEditor: - self.bitEditor[editor].updateProperty() + for lbl, qsb, editor in self.widgets: + editor.updateProperty() # self.tool.Proxy._updateBitShape(self.tool) From 72b70c52a14a710ac4be99d1cd806dce6fa4cef5 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Dec 2020 23:11:02 -0800 Subject: [PATCH 23/32] Added provision for gcc peculiarities in unit tests --- src/Mod/Path/PathTests/TestPathCore.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathTests/TestPathCore.py b/src/Mod/Path/PathTests/TestPathCore.py index dae7c091e5..34d84d6477 100644 --- a/src/Mod/Path/PathTests/TestPathCore.py +++ b/src/Mod/Path/PathTests/TestPathCore.py @@ -152,7 +152,9 @@ G0 Z0.500000 table.addTools(t2) self.assertEqual(len(table.Tools), 2) - self.assertEqual(str(table.Tools), '{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}' ) + # gcc7 build needs some special treatment (makes 1L out of a 1) ... + if str(table.Tools) != '{1L: Tool 12.7mm Drill Bit, 2L: Tool my other tool}': + self.assertEqual(str(table.Tools), '{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}') def test50(self): """Test Path.Length calculation""" From def9e29fa99bd6fead4d163efd9c303732f65395 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 18 Dec 2020 11:46:31 +0100 Subject: [PATCH 24/32] Gui: [skip ci] fix crash when using a dialog instance twice for a task panel --- src/Gui/TaskView/TaskDialogPython.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Gui/TaskView/TaskDialogPython.cpp b/src/Gui/TaskView/TaskDialogPython.cpp index b353fcb0a1..5a9cb4b864 100644 --- a/src/Gui/TaskView/TaskDialogPython.cpp +++ b/src/Gui/TaskView/TaskDialogPython.cpp @@ -337,8 +337,20 @@ TaskDialogPython::~TaskDialogPython() std::vector< QPointer > guarded; guarded.insert(guarded.begin(), Content.begin(), Content.end()); Content.clear(); + Base::PyGILStateLocker lock; + + // The widgets stored in the 'form' attribute will be deleted. + // Thus, set this attribute to None to make sure that when using + // the same dialog instance for a task panel won't segfault. + if (this->dlg.hasAttr(std::string("form"))) { + this->dlg.setAttr(std::string("form"), Py::None()); + } this->dlg = Py::None(); + + // Assigning None to 'dlg' may destroy some of the stored widgets. + // By guarding them with QPointer their pointers will be set to null + // so that the destructor of the base class can reliably call 'delete'. Content.insert(Content.begin(), guarded.begin(), guarded.end()); } From b41cf985834fd2c4b0636b932c25848f97423d88 Mon Sep 17 00:00:00 2001 From: Aapo Date: Tue, 15 Dec 2020 22:56:51 +0200 Subject: [PATCH 25/32] [TD] Balloon, change ordering of the data properties to more logical. --- src/Mod/TechDraw/App/DrawViewBalloon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.cpp b/src/Mod/TechDraw/App/DrawViewBalloon.cpp index 9f8a397c39..ed9f6ca837 100644 --- a/src/Mod/TechDraw/App/DrawViewBalloon.cpp +++ b/src/Mod/TechDraw/App/DrawViewBalloon.cpp @@ -99,15 +99,15 @@ DrawViewBalloon::DrawViewBalloon(void) EndType.setEnums(ArrowPropEnum::ArrowTypeEnums); ADD_PROPERTY(EndType,(prefEnd())); + ADD_PROPERTY_TYPE(EndTypeScale,(1.0),"",(App::PropertyType)(App::Prop_None),"EndType shape scale"); + ShapeScale.setConstraints(&SymbolScaleRange); + BubbleShape.setEnums(balloonTypeEnums); ADD_PROPERTY(BubbleShape,(prefShape())); ADD_PROPERTY_TYPE(ShapeScale,(1.0),"",(App::PropertyType)(App::Prop_None),"Balloon shape scale"); ShapeScale.setConstraints(&SymbolScaleRange); - ADD_PROPERTY_TYPE(EndTypeScale,(1.0),"",(App::PropertyType)(App::Prop_None),"EndType shape scale"); - ShapeScale.setConstraints(&SymbolScaleRange); - ADD_PROPERTY_TYPE(TextWrapLen,(-1),"",(App::PropertyType)(App::Prop_None),"Text wrap length; -1 means no wrap"); ADD_PROPERTY_TYPE(KinkLength,(prefKinkLength()),"",(App::PropertyType)(App::Prop_None), From f3bce14f29dbbc1cfecf16f3ab6021ca56c04240 Mon Sep 17 00:00:00 2001 From: Aapo Date: Tue, 15 Dec 2020 23:32:11 +0200 Subject: [PATCH 26/32] [TD] Balloon, move property LineVisible to View tab and fix a refresh bug. --- src/Mod/TechDraw/App/DrawViewBalloon.cpp | 2 -- src/Mod/TechDraw/App/DrawViewBalloon.h | 1 - src/Mod/TechDraw/Gui/QGIViewBalloon.cpp | 2 +- src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp | 4 +++- src/Mod/TechDraw/Gui/ViewProviderBalloon.h | 1 + 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.cpp b/src/Mod/TechDraw/App/DrawViewBalloon.cpp index ed9f6ca837..2f40d08e37 100644 --- a/src/Mod/TechDraw/App/DrawViewBalloon.cpp +++ b/src/Mod/TechDraw/App/DrawViewBalloon.cpp @@ -113,8 +113,6 @@ DrawViewBalloon::DrawViewBalloon(void) ADD_PROPERTY_TYPE(KinkLength,(prefKinkLength()),"",(App::PropertyType)(App::Prop_None), "Distance from symbol to leader kink"); - ADD_PROPERTY_TYPE(LineVisible,(true),"",(App::PropertyType)(App::Prop_None),"Balloon line visible or hidden"); - SourceView.setScope(App::LinkScope::Global); Rotation.setStatus(App::Property::Hidden,true); Caption.setStatus(App::Property::Hidden,true); diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.h b/src/Mod/TechDraw/App/DrawViewBalloon.h index 0892aac32a..ac72ae8699 100644 --- a/src/Mod/TechDraw/App/DrawViewBalloon.h +++ b/src/Mod/TechDraw/App/DrawViewBalloon.h @@ -59,7 +59,6 @@ public: App::PropertyDistance OriginY; App::PropertyFloat TextWrapLen; App::PropertyDistance KinkLength; - App::PropertyBool LineVisible; short mustExecute() const override; diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp index 1b7b062a0e..205cbfe093 100644 --- a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp @@ -766,7 +766,7 @@ void QGIViewBalloon::draw() balloonLines->setPath(dLinePath); // This overwrites the previously created QPainterPath with empty one, in case it should be hidden. Should be refactored. - if (!balloon->LineVisible.getValue()) { + if (!vp->LineVisible.getValue()) { arrow->hide(); balloonLines->setPath(QPainterPath()); } diff --git a/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp b/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp index f88dbb6f09..f79910b5e5 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp @@ -75,6 +75,7 @@ ViewProviderBalloon::ViewProviderBalloon() double weight = lg->getWeight("Thin"); delete lg; //Coverity CID 174670 ADD_PROPERTY_TYPE(LineWidth,(weight),group,(App::PropertyType)(App::Prop_None),"Leader line width"); + ADD_PROPERTY_TYPE(LineVisible,(true),group,(App::PropertyType)(App::Prop_None),"Balloon line visible or hidden"); ADD_PROPERTY_TYPE(Color,(PreferencesGui::dimColor()), group,App::Prop_None,"Color of the balloon"); @@ -148,7 +149,8 @@ void ViewProviderBalloon::onChanged(const App::Property* p) if ((p == &Font) || (p == &Fontsize) || (p == &Color) || - (p == &LineWidth)) { + (p == &LineWidth) || + (p == &LineVisible)) { QGIView* qgiv = getQView(); if (qgiv) { qgiv->updateView(true); diff --git a/src/Mod/TechDraw/Gui/ViewProviderBalloon.h b/src/Mod/TechDraw/Gui/ViewProviderBalloon.h index fd9cb6ad27..2f88a50434 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderBalloon.h +++ b/src/Mod/TechDraw/Gui/ViewProviderBalloon.h @@ -48,6 +48,7 @@ public: App::PropertyFont Font; App::PropertyLength Fontsize; App::PropertyLength LineWidth; + App::PropertyBool LineVisible; App::PropertyColor Color; virtual void attach(App::DocumentObject *); From 30386330690763f2f6ffcd590c77677f4fb1179e Mon Sep 17 00:00:00 2001 From: Aapo Date: Tue, 15 Dec 2020 23:39:14 +0200 Subject: [PATCH 27/32] [TD] Balloon, fix EndTypeScale refresh bug. --- src/Mod/TechDraw/App/DrawViewBalloon.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.cpp b/src/Mod/TechDraw/App/DrawViewBalloon.cpp index 2f40d08e37..8331d0809d 100644 --- a/src/Mod/TechDraw/App/DrawViewBalloon.cpp +++ b/src/Mod/TechDraw/App/DrawViewBalloon.cpp @@ -129,7 +129,8 @@ void DrawViewBalloon::onChanged(const App::Property* prop) if ( (prop == &EndType) || (prop == &BubbleShape) || (prop == &Text) || - (prop == &KinkLength) ) { + (prop == &KinkLength) || + (prop == &EndTypeScale) ) { requestPaint(); } } From 8462fc184274a8c1ee8c695a38f945a157b5794b Mon Sep 17 00:00:00 2001 From: Aapo Date: Wed, 16 Dec 2020 14:10:41 +0200 Subject: [PATCH 28/32] [TD] Balloon, fix Origin and scale refresh bugs, make Balloon scale change more reasonable per UI click. --- src/Mod/TechDraw/App/DrawViewBalloon.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.cpp b/src/Mod/TechDraw/App/DrawViewBalloon.cpp index 8331d0809d..2a19855a21 100644 --- a/src/Mod/TechDraw/App/DrawViewBalloon.cpp +++ b/src/Mod/TechDraw/App/DrawViewBalloon.cpp @@ -65,7 +65,7 @@ using namespace TechDraw; App::PropertyFloatConstraint::Constraints DrawViewBalloon::SymbolScaleRange = { Precision::Confusion(), std::numeric_limits::max(), - (1.0) }; + (0.1) }; //=========================================================================== // DrawViewBalloon @@ -100,7 +100,7 @@ DrawViewBalloon::DrawViewBalloon(void) ADD_PROPERTY(EndType,(prefEnd())); ADD_PROPERTY_TYPE(EndTypeScale,(1.0),"",(App::PropertyType)(App::Prop_None),"EndType shape scale"); - ShapeScale.setConstraints(&SymbolScaleRange); + EndTypeScale.setConstraints(&SymbolScaleRange); BubbleShape.setEnums(balloonTypeEnums); ADD_PROPERTY(BubbleShape,(prefShape())); @@ -128,9 +128,12 @@ void DrawViewBalloon::onChanged(const App::Property* prop) if (!isRestoring()) { if ( (prop == &EndType) || (prop == &BubbleShape) || + (prop == &ShapeScale) || (prop == &Text) || (prop == &KinkLength) || - (prop == &EndTypeScale) ) { + (prop == &EndTypeScale) || + (prop == &OriginX) || + (prop == &OriginY) ) { requestPaint(); } } From 126c58471ab95d01502b5dececa0178ac6f1b73d Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 18 Dec 2020 14:35:17 +0100 Subject: [PATCH 29/32] Gui: [skip ci] avoid adding a wrong file name to the recent files list --- src/Gui/Document.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index e6463eec6a..51e97c648a 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -1189,6 +1189,8 @@ bool Document::saveAs(void) escapedstr = Base::Tools::escapeEncodeFilename(escapedstr); Command::doCommand(Command::Doc,"App.getDocument(\"%s\").saveAs(u\"%s\")" , DocName, escapedstr.c_str()); + // App::Document::saveAs() may modify the passed file name + fi.setFile(QString::fromUtf8(d->_pcDocument->FileName.getValue())); setModified(false); getMainWindow()->appendRecentFile(fi.filePath()); } From 9be9abe476cf32f62704b188a1ba99b557f22544 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 18 Dec 2020 16:28:35 +0100 Subject: [PATCH 30/32] PartDesign: [skip ci] fix layout of Drill point controls --- src/Mod/PartDesign/Gui/TaskHoleParameters.ui | 60 +++++++++----------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui index 7d0a694760..eef3d883f5 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui @@ -267,7 +267,7 @@ Only available for holes without thread - + @@ -314,41 +314,37 @@ Only available for holes without thread - - - - - - 0 - 0 - - - - Angled - - - - - - - - 0 - 0 - - - - deg - - - 0.000000000000000 - - - - + + + + 0 + 0 + + + + Angled + + + + + + + 0 + 0 + + + + deg + + + 0.000000000000000 + + + From 055424342e28364cbd8a087c7dc22687f67424b6 Mon Sep 17 00:00:00 2001 From: M G Berberich Date: Fri, 18 Dec 2020 15:19:31 +0100 Subject: [PATCH 31/32] cleanup of hole dialog * replaced widget with radiobuttons by button Group, to improve alignemt of DrillPointAngle with grid-layout * made Thread Pitch/Angle/Cutoffs widgets wider --- src/Mod/PartDesign/Gui/TaskHoleParameters.ui | 972 +++++++++---------- 1 file changed, 461 insertions(+), 511 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui index eef3d883f5..e0c34186be 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui @@ -6,8 +6,8 @@ 0 0 - 373 - 560 + 441 + 710 @@ -20,21 +20,65 @@ Task Hole Parameters - - - - false - - - mm - - - 0.000000000000000 + + + + Clearance - - + + + + + 0 + 0 + + + + <b>Drill point</b> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Depth + + + + + + + false + + + Angle + + + + + + + false + + + Cutoff outer + + + + + + + Tapered + + + + + 0 @@ -43,48 +87,36 @@ - 140 + 16777215 16777215 - - - Dimension - - - - - Through all - - - - - - - 0 - 0 - - - - - 110 - 16777215 - - + + - Hole diameter + - - mm - - - 0.000000000000000 + + Right hand + + directionButtonGroup + - + + + + Left hand + + + directionButtonGroup + + + + @@ -119,94 +151,91 @@ Only available for holes without thread - - - - false + + + + + 0 + 0 + - Angle + Profile - - - - Qt::Horizontal + + + + + 0 + 0 + - - QSizePolicy::Fixed + + Direction - + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 0 + 0 + + + - 13 - 20 + 140 + 16777215 - - - - - - <b>Hole cut</b> - + + + Dimension + + + + + Through all + + - - - - Thread direction + + + + + 0 + 0 + - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Right hand - - - - - - - Left hand - - - - - - - - - - false + + + 16777215 + 16777215 + - mm + deg 0.000000000000000 - + + + + Countersink angle + + + + @@ -231,281 +260,7 @@ Only available for holes without thread - - - - false - - - Model actual thread - - - - - - - - 0 - 0 - - - - Depth - - - - - - - - 0 - 0 - - - - Profile - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Ending of the hole if 'Depth' is set to 'Dimension' - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Flat - - - - - - - - 0 - 0 - - - - Angled - - - - - - - - - - - 0 - 0 - - - - deg - - - 0.000000000000000 - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - - - 0 - 0 - - - - Class - - - - - - - Tapered - - - - - - - false - - - Cutoff inner - - - - - - - - 0 - 0 - - - - - 140 - 16777215 - - - - Tolerance class for threaded holes according to hole profile - - - - - - - - 0 - 0 - - - - mm - - - - - - - - 0 - 0 - - - - Size - - - - - - - - 0 - 0 - - - - <b>Drill point</b> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Type - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - false - - - Cutoff outer - - - - - - - - 0 - 0 - - - - Type - - - - - - - false - - - deg - - - 0.000000000000000 - - - - - - - <b>Threading and size</b> - - - - + @@ -524,102 +279,13 @@ Only available for holes without thread - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - deg - - - 0.000000000000000 - - - - - - - <b>Misc</b> - - - - - - - - 0 - 0 - - - - Direction - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - 0 - 0 - - - - Diameter - - - - - - - Depth - - - - - - - Countersink angle - - - - - - - Diameter - - - - - - - Clearance - - - - - + + false - - mm - - - 0.000000000000000 + + Pitch @@ -633,7 +299,174 @@ Only available for holes without thread - + + + + + 0 + 0 + + + + Class + + + + + + + false + + + Cutoff inner + + + + + + + Taper angle for the hole +90 degree: straight hole +under 90: smaller hole radius at the bottom +over 90: larger hole radius at the bottom + + + deg + + + 0.000000000000000 + + + + + + + false + + + mm + + + 0.000000000000000 + + + + + + + <b>Hole cut</b> + + + + + + + false + + + deg + + + 0.000000000000000 + + + + + + + + 0 + 0 + + + + deg + + + 0.000000000000000 + + + + + + + + 0 + 0 + + + + Flat + + + drillPointButtonGroup + + + + + + + <b>Threading and size</b> + + + + + + + + 0 + 0 + + + + Diameter + + + + + + + + 0 + 0 + + + + Type + + + + + + + <b>Misc</b> + + + + + + + + 0 + 0 + + + + mm + + + + + + + false + + + Model actual thread + + + + @@ -649,39 +482,23 @@ Only available for holes without thread - - - - - 120 - 16777215 - - - - Taper angle for the hole -90 degree: straight hole -under 90: smaller hole radius at the bottom -over 90: larger hole radius at the bottom - - - deg - - - 0.000000000000000 - - - - - - - false + + + + + 0 + 0 + - Pitch + Angled + + drillPointButtonGroup + - + @@ -709,7 +526,136 @@ over 90: larger hole radius at the bottom - + + + + + 0 + 0 + + + + Depth + + + + + + + Diameter + + + + + + + + 0 + 0 + + + + + 110 + 16777215 + + + + Hole diameter + + + mm + + + 0.000000000000000 + + + + + + + + 0 + 0 + + + + + 140 + 16777215 + + + + Tolerance class for threaded holes according to hole profile + + + + + + + Type + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + false + + + mm + + + 0.000000000000000 + + + + + + + + 0 + 0 + + + + Size + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 13 + 20 + + + + + + + + false + + + mm + + + 0.000000000000000 + + + + Reverses the hole direction @@ -784,4 +730,8 @@ over 90: larger hole radius at the bottom + + + + From 1c897b7ba997188a68c0be32299848bca7c944dc Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 18 Dec 2020 17:04:27 +0100 Subject: [PATCH 32/32] PartDesign: [skip ci] fix order of controls so that by pressing TAB always the next control gets focus --- src/Mod/PartDesign/Gui/TaskHoleParameters.ui | 732 +++++++++---------- 1 file changed, 366 insertions(+), 366 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui index e0c34186be..c723acb05c 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.ui @@ -20,15 +20,15 @@ Task Hole Parameters - - + + - Clearance + <b>Threading and size</b> - - + + 0 @@ -36,44 +36,7 @@ - <b>Drill point</b> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Depth - - - - - - - false - - - Angle - - - - - - - false - - - Cutoff outer - - - - - - - Tapered + Profile @@ -93,6 +56,141 @@ + + + + Whether the hole gets a thread + + + Threaded + + + + + + + false + + + Model actual thread + + + + + + + false + + + Pitch + + + + + + + false + + + mm + + + 0.000000000000000 + + + + + + + <b>Hole cut</b> + + + + + + + false + + + Angle + + + + + + + false + + + deg + + + 0.000000000000000 + + + + + + + false + + + Cutoff inner + + + + + + + false + + + mm + + + 0.000000000000000 + + + + + + + false + + + Cutoff outer + + + + + + + false + + + mm + + + 0.000000000000000 + + + + + + + + 0 + 0 + + + + Direction + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + @@ -116,6 +214,42 @@ + + + + + 0 + 0 + + + + Size + + + + + + + + 0 + 0 + + + + + 140 + 16777215 + + + + + + + + Clearance + + + @@ -151,8 +285,8 @@ Only available for holes without thread - - + + 0 @@ -160,12 +294,72 @@ Only available for holes without thread - Profile + Class - - + + + + + 0 + 0 + + + + + 140 + 16777215 + + + + Tolerance class for threaded holes according to hole profile + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 13 + 20 + + + + + + + + + 0 + 0 + + + + + 110 + 16777215 + + + + Hole diameter + + + mm + + + 0.000000000000000 + + + + + 0 @@ -173,10 +367,20 @@ Only available for holes without thread - Direction + Diameter - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + 0 + 0 + + + + Depth @@ -206,57 +410,29 @@ Only available for holes without thread - - + + 0 0 - - - 16777215 - 16777215 - - - - deg - - - 0.000000000000000 - - - - - - - Countersink angle - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - mm - - 0.000000000000000 + + + + + + + 0 + 0 + - - 0.100000000000000 + + Type @@ -279,225 +455,13 @@ Only available for holes without thread - - - - false - - - Pitch - - - - - - - Whether the hole gets a thread - - - Threaded - - - - - - - - 0 - 0 - - - - Class - - - - - - - false - - - Cutoff inner - - - - - - - Taper angle for the hole -90 degree: straight hole -under 90: smaller hole radius at the bottom -over 90: larger hole radius at the bottom - - - deg - - - 0.000000000000000 - - - - - - - false - - - mm - - - 0.000000000000000 - - - - - - - <b>Hole cut</b> - - - - - - - false - - - deg - - - 0.000000000000000 - - - - - - - - 0 - 0 - - - - deg - - - 0.000000000000000 - - - - - - - - 0 - 0 - - - - Flat - - - drillPointButtonGroup - - - - - - - <b>Threading and size</b> - - - - - - - - 0 - 0 - - + + Diameter - - - - - 0 - 0 - - - - Type - - - - - - - <b>Misc</b> - - - - - - - - 0 - 0 - - - - mm - - - - - - - false - - - Model actual thread - - - - - - - - 0 - 0 - - - - - 140 - 16777215 - - - - - - - - - 0 - 0 - - - - Angled - - - drillPointButtonGroup - - - @@ -526,28 +490,15 @@ over 90: larger hole radius at the bottom - - - - - 0 - 0 - - + + Depth - - - - Diameter - - - - - + + 0 @@ -556,37 +507,63 @@ over 90: larger hole radius at the bottom - 110 + 16777215 16777215 - - Hole diameter - mm 0.000000000000000 + + 0.100000000000000 + - - + + + + Countersink angle + + + + + - + 0 0 - 140 + 16777215 16777215 - - Tolerance class for threaded holes according to hole profile + + deg + + + 0.000000000000000 + + + + + + + + 0 + 0 + + + + <b>Drill point</b> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -600,55 +577,78 @@ over 90: larger hole radius at the bottom - - - - false + + + + + 0 + 0 + + + + Flat + + + drillPointButtonGroup + + + + + + + + 0 + 0 + + + + Angled + + + drillPointButtonGroup + + + + + + + + 0 + 0 + - mm + deg 0.000000000000000 - - - - - 0 - 0 - - + + - Size + <b>Misc</b> - - - - Qt::Horizontal + + + + Tapered - - QSizePolicy::Fixed - - - - 13 - 20 - - - + - - - - false + + + + Taper angle for the hole +90 degree: straight hole +under 90: smaller hole radius at the bottom +over 90: larger hole radius at the bottom - mm + deg 0.000000000000000