[OpenSCAD] Add ability to communicate via pipes
In 2021.01 OpenSCAD added the ability to read from stdin and write output to stdout: this allows us to communicate with an OpenSCAD process that does not have read/write access to the same directories that FreeCAD does (for example, if one or the other is installed via a Snap package). This commit adds an additional preference to the OpenSCAD workbench allowing the user to choose between communication methods, as well as to optionally specify their own temporary directory for the data transfer, for use in cases where their version of OpenSCAD is installed via Snap, etc., but does not yet support the piped input and output.
This commit is contained in:
@@ -168,12 +168,22 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals
|
||||
FreeCAD.Console.PrintMessage(stdoutd+u'\n')
|
||||
return stdoutd
|
||||
|
||||
osfilename = FreeCAD.ParamGet(\
|
||||
"User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
|
||||
GetString('openscadexecutable')
|
||||
preferences = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
|
||||
osfilename = preferences.GetString('openscadexecutable')
|
||||
transferMechanism = preferences.GetInt('transfermechanism',0)
|
||||
if transferMechanism == 0: # Use the Python temp-directory creation function
|
||||
transferDirectory = tempfile.gettempdir()
|
||||
elif transferMechanism == 1: # Use a user-specified directory for the transfer
|
||||
transferDirectory = preferences.GetString('transferdirectory')
|
||||
elif transferMechanism == 2: # Use pipes instead of tempfiles
|
||||
return call_openscad_with_pipes(inputfilename, outputfilename, outputext, keepname)
|
||||
else:
|
||||
raise OpenSCADError("Invalid transfer mechanism specified");
|
||||
|
||||
if osfilename and os.path.isfile(osfilename):
|
||||
if not outputfilename:
|
||||
dir1=tempfile.gettempdir()
|
||||
|
||||
dir1 = transferDirectory
|
||||
if keepname:
|
||||
outputfilename=os.path.join(dir1,'%s.%s' % (os.path.split(\
|
||||
inputfilename)[1].rsplit('.',1)[0],outputext))
|
||||
@@ -185,6 +195,47 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals
|
||||
else:
|
||||
raise OpenSCADError('OpenSCAD executable unavailable')
|
||||
|
||||
def call_openscad_with_pipes(input_filename, output_filename, output_extension, keep_name):
|
||||
''' Call OpenSCAD by sending input data to stdin, and read the output from stdout.
|
||||
Returns the tempfile the output is stored in on success, or None on failure.
|
||||
NOTE: This feature was added to OpenSCAD in 2021.01'''
|
||||
|
||||
# For testing purposes continue using temp files, but now OpenSCAD does not need
|
||||
# read or write access to the files, only the FreeCAD process does. In the future
|
||||
# this could be changed to keep everything in memory, if desired.
|
||||
import subprocess,tempfile,os
|
||||
transfer_directory = tempfile.gettempdir()
|
||||
|
||||
# Load the data back in from our tempfile:
|
||||
with open(input_filename) as datafile:
|
||||
openscad_data = datafile.read()
|
||||
# On the command line this looks like:
|
||||
# $ cat myfile.scad | openscad --export-format csg -o - -
|
||||
preferences = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
|
||||
openscad_executable = preferences.GetString('openscadexecutable')
|
||||
p = subprocess.Popen([openscad_executable,"--export-format","csg", "-o", "-", "-"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdoutd,stderrd = p.communicate (input = openscad_data.encode('utf8'), timeout=15)
|
||||
stdoutd = stdoutd.decode("utf8")
|
||||
stderrd = stderrd.decode("utf8")
|
||||
if p.returncode != 0:
|
||||
raise OpenSCADError('%s %s\n' % (stdoutd.strip(),stderrd.strip()))
|
||||
|
||||
if not output_filename:
|
||||
dir1 = transfer_directory
|
||||
if keep_name:
|
||||
output_filename=os.path.join(dir1,'%s.%s' % (os.path.split(\
|
||||
input_filename)[1].rsplit('.',1)[0],output_extension))
|
||||
else:
|
||||
output_filename=os.path.join(dir1,'%s.%s' % \
|
||||
(next(tempfilenamegen),output_extension))
|
||||
with open(output_filename,"w") as outfile:
|
||||
outfile.write(stdoutd);
|
||||
return output_filename
|
||||
return None
|
||||
|
||||
def callopenscadstring(scadstr,outputext='csg'):
|
||||
'''create a tempfile and call the open scad binary
|
||||
returns the filename of the result (or None),
|
||||
|
||||
@@ -17,7 +17,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -36,7 +45,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefFileChooser" name="gui::preffilechooser">
|
||||
<widget class="Gui::PrefFileChooser" name="gui::preffilechooser" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
@@ -166,7 +175,79 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3"/>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Send to OpenSCAD via:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefComboBox" name="guiprefcomboSendVia">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The transfer mechanism for getting data to and from OpenSCAD</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>transfermechanism</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/OpenSCAD</cstring>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Standard temp directory</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>User-specified directory</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>stdout pipe (requires OpenSCAD >= 2021.1)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="transferDirectoryLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Transfer directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefFileChooser" name="gui::preffilechooser" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The path to the directory for transfering files to and from OpenSCAD</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>transferdirectory</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/OpenSCAD</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -442,6 +523,11 @@
|
||||
<extends>QDoubleSpinBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
Reference in New Issue
Block a user