LumAPI 1.0.0__py3-none-any.whl
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.
- lumapi/__init__.py +3 -0
- lumapi/cli.py +194 -0
- lumapi/config.json +0 -0
- lumapi/gui.py +216 -0
- lumapi/lumapi.py +626 -0
- lumapi-1.0.0.dist-info/METADATA +171 -0
- lumapi-1.0.0.dist-info/RECORD +11 -0
- lumapi-1.0.0.dist-info/WHEEL +5 -0
- lumapi-1.0.0.dist-info/entry_points.txt +3 -0
- lumapi-1.0.0.dist-info/licenses/LICENSE +674 -0
- lumapi-1.0.0.dist-info/top_level.txt +1 -0
lumapi/__init__.py
ADDED
lumapi/cli.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import importlib.util
|
|
4
|
+
import re
|
|
5
|
+
import platform
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
# --- 路径处理逻辑 ---
|
|
9
|
+
# 对于安装版,cli.py 位于 lumapi 包内部
|
|
10
|
+
LUMAPI_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
11
|
+
CONFIG_PATH = os.path.join(LUMAPI_DIR, "config.json")
|
|
12
|
+
INIT_PATH = os.path.join(LUMAPI_DIR, "__init__.py")
|
|
13
|
+
|
|
14
|
+
# 确保 __init__.py 存在
|
|
15
|
+
if not os.path.exists(INIT_PATH):
|
|
16
|
+
try:
|
|
17
|
+
with open(INIT_PATH, 'w') as f: f.write("from lumapi.lumapi import *\n")
|
|
18
|
+
except: pass
|
|
19
|
+
# ---------------------------
|
|
20
|
+
|
|
21
|
+
def get_lumapi_path(lumerical_root, version):
|
|
22
|
+
base_path = os.path.join(lumerical_root, version)
|
|
23
|
+
ansys_path = os.path.join(base_path, "Lumerical", "api", "python", "lumapi.py")
|
|
24
|
+
if os.path.exists(ansys_path): return ansys_path
|
|
25
|
+
standalone_path = os.path.join(base_path, "api", "python", "lumapi.py")
|
|
26
|
+
if os.path.exists(standalone_path): return standalone_path
|
|
27
|
+
return standalone_path
|
|
28
|
+
|
|
29
|
+
def detect_version(lumerical_root):
|
|
30
|
+
try:
|
|
31
|
+
if not os.path.exists(lumerical_root): return None
|
|
32
|
+
for item in os.listdir(lumerical_root):
|
|
33
|
+
item_path = os.path.join(lumerical_root, item)
|
|
34
|
+
if os.path.isdir(item_path) and re.match(r'^v\d{3}$', item):
|
|
35
|
+
if os.path.exists(get_lumapi_path(lumerical_root, item)):
|
|
36
|
+
return item
|
|
37
|
+
return None
|
|
38
|
+
except: return None
|
|
39
|
+
|
|
40
|
+
def detect_common_paths():
|
|
41
|
+
print("正在扫描 Lumerical 路径...", end="", flush=True)
|
|
42
|
+
common_paths = []
|
|
43
|
+
if platform.system() == "Windows":
|
|
44
|
+
import string
|
|
45
|
+
from ctypes import windll
|
|
46
|
+
drives = []
|
|
47
|
+
bitmask = windll.kernel32.GetLogicalDrives()
|
|
48
|
+
for letter in string.ascii_uppercase:
|
|
49
|
+
if bitmask & 1: drives.append(letter + ":\\")
|
|
50
|
+
bitmask >>= 1
|
|
51
|
+
for drive in drives:
|
|
52
|
+
paths = [
|
|
53
|
+
os.path.join(drive, "Program Files", "Lumerical"),
|
|
54
|
+
os.path.join(drive, "Program Files (x86)", "Lumerical"),
|
|
55
|
+
os.path.join(drive, "Lumerical"),
|
|
56
|
+
os.path.join(drive, "Program Files", "Ansys Inc"),
|
|
57
|
+
os.path.join(drive, "Program Files (x86)", "Ansys Inc")
|
|
58
|
+
]
|
|
59
|
+
for p in paths:
|
|
60
|
+
if os.path.exists(p):
|
|
61
|
+
v = detect_version(p)
|
|
62
|
+
if v: common_paths.append((p, v))
|
|
63
|
+
elif platform.system() == "Linux":
|
|
64
|
+
paths = [
|
|
65
|
+
"/opt/lumerical", "/usr/local/lumerical",
|
|
66
|
+
"/usr/ansys_inc", "/opt/ansys_inc",
|
|
67
|
+
os.path.expanduser("~/Ansys/ansys_inc"), os.path.expanduser("~/ansys_inc")
|
|
68
|
+
]
|
|
69
|
+
for p in paths:
|
|
70
|
+
if os.path.exists(p):
|
|
71
|
+
v = detect_version(p)
|
|
72
|
+
if v: common_paths.append((p, v))
|
|
73
|
+
print(" 完成。")
|
|
74
|
+
return common_paths
|
|
75
|
+
|
|
76
|
+
def validate_path(lumerical_root):
|
|
77
|
+
try:
|
|
78
|
+
if not lumerical_root: return "", ""
|
|
79
|
+
lumerical_root = os.path.abspath(lumerical_root)
|
|
80
|
+
version = detect_version(lumerical_root)
|
|
81
|
+
if not version:
|
|
82
|
+
print(f"错误:未找到有效版本 (在 {lumerical_root})")
|
|
83
|
+
return "", ""
|
|
84
|
+
lumapi_path = get_lumapi_path(lumerical_root, version)
|
|
85
|
+
if not os.path.exists(lumapi_path): return "", ""
|
|
86
|
+
|
|
87
|
+
spec = importlib.util.spec_from_file_location('lumapi', lumapi_path)
|
|
88
|
+
lumapi = importlib.util.module_from_spec(spec)
|
|
89
|
+
spec.loader.exec_module(lumapi)
|
|
90
|
+
|
|
91
|
+
if platform.system() == "Windows":
|
|
92
|
+
os.add_dll_directory(lumerical_root)
|
|
93
|
+
bin_path = os.path.join(lumerical_root, version, "bin")
|
|
94
|
+
if not os.path.exists(bin_path):
|
|
95
|
+
bin_path = os.path.join(lumerical_root, version, "Lumerical", "bin")
|
|
96
|
+
if os.path.exists(bin_path):
|
|
97
|
+
os.add_dll_directory(bin_path)
|
|
98
|
+
return lumapi_path, version
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"验证失败: {e}")
|
|
101
|
+
return "", ""
|
|
102
|
+
|
|
103
|
+
def save_config(lumerical_path, version):
|
|
104
|
+
try:
|
|
105
|
+
dir_name = os.path.dirname(CONFIG_PATH)
|
|
106
|
+
if not os.path.exists(dir_name):
|
|
107
|
+
os.makedirs(dir_name, exist_ok=True)
|
|
108
|
+
|
|
109
|
+
with open(CONFIG_PATH, 'w') as f:
|
|
110
|
+
json.dump({'lumerical_path': os.path.abspath(lumerical_path), 'version': version}, f, indent=4)
|
|
111
|
+
print(f"[成功] 配置已保存到 {CONFIG_PATH}。")
|
|
112
|
+
return True
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"[错误] 保存失败: {e}")
|
|
115
|
+
print("提示:如果安装在系统目录,可能需要管理员权限运行。")
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
# ================= 主流程 =================
|
|
119
|
+
|
|
120
|
+
def load_config():
|
|
121
|
+
try:
|
|
122
|
+
if os.path.exists(CONFIG_PATH):
|
|
123
|
+
with open(CONFIG_PATH, 'r') as f:
|
|
124
|
+
config = json.load(f)
|
|
125
|
+
return config.get('lumerical_path'), config.get('version')
|
|
126
|
+
except: pass
|
|
127
|
+
return None, None
|
|
128
|
+
|
|
129
|
+
def load_lumapi(lumerical_path, version):
|
|
130
|
+
# 直接使用 validate_path 里的逻辑来测试
|
|
131
|
+
# 或者尝试从 lumapi 包导入
|
|
132
|
+
try:
|
|
133
|
+
print(f"正在尝试加载 Lumerical API ({version})...")
|
|
134
|
+
path, ver = validate_path(lumerical_path)
|
|
135
|
+
if path:
|
|
136
|
+
print(f"\n[成功] 成功验证并加载 API: {path}")
|
|
137
|
+
else:
|
|
138
|
+
print(f"\n[失败] 无法加载 API")
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print(f"\n[错误] {e}")
|
|
141
|
+
|
|
142
|
+
def perform_configuration():
|
|
143
|
+
detected = detect_common_paths()
|
|
144
|
+
if detected:
|
|
145
|
+
print("\n检测到以下路径:")
|
|
146
|
+
for i, (p, v) in enumerate(detected):
|
|
147
|
+
print(f"{i+1}. {p} ({v})")
|
|
148
|
+
print(f"{len(detected)+1}. 手动输入")
|
|
149
|
+
try:
|
|
150
|
+
sel_str = input("选择: ").strip()
|
|
151
|
+
if not sel_str: return
|
|
152
|
+
sel = int(sel_str)
|
|
153
|
+
if 1 <= sel <= len(detected):
|
|
154
|
+
path, ver = detected[sel-1]
|
|
155
|
+
if validate_path(path)[0]:
|
|
156
|
+
save_config(path, ver)
|
|
157
|
+
return
|
|
158
|
+
except ValueError: pass
|
|
159
|
+
|
|
160
|
+
path = input("\n请输入安装根目录: ").strip()
|
|
161
|
+
if not path: return
|
|
162
|
+
p_valid, v_valid = validate_path(path)
|
|
163
|
+
if p_valid: save_config(path, v_valid)
|
|
164
|
+
|
|
165
|
+
def main():
|
|
166
|
+
print("=== Lumerical Python API 配置工具 (PyPI版) ===")
|
|
167
|
+
|
|
168
|
+
while True:
|
|
169
|
+
cfg_path, cfg_ver = load_config()
|
|
170
|
+
has_config = (cfg_path is not None)
|
|
171
|
+
|
|
172
|
+
print("\n-------------------------")
|
|
173
|
+
if has_config:
|
|
174
|
+
print(f"当前配置: {cfg_path} ({cfg_ver})")
|
|
175
|
+
print("1. [测试] 测试加载环境")
|
|
176
|
+
print("2. [配置] 重新配置路径")
|
|
177
|
+
print("3. [退出] 退出")
|
|
178
|
+
else:
|
|
179
|
+
print("当前状态: 未配置")
|
|
180
|
+
print("1. [配置] 开始配置新路径")
|
|
181
|
+
print("2. [退出] 退出")
|
|
182
|
+
|
|
183
|
+
choice = input("\n选项: ").strip()
|
|
184
|
+
|
|
185
|
+
if has_config:
|
|
186
|
+
if choice == '1': load_lumapi(cfg_path, cfg_ver)
|
|
187
|
+
elif choice == '2': perform_configuration()
|
|
188
|
+
elif choice == '3': sys.exit()
|
|
189
|
+
else:
|
|
190
|
+
if choice == '1': perform_configuration()
|
|
191
|
+
elif choice == '2': sys.exit()
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
main()
|
lumapi/config.json
ADDED
|
File without changes
|
lumapi/gui.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import tkinter.ttk as ttk
|
|
3
|
+
from tkinter import filedialog, messagebox
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import platform
|
|
7
|
+
import importlib.util
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
import shutil
|
|
11
|
+
|
|
12
|
+
class LumericalGUI:
|
|
13
|
+
def __init__(self, root):
|
|
14
|
+
self.root = root
|
|
15
|
+
self.root.title("Lumerical 接口配置工具 (PyPI版)")
|
|
16
|
+
|
|
17
|
+
# --- 路径处理核心逻辑 ---
|
|
18
|
+
# 对于安装版,gui.py 位于 lumapi 包内部
|
|
19
|
+
self.lumapi_dir = os.path.dirname(os.path.abspath(__file__))
|
|
20
|
+
self.config_path = os.path.join(self.lumapi_dir, "config.json")
|
|
21
|
+
self.init_file_path = os.path.join(self.lumapi_dir, "__init__.py")
|
|
22
|
+
|
|
23
|
+
# 确保 __init__.py 存在 (虽然通常安装包里会有)
|
|
24
|
+
if not os.path.exists(self.init_file_path):
|
|
25
|
+
try:
|
|
26
|
+
with open(self.init_file_path, 'w') as f:
|
|
27
|
+
f.write("from lumapi.lumapi import *\n")
|
|
28
|
+
except: pass
|
|
29
|
+
|
|
30
|
+
self.create_widgets()
|
|
31
|
+
self.check_config() # 检查 Lumerical 配置
|
|
32
|
+
|
|
33
|
+
def create_widgets(self):
|
|
34
|
+
# 配置列权重
|
|
35
|
+
self.root.columnconfigure(1, weight=1)
|
|
36
|
+
|
|
37
|
+
# ==================== Lumerical 部分 ====================
|
|
38
|
+
|
|
39
|
+
# --- Row 0: Lumerical 路径配置 ---
|
|
40
|
+
tk.Label(self.root, text="Lumerical路径:").grid(row=0, column=0, padx=10, pady=(15, 2), sticky="e")
|
|
41
|
+
|
|
42
|
+
self.path_var = tk.StringVar()
|
|
43
|
+
self.path_combo = ttk.Combobox(self.root, textvariable=self.path_var, width=50)
|
|
44
|
+
self.path_combo.grid(row=0, column=1, padx=5, pady=(15, 2), sticky="ew")
|
|
45
|
+
self.path_combo.bind('<KeyRelease>', lambda e: self.validate_path(self.path_var.get()))
|
|
46
|
+
self.path_combo.bind("<<ComboboxSelected>>", self.on_path_selected)
|
|
47
|
+
|
|
48
|
+
tk.Button(self.root, text="浏览...", command=self.browse_path).grid(row=0, column=2, padx=10, pady=(15, 2))
|
|
49
|
+
|
|
50
|
+
# --- Row 1: Lumerical 配置状态 ---
|
|
51
|
+
self.status_label = tk.Label(self.root, text="等待配置...", fg="gray", font=("TkDefaultFont", 9))
|
|
52
|
+
self.status_label.grid(row=1, column=0, columnspan=3, pady=0, sticky="n")
|
|
53
|
+
|
|
54
|
+
# --- Row 2: 按钮组 ---
|
|
55
|
+
btn_frame = tk.Frame(self.root)
|
|
56
|
+
btn_frame.grid(row=2, column=0, columnspan=3, pady=(15, 25))
|
|
57
|
+
|
|
58
|
+
self.verify_btn = tk.Button(btn_frame, text="保存配置", command=self.confirm_path, state=tk.DISABLED, width=15)
|
|
59
|
+
self.verify_btn.pack(side=tk.LEFT, padx=10)
|
|
60
|
+
|
|
61
|
+
self.test_btn = tk.Button(btn_frame, text="测试加载", command=self.test_load, state=tk.DISABLED, width=15)
|
|
62
|
+
self.test_btn.pack(side=tk.LEFT, padx=10)
|
|
63
|
+
|
|
64
|
+
# ================= Lumerical 路径逻辑 =================
|
|
65
|
+
def detect_common_paths(self):
|
|
66
|
+
common_paths = []
|
|
67
|
+
if platform.system() == "Windows":
|
|
68
|
+
import string
|
|
69
|
+
from ctypes import windll
|
|
70
|
+
drives = []
|
|
71
|
+
bitmask = windll.kernel32.GetLogicalDrives()
|
|
72
|
+
for letter in string.ascii_uppercase:
|
|
73
|
+
if bitmask & 1: drives.append(letter + ":\\")
|
|
74
|
+
bitmask >>= 1
|
|
75
|
+
for drive in drives:
|
|
76
|
+
potential_paths = [
|
|
77
|
+
os.path.join(drive, "Program Files", "Lumerical"),
|
|
78
|
+
os.path.join(drive, "Program Files (x86)", "Lumerical"),
|
|
79
|
+
os.path.join(drive, "Lumerical"),
|
|
80
|
+
os.path.join(drive, "Program Files", "Ansys Inc"),
|
|
81
|
+
os.path.join(drive, "Program Files (x86)", "Ansys Inc")
|
|
82
|
+
]
|
|
83
|
+
for path in potential_paths:
|
|
84
|
+
if os.path.exists(path):
|
|
85
|
+
version = self.detect_version(path)
|
|
86
|
+
if version: common_paths.append((path, version))
|
|
87
|
+
elif platform.system() == "Linux":
|
|
88
|
+
potential_paths = [
|
|
89
|
+
"/opt/lumerical", "/usr/local/lumerical",
|
|
90
|
+
"/usr/ansys_inc", "/opt/ansys_inc",
|
|
91
|
+
os.path.expanduser("~/Ansys/ansys_inc"), os.path.expanduser("~/ansys_inc")
|
|
92
|
+
]
|
|
93
|
+
for path in potential_paths:
|
|
94
|
+
if os.path.exists(path):
|
|
95
|
+
version = self.detect_version(path)
|
|
96
|
+
if version: common_paths.append((path, version))
|
|
97
|
+
return common_paths
|
|
98
|
+
|
|
99
|
+
def detect_version(self, root):
|
|
100
|
+
try:
|
|
101
|
+
if not os.path.exists(root): return None
|
|
102
|
+
for item in os.listdir(root):
|
|
103
|
+
item_path = os.path.join(root, item)
|
|
104
|
+
if os.path.isdir(item_path) and re.match(r'^v\d{3}$', item):
|
|
105
|
+
if self.get_lumapi_path_check(root, item):
|
|
106
|
+
return item
|
|
107
|
+
return None
|
|
108
|
+
except: return None
|
|
109
|
+
|
|
110
|
+
def get_lumapi_path_check(self, lumerical_root, version):
|
|
111
|
+
base = os.path.join(lumerical_root, version)
|
|
112
|
+
p1 = os.path.join(base, "Lumerical", "api", "python", "lumapi.py")
|
|
113
|
+
if os.path.exists(p1): return True
|
|
114
|
+
p2 = os.path.join(base, "api", "python", "lumapi.py")
|
|
115
|
+
if os.path.exists(p2): return True
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
def check_config(self):
|
|
119
|
+
common = self.detect_common_paths()
|
|
120
|
+
valid_config = False
|
|
121
|
+
config_path_val = None
|
|
122
|
+
|
|
123
|
+
if os.path.exists(self.config_path):
|
|
124
|
+
try:
|
|
125
|
+
with open(self.config_path, 'r') as f:
|
|
126
|
+
config = json.load(f)
|
|
127
|
+
lumerical_path = config.get('lumerical_path')
|
|
128
|
+
version = config.get('version')
|
|
129
|
+
if lumerical_path and version:
|
|
130
|
+
if self.get_lumapi_path_check(lumerical_path, version):
|
|
131
|
+
config_path_val = lumerical_path
|
|
132
|
+
valid_config = True
|
|
133
|
+
except: pass
|
|
134
|
+
|
|
135
|
+
values = [f"{p} ({v})" for p, v in common]
|
|
136
|
+
if config_path_val and not any(config_path_val in v for v in values):
|
|
137
|
+
values.insert(0, f"{config_path_val} (Config)")
|
|
138
|
+
|
|
139
|
+
self.path_combo['values'] = values
|
|
140
|
+
if values:
|
|
141
|
+
self.path_combo.current(0)
|
|
142
|
+
self.on_path_selected(None)
|
|
143
|
+
|
|
144
|
+
if valid_config:
|
|
145
|
+
self.path_var.set(config_path_val)
|
|
146
|
+
self.validate_path(config_path_val)
|
|
147
|
+
self.test_btn.config(state=tk.NORMAL)
|
|
148
|
+
else:
|
|
149
|
+
self.test_btn.config(state=tk.DISABLED)
|
|
150
|
+
|
|
151
|
+
def on_path_selected(self, event):
|
|
152
|
+
val = self.path_combo.get()
|
|
153
|
+
if " (" in val:
|
|
154
|
+
self.path_var.set(val.split(" (")[0])
|
|
155
|
+
self.validate_path(self.path_var.get())
|
|
156
|
+
|
|
157
|
+
def browse_path(self):
|
|
158
|
+
path = filedialog.askdirectory()
|
|
159
|
+
if path:
|
|
160
|
+
self.path_var.set(path)
|
|
161
|
+
self.validate_path(path)
|
|
162
|
+
|
|
163
|
+
def validate_path(self, path):
|
|
164
|
+
if not path:
|
|
165
|
+
self.status_label.config(text="请输入路径", fg="red")
|
|
166
|
+
self.verify_btn.config(state=tk.DISABLED)
|
|
167
|
+
self.test_btn.config(state=tk.DISABLED)
|
|
168
|
+
return
|
|
169
|
+
ver = self.detect_version(path)
|
|
170
|
+
if ver:
|
|
171
|
+
self.status_label.config(text=f"状态: 有效 ({ver})", fg="green")
|
|
172
|
+
self.verify_btn.config(state=tk.NORMAL)
|
|
173
|
+
# 是否启用测试按钮取决于是否已经保存,这里简化逻辑,只要有效即可尝试保存
|
|
174
|
+
else:
|
|
175
|
+
self.status_label.config(text="状态: 无效路径", fg="red")
|
|
176
|
+
self.verify_btn.config(state=tk.DISABLED)
|
|
177
|
+
self.test_btn.config(state=tk.DISABLED)
|
|
178
|
+
|
|
179
|
+
def confirm_path(self):
|
|
180
|
+
path = self.path_var.get()
|
|
181
|
+
version = self.detect_version(path)
|
|
182
|
+
try:
|
|
183
|
+
with open(self.config_path, 'w') as f:
|
|
184
|
+
json.dump({'lumerical_path': os.path.abspath(path), 'version': version}, f, indent=4)
|
|
185
|
+
|
|
186
|
+
self.status_label.config(text=f"状态: 配置已保存 ({version})", fg="blue")
|
|
187
|
+
self.verify_btn.config(state=tk.DISABLED) # 提示已保存
|
|
188
|
+
self.test_btn.config(state=tk.NORMAL)
|
|
189
|
+
|
|
190
|
+
messagebox.showinfo("成功", "配置已保存。")
|
|
191
|
+
except Exception as e:
|
|
192
|
+
messagebox.showerror("错误", f"保存配置失败: {str(e)}\n可能需要管理员权限。")
|
|
193
|
+
|
|
194
|
+
def test_load(self):
|
|
195
|
+
try:
|
|
196
|
+
# 重新加载模块以应用新配置 (如果lumapi本身有缓存)
|
|
197
|
+
# 这里简单尝试验证路径
|
|
198
|
+
from lumapi.lumapi import validate_path
|
|
199
|
+
path = self.path_var.get()
|
|
200
|
+
version = self.detect_version(path)
|
|
201
|
+
|
|
202
|
+
lumapi = validate_path(path, version)
|
|
203
|
+
if lumapi:
|
|
204
|
+
messagebox.showinfo("测试成功", f"成功加载 Lumerical API\n版本: {version}")
|
|
205
|
+
else:
|
|
206
|
+
messagebox.showerror("测试失败", "无法加载 Lumerical API")
|
|
207
|
+
except Exception as e:
|
|
208
|
+
messagebox.showerror("错误", f"测试过程出错: {str(e)}")
|
|
209
|
+
|
|
210
|
+
def main():
|
|
211
|
+
root = tk.Tk()
|
|
212
|
+
app = LumericalGUI(root)
|
|
213
|
+
root.mainloop()
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
main()
|