mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-08 17:01:40 +00:00
Open recent IFC project #3657
Example - https://imgur.com/a/EDfBTJS Paths to the opened IFC projects are now stored and then you can open them from File -> Open Recent IFC Project menu. It's done in very similar way to how Blender does it, list of files is stored in `Blender\4.1\config\recent-ifc-projects.txt`.
This commit is contained in:
@@ -30,6 +30,7 @@ classes = (
|
||||
operator.ChangeLibraryElement,
|
||||
operator.CreateClippingPlane,
|
||||
operator.CreateProject,
|
||||
operator.ClearRecentIFCProjects,
|
||||
operator.DisableCulling,
|
||||
operator.DisableEditingHeader,
|
||||
operator.EditHeader,
|
||||
@@ -66,6 +67,7 @@ classes = (
|
||||
ui.BIM_MT_new_project,
|
||||
ui.BIM_MT_project,
|
||||
ui.BIM_PT_new_project_wizard,
|
||||
ui.BIM_MT_recent_projects,
|
||||
ui.BIM_PT_project,
|
||||
ui.BIM_PT_project_library,
|
||||
ui.BIM_PT_links,
|
||||
|
||||
@@ -24,6 +24,7 @@ import logging
|
||||
import tempfile
|
||||
import traceback
|
||||
import subprocess
|
||||
import datetime
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
@@ -655,6 +656,46 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
||||
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
should_start_fresh_session: bpy.props.BoolProperty(name="Should Start Fresh Session", default=True)
|
||||
use_detailed_tooltip: bpy.props.BoolProperty(default=False, options={"HIDDEN"})
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
tooltip = cls.bl_description
|
||||
if not properties.use_detailed_tooltip:
|
||||
return tooltip
|
||||
|
||||
filepath = properties.filepath
|
||||
if not filepath:
|
||||
return tooltip
|
||||
filepath = Path(filepath)
|
||||
tooltip += f".\n\n"
|
||||
if not filepath.exists():
|
||||
tooltip += "File does not exist"
|
||||
return tooltip
|
||||
|
||||
def get_modified_date(st_mtime: float) -> str:
|
||||
mod_time = datetime.datetime.fromtimestamp(st_mtime)
|
||||
|
||||
today = datetime.date.today()
|
||||
if mod_time.date() == today:
|
||||
return f"Today {mod_time.strftime('%I:%M %p')}"
|
||||
elif mod_time.date() == today - datetime.timedelta(days=1):
|
||||
return f"Yesterday {mod_time.strftime('%I:%M %p')}"
|
||||
return mod_time.strftime("%d %b %Y")
|
||||
|
||||
def get_file_size(size_bytes: float) -> str:
|
||||
if size_bytes < 1024 * 1024: # Less than 1 MiB
|
||||
size = size_bytes / 1024
|
||||
return f"{size:.0f} KiB"
|
||||
else:
|
||||
size = size_bytes / (1024 * 1024)
|
||||
return f"{size:.1f} MiB"
|
||||
|
||||
file_stat = filepath.stat()
|
||||
tooltip += f"Modified: {get_modified_date(file_stat.st_mtime)}"
|
||||
tooltip += f"\nSize: {get_file_size(file_stat.st_size)}"
|
||||
|
||||
return tooltip
|
||||
|
||||
def execute(self, context):
|
||||
@persistent
|
||||
@@ -683,10 +724,13 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
||||
for obj in bpy.data.objects:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
context.scene.BIMProperties.ifc_file = self.get_filepath()
|
||||
filepath = Path(self.get_filepath())
|
||||
context.scene.BIMProperties.ifc_file = str(filepath)
|
||||
context.scene.BIMProjectProperties.is_loading = True
|
||||
context.scene.BIMProjectProperties.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
|
||||
tool.Blender.register_toolbar()
|
||||
tool.Blender.add_recent_ifc_project(filepath.absolute())
|
||||
|
||||
if not self.is_advanced:
|
||||
bpy.ops.bim.load_project_elements()
|
||||
except:
|
||||
@@ -703,6 +747,16 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
||||
IFCFileSelector.draw(self, context)
|
||||
|
||||
|
||||
class ClearRecentIFCProjects(bpy.types.Operator):
|
||||
bl_idname = "bim.clear_recent_ifc_projects"
|
||||
bl_label = "Clear Recent IFC Projects List"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
def execute(self, context):
|
||||
tool.Blender.clear_recent_ifc_projects()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RevertProject(bpy.types.Operator, IFCFileSelector):
|
||||
bl_idname = "bim.revert_project"
|
||||
bl_label = "Revert IFC Project"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import os
|
||||
import blenderbim.bim
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from bpy.types import Panel, Menu, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -36,6 +37,28 @@ class BIM_MT_project(Menu):
|
||||
self.layout.operator("bim.new_project", text="New Project Wizard").preset = "wizard"
|
||||
|
||||
|
||||
class BIM_MT_recent_projects(Menu):
|
||||
bl_idname = "BIM_MT_recent_projects"
|
||||
bl_label = "Open Recent IFC Project"
|
||||
|
||||
def draw(self, context):
|
||||
paths = tool.Blender.get_recent_ifc_projects()
|
||||
if not paths:
|
||||
self.layout.label(text="No Recent IFC Projects")
|
||||
return
|
||||
|
||||
for path in paths:
|
||||
op = self.layout.operator(
|
||||
"bim.load_project", text=path.name, icon_value=blenderbim.bim.icons["IFC"].icon_id
|
||||
)
|
||||
op.filepath = str(path)
|
||||
op.should_start_fresh_session = True
|
||||
op.use_detailed_tooltip = True
|
||||
|
||||
self.layout.separator()
|
||||
self.layout.operator("bim.clear_recent_ifc_projects", icon="TRASH")
|
||||
|
||||
|
||||
class BIM_MT_new_project(Menu):
|
||||
bl_idname = "BIM_MT_new_project"
|
||||
bl_label = "New Project"
|
||||
@@ -44,6 +67,7 @@ class BIM_MT_new_project(Menu):
|
||||
self.layout.operator_context = "INVOKE_DEFAULT"
|
||||
op = self.layout.operator("bim.load_project", text="Open IFC Project", icon="FILEBROWSER")
|
||||
op.should_start_fresh_session = True
|
||||
self.layout.menu("BIM_MT_recent_projects", icon="NONE")
|
||||
# Do we need to set it back to exec default?
|
||||
# self.layout.operator_context = "EXEC_DEFAULT"
|
||||
self.layout.separator()
|
||||
@@ -67,6 +91,7 @@ def file_menu(self, context):
|
||||
self.layout.menu("BIM_MT_project", icon="COLLECTION_NEW")
|
||||
op = self.layout.operator("bim.load_project", text="Open IFC Project", icon="FILEBROWSER")
|
||||
op.should_start_fresh_session = True
|
||||
self.layout.menu("BIM_MT_recent_projects", icon="NONE")
|
||||
self.layout.separator()
|
||||
op = self.layout.operator("bim.save_project", icon="FILE_TICK", text="Save IFC Project")
|
||||
op.should_save_as = False
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
import json
|
||||
import os
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.core.tool
|
||||
@@ -1069,3 +1070,65 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
bpy.utils.unregister_class(panel)
|
||||
bpy.utils.register_class(panel)
|
||||
del polls[panel]
|
||||
|
||||
@classmethod
|
||||
def get_recent_ifc_projects_path(cls) -> Path:
|
||||
return Path(bpy.utils.user_resource("CONFIG")) / "recent-ifc-projects.txt"
|
||||
|
||||
_recent_ifc_projects_loaded: bool = False
|
||||
_recent_ifc_projects: list[Path] = []
|
||||
|
||||
@classmethod
|
||||
def get_recent_ifc_projects(cls) -> list[Path]:
|
||||
if cls._recent_ifc_projects_loaded:
|
||||
return cls._recent_ifc_projects
|
||||
|
||||
filepath = cls.get_recent_ifc_projects_path()
|
||||
if not filepath.exists():
|
||||
cls._recent_ifc_projects = []
|
||||
return []
|
||||
|
||||
paths = []
|
||||
with open(filepath, "r") as fi:
|
||||
for line in fi:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
paths.append(Path(line))
|
||||
|
||||
cls._recent_ifc_projects = paths
|
||||
return paths
|
||||
|
||||
@classmethod
|
||||
def write_recent_ifc_projects(cls, filepaths: list[Path]) -> None:
|
||||
recent_projects_path = cls.get_recent_ifc_projects_path()
|
||||
try:
|
||||
recent_projects_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(recent_projects_path, "w") as fo:
|
||||
fo.write("\n".join(str(p) for p in filepaths))
|
||||
cls._recent_ifc_projects_loaded = False
|
||||
except PermissionError:
|
||||
msg = (
|
||||
f"WARNING. PermissionError trying to access '{str(recent_projects_path)}'. "
|
||||
"List of recently opened IFC projects won't be stored between Blender sessions."
|
||||
)
|
||||
print(msg)
|
||||
cls._recent_ifc_projects = filepaths
|
||||
|
||||
@classmethod
|
||||
def add_recent_ifc_project(cls, filepath: Path) -> None:
|
||||
"""Add `filepath` to the list of the recently opened IFC projects.
|
||||
|
||||
If `filepath` was opened before, bump it in the list.
|
||||
"""
|
||||
current_filepaths = cls.get_recent_ifc_projects()
|
||||
if filepath in current_filepaths:
|
||||
current_filepaths.remove(filepath)
|
||||
current_filepaths = [filepath] + current_filepaths
|
||||
# Limit it to 20 recent files.
|
||||
current_filepaths = current_filepaths[:20]
|
||||
cls.write_recent_ifc_projects(current_filepaths)
|
||||
|
||||
@classmethod
|
||||
def clear_recent_ifc_projects(cls) -> None:
|
||||
cls.write_recent_ifc_projects([])
|
||||
|
||||
Reference in New Issue
Block a user