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 ADDED
@@ -0,0 +1,3 @@
1
+ from lumapi.lumapi import *
2
+
3
+
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()