fix/FreeCAD模板中文界面与启动稳定-zwl-0520

dev
Zhaowenlong 6 days ago
parent 0015b92d01
commit 82be30e69c

@ -2,73 +2,141 @@
import os import os
from pathlib import Path from pathlib import Path
import traceback
import FreeCADGui as Gui import FreeCADGui as Gui
try:
from PySide6 import QtCore
except ImportError:
try:
from PySide2 import QtCore
except ImportError:
from PySide import QtCore
import ExchangeBootstrap
import ExchangeWriteBack
import ManualWiring
import TemplateAuthoring
import TemplateAuthoringPanel
COMMANDS = [ COMMANDS = [
TemplateAuthoringPanel.COMMAND_NAME, "QET_Template_OpenAuthoringPanel",
"QET_Template_AddTerminal", "QET_Template_AddTerminal",
"QET_Template_ValidateTerminals", "QET_Template_ValidateTerminals",
"QET_Template_SaveAsFCStd", "QET_Template_SaveAsFCStd",
] ]
def _append_init_log(message): def _append_init_log(message, os_module=os, path_class=Path):
try: try:
local_app_data = os.environ.get("LOCALAPPDATA", "").strip() local_app_data = os_module.environ.get("LOCALAPPDATA", "").strip()
if local_app_data: if local_app_data:
log_path = os.path.join(local_app_data, "QETDeps", "freecad_exchange_bootstrap.log") log_path = os_module.path.join(local_app_data, "QETDeps", "freecad_exchange_bootstrap.log")
else: else:
log_path = os.path.join(str(Path.home()), "AppData", "Local", "QETDeps", "freecad_exchange_bootstrap.log") log_path = os_module.path.join(
os.makedirs(os.path.dirname(log_path), exist_ok=True) str(path_class.home()),
"AppData",
"Local",
"QETDeps",
"freecad_exchange_bootstrap.log",
)
os_module.makedirs(os_module.path.dirname(log_path), exist_ok=True)
with open(log_path, "a", encoding="utf-8") as handle: with open(log_path, "a", encoding="utf-8") as handle:
handle.write(message + "\n") handle.write(message + "\n")
except Exception: except Exception:
pass pass
_append_init_log("InitGui imported") _append_init_log("InitGui start")
def _register_exchange_commands():
def _safe_import(module_name, append_init_log=_append_init_log, traceback_module=traceback):
try: try:
ExchangeWriteBack.ensure_document_observer_installed() module = __import__(module_name)
append_init_log("InitGui imported {0}".format(module_name))
return module
except Exception: except Exception:
pass append_init_log(
"InitGui failed to import {0}:\n{1}".format(
module_name,
traceback_module.format_exc(),
)
)
return None
def _register_exchange_commands(
safe_import=_safe_import,
append_init_log=_append_init_log,
traceback_module=traceback,
):
exchange_write_back = safe_import("ExchangeWriteBack")
manual_wiring = safe_import("ManualWiring")
template_authoring = safe_import("TemplateAuthoring")
template_authoring_panel = safe_import("TemplateAuthoringPanel")
try: try:
ExchangeWriteBack.register_commands() if exchange_write_back is not None:
exchange_write_back.ensure_document_observer_installed()
except Exception: except Exception:
pass append_init_log(
"InitGui failed to install write-back observer:\n{0}".format(
traceback_module.format_exc()
)
)
try: try:
ManualWiring.register_commands() if exchange_write_back is not None:
exchange_write_back.register_commands()
except Exception: except Exception:
pass append_init_log(
"InitGui failed to register write-back commands:\n{0}".format(
traceback_module.format_exc()
)
)
try: try:
TemplateAuthoring.register_commands() if manual_wiring is not None:
manual_wiring.register_commands()
except Exception: except Exception:
pass append_init_log(
"InitGui failed to register wiring commands:\n{0}".format(
traceback_module.format_exc()
)
)
try: try:
TemplateAuthoringPanel.register_commands() if template_authoring is not None:
template_authoring.register_commands()
except Exception: except Exception:
pass append_init_log(
"InitGui failed to register template authoring commands:\n{0}".format(
traceback_module.format_exc()
)
)
try:
if template_authoring_panel is not None:
template_authoring_panel.register_commands()
except Exception:
append_init_log(
"InitGui failed to register template panel command:\n{0}".format(
traceback_module.format_exc()
)
)
def _bootstrap_if_requested(
safe_import=_safe_import,
append_init_log=_append_init_log,
traceback_module=traceback,
):
exchange_bootstrap = safe_import("ExchangeBootstrap")
if exchange_bootstrap is None:
return
try:
exchange_bootstrap.bootstrap_if_requested()
except Exception:
append_init_log(
"InitGui bootstrap_if_requested failed:\n{0}".format(
traceback_module.format_exc()
)
)
globals()["FreeCADExchange_COMMANDS"] = COMMANDS
globals()["FreeCADExchange_append_init_log"] = _append_init_log
globals()["FreeCADExchange_register_exchange_commands"] = _register_exchange_commands
globals()["FreeCADExchange_bootstrap_if_requested"] = _bootstrap_if_requested
class FreeCADExchangeWorkbench(Gui.Workbench): class FreeCADExchangeWorkbench(Gui.Workbench):
@ -99,20 +167,47 @@ class FreeCADExchangeWorkbench(Gui.Workbench):
"................"}; "................"};
""" """
def Initialize(self): def Initialize(
_register_exchange_commands() self,
self.appendToolbar("QET模板", COMMANDS) register_exchange_commands=FreeCADExchange_register_exchange_commands,
self.appendMenu("QET模板", COMMANDS) append_init_log=FreeCADExchange_append_init_log,
_append_init_log("FreeCADExchangeWorkbench initialized") commands=FreeCADExchange_COMMANDS,
):
def Activated(self): register_exchange_commands()
_register_exchange_commands() self.appendToolbar("QET模板", commands)
_append_init_log("FreeCADExchangeWorkbench activated") self.appendMenu("QET模板", commands)
append_init_log("FreeCADExchangeWorkbench initialized")
def Activated(
self,
register_exchange_commands=FreeCADExchange_register_exchange_commands,
append_init_log=FreeCADExchange_append_init_log,
):
register_exchange_commands()
append_init_log("FreeCADExchangeWorkbench activated")
def Deactivated(self): def Deactivated(self):
pass pass
def GetClassName(self):
return "Gui::PythonWorkbench"
_register_exchange_commands()
Gui.addWorkbench(FreeCADExchangeWorkbench()) Gui.addWorkbench(FreeCADExchangeWorkbench())
QtCore.QTimer.singleShot(0, ExchangeBootstrap.bootstrap_if_requested) _append_init_log("InitGui workbench registered")
try:
from PySide6 import QtCore
except ImportError:
try:
from PySide2 import QtCore
except ImportError:
try:
from PySide import QtCore
except ImportError:
QtCore = None
if QtCore is not None:
QtCore.QTimer.singleShot(0, FreeCADExchange_bootstrap_if_requested)
else:
FreeCADExchange_bootstrap_if_requested()

@ -221,8 +221,8 @@ def _selection_position():
class CommandAddTemplateTerminal: class CommandAddTemplateTerminal:
def GetResources(self): def GetResources(self):
return { return {
"MenuText": "Add Template Terminal", "MenuText": "添加模板端子",
"ToolTip": "Create a reusable electrical terminal LCS for an FCStd equipment template", "ToolTip": "在 FCStd 设备模板中创建可接线端子 LCS",
} }
def IsActive(self): def IsActive(self):
@ -257,8 +257,8 @@ class CommandAddTemplateTerminal:
class CommandValidateTemplateTerminals: class CommandValidateTemplateTerminals:
def GetResources(self): def GetResources(self):
return { return {
"MenuText": "Validate Template Terminals", "MenuText": "校验模板端子",
"ToolTip": "Validate electrical terminal LCS objects in the current FCStd template", "ToolTip": "校验当前 FCStd 模板中的电气端子 LCS",
} }
def IsActive(self): def IsActive(self):
@ -279,8 +279,8 @@ class CommandValidateTemplateTerminals:
class CommandSaveTemplateAsFCStd: class CommandSaveTemplateAsFCStd:
def GetResources(self): def GetResources(self):
return { return {
"MenuText": "Save Template As FCStd", "MenuText": "保存模板为 FCStd",
"ToolTip": "Validate and save the current document as a reusable FCStd equipment template", "ToolTip": "校验并保存当前文档为可复用 FCStd 设备模板",
} }
def IsActive(self): def IsActive(self):
@ -293,7 +293,7 @@ class CommandSaveTemplateAsFCStd:
file_path, _selected_filter = QtWidgets.QFileDialog.getSaveFileName( file_path, _selected_filter = QtWidgets.QFileDialog.getSaveFileName(
None, None,
"Save FCStd Equipment Template", "保存 FCStd 设备模板",
"", "",
"FreeCAD template (*.FCStd *.fcstd);;All files (*.*)", "FreeCAD template (*.FCStd *.fcstd);;All files (*.*)",
) )

@ -27,6 +27,26 @@ COMMAND_NAME = "QET_Template_OpenAuthoringPanel"
MENU_ACTION_OBJECT_NAME = "QET_Template_OpenAuthoringPanel_MenuAction" MENU_ACTION_OBJECT_NAME = "QET_Template_OpenAuthoringPanel_MenuAction"
TOOLBAR_OBJECT_NAME = "QET_Template_Authoring_Toolbar" TOOLBAR_OBJECT_NAME = "QET_Template_Authoring_Toolbar"
TOOLBAR_ACTION_OBJECT_NAME = "QET_Template_OpenAuthoringPanel_ToolbarAction" TOOLBAR_ACTION_OBJECT_NAME = "QET_Template_OpenAuthoringPanel_ToolbarAction"
TERMINAL_TYPE_OPTIONS = [
("通用", "generic"),
("主回路", "primary"),
("电源", "power"),
("控制", "control"),
]
def terminal_type_value(combo):
value = None
if hasattr(combo, "currentData"):
value = combo.currentData()
if value:
return str(value).strip()
text = combo.currentText().strip() if hasattr(combo, "currentText") else ""
for label, option_value in TERMINAL_TYPE_OPTIONS:
if text == label or text == option_value:
return option_value
return "generic"
def next_slot_name(report): def next_slot_name(report):
@ -64,7 +84,8 @@ class TemplateAuthoringTaskPanel:
type_row = QtWidgets.QHBoxLayout() type_row = QtWidgets.QHBoxLayout()
type_row.addWidget(QtWidgets.QLabel("端子类型")) type_row.addWidget(QtWidgets.QLabel("端子类型"))
self.terminal_type_combo = QtWidgets.QComboBox() self.terminal_type_combo = QtWidgets.QComboBox()
self.terminal_type_combo.addItems(["generic", "primary", "power", "control"]) for label, value in TERMINAL_TYPE_OPTIONS:
self.terminal_type_combo.addItem(label, value)
type_row.addWidget(self.terminal_type_combo) type_row.addWidget(self.terminal_type_combo)
layout.addLayout(type_row) layout.addLayout(type_row)
@ -140,7 +161,7 @@ class TemplateAuthoringTaskPanel:
position = TemplateAuthoring._selection_position() position = TemplateAuthoring._selection_position()
if position is None: if position is None:
raise TemplateAuthoring.TemplateAuthoringError("请先在模型上选择端子位置。") raise TemplateAuthoring.TemplateAuthoringError("请先在模型上选择端子位置。")
terminal_type = self.terminal_type_combo.currentText().strip() or "generic" terminal_type = terminal_type_value(self.terminal_type_combo)
TemplateAuthoring.create_template_terminal( TemplateAuthoring.create_template_terminal(
doc, doc,
slot_name, slot_name,
@ -283,6 +304,15 @@ def install_toolbar_action():
_TOOLBAR_ACTION_INSTALLED = True _TOOLBAR_ACTION_INSTALLED = True
def _warn_register_commands_failed(target, exc):
try:
App.Console.PrintWarning(
"[FreeCADExchange] {0} installation skipped: {1}\n".format(target, exc)
)
except Exception:
pass
def register_commands(): def register_commands():
global _COMMANDS_REGISTERED global _COMMANDS_REGISTERED
if Gui is None or not hasattr(Gui, "addCommand"): if Gui is None or not hasattr(Gui, "addCommand"):
@ -290,8 +320,11 @@ def register_commands():
if not _COMMANDS_REGISTERED: if not _COMMANDS_REGISTERED:
Gui.addCommand(COMMAND_NAME, CommandOpenTemplateAuthoringPanel()) Gui.addCommand(COMMAND_NAME, CommandOpenTemplateAuthoringPanel())
_COMMANDS_REGISTERED = True _COMMANDS_REGISTERED = True
try:
install_menu_action() install_menu_action()
except RuntimeError as exc:
_warn_register_commands_failed("menu action", exc)
try:
install_toolbar_action() install_toolbar_action()
except RuntimeError as exc:
_warn_register_commands_failed("toolbar action", exc)
register_commands()

@ -49,6 +49,33 @@ def _reload_panel_module():
class TemplateAuthoringPanelTest(unittest.TestCase): class TemplateAuthoringPanelTest(unittest.TestCase):
def test_register_commands_ignores_menu_install_runtime_errors(self):
_install_fake_modules()
panel_module = _reload_panel_module()
panel_module._COMMANDS_REGISTERED = False
def raise_deleted_menu_error():
raise RuntimeError("Internal C++ object already deleted")
panel_module.install_menu_action = raise_deleted_menu_error
panel_module.install_toolbar_action = raise_deleted_menu_error
panel_module.register_commands()
def test_terminal_type_options_show_chinese_labels_with_stable_values(self):
_install_fake_modules()
panel_module = _reload_panel_module()
self.assertEqual(
[
("通用", "generic"),
("主回路", "primary"),
("电源", "power"),
("控制", "control"),
],
panel_module.TERMINAL_TYPE_OPTIONS,
)
def test_next_slot_name_uses_next_terminal_number(self): def test_next_slot_name_uses_next_terminal_number(self):
_install_fake_modules() _install_fake_modules()
panel_module = _reload_panel_module() panel_module = _reload_panel_module()

@ -120,6 +120,23 @@ def _reload_modules():
class TemplateAuthoringTest(unittest.TestCase): class TemplateAuthoringTest(unittest.TestCase):
def test_template_authoring_command_titles_are_chinese(self):
_install_fake_freecad()
template_authoring = _reload_modules()
self.assertEqual(
"添加模板端子",
template_authoring.CommandAddTemplateTerminal().GetResources()["MenuText"],
)
self.assertEqual(
"校验模板端子",
template_authoring.CommandValidateTemplateTerminals().GetResources()["MenuText"],
)
self.assertEqual(
"保存模板为 FCStd",
template_authoring.CommandSaveTemplateAsFCStd().GetResources()["MenuText"],
)
def test_import_skips_command_registration_when_gui_has_no_add_command(self): def test_import_skips_command_registration_when_gui_has_no_add_command(self):
_install_fake_freecad_without_gui_commands() _install_fake_freecad_without_gui_commands()

Loading…
Cancel
Save