From 116ad507cf16867f5ee0bc56a4a5ba81d7e5afcd Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 31 Aug 2023 22:28:18 +1000 Subject: [PATCH] Fix #3647. Fix bug where Windows .exe suffix needed to be explicit in commands. No more eval, and commands must now be provided in json form, and variables should be given as simple strings. --- .../blenderbim/bim/module/drawing/operator.py | 58 +++++++++---------- src/blenderbim/blenderbim/bim/ui.py | 10 ++-- src/blenderbim/blenderbim/tool/drawing.py | 9 ++- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index a158fb0235..1a05c5cefc 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -70,15 +70,6 @@ class profile: print(self.task, timer() - self.start) -def open_with_user_command(user_command, path): - if user_command: - commands = eval(user_command) - for command in commands: - subprocess.Popen(command) - else: - webbrowser.open("file://" + path) - - class Operator: def execute(self, context): IfcStore.execute_ifc_operator(self, context) @@ -269,7 +260,7 @@ class CreateDrawing(bpy.types.Operator): with profile("Combine SVG layers"): svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg) - open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path) + tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path) if self.print_all: bpy.ops.bim.activate_drawing(drawing=original_drawing_id, camera_view_point=False) @@ -572,11 +563,13 @@ class CreateDrawing(bpy.types.Operator): if obj and obj.type == "MESH" and len(obj.data.polygons): elements_with_faces.add(element.GlobalId) - projections = root.xpath(".//svg:g[contains(@class, 'projection')]", namespaces={'svg': 'http://www.w3.org/2000/svg'}) + projections = root.xpath( + ".//svg:g[contains(@class, 'projection')]", namespaces={"svg": "http://www.w3.org/2000/svg"} + ) boundary_lines = [] for projection in projections: - global_id = projection.attrib['{http://www.ifcopenshell.org/ns}guid'] + global_id = projection.attrib["{http://www.ifcopenshell.org/ns}guid"] if global_id not in elements_with_faces: continue for path in projection.findall("./{http://www.w3.org/2000/svg}path"): @@ -616,9 +609,7 @@ class CreateDrawing(bpy.types.Operator): path = etree.Element("path") d = ( "M" - + " L".join( - [",".join([str(o) for o in co]) for co in polygon.exterior.coords[0:-1]] - ) + + " L".join([",".join([str(o) for o in co]) for co in polygon.exterior.coords[0:-1]]) + " Z" ) for interior in polygon.interiors: @@ -1068,7 +1059,9 @@ class CreateDrawing(bpy.types.Operator): # IfcConvert puts the projection afterwards which is not correct since # projection should be drawn underneath the cut. group = root.find("{http://www.w3.org/2000/svg}g") - projections = root.xpath(".//svg:g[contains(@class, 'projection')]", namespaces={'svg': 'http://www.w3.org/2000/svg'}) + projections = root.xpath( + ".//svg:g[contains(@class, 'projection')]", namespaces={"svg": "http://www.w3.org/2000/svg"} + ) for projection in projections: projection.getparent().remove(projection) group.insert(0, projection) @@ -1285,11 +1278,15 @@ class CreateSheets(bpy.types.Operator, Operator): # These variables will be made available to the evaluated commands svg = references["SHEET"] - basename = os.path.basename(svg) - path = os.path.dirname(svg) pdf = os.path.splitext(svg)[0] + ".pdf" - eps = os.path.splitext(svg)[0] + ".eps" - dxf = os.path.splitext(svg)[0] + ".dxf" + replacements = { + "svg": svg, + "basename": os.path.basename(svg), + "path": os.path.dirname(svg), + "pdf": pdf, + "eps": os.path.splitext(svg)[0] + ".eps", + "dxf": os.path.splitext(svg)[0] + ".dxf", + } has_sheet_reference = False for reference in tool.Drawing.get_document_references(sheet): @@ -1322,22 +1319,23 @@ class CreateSheets(bpy.types.Operator, Operator): if svg2pdf_command: # With great power comes great responsibility. Example: - # [['inkscape', svg, '-o', pdf]] - commands = eval(svg2pdf_command) + # [["inkscape", "svg", "-o", "pdf"]] + commands = json.loads(svg2pdf_command) for command in commands: - subprocess.run(command) + subprocess.run([replacements.get(c, c) for c in command]) if svg2dxf_command: # With great power comes great responsibility. Example: - # [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']] - commands = eval(svg2dxf_command) + # [["inkscape", "svg", "-o", "eps"], ["pstoedit", "-dt", "-f", "dxf:-polyaslines -mm", "eps", "dxf", "-psarg", "-dNOSAFER"]] + commands = json.loads(svg2dxf_command) for command in commands: - subprocess.run(command) + command[0] = shutil.which(command[0]) or command[0] + subprocess.run([replacements.get(c, c) for c in command]) if svg2pdf_command: - open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf) + tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf) else: - open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg) + tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg) class SelectAllDrawings(bpy.types.Operator): @@ -1404,7 +1402,9 @@ class OpenDrawing(bpy.types.Operator): return {"CANCELLED"} for drawing_uri in drawing_uris: - open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri) + tool.Drawing.open_with_user_command( + context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri + ) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py index 706a3b8163..33112cbb8f 100644 --- a/src/blenderbim/blenderbim/bim/ui.py +++ b/src/blenderbim/blenderbim/bim/ui.py @@ -126,14 +126,14 @@ class BIM_UL_topics(bpy.types.UIList): class BIM_ADDON_preferences(bpy.types.AddonPreferences): bl_idname = "blenderbim" - svg2pdf_command: StringProperty(name="SVG to PDF Command", description="E.g. [['inkscape', svg, '-o', pdf]]") + svg2pdf_command: StringProperty(name="SVG to PDF Command", description='E.g. [["inkscape", "svg", "-o", pdf]]') svg2dxf_command: StringProperty( name="SVG to DXF Command", - description="E.g. [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]", + description='E.g. [["inkscape", "svg", "-o", "eps"], ["pstoedit", "-dt", "-f", "dxf:-polyaslines -mm", "eps", "dxf", "-psarg", "-dNOSAFER"]]', ) - svg_command: StringProperty(name="SVG Command", description="E.g. [['firefox', path]]") - pdf_command: StringProperty(name="PDF Command", description="E.g. [['firefox', path]]") - spreadsheet_command: StringProperty(name="Spreadsheet Command", description="E.g. [['libreoffice', path]]") + svg_command: StringProperty(name="SVG Command", description='E.g. [["firefox", "path"]]') + pdf_command: StringProperty(name="PDF Command", description='E.g. [["firefox", "path"]]') + spreadsheet_command: StringProperty(name="Spreadsheet Command", description='E.g. [["libreoffice", "path"]]') openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080) should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True) should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True) diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index 32f1836ae6..36b99fb044 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -20,6 +20,7 @@ import os import re import bpy import math +import json import lark import bmesh import shutil @@ -835,9 +836,11 @@ class Drawing(blenderbim.core.tool.Drawing): @classmethod def open_with_user_command(cls, user_command, path): if user_command: - commands = eval(user_command) + commands = json.loads(user_command) + replacements = {"path": path} for command in commands: - subprocess.Popen(command) + command[0] = shutil.which(command[0]) or command[0] + subprocess.Popen([replacements.get(c, c) for c in command]) else: webbrowser.open("file://" + path) @@ -1849,7 +1852,7 @@ class Drawing(blenderbim.core.tool.Drawing): sheet_references.append(reference) break return sheet_references - + @classmethod def get_camera_matrix(cls, camera): matrix_world = camera.matrix_world.copy().normalized()