"""CadQuery GUI init module for FreeCAD This adds a workbench with a scripting editor to FreeCAD's GUI.""" # (c) 2014-2015 Jeremy Wright LGPL v3 import FreeCAD, FreeCADGui from Gui.Command import * import CadQuery_rc class CadQueryWorkbench (Workbench): """CadQuery workbench for FreeCAD""" MenuText = "CadQuery" ToolTip = "CadQuery workbench" Icon = ":/icons/CQ_Logo.svg" #Keeps track of which workbenches we have hidden so we can reshow them closedWidgets = [] def Initialize(self): import os #Need to set this for PyQode os.environ['QT_API'] = 'pyside' #Turn off logging for now #import logging #logging.basicConfig(filename='C:\\Users\\Jeremy\\Documents\\', level=logging.DEBUG) #logging.basicConfig(filename='/home/jwright/Documents/log.txt', level=logging.DEBUG) submenu = [] dirs = self.ListExamples() # Step through and add an Examples submenu item for each example for curFile in dirs: submenu.append(str(curFile)) #We have our own CQ menu that's added when the user chooses our workbench self.appendMenu('CadQuery', ['CadQueryNewScript', 'CadQueryOpenScript', 'CadQuerySaveScript', 'CadQuerySaveAsScript', 'CadQueryCloseScript']) self.appendMenu(['CadQuery', 'Examples'], submenu) self.appendMenu('CadQuery', ['Separator', 'CadQueryExecuteScript', 'CadQueryClearOutput']) def Activated(self): import os, sys import module_locator from Gui import Command, ImportCQ import Settings #Set up so that we can import from our embedded packages module_base_path = module_locator.module_path() libs_dir_path = os.path.join(module_base_path, 'Libs') sys.path.insert(0, libs_dir_path) # Tack on our CadQuery library git subtree cq_lib_path = os.path.join(libs_dir_path, 'cadquery-lib') sys.path.insert(1, cq_lib_path) #Make sure we get the right libs under the FreeCAD installation fc_base_path = os.path.dirname(os.path.dirname(module_base_path)) fc_lib_path = os.path.join(fc_base_path, 'lib') fc_bin_path = os.path.join(fc_base_path, 'bin') #Make sure that the directories exist before we add them to sys.path #This could cause problems or solve them by overriding what CQ is setting for the paths if os.path.exists(fc_lib_path): sys.path.insert(1, fc_lib_path) if os.path.exists(fc_bin_path): sys.path.insert(1, fc_bin_path) import cadquery from pyqode.core.modes import FileWatcherMode from pyqode.python.widgets import PyCodeEdit from PySide import QtGui, QtCore msg = QtGui.QApplication.translate( "cqCodeWidget", "CadQuery " + cadquery.__version__ + "\r\n" "CadQuery is a parametric scripting language " "for creating and traversing CAD models\r\n" "Author: David Cowden\r\n" "License: Apache-2.0\r\n" "Website: https://github.com/dcowden/cadquery\r\n", None, QtGui.QApplication.UnicodeUTF8) FreeCAD.Console.PrintMessage(msg) #Make sure that we enforce a specific version (2.7) of the Python interpreter ver = hex(sys.hexversion) interpreter = "python%s.%s" % (ver[2], ver[4]) # => 'python2.7' #If the user doesn't have Python 2.7, warn them if interpreter != 'python2.7': msg = QtGui.QApplication.translate( "cqCodeWidget", "Please install Python 2.7", None, QtGui.QApplication.UnicodeUTF8) FreeCAD.Console.PrintError(msg + "\r\n") #The extra version numbers won't work on Windows if sys.platform.startswith('win'): interpreter = 'python' #Getting the main window will allow us to start setting things up the way we want mw = FreeCADGui.getMainWindow() dockWidgets = mw.findChildren(QtGui.QDockWidget) for widget in dockWidgets: if widget.objectName() == "Report view": widget.setVisible(True) #Add a new widget here that's a simple text area to begin with. It will become the CQ coding area cqCodeWidget = QtGui.QDockWidget("CadQuery Code View") cqCodeWidget.setObjectName("cqCodeView") mw.addDockWidget(QtCore.Qt.LeftDockWidgetArea, cqCodeWidget) #Set up the text area for our CQ code server_path = os.path.join(module_base_path, 'cq_server.py') #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]) else: codePane = PyCodeEdit(server_script=server_path, interpreter=interpreter , args=['-s', libs_dir_path]) # Allow easy use of an external editor if Settings.use_external_editor: codePane.modes.append(FileWatcherMode()) codePane.modes.get(FileWatcherMode).file_reloaded.connect(self.AutoExecute) codePane.modes.get(FileWatcherMode).auto_reload = True codePane.setObjectName("cqCodePane") #Add the text area to our dock widget cqCodeWidget.setWidget(codePane) #Set up the paths to allow us to 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') # TODO: Enable this for FreeCAD 0.16 or greater # Make sure we get the correct MdiArea object # for child in mw.children(): # if child.__class__ == QtGui.QMdiArea: # mdiArea = child # # # Set up the editor in a new subwindow # #sub_window = QtGui.QMdiSubWindow(mw.centralWidget()) # sub_window = QtGui.QMdiSubWindow(mdiArea) # #sub_window.setWidget(codePane) # sub_window.setWidget(QtGui.QPlainTextEdit()) # sub_window.setWindowTitle('Ex000_Introduction.py') # sub_window.setWindowIcon(QtGui.QIcon(':/icons/applications-python.svg')) # # #mw.centralWidget().addSubWindow(sub_window) # mdiArea.addSubWindow(sub_window) # Set up the paths to allow us to open the template template_path = os.path.join(module_base_path, 'Templates') template_path = os.path.join(template_path, 'script_template.py') ImportCQ.open(template_path) docname = os.path.splitext(os.path.basename(template_path))[0] #FreeCAD.newDocument(docname) #Command.CadQueryExecuteScript().Activated() #Get a nice view of our example #FreeCADGui.activeDocument().activeView().viewAxometric() #FreeCADGui.SendMsgToActiveView("ViewFit") def AutoExecute(self): """We should be able to pass the Gui.Commands.CadQueryExecuteScript function directly to the file_reloaded connect function, but that causes a segfault in FreeCAD. This function is a work-around for that. This function is passed to file_reloaded signal and in turn calls the CadQueryExecuteScript.Activated function.""" import Gui.Command Gui.Command.CadQueryExecuteScript().Activated() def Deactivated(self): import Gui.Command from PySide import QtGui # msg = QtGui.QApplication.translate( # "cqCodeWidget", # "\r\nCadQuery Workbench Deactivated\r\n", # None, # QtGui.QApplication.UnicodeUTF8) # # #Put the UI back the way we found it # FreeCAD.Console.PrintMessage(msg) #Getting the main window will allow us to start setting things up the way we want mw = FreeCADGui.getMainWindow() cqCodePane = mw.findChild(QtGui.QPlainTextEdit, "cqCodePane") cqCodePane.close() cqCodePane.setParent(None) dockWidgets = mw.findChildren(QtGui.QDockWidget) for widget in dockWidgets: if widget.objectName() == "cqCodeView": mw.removeDockWidget(widget) @staticmethod def ListExamples(): import os import module_locator dirs = [] # List all of the example files in an order that makes sense module_base_path = module_locator.module_path() exs_dir_path = os.path.join(module_base_path, 'Examples') dirs = os.listdir(exs_dir_path) dirs.sort() return dirs FreeCADGui.addCommand('CadQueryNewScript', CadQueryNewScript()) FreeCADGui.addCommand('CadQueryOpenScript', CadQueryOpenScript()) FreeCADGui.addCommand('CadQuerySaveScript', CadQuerySaveScript()) FreeCADGui.addCommand('CadQuerySaveAsScript', CadQuerySaveAsScript()) FreeCADGui.addCommand('CadQueryExecuteScript', CadQueryExecuteScript()) FreeCADGui.addCommand('CadQueryCloseScript', CadQueryCloseScript()) FreeCADGui.addCommand('CadQueryClearOutput', CadQueryClearOutput()) # Step through and add an Examples submenu item for each example dirs = CadQueryWorkbench.ListExamples() for curFile in dirs: FreeCADGui.addCommand(curFile, CadQueryExecuteExample(curFile)) FreeCADGui.addWorkbench(CadQueryWorkbench())