|
|
|
@ -1,5 +1,7 @@
|
|
|
|
# FreeCADExchange GUI panel for guided manual 3D wiring.
|
|
|
|
# FreeCADExchange GUI panel for guided manual 3D wiring.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
import FreeCAD as App
|
|
|
|
import FreeCAD as App
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
@ -27,14 +29,24 @@ try:
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
|
ExchangeWriteBack = None
|
|
|
|
ExchangeWriteBack = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
import ImportGui
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
ImportGui = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
COMMAND_NAME = "QET_Exchange_OpenManualWiringPanel"
|
|
|
|
COMMAND_NAME = "QET_Exchange_OpenManualWiringPanel"
|
|
|
|
DEFAULT_TERMINAL_EXIT_LENGTH = 20.0
|
|
|
|
DEFAULT_TERMINAL_EXIT_LENGTH = 20.0
|
|
|
|
|
|
|
|
DEFAULT_CARRIER_BASE_LENGTH = 200.0
|
|
|
|
CARRIER_ROLE_LABELS = {
|
|
|
|
CARRIER_ROLE_LABELS = {
|
|
|
|
"wire_duct": "线槽",
|
|
|
|
"wire_duct": "线槽",
|
|
|
|
"cabinet": "柜面",
|
|
|
|
"cabinet": "柜面",
|
|
|
|
"rail": "导轨",
|
|
|
|
"rail": "导轨",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BUILTIN_CARRIER_ASSETS = {
|
|
|
|
|
|
|
|
"wire_duct": "qet_wire_duct.FCStd",
|
|
|
|
|
|
|
|
"rail": "qet_din_rail.FCStd",
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ManualWiringPanelError(RuntimeError):
|
|
|
|
class ManualWiringPanelError(RuntimeError):
|
|
|
|
@ -55,6 +67,22 @@ def _console_error(message):
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _repo_root():
|
|
|
|
|
|
|
|
return Path(__file__).resolve().parents[3]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _builtin_carrier_asset_path(carrier_kind):
|
|
|
|
|
|
|
|
file_name = BUILTIN_CARRIER_ASSETS.get((carrier_kind or "").strip())
|
|
|
|
|
|
|
|
if not file_name:
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
return str(_repo_root() / "data" / "examples" / "qet_cabinet_assets" / file_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _supported_carrier_asset(path):
|
|
|
|
|
|
|
|
suffix = Path(path or "").suffix.lower()
|
|
|
|
|
|
|
|
return suffix in {".step", ".stp", ".fcstd"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _active_document():
|
|
|
|
def _active_document():
|
|
|
|
doc = getattr(App, "ActiveDocument", None)
|
|
|
|
doc = getattr(App, "ActiveDocument", None)
|
|
|
|
if doc is None:
|
|
|
|
if doc is None:
|
|
|
|
@ -163,14 +191,22 @@ def _iter_terminal_objects(doc):
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _find_terminal_by_uuid(doc, terminal_uuid):
|
|
|
|
def _find_terminal_by_uuid(doc, terminal_uuid, element_uuid=""):
|
|
|
|
target = (terminal_uuid or "").strip()
|
|
|
|
target = (terminal_uuid or "").strip()
|
|
|
|
if not target:
|
|
|
|
if not target:
|
|
|
|
return None
|
|
|
|
return None
|
|
|
|
|
|
|
|
target_element = (element_uuid or "").strip()
|
|
|
|
|
|
|
|
fallback = None
|
|
|
|
for terminal in _iter_terminal_objects(doc):
|
|
|
|
for terminal in _iter_terminal_objects(doc):
|
|
|
|
if getattr(terminal, "QetTerminalUuid", "").strip() == target:
|
|
|
|
if getattr(terminal, "QetTerminalUuid", "").strip() != target:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
if not target_element:
|
|
|
|
return terminal
|
|
|
|
return terminal
|
|
|
|
return None
|
|
|
|
if getattr(terminal, "QetElementUuid", "").strip() == target_element:
|
|
|
|
|
|
|
|
return terminal
|
|
|
|
|
|
|
|
if fallback is None:
|
|
|
|
|
|
|
|
fallback = terminal
|
|
|
|
|
|
|
|
return fallback
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _terminal_label(obj):
|
|
|
|
def _terminal_label(obj):
|
|
|
|
@ -225,21 +261,14 @@ def _dominant_axis(vector):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _carrier_kind_from_object(obj):
|
|
|
|
def _carrier_kind_from_object(obj):
|
|
|
|
candidates = []
|
|
|
|
carrier = _carrier_object_from_object(obj)
|
|
|
|
current = obj
|
|
|
|
if carrier is not None:
|
|
|
|
if current is not None:
|
|
|
|
return (getattr(carrier, "QetCarrierKind", "") or "").strip()
|
|
|
|
candidates.append(current)
|
|
|
|
|
|
|
|
candidates.extend(list(getattr(current, "InList", []) or []))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for candidate in candidates:
|
|
|
|
|
|
|
|
carrier_kind = (getattr(candidate, "QetCarrierKind", "") or "").strip()
|
|
|
|
|
|
|
|
if carrier_kind:
|
|
|
|
|
|
|
|
return carrier_kind
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
text_parts = []
|
|
|
|
text_parts = []
|
|
|
|
for candidate in candidates:
|
|
|
|
if obj is not None:
|
|
|
|
text_parts.append(getattr(candidate, "Name", "") or "")
|
|
|
|
text_parts.append(getattr(obj, "Name", "") or "")
|
|
|
|
text_parts.append(getattr(candidate, "Label", "") or "")
|
|
|
|
text_parts.append(getattr(obj, "Label", "") or "")
|
|
|
|
text = " ".join(text_parts).lower()
|
|
|
|
text = " ".join(text_parts).lower()
|
|
|
|
if "线槽" in text or "duct" in text or "trunking" in text:
|
|
|
|
if "线槽" in text or "duct" in text or "trunking" in text:
|
|
|
|
return "wire_duct"
|
|
|
|
return "wire_duct"
|
|
|
|
@ -250,6 +279,20 @@ def _carrier_kind_from_object(obj):
|
|
|
|
return ""
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _carrier_object_from_object(obj):
|
|
|
|
|
|
|
|
candidates = []
|
|
|
|
|
|
|
|
current = obj
|
|
|
|
|
|
|
|
if current is not None:
|
|
|
|
|
|
|
|
candidates.append(current)
|
|
|
|
|
|
|
|
candidates.extend(list(getattr(current, "InList", []) or []))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for candidate in candidates:
|
|
|
|
|
|
|
|
carrier_kind = (getattr(candidate, "QetCarrierKind", "") or "").strip()
|
|
|
|
|
|
|
|
if carrier_kind:
|
|
|
|
|
|
|
|
return candidate
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _edge_carrier_axis(edge):
|
|
|
|
def _edge_carrier_axis(edge):
|
|
|
|
vertexes = list(getattr(edge, "Vertexes", []) or [])
|
|
|
|
vertexes = list(getattr(edge, "Vertexes", []) or [])
|
|
|
|
if len(vertexes) >= 2:
|
|
|
|
if len(vertexes) >= 2:
|
|
|
|
@ -278,6 +321,141 @@ def _selected_carrier_objects():
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _existing_object_names(doc):
|
|
|
|
|
|
|
|
return {getattr(obj, "Name", "") for obj in list(getattr(doc, "Objects", []) or [])}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _new_objects_since(doc, before_names):
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
|
|
obj
|
|
|
|
|
|
|
|
for obj in list(getattr(doc, "Objects", []) or [])
|
|
|
|
|
|
|
|
if getattr(obj, "Name", "") not in before_names
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _top_level_objects(objects):
|
|
|
|
|
|
|
|
object_set = set(objects or [])
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
|
|
|
for obj in objects or []:
|
|
|
|
|
|
|
|
parents = set(getattr(obj, "InList", []) or [])
|
|
|
|
|
|
|
|
if parents.intersection(object_set):
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
result.append(obj)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _open_fcstd_source(path):
|
|
|
|
|
|
|
|
source_doc = App.openDocument(path, hidden=True, temporary=True)
|
|
|
|
|
|
|
|
return source_doc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _import_carrier_objects_from_path(doc, path):
|
|
|
|
|
|
|
|
if not _supported_carrier_asset(path):
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("请选择 STEP/STP/FCStd 线槽或导轨模型。")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
suffix = Path(path).suffix.lower()
|
|
|
|
|
|
|
|
if suffix == ".fcstd":
|
|
|
|
|
|
|
|
source_doc = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
source_doc = _open_fcstd_source(path)
|
|
|
|
|
|
|
|
copied = []
|
|
|
|
|
|
|
|
for source_obj in _top_level_objects(list(getattr(source_doc, "Objects", []) or [])):
|
|
|
|
|
|
|
|
copied.append(doc.copyObject(source_obj, True))
|
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
|
|
if source_doc is not None:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
App.closeDocument(source_doc.Name)
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ImportGui is None:
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("当前 FreeCAD 无法导入 STEP/STP 文件。")
|
|
|
|
|
|
|
|
before = _existing_object_names(doc)
|
|
|
|
|
|
|
|
ImportGui.insert(
|
|
|
|
|
|
|
|
name=path,
|
|
|
|
|
|
|
|
docName=doc.Name,
|
|
|
|
|
|
|
|
merge=False,
|
|
|
|
|
|
|
|
useLinkGroup=True,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return _top_level_objects(_new_objects_since(doc, before))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _ensure_float_property(obj, prop_name, value, description):
|
|
|
|
|
|
|
|
if prop_name not in getattr(obj, "PropertiesList", []):
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyFloat", prop_name, "QET Wiring", description)
|
|
|
|
|
|
|
|
setattr(obj, prop_name, float(value or 0.0))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _set_carrier_properties(obj, carrier_kind, source_path="", base_length=DEFAULT_CARRIER_BASE_LENGTH):
|
|
|
|
|
|
|
|
role_label = _carrier_role_label(carrier_kind)
|
|
|
|
|
|
|
|
TerminalObjects.ensure_string_property(
|
|
|
|
|
|
|
|
obj,
|
|
|
|
|
|
|
|
"QetCarrierKind",
|
|
|
|
|
|
|
|
"QET Wiring",
|
|
|
|
|
|
|
|
"3D wiring carrier kind",
|
|
|
|
|
|
|
|
carrier_kind,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
TerminalObjects.ensure_string_property(
|
|
|
|
|
|
|
|
obj,
|
|
|
|
|
|
|
|
"QetCarrierRoleLabel",
|
|
|
|
|
|
|
|
"QET Wiring",
|
|
|
|
|
|
|
|
"3D wiring carrier role label",
|
|
|
|
|
|
|
|
role_label,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
TerminalObjects.ensure_string_property(
|
|
|
|
|
|
|
|
obj,
|
|
|
|
|
|
|
|
"QetCarrierAxis",
|
|
|
|
|
|
|
|
"QET Wiring",
|
|
|
|
|
|
|
|
"3D wiring carrier axis",
|
|
|
|
|
|
|
|
"x",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
TerminalObjects.ensure_string_property(
|
|
|
|
|
|
|
|
obj,
|
|
|
|
|
|
|
|
"QetCarrierSourcePath",
|
|
|
|
|
|
|
|
"QET Wiring",
|
|
|
|
|
|
|
|
"3D wiring carrier source path",
|
|
|
|
|
|
|
|
source_path,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
_ensure_float_property(obj, "QetCarrierBaseLength", base_length, "Carrier base length")
|
|
|
|
|
|
|
|
return obj
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _iter_object_tree(obj):
|
|
|
|
|
|
|
|
yield obj
|
|
|
|
|
|
|
|
for child in list(getattr(obj, "Group", []) or []):
|
|
|
|
|
|
|
|
for nested in _iter_object_tree(child):
|
|
|
|
|
|
|
|
yield nested
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _scale_shape_x(obj, factor):
|
|
|
|
|
|
|
|
shape = getattr(obj, "Shape", None)
|
|
|
|
|
|
|
|
if shape is None or not hasattr(shape, "transformGeometry"):
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
matrix = App.Matrix()
|
|
|
|
|
|
|
|
matrix.scale(float(factor), 1.0, 1.0)
|
|
|
|
|
|
|
|
obj.Shape = shape.transformGeometry(matrix)
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _apply_carrier_length(carrier, length_mm):
|
|
|
|
|
|
|
|
length = max(float(length_mm or 0.0), 1.0)
|
|
|
|
|
|
|
|
base_length = float(getattr(carrier, "QetCarrierBaseLength", 0.0) or DEFAULT_CARRIER_BASE_LENGTH)
|
|
|
|
|
|
|
|
target_scale = length / base_length
|
|
|
|
|
|
|
|
current_scale = float(getattr(carrier, "QetCarrierScaleX", 1.0) or 1.0)
|
|
|
|
|
|
|
|
factor = target_scale / current_scale if current_scale else target_scale
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for obj in _iter_object_tree(carrier):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
_scale_shape_x(obj, factor)
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_ensure_float_property(carrier, "QetCarrierLength", length, "Carrier length")
|
|
|
|
|
|
|
|
_ensure_float_property(carrier, "QetCarrierScaleX", target_scale, "Carrier X scale")
|
|
|
|
|
|
|
|
return carrier
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _selected_waypoint():
|
|
|
|
def _selected_waypoint():
|
|
|
|
for picked in _selection_ex():
|
|
|
|
for picked in _selection_ex():
|
|
|
|
picked_points = list(getattr(picked, "PickedPoints", []) or [])
|
|
|
|
picked_points = list(getattr(picked, "PickedPoints", []) or [])
|
|
|
|
@ -324,7 +502,7 @@ def _selected_waypoint():
|
|
|
|
"carrier_kind": _carrier_kind_from_object(obj),
|
|
|
|
"carrier_kind": _carrier_kind_from_object(obj),
|
|
|
|
"carrier_axis": carrier_axis,
|
|
|
|
"carrier_axis": carrier_axis,
|
|
|
|
"source_label": getattr(obj, "Label", "") if obj is not None else "",
|
|
|
|
"source_label": getattr(obj, "Label", "") if obj is not None else "",
|
|
|
|
"source_object_name": getattr(obj, "Name", "") if obj is not None else "",
|
|
|
|
"source_object_name": getattr(_carrier_object_from_object(obj) or obj, "Name", "") if obj is not None else "",
|
|
|
|
"subelement_name": subelement_names[0] if subelement_names else "",
|
|
|
|
"subelement_name": subelement_names[0] if subelement_names else "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return None
|
|
|
|
return None
|
|
|
|
@ -488,6 +666,65 @@ class ManualWiringController:
|
|
|
|
marked.append(obj)
|
|
|
|
marked.append(obj)
|
|
|
|
return marked
|
|
|
|
return marked
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def import_carrier_asset(
|
|
|
|
|
|
|
|
self,
|
|
|
|
|
|
|
|
asset_path,
|
|
|
|
|
|
|
|
carrier_kind,
|
|
|
|
|
|
|
|
length_mm=DEFAULT_CARRIER_BASE_LENGTH,
|
|
|
|
|
|
|
|
importer=None,
|
|
|
|
|
|
|
|
):
|
|
|
|
|
|
|
|
carrier_kind = (carrier_kind or "").strip()
|
|
|
|
|
|
|
|
if carrier_kind not in {"wire_duct", "rail"}:
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("只能导入线槽或导轨资产。")
|
|
|
|
|
|
|
|
path = str(asset_path or "").strip()
|
|
|
|
|
|
|
|
if not path:
|
|
|
|
|
|
|
|
path = _builtin_carrier_asset_path(carrier_kind)
|
|
|
|
|
|
|
|
if not path:
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("找不到内置线槽/导轨资产。")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
doc = _active_document()
|
|
|
|
|
|
|
|
project_uuid = getattr(TerminalObjects.ensure_root_group(doc), "QetProjectUuid", "").strip()
|
|
|
|
|
|
|
|
carrier_group = WiringObjects.ensure_carrier_group(doc, project_uuid)
|
|
|
|
|
|
|
|
imported = list((importer or _import_carrier_objects_from_path)(doc, path) or [])
|
|
|
|
|
|
|
|
if not imported:
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("没有从模型文件导入任何对象。")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
role_label = _carrier_role_label(carrier_kind)
|
|
|
|
|
|
|
|
container_name = "QETCarrier_{0}".format(TerminalObjects.safe_token(carrier_kind))
|
|
|
|
|
|
|
|
container = doc.addObject("App::DocumentObjectGroup", container_name)
|
|
|
|
|
|
|
|
container.Label = "QET {0}".format(role_label or carrier_kind)
|
|
|
|
|
|
|
|
carrier_group.addObject(container)
|
|
|
|
|
|
|
|
for obj in imported:
|
|
|
|
|
|
|
|
if obj not in getattr(container, "Group", []):
|
|
|
|
|
|
|
|
container.addObject(obj)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_set_carrier_properties(container, carrier_kind, source_path=path)
|
|
|
|
|
|
|
|
for obj in imported:
|
|
|
|
|
|
|
|
_set_carrier_properties(obj, carrier_kind, source_path=path)
|
|
|
|
|
|
|
|
_apply_carrier_length(container, length_mm)
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
doc.recompute()
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
return container
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_length_to_selected_carriers(self, length_mm):
|
|
|
|
|
|
|
|
selected = _selected_carrier_objects()
|
|
|
|
|
|
|
|
if not selected:
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("请先选择线槽或导轨对象。")
|
|
|
|
|
|
|
|
updated = []
|
|
|
|
|
|
|
|
for carrier in selected:
|
|
|
|
|
|
|
|
if not (getattr(carrier, "QetCarrierKind", "") or "").strip():
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
updated.append(_apply_carrier_length(carrier, length_mm))
|
|
|
|
|
|
|
|
if not updated:
|
|
|
|
|
|
|
|
raise ManualWiringPanelError("所选对象不是已标记的线槽或导轨。")
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
_active_document().recompute()
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
return updated
|
|
|
|
|
|
|
|
|
|
|
|
def _clear_preview_objects(self):
|
|
|
|
def _clear_preview_objects(self):
|
|
|
|
doc = getattr(App, "ActiveDocument", None)
|
|
|
|
doc = getattr(App, "ActiveDocument", None)
|
|
|
|
if doc is None:
|
|
|
|
if doc is None:
|
|
|
|
@ -527,10 +764,12 @@ class ManualWiringController:
|
|
|
|
start_terminal = _find_terminal_by_uuid(
|
|
|
|
start_terminal = _find_terminal_by_uuid(
|
|
|
|
doc,
|
|
|
|
doc,
|
|
|
|
getattr(task, "QetStartTerminalUuid", ""),
|
|
|
|
getattr(task, "QetStartTerminalUuid", ""),
|
|
|
|
|
|
|
|
element_uuid=getattr(task, "QetStartElementUuid", ""),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end_terminal = _find_terminal_by_uuid(
|
|
|
|
end_terminal = _find_terminal_by_uuid(
|
|
|
|
doc,
|
|
|
|
doc,
|
|
|
|
getattr(task, "QetEndTerminalUuid", ""),
|
|
|
|
getattr(task, "QetEndTerminalUuid", ""),
|
|
|
|
|
|
|
|
element_uuid=getattr(task, "QetEndElementUuid", ""),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if start_terminal is None or end_terminal is None:
|
|
|
|
if start_terminal is None or end_terminal is None:
|
|
|
|
raise ManualWiringPanelError("导线任务的起点或终点工程端子未找到。")
|
|
|
|
raise ManualWiringPanelError("导线任务的起点或终点工程端子未找到。")
|
|
|
|
@ -576,6 +815,7 @@ class ManualWiringController:
|
|
|
|
end_terminal = _find_terminal_by_uuid(
|
|
|
|
end_terminal = _find_terminal_by_uuid(
|
|
|
|
doc,
|
|
|
|
doc,
|
|
|
|
getattr(self.current_task, "QetEndTerminalUuid", ""),
|
|
|
|
getattr(self.current_task, "QetEndTerminalUuid", ""),
|
|
|
|
|
|
|
|
element_uuid=getattr(self.current_task, "QetEndElementUuid", ""),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
wire_kwargs = _task_wire_kwargs(self.current_task)
|
|
|
|
wire_kwargs = _task_wire_kwargs(self.current_task)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
@ -668,6 +908,15 @@ class ManualWiringTaskPanel:
|
|
|
|
self.exit_length_input.setSingleStep(5.0)
|
|
|
|
self.exit_length_input.setSingleStep(5.0)
|
|
|
|
self.exit_length_input.setSuffix(" mm")
|
|
|
|
self.exit_length_input.setSuffix(" mm")
|
|
|
|
self.exit_length_input.setValue(self.controller.terminal_exit_length)
|
|
|
|
self.exit_length_input.setValue(self.controller.terminal_exit_length)
|
|
|
|
|
|
|
|
self.carrier_length_input = QtWidgets.QDoubleSpinBox()
|
|
|
|
|
|
|
|
self.carrier_length_input.setRange(1.0, 10000.0)
|
|
|
|
|
|
|
|
self.carrier_length_input.setDecimals(1)
|
|
|
|
|
|
|
|
self.carrier_length_input.setSingleStep(50.0)
|
|
|
|
|
|
|
|
self.carrier_length_input.setSuffix(" mm")
|
|
|
|
|
|
|
|
self.carrier_length_input.setValue(DEFAULT_CARRIER_BASE_LENGTH)
|
|
|
|
|
|
|
|
self.import_duct_button = QtWidgets.QPushButton("导入线槽")
|
|
|
|
|
|
|
|
self.import_rail_button = QtWidgets.QPushButton("导入导轨")
|
|
|
|
|
|
|
|
self.apply_carrier_length_button = QtWidgets.QPushButton("应用载体长度")
|
|
|
|
self.start_button = QtWidgets.QPushButton("设为起点")
|
|
|
|
self.start_button = QtWidgets.QPushButton("设为起点")
|
|
|
|
self.mark_duct_button = QtWidgets.QPushButton("标记为线槽")
|
|
|
|
self.mark_duct_button = QtWidgets.QPushButton("标记为线槽")
|
|
|
|
self.mark_cabinet_button = QtWidgets.QPushButton("标记为柜面")
|
|
|
|
self.mark_cabinet_button = QtWidgets.QPushButton("标记为柜面")
|
|
|
|
@ -687,6 +936,15 @@ class ManualWiringTaskPanel:
|
|
|
|
exit_layout.addWidget(QtWidgets.QLabel("端子出线长度"))
|
|
|
|
exit_layout.addWidget(QtWidgets.QLabel("端子出线长度"))
|
|
|
|
exit_layout.addWidget(self.exit_length_input)
|
|
|
|
exit_layout.addWidget(self.exit_length_input)
|
|
|
|
layout.addLayout(exit_layout)
|
|
|
|
layout.addLayout(exit_layout)
|
|
|
|
|
|
|
|
carrier_length_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
|
|
|
carrier_length_layout.addWidget(QtWidgets.QLabel("载体长度"))
|
|
|
|
|
|
|
|
carrier_length_layout.addWidget(self.carrier_length_input)
|
|
|
|
|
|
|
|
layout.addLayout(carrier_length_layout)
|
|
|
|
|
|
|
|
import_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
|
|
|
import_layout.addWidget(self.import_duct_button)
|
|
|
|
|
|
|
|
import_layout.addWidget(self.import_rail_button)
|
|
|
|
|
|
|
|
import_layout.addWidget(self.apply_carrier_length_button)
|
|
|
|
|
|
|
|
layout.addLayout(import_layout)
|
|
|
|
carrier_layout = QtWidgets.QHBoxLayout()
|
|
|
|
carrier_layout = QtWidgets.QHBoxLayout()
|
|
|
|
carrier_layout.addWidget(self.mark_duct_button)
|
|
|
|
carrier_layout.addWidget(self.mark_duct_button)
|
|
|
|
carrier_layout.addWidget(self.mark_cabinet_button)
|
|
|
|
carrier_layout.addWidget(self.mark_cabinet_button)
|
|
|
|
@ -712,6 +970,9 @@ class ManualWiringTaskPanel:
|
|
|
|
self.use_task_button.clicked.connect(self.use_selected_task)
|
|
|
|
self.use_task_button.clicked.connect(self.use_selected_task)
|
|
|
|
self.reload_tasks_button.clicked.connect(self._refresh_task_list)
|
|
|
|
self.reload_tasks_button.clicked.connect(self._refresh_task_list)
|
|
|
|
self.exit_length_input.valueChanged.connect(self.set_exit_length)
|
|
|
|
self.exit_length_input.valueChanged.connect(self.set_exit_length)
|
|
|
|
|
|
|
|
self.import_duct_button.clicked.connect(self.import_wire_duct)
|
|
|
|
|
|
|
|
self.import_rail_button.clicked.connect(self.import_din_rail)
|
|
|
|
|
|
|
|
self.apply_carrier_length_button.clicked.connect(self.apply_carrier_length)
|
|
|
|
self.mark_duct_button.clicked.connect(self.mark_wire_duct)
|
|
|
|
self.mark_duct_button.clicked.connect(self.mark_wire_duct)
|
|
|
|
self.mark_cabinet_button.clicked.connect(self.mark_cabinet)
|
|
|
|
self.mark_cabinet_button.clicked.connect(self.mark_cabinet)
|
|
|
|
self.mark_rail_button.clicked.connect(self.mark_rail)
|
|
|
|
self.mark_rail_button.clicked.connect(self.mark_rail)
|
|
|
|
@ -787,6 +1048,60 @@ class ManualWiringTaskPanel:
|
|
|
|
except Exception as exc:
|
|
|
|
except Exception as exc:
|
|
|
|
self._set_error(str(exc))
|
|
|
|
self._set_error(str(exc))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _select_carrier_asset_path(self, carrier_kind):
|
|
|
|
|
|
|
|
default_path = _builtin_carrier_asset_path(carrier_kind)
|
|
|
|
|
|
|
|
default_dir = str(Path(default_path).parent) if default_path else ""
|
|
|
|
|
|
|
|
title = "选择线槽模型" if carrier_kind == "wire_duct" else "选择导轨模型"
|
|
|
|
|
|
|
|
if not hasattr(QtWidgets, "QFileDialog"):
|
|
|
|
|
|
|
|
return default_path
|
|
|
|
|
|
|
|
path, _selected_filter = QtWidgets.QFileDialog.getOpenFileName(
|
|
|
|
|
|
|
|
self.form,
|
|
|
|
|
|
|
|
title,
|
|
|
|
|
|
|
|
default_dir,
|
|
|
|
|
|
|
|
"3D carrier asset (*.FCStd *.fcstd *.step *.stp);;All files (*.*)",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return path or default_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def import_carrier(self, carrier_kind):
|
|
|
|
|
|
|
|
path = self._select_carrier_asset_path(carrier_kind)
|
|
|
|
|
|
|
|
carrier = self.controller.import_carrier_asset(
|
|
|
|
|
|
|
|
path,
|
|
|
|
|
|
|
|
carrier_kind,
|
|
|
|
|
|
|
|
length_mm=self.carrier_length_input.value(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self._set_status(
|
|
|
|
|
|
|
|
"已导入{0}:{1}".format(
|
|
|
|
|
|
|
|
_carrier_role_label(carrier_kind) or carrier_kind,
|
|
|
|
|
|
|
|
getattr(carrier, "Label", "") or getattr(carrier, "Name", ""),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def import_wire_duct(self):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
self.import_carrier("wire_duct")
|
|
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
|
|
|
self._set_error(str(exc))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def import_din_rail(self):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
self.import_carrier("rail")
|
|
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
|
|
|
self._set_error(str(exc))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_carrier_length(self):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
updated = self.controller.apply_length_to_selected_carriers(
|
|
|
|
|
|
|
|
self.carrier_length_input.value()
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self._set_status(
|
|
|
|
|
|
|
|
"已更新 {0} 个线槽/导轨长度为 {1:.1f} mm。".format(
|
|
|
|
|
|
|
|
len(updated),
|
|
|
|
|
|
|
|
self.carrier_length_input.value(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
|
|
|
self._set_error(str(exc))
|
|
|
|
|
|
|
|
|
|
|
|
def mark_carrier(self, carrier_kind):
|
|
|
|
def mark_carrier(self, carrier_kind):
|
|
|
|
marked = self.controller.mark_selected_carriers(carrier_kind)
|
|
|
|
marked = self.controller.mark_selected_carriers(carrier_kind)
|
|
|
|
role_label = _carrier_role_label(carrier_kind) or carrier_kind
|
|
|
|
role_label = _carrier_role_label(carrier_kind) or carrier_kind
|
|
|
|
|