[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:
Chris Hennes
2021-09-29 09:36:58 -05:00
parent b8c1877133
commit 2e504ab3b6
2 changed files with 144 additions and 7 deletions

View File

@@ -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),

View File

@@ -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 &gt;= 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/>