pyfemtet 0.6.4__tar.gz → 0.6.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyfemtet might be problematic. Click here for more details.
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/PKG-INFO +3 -1
- pyfemtet-0.6.6/pyfemtet/__init__.py +1 -0
- pyfemtet-0.6.6/pyfemtet/_util/excel_macro_util.py +197 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_warning.py +13 -5
- pyfemtet-0.6.6/pyfemtet/brep/__init__.py +3 -0
- pyfemtet-0.6.6/pyfemtet/brep/_impl.py +14 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_excel_interface.py +30 -20
- pyfemtet-0.6.6/pyfemtet/opt/interface/_femtet_with_sldworks.py +298 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/_optuna/_optuna.py +62 -48
- pyfemtet-0.6.6/pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyproject.toml +6 -1
- pyfemtet-0.6.4/pyfemtet/__init__.py +0 -1
- pyfemtet-0.6.4/pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -189
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/LICENSE +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/README.md +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_femtet_config_util/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_femtet_config_util/autosave.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_femtet_config_util/exit.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/1. make_pot.bat +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/2. make_mo.bat +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/babel.cfg +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/locales/messages.pot +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/_message/messages.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/_test_utils → pyfemtet-0.6.6/pyfemtet/_util}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/core.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/dispatch_extensions/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/dispatch_extensions/_impl.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/logger/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/logger/_impl.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/_femopt.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/_femopt_core.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/optimizer/_optuna → pyfemtet-0.6.6/pyfemtet/opt/_test_utils}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/_test_utils/control_femtet.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/_test_utils/hyper_sphere.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/_test_utils/record_history.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_base.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_femtet.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_femtet_parametric.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_femtet_with_nx/_interface.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/interface/_femtet_with_nx/update_model.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/_base.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/optimizer/_optuna/_botorch_patch → pyfemtet-0.6.6/pyfemtet/opt/optimizer/_optuna}/__init__.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/prediction → pyfemtet-0.6.6/pyfemtet/opt/optimizer/_optuna/_botorch_patch}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/_scipy.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/_scipy_scalar.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/optimizer/parameter.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/visualization → pyfemtet-0.6.6/pyfemtet/opt/prediction}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/prediction/_base.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/prediction/single_task_gp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/ParametricIF.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/ParametricIF.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/ParametricIF_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.prt +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_parallel.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_parallel.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.femprj +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/visualization/_complex_components → pyfemtet-0.6.6/pyfemtet/opt/visualization}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_base.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/visualization/_process_monitor → pyfemtet-0.6.6/pyfemtet/opt/visualization/_complex_components}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_complex_components/alert_region.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_complex_components/control_femtet.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_complex_components/main_graph.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_complex_components/pm_graph.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_complex_components/pm_graph_creator.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_create_wrapped_components.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/visualization/_wrapped_components → pyfemtet-0.6.6/pyfemtet/opt/visualization/_process_monitor}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_process_monitor/application.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_process_monitor/pages.py +0 -0
- {pyfemtet-0.6.4/pyfemtet/opt/visualization/result_viewer → pyfemtet-0.6.6/pyfemtet/opt/visualization/_wrapped_components}/__init__.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_wrapped_components/dbc.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_wrapped_components/dcc.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_wrapped_components/html.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/_wrapped_components/str_enum.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/result_viewer/.gitignore +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/result_viewer/application.py +0 -0
- {pyfemtet-0.6.4 → pyfemtet-0.6.6}/pyfemtet/opt/visualization/result_viewer/pages.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyfemtet
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.6
|
|
4
4
|
Summary: Design parameter optimization using Femtet.
|
|
5
5
|
Home-page: https://github.com/pyfemtet/pyfemtet
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -12,8 +12,10 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Provides-Extra: brep
|
|
15
16
|
Requires-Dist: babel (>=2.15.0,<3.0.0)
|
|
16
17
|
Requires-Dist: botorch (==0.9.5)
|
|
18
|
+
Requires-Dist: brepmatching (>=0.1.6,<0.2.0) ; extra == "brep"
|
|
17
19
|
Requires-Dist: colorlog (>=6.8.0,<7.0.0)
|
|
18
20
|
Requires-Dist: dash (>=2.17.0,<3.0.0)
|
|
19
21
|
Requires-Dist: dash-bootstrap-components (>=1.5.0,<2.0.0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.6"
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Excel のエラーダイアログを補足します。"""
|
|
2
|
+
import sys
|
|
3
|
+
from time import sleep
|
|
4
|
+
from threading import Thread
|
|
5
|
+
import logging
|
|
6
|
+
import asyncio # for timeout
|
|
7
|
+
import win32gui
|
|
8
|
+
import win32con
|
|
9
|
+
import win32api
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
if __name__ == '__main__':
|
|
13
|
+
formatter = logging.Formatter(logging.BASIC_FORMAT)
|
|
14
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
15
|
+
handler.setFormatter(formatter)
|
|
16
|
+
logger.addHandler(handler)
|
|
17
|
+
logger.setLevel(logging.DEBUG)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _ExcelDialogProcessor:
|
|
21
|
+
|
|
22
|
+
def __init__(self, excel_, timeout):
|
|
23
|
+
self.excel = excel_
|
|
24
|
+
self.__excel_window_title = f' - Excel' # {basename} - Excel
|
|
25
|
+
self.__error_dialog_title = 'Microsoft Visual Basic'
|
|
26
|
+
self.__vbe_window_title = f'Microsoft Visual Basic for Applications - ' # Microsoft Visual Basic for Applications - {basename}
|
|
27
|
+
self.should_stop = False
|
|
28
|
+
self.timeout = timeout
|
|
29
|
+
self.__timed_out = False
|
|
30
|
+
self.__workbook_paths = [wb.FullName for wb in excel_.Workbooks]
|
|
31
|
+
self.__error_raised = False
|
|
32
|
+
self.__excel_state_stash = dict()
|
|
33
|
+
self.__watch_thread = None
|
|
34
|
+
|
|
35
|
+
async def watch(self):
|
|
36
|
+
|
|
37
|
+
while True:
|
|
38
|
+
if self.should_stop:
|
|
39
|
+
logger.debug('エラーダイアログの監視を終了')
|
|
40
|
+
return
|
|
41
|
+
logger.debug('エラーダイアログを監視中...')
|
|
42
|
+
|
|
43
|
+
win32gui.EnumWindows(self.enum_callback_to_activate, [])
|
|
44
|
+
await asyncio.sleep(0.5)
|
|
45
|
+
|
|
46
|
+
found = []
|
|
47
|
+
win32gui.EnumWindows(self.enum_callback_to_close_dialog, found)
|
|
48
|
+
await asyncio.sleep(0.5)
|
|
49
|
+
if any(found):
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
logger.debug('ブックを閉じます。')
|
|
53
|
+
win32gui.EnumWindows(self.enum_callback_to_close_book, []) # 成功していればこの時点でメイン処理では例外がスローされる
|
|
54
|
+
await asyncio.sleep(1)
|
|
55
|
+
|
|
56
|
+
logger.debug('確認ダイアログがあれば閉じます。')
|
|
57
|
+
win32gui.EnumWindows(self.enum_callback_to_close_confirm_dialog, [])
|
|
58
|
+
await asyncio.sleep(1)
|
|
59
|
+
self.__error_raised = True
|
|
60
|
+
|
|
61
|
+
def enum_callback_to_activate(self, hwnd, _):
|
|
62
|
+
title = win32gui.GetWindowText(hwnd)
|
|
63
|
+
# Excel 本体
|
|
64
|
+
if self.__excel_window_title in title:
|
|
65
|
+
# Visible == True の際、エラーが発生した際、
|
|
66
|
+
# 一度 Excel ウィンドウをアクティブ化しないと dialog が出てこない
|
|
67
|
+
# が、これだけではダメかも。
|
|
68
|
+
win32gui.PostMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
|
|
69
|
+
|
|
70
|
+
def enum_callback_to_close_dialog(self, hwnd, found):
|
|
71
|
+
title = win32gui.GetWindowText(hwnd)
|
|
72
|
+
# エラーダイアログ
|
|
73
|
+
if self.__error_dialog_title == title:
|
|
74
|
+
# 何故かこのコマンド以外受け付けず、
|
|
75
|
+
# このコマンドで問答無用でデバッグモードに入る
|
|
76
|
+
logger.debug('エラーダイアログを見つけました。')
|
|
77
|
+
win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
|
|
78
|
+
win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
|
|
79
|
+
logger.debug('エラーダイアログを閉じました。')
|
|
80
|
+
found.append(True)
|
|
81
|
+
|
|
82
|
+
def enum_callback_to_close_confirm_dialog(self, hwnd, _):
|
|
83
|
+
title = win32gui.GetWindowText(hwnd)
|
|
84
|
+
# 確認ダイアログ
|
|
85
|
+
if "Microsoft Excel" in title:
|
|
86
|
+
# DisplayAlerts が False の場合は不要
|
|
87
|
+
win32gui.SendMessage(hwnd, win32con.WM_SYSCOMMAND, win32con.SC_CLOSE, 0)
|
|
88
|
+
|
|
89
|
+
def enum_callback_to_close_book(self, hwnd, _):
|
|
90
|
+
title = win32gui.GetWindowText(hwnd)
|
|
91
|
+
# VBE
|
|
92
|
+
if self.__vbe_window_title in title:
|
|
93
|
+
# 何故かこれで book 本体が閉じる
|
|
94
|
+
win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0)
|
|
95
|
+
|
|
96
|
+
async def watch_main(self):
|
|
97
|
+
try:
|
|
98
|
+
await asyncio.wait_for(self.watch(), timeout=self.timeout)
|
|
99
|
+
except asyncio.TimeoutError:
|
|
100
|
+
logger.debug('タイムアウトしました。')
|
|
101
|
+
self.should_stop = True
|
|
102
|
+
self.__timed_out = True
|
|
103
|
+
|
|
104
|
+
def __enter__(self):
|
|
105
|
+
logger.debug('Excel を 不可視にします。')
|
|
106
|
+
self.__excel_state_stash['visible'] = self.excel.Visible
|
|
107
|
+
self.__excel_state_stash['display_alerts'] = self.excel.DisplayAlerts
|
|
108
|
+
self.excel.Visible = False
|
|
109
|
+
self.excel.DisplayAlerts = False
|
|
110
|
+
|
|
111
|
+
logger.debug('エラー監視を開始します。')
|
|
112
|
+
self.__watch_thread = Thread(
|
|
113
|
+
target=asyncio.run,
|
|
114
|
+
args=(self.watch_main(),),
|
|
115
|
+
)
|
|
116
|
+
self.__watch_thread.start()
|
|
117
|
+
|
|
118
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
119
|
+
|
|
120
|
+
logger.debug('Excel の状態を回復します。')
|
|
121
|
+
|
|
122
|
+
self.should_stop = True
|
|
123
|
+
self.__watch_thread.join()
|
|
124
|
+
|
|
125
|
+
self.excel.Visible = self.__excel_state_stash['visible']
|
|
126
|
+
self.excel.DisplayAlerts = self.__excel_state_stash['display_alerts']
|
|
127
|
+
|
|
128
|
+
if self.__timed_out:
|
|
129
|
+
logger.debug('Excel プロセスを強制終了します。')
|
|
130
|
+
logger.error('Excel プロセス強制終了は未実装です。')
|
|
131
|
+
raise TimeoutError('マクロの実行がタイムアウトしました。')
|
|
132
|
+
|
|
133
|
+
# if exc_type is not None:
|
|
134
|
+
# if issubclass(exc_type, com_error) and self.__error_raised:
|
|
135
|
+
if self.__error_raised:
|
|
136
|
+
logger.debug('エラーハンドリングの副作用でブックを閉じているので'
|
|
137
|
+
'Excel のブックを開きなおします。')
|
|
138
|
+
for wb_path in self.__workbook_paths:
|
|
139
|
+
self.excel.Workbooks.Open(wb_path)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def watch_excel_macro_error(excel_, timeout):
|
|
143
|
+
"""Excel のエラーダイアログの出現を監視し、検出されればブックを閉じます。"""
|
|
144
|
+
return _ExcelDialogProcessor(excel_, timeout)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == '__main__':
|
|
148
|
+
|
|
149
|
+
import os
|
|
150
|
+
os.chdir(os.path.dirname(__file__))
|
|
151
|
+
|
|
152
|
+
path = os.path.abspath('sample.xlsm')
|
|
153
|
+
path2 = os.path.abspath('sample2.xlsm')
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
from win32com.client import Dispatch
|
|
157
|
+
from pythoncom import com_error
|
|
158
|
+
|
|
159
|
+
logger.debug('Excel を起動しています。')
|
|
160
|
+
excel = Dispatch('Excel.Application')
|
|
161
|
+
excel.Visible = True
|
|
162
|
+
excel.DisplayAlerts = False
|
|
163
|
+
excel.Interactive = True
|
|
164
|
+
|
|
165
|
+
logger.debug('Workbook を開いています。')
|
|
166
|
+
excel.Workbooks.Open(path)
|
|
167
|
+
|
|
168
|
+
logger.debug('別の Workbook を開いています。')
|
|
169
|
+
excel.Workbooks.Open(path2)
|
|
170
|
+
|
|
171
|
+
logger.debug('Workbook に変更を加えます。')
|
|
172
|
+
excel.Workbooks(1).ActiveSheet.Range('A1').Value = 1.
|
|
173
|
+
|
|
174
|
+
logger.debug('開いている Workbooks 数:')
|
|
175
|
+
logger.debug(excel.Workbooks.Count)
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
with watch_excel_macro_error(excel, timeout=10):
|
|
179
|
+
sleep(3)
|
|
180
|
+
try:
|
|
181
|
+
excel.Run('raise_error')
|
|
182
|
+
except com_error as e:
|
|
183
|
+
logger.debug('この段階ではまだ Excel 回復機能が働きません。')
|
|
184
|
+
logger.debug('開いている Workbooks 数:')
|
|
185
|
+
logger.debug(excel.Workbooks.Count)
|
|
186
|
+
raise e
|
|
187
|
+
|
|
188
|
+
# excel.Run('no_error')
|
|
189
|
+
|
|
190
|
+
except com_error as e:
|
|
191
|
+
logger.debug('メイン処理でエラーを補足しました。:')
|
|
192
|
+
logger.debug(e)
|
|
193
|
+
|
|
194
|
+
logger.debug('開いている Workbooks 数:')
|
|
195
|
+
logger.debug(excel.Workbooks.Count)
|
|
196
|
+
|
|
197
|
+
logger.debug('保存していない場合、Workbook の変更は失われます。')
|
|
@@ -2,15 +2,19 @@ import warnings
|
|
|
2
2
|
from functools import wraps
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
def show_experimental_warning(feature_name):
|
|
6
|
+
warnings.warn(
|
|
7
|
+
f"The function '{feature_name}' is experimental and may change in the future.",
|
|
8
|
+
category=UserWarning,
|
|
9
|
+
stacklevel=2
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
def experimental_feature(func):
|
|
6
14
|
|
|
7
15
|
@wraps(func)
|
|
8
16
|
def wrapper(*args, **kwargs):
|
|
9
|
-
|
|
10
|
-
f"The function '{func.__name__}' is experimental and may change in the future.",
|
|
11
|
-
category=UserWarning,
|
|
12
|
-
stacklevel=2
|
|
13
|
-
)
|
|
17
|
+
show_experimental_warning(func.__name__)
|
|
14
18
|
return func(*args, **kwargs)
|
|
15
19
|
|
|
16
20
|
return wrapper
|
|
@@ -62,6 +66,10 @@ if __name__ == '__main__':
|
|
|
62
66
|
print("This is an experimental function.")
|
|
63
67
|
|
|
64
68
|
class Sample:
|
|
69
|
+
|
|
70
|
+
def __init__(self):
|
|
71
|
+
show_experimental_warning("Sample")
|
|
72
|
+
|
|
65
73
|
@experimental_feature
|
|
66
74
|
def add(self, a, b):
|
|
67
75
|
return a + b
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
try:
|
|
2
|
+
import brepmatching
|
|
3
|
+
except ModuleNotFoundError as e:
|
|
4
|
+
import warnings
|
|
5
|
+
warnings.warn(
|
|
6
|
+
'There is no installation of `brepmatching`. '
|
|
7
|
+
'Please confirm installation via '
|
|
8
|
+
'`pip install pyfemtet[brep]` or '
|
|
9
|
+
'`pip install brepmatching` command.'
|
|
10
|
+
)
|
|
11
|
+
raise e
|
|
12
|
+
|
|
13
|
+
from brepmatching.pyfemtet_scripts.replace_model_considering_their_matching import ModelUpdater
|
|
14
|
+
|
|
@@ -25,7 +25,9 @@ from pyfemtet.dispatch_extensions._impl import _NestableSpawnProcess
|
|
|
25
25
|
from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
|
|
26
26
|
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
|
|
27
27
|
|
|
28
|
-
from pyfemtet.
|
|
28
|
+
from pyfemtet._util.excel_macro_util import watch_excel_macro_error
|
|
29
|
+
|
|
30
|
+
from pyfemtet._warning import show_experimental_warning
|
|
29
31
|
|
|
30
32
|
import logging
|
|
31
33
|
|
|
@@ -33,7 +35,6 @@ logger = logging.getLogger(__name__)
|
|
|
33
35
|
logger.setLevel(logging.INFO)
|
|
34
36
|
|
|
35
37
|
|
|
36
|
-
@experimental_class
|
|
37
38
|
class ExcelInterface(FEMInterface):
|
|
38
39
|
|
|
39
40
|
input_xlsm_path: Path # 操作対象の xlsm パス
|
|
@@ -52,8 +53,8 @@ class ExcelInterface(FEMInterface):
|
|
|
52
53
|
wb_output: CDispatch # システムを構成する Workbook
|
|
53
54
|
sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
54
55
|
|
|
55
|
-
visible: bool =
|
|
56
|
-
display_alerts: bool =
|
|
56
|
+
visible: bool = False # excel を可視化するかどうか
|
|
57
|
+
display_alerts: bool = False # ダイアログを表示するかどうか
|
|
57
58
|
|
|
58
59
|
_load_problem_from_me: bool = True # TODO: add_parameter() 等を省略するかどうか。定義するだけでフラグとして機能する。
|
|
59
60
|
_excel_pid: int
|
|
@@ -69,7 +70,9 @@ class ExcelInterface(FEMInterface):
|
|
|
69
70
|
procedure_name: str = None,
|
|
70
71
|
procedure_args: list or tuple = None,
|
|
71
72
|
connect_method: str = 'auto', # or 'new'
|
|
73
|
+
procedure_timeout: float or None = None,
|
|
72
74
|
):
|
|
75
|
+
show_experimental_warning("ExcelInterface")
|
|
73
76
|
|
|
74
77
|
# 初期化
|
|
75
78
|
self.input_xlsm_path = None # あとで取得する
|
|
@@ -81,6 +84,7 @@ class ExcelInterface(FEMInterface):
|
|
|
81
84
|
assert connect_method in ['new', 'auto']
|
|
82
85
|
self.connect_method = connect_method
|
|
83
86
|
self._femtet_autosave_buffer = _get_autosave_enabled()
|
|
87
|
+
self.procedure_timeout = procedure_timeout
|
|
84
88
|
|
|
85
89
|
# dask サブプロセスのときは space 直下の input_xlsm_path を参照する
|
|
86
90
|
try:
|
|
@@ -119,8 +123,9 @@ class ExcelInterface(FEMInterface):
|
|
|
119
123
|
procedure_name=self.procedure_name,
|
|
120
124
|
procedure_args=self.procedure_args,
|
|
121
125
|
connect_method='new', # subprocess で connect する際は new を強制する
|
|
126
|
+
procedure_timeout=self.procedure_timeout,
|
|
122
127
|
)
|
|
123
|
-
|
|
128
|
+
FEMInterface.__init__(self, **kwargs)
|
|
124
129
|
|
|
125
130
|
def __del__(self):
|
|
126
131
|
try:
|
|
@@ -256,20 +261,18 @@ class ExcelInterface(FEMInterface):
|
|
|
256
261
|
|
|
257
262
|
# マクロ実行
|
|
258
263
|
try:
|
|
259
|
-
self.excel.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout):
|
|
265
|
+
self.excel.Run(
|
|
266
|
+
f'{self.procedure_name}',
|
|
267
|
+
*self.procedure_args
|
|
268
|
+
)
|
|
263
269
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
raise SolveError('Excelアップデートに失敗しました')
|
|
270
|
+
# 再計算
|
|
271
|
+
self.excel.CalculateFull()
|
|
272
|
+
|
|
273
|
+
except com_error as e:
|
|
274
|
+
raise SolveError(f'Failed to run macro {self.procedure_name}. The original message is: {e}')
|
|
270
275
|
|
|
271
|
-
# 再計算
|
|
272
|
-
self.excel.CalculateFull()
|
|
273
276
|
|
|
274
277
|
def quit(self):
|
|
275
278
|
logger.info('Excel-Femtet の終了処理を開始します。') # FIXME: message にする
|
|
@@ -290,7 +293,7 @@ class ExcelInterface(FEMInterface):
|
|
|
290
293
|
gc.collect()
|
|
291
294
|
|
|
292
295
|
# quit した後ならば femtet を終了できる
|
|
293
|
-
# excel の process
|
|
296
|
+
# excel の process の消滅を待つ
|
|
294
297
|
logger.info('Excel の終了を待っています。')
|
|
295
298
|
while self._excel_pid == _get_pid(self._excel_hwnd):
|
|
296
299
|
sleep(1)
|
|
@@ -298,12 +301,14 @@ class ExcelInterface(FEMInterface):
|
|
|
298
301
|
# 正確だが時間がかかるかも
|
|
299
302
|
logger.info('終了する Femtet を特定しています。')
|
|
300
303
|
femtet_pid = util.get_last_executed_femtet_process_id()
|
|
301
|
-
|
|
302
|
-
|
|
304
|
+
from multiprocessing import Process
|
|
305
|
+
p = Process(target=_terminate_femtet, args=(femtet_pid,))
|
|
306
|
+
p.start()
|
|
303
307
|
|
|
304
308
|
logger.info('自動保存機能の設定を元に戻しています。')
|
|
305
309
|
_set_autosave_enabled(self._femtet_autosave_buffer)
|
|
306
310
|
|
|
311
|
+
p.join()
|
|
307
312
|
logger.info('Excel-Femtet を終了しました。')
|
|
308
313
|
|
|
309
314
|
# 直接アクセスしてもよいが、ユーザーに易しい名前にするためだけのプロパティ
|
|
@@ -422,6 +427,11 @@ def wait_femtet():
|
|
|
422
427
|
Femtet = Dispatch('FemtetMacro.Femtet')
|
|
423
428
|
|
|
424
429
|
|
|
430
|
+
def _terminate_femtet(femtet_pid_):
|
|
431
|
+
CoInitialize()
|
|
432
|
+
Femtet, caught_pid = dispatch_specific_femtet(femtet_pid_)
|
|
433
|
+
_exit_or_force_terminate(timeout=3, Femtet=Femtet, force=True)
|
|
434
|
+
|
|
425
435
|
# main thread で作成した excel への参照を含む関数を
|
|
426
436
|
# 直接 thread や process に渡すと機能しない
|
|
427
437
|
class ScapeGoatObjective:
|