mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Bonsai Mac app to quickly open .ifc files
This commit is contained in:
@@ -110,6 +110,7 @@ classes = [
|
||||
operator.ClippingPlaneCutWithCappings,
|
||||
operator.CloseBlendWarning,
|
||||
operator.CloseError,
|
||||
operator.CreateMacBonsaiApp,
|
||||
operator.CopyTextToClipboard,
|
||||
operator.EditBlenderCollection,
|
||||
operator.FileAssociate,
|
||||
|
||||
@@ -272,6 +272,9 @@ class WinRegistryKeys(Enum):
|
||||
return cls.__bonsai_key
|
||||
|
||||
|
||||
LIBS_DESKTOP = Path(__file__).parent.parent / "libs" / "desktop"
|
||||
|
||||
|
||||
class FileAssociate(bpy.types.Operator):
|
||||
bl_idname = "bim.file_associate"
|
||||
bl_label = "Associate Bonsai with *.ifc files"
|
||||
@@ -288,11 +291,10 @@ class FileAssociate(bpy.types.Operator):
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
src_dir = os.path.join(os.path.dirname(__file__), "../libs/desktop")
|
||||
binary_path = bpy.app.binary_path
|
||||
if platform.system() == "Linux":
|
||||
destdir = os.path.join(os.environ["HOME"], ".local")
|
||||
self.install_desktop_linux(src_dir=src_dir, destdir=destdir, binary_path=binary_path)
|
||||
self.install_desktop_linux(src_dir=LIBS_DESKTOP, destdir=destdir, binary_path=binary_path)
|
||||
elif platform.system() == "Windows":
|
||||
self.install_desktop_windows(binary_path)
|
||||
self.report({"INFO"}, "Associations established.")
|
||||
@@ -404,6 +406,7 @@ class FileUnassociate(bpy.types.Operator):
|
||||
self.uninstall_desktop_linux(destdir=destdir)
|
||||
elif platform.system() == "Windows":
|
||||
self.uninstall_desktop_windows()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def uninstall_desktop_windows(self) -> None:
|
||||
@@ -474,6 +477,97 @@ class FileUnassociate(bpy.types.Operator):
|
||||
pass
|
||||
|
||||
|
||||
class CreateMacBonsaiApp(bpy.types.Operator):
|
||||
bl_idname = "bim.create_mac_bonsai_app"
|
||||
bl_label = "Create Bonsai App to Open .ifc Files."
|
||||
bl_options = set()
|
||||
bl_description = (
|
||||
"Create 'Bonsai' application on Mac.\n\n"
|
||||
"To make Mac use Bonsai to open IFC files automatically:\n"
|
||||
"- Open context menu on any .ifc file\n"
|
||||
"- Open With\n"
|
||||
"- Other\n"
|
||||
"- Select Bonsai in 'Applications'\n"
|
||||
"- Check 'Always Open With'\n"
|
||||
"- 'Open'\n"
|
||||
"\n"
|
||||
"ALT+click to uninstall Bonsai app if it was installed previously."
|
||||
)
|
||||
|
||||
uninstall: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
uninstall: bool
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if platform.system() == "Darwin":
|
||||
return True
|
||||
cls.poll_message_set("Mac Only.")
|
||||
return False
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.uninstall = event.alt
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
# I've tried to create AppleScript that would create this kind of app using Automator,
|
||||
# but using poorly document AppleScript was unbearable :(
|
||||
# So we just try to create automator app ourselves from a template.
|
||||
|
||||
# Couldn't find a way to automatically establish .ifc to app association,
|
||||
# without installing some other app to handle it (e.g. 'duti').
|
||||
# So currently we rely on the final user action to select Bonsai in 'Open With'.
|
||||
|
||||
# NOTE: shell script to execute is part of .wflow file.
|
||||
app_path = Path("/Applications") / "Bonsai.app"
|
||||
|
||||
if self.uninstall:
|
||||
if app_path.exists():
|
||||
shutil.rmtree(app_path)
|
||||
self.report({"INFO"}, "Bonsai app was successfully removed.")
|
||||
else:
|
||||
self.report({"WARNING"}, f"Couldn't remove Bonsai app as it's not found at '{app_path}'.")
|
||||
return {"FINISHED"}
|
||||
|
||||
if app_path.exists():
|
||||
self.report({"WARNING"}, f"Bonsai.app already exists at '{app_path}'.")
|
||||
return {"FINISHED"}
|
||||
|
||||
# 1. Create .app bundle structure
|
||||
contents = app_path / "Contents"
|
||||
macos = contents / "MacOS"
|
||||
resources = contents / "Resources"
|
||||
for p in (macos, resources):
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy Automator Application Stub.
|
||||
stub_src = Path(
|
||||
"/System/Library/CoreServices/Automator Application Stub.app/Contents/MacOS/Automator Application Stub"
|
||||
)
|
||||
stub_dest = macos / "Automator Application Stub"
|
||||
shutil.copy(stub_src, stub_dest)
|
||||
stub_dest.chmod(0o755)
|
||||
|
||||
# xml files.
|
||||
shutil.copy2(LIBS_DESKTOP / "Mac" / "Info.plist", contents / "Info.plist")
|
||||
wflow_template = (LIBS_DESKTOP / "Mac" / "document.wflow").read_text()
|
||||
wflow_template = wflow_template.replace("{{BLENDER_BINARY}}", bpy.app.binary_path)
|
||||
(contents / "document.wflow").write_text(wflow_template)
|
||||
|
||||
# Convert PNG icon to ICNS.
|
||||
png_icon = LIBS_DESKTOP / "bonsai.png"
|
||||
icns_icon = app_path / "Contents/Resources/AppIcon.icns"
|
||||
subprocess.run(["sips", "-s", "format", "icns", png_icon, "--out", icns_icon], check=True)
|
||||
|
||||
self.report(
|
||||
{"INFO"},
|
||||
"Bonsai app was successfully created. "
|
||||
f"Follow instructions in '{self.bl_label}' description to use Bonsai for .ifc files automatically.",
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OpenUpstream(bpy.types.Operator):
|
||||
bl_idname = "bim.open_upstream"
|
||||
bl_label = "Open Upstream Reference"
|
||||
|
||||
@@ -521,9 +521,13 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row = layout.row()
|
||||
row.operator("bim.open_upstream", text="Visit Wiki").page = "wiki"
|
||||
row.operator("bim.open_upstream", text="Visit Community").page = "community"
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.file_associate", icon="LOCKVIEW_ON")
|
||||
row.operator("bim.file_unassociate", icon="LOCKVIEW_OFF")
|
||||
if platform.system() == "Darwin":
|
||||
row.operator("bim.create_mac_bonsai_app", icon="LOCKVIEW_ON")
|
||||
else:
|
||||
row.operator("bim.file_associate", icon="LOCKVIEW_ON")
|
||||
row.operator("bim.file_unassociate", icon="LOCKVIEW_OFF")
|
||||
|
||||
bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Interface", self.draw_interface_settings)
|
||||
bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Command Paths", self.draw_commands)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AMIsApplet</key>
|
||||
<true/>
|
||||
<key>AMStayOpen</key>
|
||||
<false/>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Automator Application Stub</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.automator.Bonsai</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Bonsai</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>ifc</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>IFC File</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AMApplicationBuild</key>
|
||||
<string>526</string>
|
||||
<key>AMApplicationVersion</key>
|
||||
<string>2.10</string>
|
||||
<key>AMDocumentVersion</key>
|
||||
<string>2</string>
|
||||
<key>actions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>action</key>
|
||||
<dict>
|
||||
<key>ActionBundlePath</key>
|
||||
<string>/System/Library/Automator/Run Shell Script.action</string>
|
||||
<key>ActionParameters</key>
|
||||
<dict>
|
||||
<key>COMMAND_STRING</key>
|
||||
<string>"{{BLENDER_BINARY}}" --python-expr "import bpy; \
|
||||
import sys; \
|
||||
filepath = sys.argv[sys.argv.index('--') + 1]; \
|
||||
bpy.ops.bim.load_project(filepath=filepath)" -- "$1"</string>
|
||||
<key>CheckedForUserDefaultShell</key>
|
||||
<true/>
|
||||
<key>inputMethod</key>
|
||||
<integer>1</integer>
|
||||
<key>shell</key>
|
||||
<string>/bin/zsh</string>
|
||||
<key>source</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<key>BundleIdentifier</key>
|
||||
<string>com.apple.RunShellScript</string>
|
||||
<key>Class Name</key>
|
||||
<string>RunShellScriptAction</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>workflowMetaData</key>
|
||||
<dict>
|
||||
<key>workflowTypeIdentifier</key>
|
||||
<string>com.apple.Automator.application</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user