Merge pull request #32 from jmwright/new-script
Fixes for Script Manipulation Annoyances
This commit is contained in:
commit
20f33d1710
90
CadQuery/Examples/Ex027_Remote_Enclosure.py
Normal file
90
CadQuery/Examples/Ex027_Remote_Enclosure.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
# This example is meant to be used from within the CadQuery module of FreeCAD.
|
||||
import cadquery as cq
|
||||
import Part
|
||||
|
||||
exploded = False # when true, moves the base away from the top so we see
|
||||
showTop = True # When true, the top is rendered.
|
||||
showCover = True # When true, the cover is rendered
|
||||
|
||||
width = 2.2 # Nominal x dimension of the part
|
||||
height = 0.5 # Height from bottom top to the top of the top :P
|
||||
length = 1.5 # Nominal y dimension of the part
|
||||
trapezoidFudge = 0.7 # ratio of trapezoid bases. set to 1.0 for cube
|
||||
xHoleOffset = 0.500 # Holes are distributed symetrically about each axis
|
||||
yHoleOffset = 0.500
|
||||
zFilletRadius = 0.50 # Fillet radius of corners perp. to Z axis.
|
||||
yFilletRadius = 0.250 # Fillet readius of the top edge of the case
|
||||
lipHeight = 0.1 # The height of the lip on the inside of the cover
|
||||
wallThickness = 0.06 # Wall thickness for the case
|
||||
coverThickness = 0.2 # Thickness of the cover plate
|
||||
holeRadius = 0.30 # Button hole radius
|
||||
counterSyncAngle = 100 # Countersink angle.
|
||||
|
||||
xyplane = cq.Workplane("XY")
|
||||
yzplane = cq.Workplane("YZ")
|
||||
|
||||
|
||||
def trapezoid(b1, b2, h):
|
||||
"Defines a symetrical trapezoid in the XY plane."
|
||||
|
||||
y = h / 2
|
||||
x1 = b1 / 2
|
||||
x2 = b2 / 2
|
||||
return (xyplane
|
||||
.polyline([(-x1, y),
|
||||
(x1, y),
|
||||
(x2, -y),
|
||||
(-x2, -y),
|
||||
(-x1, y)]))
|
||||
|
||||
|
||||
# Defines our base shape: a box with fillets around the vertical edges.
|
||||
# This has to be a function because we need to create multiple copies of
|
||||
# the shape.
|
||||
def base(h):
|
||||
global width, trapezoidFudge, length, zFilletRadius
|
||||
return (trapezoid(width, width * trapezoidFudge, length)
|
||||
.extrude(h)
|
||||
.translate((0, 0, height / 2))
|
||||
.edges("Z")
|
||||
.fillet(zFilletRadius))
|
||||
|
||||
# start with the base shape
|
||||
top = (base(height)
|
||||
# then fillet the top edge
|
||||
.edges(">Z")
|
||||
.fillet(yFilletRadius)
|
||||
# shell the solid from the bottom face, with a .060" wall thickness
|
||||
.faces("-Z")
|
||||
.shell(-wallThickness)
|
||||
# cut five button holes into the top face in a cross pattern.
|
||||
.faces("+Z")
|
||||
.workplane()
|
||||
.pushPoints([(0, 0),
|
||||
(-xHoleOffset, 0),
|
||||
(0, -yHoleOffset),
|
||||
(xHoleOffset, 0),
|
||||
(0, yHoleOffset)])
|
||||
.cskHole(diameter=holeRadius,
|
||||
cskDiameter=holeRadius * 1.5,
|
||||
cskAngle=counterSyncAngle))
|
||||
|
||||
# the bottom cover begins with the same basic shape as the top
|
||||
cover = (base(coverThickness)
|
||||
# we need to move it upwards into the parent solid slightly.
|
||||
.translate((0, 0, -coverThickness + lipHeight))
|
||||
# now we subtract the top from the cover. This produces a lip on the
|
||||
# solid NOTE: that this does not account for mechanical tolerances.
|
||||
# But it looks cool.
|
||||
.cut(top)
|
||||
# try to fillet the inner edge of the cover lip. Technically this
|
||||
# fillets every edge perpendicular to the Z axis.
|
||||
.edges("#Z")
|
||||
.fillet(.020)
|
||||
.translate((0, 0, -0.5 if exploded else 0)))
|
||||
|
||||
# Conditionally render the parts
|
||||
if showTop:
|
||||
Part.show(top.toFreecad())
|
||||
if showCover:
|
||||
Part.show(cover.toFreecad())
|
|
@ -1,17 +1,20 @@
|
|||
"""Adds all of the commands that are used for the menus of the CadQuery module"""
|
||||
# (c) 2014 Jeremy Wright LGPL v3
|
||||
|
||||
import tempfile
|
||||
import imp, os, sys, tempfile
|
||||
import FreeCAD, FreeCADGui
|
||||
from PySide import QtGui
|
||||
import ExportCQ, ImportCQ, CadQuery_rc
|
||||
import ExportCQ, ImportCQ
|
||||
import module_locator
|
||||
|
||||
#Distinguish python built-in open function from the one declared here
|
||||
if open.__module__ == '__builtin__':
|
||||
pythonopen = open
|
||||
|
||||
|
||||
def clearAll():
|
||||
def clearActiveDocument():
|
||||
"""Clears the currently active 3D view so that we can re-render"""
|
||||
|
||||
doc = FreeCAD.ActiveDocument
|
||||
|
||||
#Make sure we have an active document to work with
|
||||
|
@ -29,35 +32,47 @@ class CadQueryCloseScript:
|
|||
|
||||
def IsActive(self):
|
||||
return True
|
||||
# if FreeCAD.ActiveDocument is None:
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
|
||||
def Activated(self):
|
||||
#Getting the main window will allow us to find the children we need to work with
|
||||
#Grab our code editor so we can interact with it
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
|
||||
#We need this so we can load the file into it
|
||||
cqCodePane = mw.findChild(QtGui.QPlainTextEdit, "cqCodePane")
|
||||
|
||||
reply = QtGui.QMessageBox.question(cqCodePane, "Save CadQuery Script", "Save script before closing?",
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel)
|
||||
|
||||
if reply == QtGui.QMessageBox.Cancel:
|
||||
#If there's nothing open in the code pane, we don't need to close it
|
||||
if len(cqCodePane.file.path) == 0:
|
||||
return
|
||||
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
#If we've got a file name already save it there, otherwise give a save-as dialog
|
||||
if len(cqCodePane.file.path) == 0:
|
||||
filename = QtGui.QFileDialog.getSaveFileName(mw, mw.tr("Save CadQuery Script As"), "/home/",
|
||||
mw.tr("CadQuery Files (*.py)"))
|
||||
else:
|
||||
filename = cqCodePane.file.path
|
||||
#Check to see if we need to save the script
|
||||
if cqCodePane.dirty:
|
||||
reply = QtGui.QMessageBox.question(cqCodePane, "Save CadQuery Script", "Save script before closing?",
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel)
|
||||
|
||||
#Make sure we got a valid file name
|
||||
if filename == None:
|
||||
ExportCQ.save(filename)
|
||||
if reply == QtGui.QMessageBox.Cancel:
|
||||
return
|
||||
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
#If we've got a file name already save it there, otherwise give a save-as dialog
|
||||
if len(cqCodePane.file.path) == 0:
|
||||
filename = QtGui.QFileDialog.getSaveFileName(mw, mw.tr("Save CadQuery Script As"), "/home/",
|
||||
mw.tr("CadQuery Files (*.py)"))
|
||||
else:
|
||||
filename = cqCodePane.file.path
|
||||
|
||||
#Make sure we got a valid file name
|
||||
if filename is not None:
|
||||
ExportCQ.save(filename)
|
||||
|
||||
#Close the matching 3D view if it's open
|
||||
if cqCodePane.file.path is not None:
|
||||
docname = os.path.splitext(os.path.basename(cqCodePane.file.path))[0]
|
||||
FreeCAD.closeDocument(docname)
|
||||
|
||||
#Clear our script and whatever was rendered by it out
|
||||
cqCodePane.file.close()
|
||||
clearAll()
|
||||
|
||||
|
||||
class CadQueryExecuteScript:
|
||||
|
@ -71,26 +86,32 @@ class CadQueryExecuteScript:
|
|||
|
||||
def IsActive(self):
|
||||
return True
|
||||
# if FreeCAD.ActiveDocument is None:
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
|
||||
def Activated(self):
|
||||
#Getting the main window will allow us to find the children we need to work with
|
||||
#Grab our code editor so we can interact with it
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
|
||||
#We need this so we can load the file into it
|
||||
cqCodePane = mw.findChild(QtGui.QPlainTextEdit, "cqCodePane")
|
||||
|
||||
clearAll()
|
||||
#Clear the old render before re-rendering
|
||||
clearActiveDocument()
|
||||
|
||||
#Save our code to a tempfile and render it
|
||||
tempFile = tempfile.NamedTemporaryFile(delete=False)
|
||||
tempFile.write(cqCodePane.toPlainText())
|
||||
tempFile.close()
|
||||
FreeCAD.Console.PrintMessage("\r\n")
|
||||
execfile(tempFile.name)
|
||||
|
||||
docname = os.path.splitext(os.path.basename(cqCodePane.file.path))[0]
|
||||
|
||||
#If the matching 3D view has been closed, we need to open a new one
|
||||
try:
|
||||
FreeCAD.getDocument(docname)
|
||||
except:
|
||||
FreeCAD.newDocument(docname)
|
||||
|
||||
|
||||
#We import this way because using execfile() causes non-standard script execution in some situations
|
||||
imp.load_source('temp.module', tempFile.name)
|
||||
|
||||
msg = QtGui.QApplication.translate(
|
||||
"cqCodeWidget",
|
||||
|
@ -100,7 +121,29 @@ class CadQueryExecuteScript:
|
|||
FreeCAD.Console.PrintMessage("\r\n" + msg + cqCodePane.file.path)
|
||||
|
||||
|
||||
class CadQueryOpenScript():
|
||||
class CadQueryNewScript:
|
||||
"""CadQuery's command to start a new script file."""
|
||||
def GetResources(self):
|
||||
return {"MenuText": "New Script",
|
||||
"Accel": "Alt+N",
|
||||
"ToolTip": "Starts a new CadQuery script",
|
||||
"Pixmap": ":/icons/document-new.svg"}
|
||||
|
||||
def IsActive(self):
|
||||
return True
|
||||
|
||||
def Activated(self):
|
||||
#We need to close any file that's already open in the editor window
|
||||
CadQueryCloseScript().Activated()
|
||||
|
||||
module_base_path = module_locator.module_path()
|
||||
templ_dir_path = os.path.join(module_base_path, 'Templates')
|
||||
|
||||
#Use the library that FreeCAD can use as well to open CQ files
|
||||
ImportCQ.open(os.path.join(templ_dir_path, 'script_template.py'))
|
||||
|
||||
|
||||
class CadQueryOpenScript:
|
||||
"""CadQuery's command to open a script file."""
|
||||
previousPath = None
|
||||
|
||||
|
@ -112,20 +155,14 @@ class CadQueryOpenScript():
|
|||
|
||||
def IsActive(self):
|
||||
return True
|
||||
# if FreeCAD.ActiveDocument is None:
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
|
||||
def Activated(self):
|
||||
import os, sys
|
||||
|
||||
#So we can open the "Open File" dialog
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
|
||||
#Try to keep track of the previous path used to open as a convenience to the user
|
||||
if self.previousPath is None:
|
||||
import os, module_locator
|
||||
|
||||
#Start off defaulting to the Examples directory
|
||||
module_base_path = module_locator.module_path()
|
||||
exs_dir_path = os.path.join(module_base_path, 'Examples')
|
||||
|
||||
|
@ -136,6 +173,9 @@ class CadQueryOpenScript():
|
|||
|
||||
#Make sure the user didn't click cancel
|
||||
if filename[0]:
|
||||
#We need to close any file that's already open in the editor window
|
||||
CadQueryCloseScript().Activated()
|
||||
|
||||
self.previousPath = filename[0]
|
||||
|
||||
#Append this script's directory to sys.path
|
||||
|
@ -144,6 +184,16 @@ class CadQueryOpenScript():
|
|||
#We've created a library that FreeCAD can use as well to open CQ files
|
||||
ImportCQ.open(filename[0])
|
||||
|
||||
docname = os.path.splitext(os.path.basename(filename[0]))[0]
|
||||
FreeCAD.newDocument(docname)
|
||||
|
||||
#Execute the script
|
||||
CadQueryExecuteScript().Activated()
|
||||
|
||||
#Get a nice view of our model
|
||||
FreeCADGui.activeDocument().activeView().viewAxometric()
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
|
||||
class CadQuerySaveScript:
|
||||
"""CadQuery's command to save a script file"""
|
||||
|
@ -156,20 +206,16 @@ class CadQuerySaveScript:
|
|||
|
||||
def IsActive(self):
|
||||
return True
|
||||
# if FreeCAD.ActiveDocument is None:
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
|
||||
def Activated(self):
|
||||
#Getting the main window will allow us to find the children we need to work with
|
||||
#Grab our code editor so we can interact with it
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
|
||||
#We need this so we can load the file into it
|
||||
cqCodePane = mw.findChild(QtGui.QPlainTextEdit, "cqCodePane")
|
||||
|
||||
#If the codepane doesn't have a filename, we need to present the save as dialog
|
||||
if len(cqCodePane.file.path) == 0:
|
||||
#If the code pane doesn't have a filename, we need to present the save as dialog
|
||||
if len(cqCodePane.file.path) == 0 or os.path.basename(cqCodePane.file.path) == 'script_template.py':
|
||||
FreeCAD.Console.PrintMessage("\r\nYou cannot save a blank file or save over a template file.")
|
||||
|
||||
CadQuerySaveAsScript().Activated()
|
||||
|
||||
return
|
||||
|
@ -189,13 +235,11 @@ class CadQuerySaveAsScript:
|
|||
|
||||
def IsActive(self):
|
||||
return True
|
||||
# if FreeCAD.ActiveDocument is None:
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
|
||||
def Activated(self):
|
||||
#So we can open the save-as dialog
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
cqCodePane = mw.findChild(QtGui.QPlainTextEdit, "cqCodePane")
|
||||
|
||||
#Try to keep track of the previous path used to open as a convenience to the user
|
||||
if self.previousPath is None:
|
||||
|
@ -208,6 +252,14 @@ class CadQuerySaveAsScript:
|
|||
|
||||
#Make sure the user didn't click cancel
|
||||
if filename[0]:
|
||||
#Save the file before the re-render
|
||||
#Close the 3D view for the original script if it's open
|
||||
try:
|
||||
docname = os.path.splitext(os.path.basename(cqCodePane.file.path))[0]
|
||||
FreeCAD.closeDocument(docname)
|
||||
except:
|
||||
#Assume that there was no 3D view to close
|
||||
pass
|
||||
|
||||
#Save the file before closing the original and the re-rendering the new one
|
||||
ExportCQ.save(filename[0])
|
||||
execfile(filename[0])
|
||||
CadQueryExecuteScript().Activated()
|
||||
|
|
|
@ -13,10 +13,8 @@ def save(filename=None):
|
|||
:param filename: The path and file name to save to. If not provided we try to pull it from the code pane itself
|
||||
"""
|
||||
|
||||
#Getting the main window will allow us to find the children we need to work with
|
||||
#Grab our code editor so we can interact with it
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
|
||||
#We need this so we can load the file into it
|
||||
cqCodePane = mw.findChild(QtGui.QPlainTextEdit, "cqCodePane")
|
||||
|
||||
#If we weren't provided a file name, we need to find it from the text field
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
"""Adds the ability to open files from disk to the CadQuery FreeCAD module"""
|
||||
# (c) 2014 Jeremy Wright LGPL v3
|
||||
|
||||
import os, FreeCAD, FreeCADGui
|
||||
import FreeCAD, FreeCADGui
|
||||
from PySide import QtGui
|
||||
|
||||
#Distinguish python built-in open function from the one declared here
|
||||
if open.__module__ == '__builtin__':
|
||||
pythonopen = open
|
||||
|
||||
def open(filename):
|
||||
docname = os.path.splitext(os.path.basename(filename))[0]
|
||||
doc = FreeCAD.newDocument(docname)
|
||||
|
||||
def open(filename):
|
||||
#All of the Gui.* calls in the Python console break after opening if we don't do this
|
||||
FreeCADGui.doCommand("import FreeCADGui as Gui")
|
||||
|
||||
|
@ -24,8 +22,6 @@ def open(filename):
|
|||
#Pull the text of the CQ script file into our code pane
|
||||
cqCodePane.file.open(filename)
|
||||
|
||||
execfile(filename)
|
||||
|
||||
msg = QtGui.QApplication.translate(
|
||||
"cqCodeWidget",
|
||||
"Opened ",
|
||||
|
@ -33,4 +29,4 @@ def open(filename):
|
|||
QtGui.QApplication.UnicodeUTF8)
|
||||
FreeCAD.Console.PrintMessage("\r\n" + msg + filename)
|
||||
|
||||
return doc
|
||||
return
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
This adds a workbench with a scripting editor to FreeCAD's GUI."""
|
||||
# (c) 2014 Jeremy Wright LGPL v3
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import FreeCAD, FreeCADGui
|
||||
from Gui.Command import *
|
||||
import CadQuery_rc
|
||||
|
||||
|
||||
class CadQueryWorkbench (Workbench):
|
||||
"""CadQuery workbench for FreeCAD"""
|
||||
|
@ -27,13 +28,14 @@ class CadQueryWorkbench (Workbench):
|
|||
#logging.basicConfig(filename='/home/jwright/Documents/log.txt', level=logging.DEBUG)
|
||||
|
||||
#We have our own CQ menu that's added when the user chooses our workbench
|
||||
commands = ['CadQueryOpenScript', 'CadQuerySaveScript', 'CadQuerySaveAsScript', 'CadQueryExecuteScript',
|
||||
'CadQueryCloseScript']
|
||||
commands = ['CadQueryNewScript', 'CadQueryOpenScript', 'CadQuerySaveScript', 'CadQuerySaveAsScript',
|
||||
'CadQueryCloseScript', 'Separator', 'CadQueryExecuteScript']
|
||||
self.appendMenu('CadQuery', commands)
|
||||
|
||||
def Activated(self):
|
||||
import os, sys
|
||||
import module_locator
|
||||
from Gui import Command, ImportCQ
|
||||
|
||||
#Set up so that we can import from our embedded packages
|
||||
module_base_path = module_locator.module_path()
|
||||
|
@ -53,7 +55,6 @@ class CadQueryWorkbench (Workbench):
|
|||
sys.path.insert(1, fc_bin_path)
|
||||
|
||||
import cadquery
|
||||
from Gui import ImportCQ
|
||||
from pyqode.python.widgets import PyCodeEdit
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
|
@ -87,7 +88,7 @@ class CadQueryWorkbench (Workbench):
|
|||
interpreter = 'python'
|
||||
|
||||
#Getting the main window will allow us to start setting things up the way we want
|
||||
mw = Gui.getMainWindow()
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
|
||||
#Find all of the docks that are open so we can close them (except the Python console)
|
||||
dockWidgets = mw.findChildren(QtGui.QDockWidget)
|
||||
|
@ -109,7 +110,7 @@ class CadQueryWorkbench (Workbench):
|
|||
#Set up the text area for our CQ code
|
||||
server_path = os.path.join(module_base_path, 'cq_server.py')
|
||||
|
||||
#Windows needs some exra help with paths
|
||||
#Windows needs some extra help with paths
|
||||
if sys.platform.startswith('win'):
|
||||
codePane = PyCodeEdit(server_script=server_path, interpreter=interpreter
|
||||
, args=['-s', fc_lib_path, libs_dir_path])
|
||||
|
@ -122,25 +123,25 @@ class CadQueryWorkbench (Workbench):
|
|||
#Add the text area to our dock widget
|
||||
cqCodeWidget.setWidget(codePane)
|
||||
|
||||
#Open our introduction example
|
||||
#Open and execute our introduction example
|
||||
example_path = os.path.join(module_base_path, 'Examples')
|
||||
example_path = os.path.join(example_path, 'Ex000_Introduction.py')
|
||||
ImportCQ.open(example_path)
|
||||
docname = os.path.splitext(os.path.basename(example_path))[0]
|
||||
FreeCAD.newDocument(docname)
|
||||
Command.CadQueryExecuteScript().Activated()
|
||||
|
||||
#Get a nice view of our example
|
||||
FreeCADGui.activeDocument().activeView().viewAxometric()
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
def Deactivated(self):
|
||||
from Gui import ExportCQ
|
||||
|
||||
#Put the UI back the way we found it
|
||||
FreeCAD.Console.PrintMessage("\r\nCadQuery Workbench Deactivated\r\n")
|
||||
|
||||
#Rely on our export library to help us save the file
|
||||
ExportCQ.save()
|
||||
|
||||
#TODO: This won't work for now because the views are destroyed when they are hidden
|
||||
# for widget in self.closedWidgets:
|
||||
# FreeCAD.Console.PrintMessage(widget.objectName())
|
||||
# widget.setVisible(True)
|
||||
Command.CadQueryCloseScript().Activated()
|
||||
|
||||
FreeCADGui.addCommand('CadQueryNewScript', CadQueryNewScript())
|
||||
FreeCADGui.addCommand('CadQueryOpenScript', CadQueryOpenScript())
|
||||
FreeCADGui.addCommand('CadQuerySaveScript', CadQuerySaveScript())
|
||||
FreeCADGui.addCommand('CadQuerySaveAsScript', CadQuerySaveAsScript())
|
||||
|
|
5
CadQuery/Templates/script_template.py
Normal file
5
CadQuery/Templates/script_template.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
# This is a CadQuery script template
|
||||
# Add your script code below
|
||||
|
||||
# Length units
|
||||
length_uom = 'mm'
|
Loading…
Reference in New Issue
Block a user