mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 01:11:40 +00:00
@@ -98,9 +98,10 @@ def draw_attribute(
|
||||
if attribute.is_uri:
|
||||
op = layout.operator("bim.select_uri_attribute", text="", icon="FILE_FOLDER")
|
||||
op.data_path = attribute.path_from_id("string_value")
|
||||
elif attribute.special_type == "DATE":
|
||||
elif attribute.special_type in ("DATE", "DATETIME"):
|
||||
op = layout.operator("bim.datepicker", text="", icon="TIME")
|
||||
op.target_prop = attribute.path_from_id("string_value")
|
||||
op.include_time = attribute.special_type == "DATETIME"
|
||||
|
||||
if attribute.is_optional:
|
||||
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
@@ -170,6 +171,8 @@ def import_attribute(
|
||||
new.is_uri = True
|
||||
elif attribute.type_of_attribute()._is("IfcDate"):
|
||||
new.special_type = "DATE"
|
||||
elif attribute.type_of_attribute()._is("IfcDateTime"):
|
||||
new.special_type = "DATETIME"
|
||||
elif data_type == "boolean":
|
||||
new.bool_value = False if new.is_null else bool(data[attribute.name()])
|
||||
elif data_type == "integer":
|
||||
|
||||
@@ -38,6 +38,7 @@ def parse_duration(value):
|
||||
|
||||
|
||||
def canonicalise_time(time: Union[datetime, None]) -> str:
|
||||
"""Actualy canonicalises datetime as just a date, time is not included."""
|
||||
if not time:
|
||||
return "-"
|
||||
return time.strftime("%d/%m/%y")
|
||||
|
||||
@@ -1178,27 +1178,45 @@ class Bonsai_DatePicker(bpy.types.Operator):
|
||||
bl_label = "Date Picker"
|
||||
bl_idname = "bim.datepicker"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
display_date: bpy.props.StringProperty(name="Display Date")
|
||||
target_prop: bpy.props.StringProperty(name="Target date prop to set")
|
||||
# TODO: base it on property type.
|
||||
include_time: bpy.props.BoolProperty(name="Include Time", default=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
target_prop: str
|
||||
include_time: bool
|
||||
|
||||
def execute(self, context):
|
||||
selected_date = context.scene.DatePickerProperties.selected_date
|
||||
try:
|
||||
value = parser.parse(context.scene.DatePickerProperties.selected_date, dayfirst=True, fuzzy=True)
|
||||
self.set_scene_prop(self.target_prop, helper.canonicalise_time(value))
|
||||
except:
|
||||
pass
|
||||
return {"FINISHED"}
|
||||
# Just to make sure the date is valid.
|
||||
tool.Sequence.parse_isodate_datetime(selected_date, self.include_time)
|
||||
self.set_scene_prop(self.target_prop, selected_date)
|
||||
return {"FINISHED"}
|
||||
except Exception as e:
|
||||
self.report({"ERROR"}, f"Provided date is invalid: '{selected_date}'. Exception: {str(e)}.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
def draw(self, context):
|
||||
current_date = parser.parse(context.scene.DatePickerProperties.display_date, dayfirst=True, fuzzy=True)
|
||||
current_month = (current_date.year, current_date.month)
|
||||
props = context.scene.DatePickerProperties
|
||||
display_date = tool.Sequence.parse_isodate_datetime(props.display_date, False)
|
||||
current_month = (display_date.year, display_date.month)
|
||||
lines = calendar.monthcalendar(*current_month)
|
||||
month_title, week_titles = calendar.month(*current_month).splitlines()[:2]
|
||||
|
||||
layout = self.layout
|
||||
row = layout.row()
|
||||
row.prop(context.scene.DatePickerProperties, "selected_date")
|
||||
row.prop(props, "selected_date", text="Date")
|
||||
|
||||
# Time.
|
||||
if self.include_time:
|
||||
row = layout.row()
|
||||
row.label(text="Time:")
|
||||
row.prop(props, "selected_hour", text="H")
|
||||
row.prop(props, "selected_min", text="M")
|
||||
row.prop(props, "selected_sec", text="S")
|
||||
|
||||
# Month.
|
||||
split = layout.split()
|
||||
col = split.row()
|
||||
op = col.operator("bim.redraw_datepicker", icon="TRIA_LEFT", text="")
|
||||
@@ -1210,12 +1228,14 @@ class Bonsai_DatePicker(bpy.types.Operator):
|
||||
op = col.operator("bim.redraw_datepicker", icon="TRIA_RIGHT", text="")
|
||||
op.action = "next"
|
||||
|
||||
# Day of week.
|
||||
row = layout.row(align=True)
|
||||
for title in week_titles.split():
|
||||
col = row.column(align=True)
|
||||
col.alignment = "CENTER"
|
||||
col.label(text=title.strip())
|
||||
|
||||
# Days calendar.
|
||||
for line in lines:
|
||||
row = layout.row(align=True)
|
||||
for i in line:
|
||||
@@ -1223,15 +1243,31 @@ class Bonsai_DatePicker(bpy.types.Operator):
|
||||
if i == 0:
|
||||
col.label(text=" ")
|
||||
else:
|
||||
selected_date = datetime(year=display_date.year, month=display_date.month, day=i)
|
||||
op = col.operator("bim.datepicker_setdate", text="{:2d}".format(i))
|
||||
selected_date = "{}/{}/{}".format(i, current_date.month, current_date.year)
|
||||
selected_date = parser.parse(selected_date, dayfirst=True, fuzzy=True)
|
||||
op.selected_date = helper.canonicalise_time(selected_date)
|
||||
if self.include_time:
|
||||
selected_date = selected_date.replace(
|
||||
hour=props.selected_hour, minute=props.selected_min, second=props.selected_sec
|
||||
)
|
||||
op.selected_date = tool.Sequence.isodate_datetime(selected_date, self.include_time)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.display_date = self.get_scene_prop(self.target_prop) or helper.canonicalise_time(datetime.now())
|
||||
context.scene.DatePickerProperties.display_date = self.display_date
|
||||
context.scene.DatePickerProperties.selected_date = self.display_date
|
||||
props = context.scene.DatePickerProperties
|
||||
current_date_str = self.get_scene_prop(self.target_prop)
|
||||
if current_date_str:
|
||||
current_date = tool.Sequence.parse_isodate_datetime(current_date_str, self.include_time)
|
||||
else:
|
||||
current_date = datetime.now()
|
||||
# Seconds of the moment when datepicker opened will probably only annoy users.
|
||||
current_date = current_date.replace(second=0)
|
||||
|
||||
if self.include_time:
|
||||
props["selected_hour"] = current_date.hour
|
||||
props["selected_min"] = current_date.minute
|
||||
props["selected_sec"] = current_date.second
|
||||
|
||||
props.display_date = tool.Sequence.isodate_datetime(current_date.replace(day=1), False)
|
||||
props.selected_date = tool.Sequence.isodate_datetime(current_date, self.include_time)
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def get_scene_prop(self, prop_path: str) -> str:
|
||||
@@ -1261,15 +1297,15 @@ class Bonsai_RedrawDatePicker(bpy.types.Operator):
|
||||
action: bpy.props.StringProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
current_date = parser.parse(context.scene.DatePickerProperties.display_date, dayfirst=True, fuzzy=True)
|
||||
props = context.scene.DatePickerProperties
|
||||
current_date = tool.Sequence.parse_isodate_datetime(props.display_date, False)
|
||||
|
||||
if self.action == "previous":
|
||||
date_to_set = current_date - relativedelta.relativedelta(months=1)
|
||||
elif self.action == "next":
|
||||
else: # "next".
|
||||
date_to_set = current_date + relativedelta.relativedelta(months=1)
|
||||
|
||||
context.scene.DatePickerProperties.display_date = helper.canonicalise_time(date_to_set)
|
||||
|
||||
props.display_date = tool.Sequence.isodate_datetime(date_to_set, False)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -520,9 +520,23 @@ class BIMWorkCalendarProperties(PropertyGroup):
|
||||
end_time: StringProperty(name="End Time")
|
||||
|
||||
|
||||
def update_selected_date(self: "DatePickerProperties", context: bpy.types.Context) -> None:
|
||||
# `include_time` is `True`, otherwise time props are not displayed in UI.
|
||||
include_time = True
|
||||
selected_date = tool.Sequence.parse_isodate_datetime(self.selected_date, include_time)
|
||||
selected_date = selected_date.replace(hour=self.selected_hour, minute=self.selected_min, second=self.selected_sec)
|
||||
self.selected_date = tool.Sequence.isodate_datetime(selected_date, include_time)
|
||||
|
||||
|
||||
class DatePickerProperties(PropertyGroup):
|
||||
display_date: StringProperty(name="Display Date")
|
||||
display_date: StringProperty(
|
||||
name="Display Date",
|
||||
description="Needed to keep track of what month is currently opened in date picker without affecting the currently selected date.",
|
||||
)
|
||||
selected_date: StringProperty(name="Selected Date")
|
||||
selected_hour: IntProperty(min=0, max=23, update=update_selected_date)
|
||||
selected_min: IntProperty(min=0, max=59, update=update_selected_date)
|
||||
selected_sec: IntProperty(min=0, max=59, update=update_selected_date)
|
||||
|
||||
|
||||
class BIMDateTextProperties(PropertyGroup):
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import re
|
||||
import bpy
|
||||
@@ -24,7 +25,9 @@ import base64
|
||||
import pystache
|
||||
import mathutils
|
||||
import webbrowser
|
||||
import isodate
|
||||
import ifcopenshell
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.sequence
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.element
|
||||
@@ -34,7 +37,11 @@ import bonsai.tool as tool
|
||||
import bonsai.bim.helper
|
||||
from dateutil import parser
|
||||
from datetime import datetime
|
||||
from typing import Optional, Any, Union, Literal
|
||||
from datetime import time as datetime_time
|
||||
from typing import Optional, Any, Union, Literal, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bonsai.bim.prop
|
||||
|
||||
|
||||
class Sequence(bonsai.core.tool.Sequence):
|
||||
@@ -344,19 +351,23 @@ class Sequence(bonsai.core.tool.Sequence):
|
||||
def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None:
|
||||
import bonsai.bim.module.sequence.helper as helper
|
||||
|
||||
def callback(name, prop, data):
|
||||
def callback(
|
||||
name: str, prop: Union[bonsai.bim.prop.Attribute, None], data: dict[str, Any]
|
||||
) -> Union[bool, None]:
|
||||
if prop and prop.data_type == "string":
|
||||
duration_props = bpy.context.scene.BIMWorkScheduleProperties.durations_attributes.add()
|
||||
duration_props.name = name
|
||||
if prop.is_null:
|
||||
for key in duration_props.keys():
|
||||
if key != "name":
|
||||
setattr(duration_props, key, 0)
|
||||
return True
|
||||
if name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"] and data[name]:
|
||||
for key, value in helper.parse_duration_as_blender_props(data[name]).items():
|
||||
duration_props[key] = value
|
||||
return True
|
||||
# TODO: Check actual attribute type instead of providing attribute names.
|
||||
if name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"):
|
||||
duration_props = bpy.context.scene.BIMWorkScheduleProperties.durations_attributes.add()
|
||||
duration_props.name = name
|
||||
if prop.is_null:
|
||||
for key in duration_props.keys():
|
||||
if key != "name":
|
||||
setattr(duration_props, key, 0)
|
||||
return True
|
||||
if data[name]:
|
||||
for key, value in helper.parse_duration_as_blender_props(data[name]).items():
|
||||
duration_props[key] = value
|
||||
return True
|
||||
if isinstance(data[name], datetime):
|
||||
prop.string_value = "" if prop.is_null else data[name].isoformat()
|
||||
return True
|
||||
@@ -1713,3 +1724,16 @@ class Sequence(bonsai.core.tool.Sequence):
|
||||
def get_animation_color_scheme(cls):
|
||||
if len(bpy.context.scene.BIMAnimationProperties.saved_color_schemes) > 0:
|
||||
return tool.Ifc.get().by_id(int(bpy.context.scene.BIMAnimationProperties.saved_color_schemes))
|
||||
|
||||
@classmethod
|
||||
def parse_isodate_datetime(cls, datetime_str: str, include_time: bool) -> datetime:
|
||||
if include_time:
|
||||
return isodate.parse_datetime(datetime_str)
|
||||
date = isodate.parse_date(datetime_str)
|
||||
return datetime.combine(date, datetime_time())
|
||||
|
||||
@classmethod
|
||||
def isodate_datetime(cls, datetime_: datetime, include_time: bool) -> str:
|
||||
if include_time:
|
||||
return isodate.datetime_isoformat(datetime_)
|
||||
return isodate.date_isoformat(datetime_)
|
||||
|
||||
Reference in New Issue
Block a user