|
3 | 3 | # Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates.
|
4 | 4 | # SPDX-License-Identifier: MIT
|
5 | 5 | #
|
6 |
| -# |
7 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
|
8 | 7 | # of this software and associated documentation files (the "Software"), to deal
|
9 | 8 | # in the Software without restriction, including without limitation the rights
|
|
22 | 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23 | 22 | # SOFTWARE.
|
24 | 23 |
|
| 24 | + |
| 25 | +from dataclasses import dataclass |
| 26 | +import os |
25 | 27 | from pathlib import Path
|
| 28 | +import tkinter |
| 29 | +from tkinter import filedialog |
| 30 | +from tkinter import ttk |
26 | 31 |
|
27 |
| -import ansys.aedt.core |
28 | 32 | from ansys.aedt.core import Circuit
|
29 |
| -import ansys.aedt.core.extensions |
| 33 | +from ansys.aedt.core import Desktop |
| 34 | +from ansys.aedt.core.extensions.misc import ExtensionCircuitCommon |
| 35 | +from ansys.aedt.core.extensions.misc import ExtensionCommonData |
30 | 36 | from ansys.aedt.core.extensions.misc import get_aedt_version
|
31 | 37 | from ansys.aedt.core.extensions.misc import get_arguments
|
32 | 38 | from ansys.aedt.core.extensions.misc import get_port
|
33 | 39 | from ansys.aedt.core.extensions.misc import get_process_id
|
34 | 40 | from ansys.aedt.core.extensions.misc import is_student
|
35 | 41 |
|
36 |
| -port = get_port() |
37 |
| -version = get_aedt_version() |
38 |
| -aedt_process_id = get_process_id() |
39 |
| -is_student = is_student() |
40 |
| - |
41 |
| -# Extension batch arguments |
42 |
| -extension_arguments = {"asc_file": ""} |
43 |
| -extension_description = "Import schematic to Circuit" |
44 |
| - |
45 |
| - |
46 |
| -def frontend(): # pragma: no cover |
47 |
| - import tkinter |
48 |
| - from tkinter import filedialog |
49 |
| - from tkinter import ttk |
50 |
| - |
51 |
| - import PIL.Image |
52 |
| - import PIL.ImageTk |
53 |
| - |
54 |
| - from ansys.aedt.core.extensions.misc import ExtensionTheme |
| 42 | +# Retrieve environment info |
| 43 | +PORT = get_port() |
| 44 | +VERSION = get_aedt_version() |
| 45 | +AEDT_PROCESS_ID = get_process_id() |
| 46 | +IS_STUDENT = is_student() |
55 | 47 |
|
56 |
| - master = tkinter.Tk() |
57 |
| - master.title(extension_description) |
| 48 | +# Extension batch arguments and title |
| 49 | +EXTENSION_DEFAULT_ARGUMENTS = {"file_extension": ""} |
| 50 | +EXTENSION_TITLE = "Import schematic to Circuit" |
58 | 51 |
|
59 |
| - # Detect if user closes the UI |
60 |
| - master.flag = False |
61 | 52 |
|
62 |
| - # Load the logo for the main window |
63 |
| - icon_path = Path(ansys.aedt.core.extensions.__path__[0]) / "images" / "large" / "logo.png" |
64 |
| - im = PIL.Image.open(icon_path) |
65 |
| - photo = PIL.ImageTk.PhotoImage(im) |
| 53 | +@dataclass |
| 54 | +class ImportSchematicData(ExtensionCommonData): |
| 55 | + """Data class for import schematic extension.""" |
66 | 56 |
|
67 |
| - # Set the icon for the main window |
68 |
| - master.iconphoto(True, photo) |
| 57 | + file_extension: str = EXTENSION_DEFAULT_ARGUMENTS["file_extension"] |
69 | 58 |
|
70 |
| - # Configure style for ttk buttons |
71 |
| - style = ttk.Style() |
72 |
| - theme = ExtensionTheme() |
73 | 59 |
|
74 |
| - # Apply light theme initially |
75 |
| - theme.apply_light_theme(style) |
76 |
| - master.theme = "light" |
| 60 | +class ImportSchematicExtension(ExtensionCircuitCommon): |
| 61 | + """Extension for importing schematic into Circuit.""" |
77 | 62 |
|
78 |
| - # Set background color of the window (optional) |
79 |
| - master.configure(bg=theme.light["widget_bg"]) |
80 |
| - |
81 |
| - label2 = ttk.Label(master, text="Browse file:", style="PyAEDT.TLabel") |
82 |
| - label2.grid(row=0, column=0, pady=10, padx=10) |
83 |
| - |
84 |
| - text = tkinter.Text(master, width=40, height=1) |
85 |
| - text.grid(row=0, column=1, pady=10, padx=5) |
86 |
| - text.configure(bg=theme.light["pane_bg"], foreground=theme.light["text"], font=theme.default_font) |
87 |
| - |
88 |
| - def browse_asc_folder(): |
89 |
| - inital_dir = text.get("1.0", tkinter.END).strip() |
90 |
| - filename = filedialog.askopenfilename( |
91 |
| - initialdir=Path(inital_dir).parent if inital_dir else "/", |
92 |
| - title="Select configuration file", |
93 |
| - filetypes=(("LTSPice file", "*.asc"), ("Spice file", "*.cir *.sp"), ("Qcv file", "*.qcv")), |
| 63 | + def __init__(self, withdraw: bool = False): |
| 64 | + super().__init__( |
| 65 | + EXTENSION_TITLE, |
| 66 | + theme_color="light", |
| 67 | + withdraw=withdraw, |
| 68 | + add_custom_content=False, |
| 69 | + toggle_row=1, |
| 70 | + toggle_column=2, |
94 | 71 | )
|
95 |
| - text.insert(tkinter.END, filename) |
96 |
| - |
97 |
| - b1 = ttk.Button(master, text="...", width=10, command=browse_asc_folder, style="PyAEDT.TButton") |
98 |
| - b1.grid(row=0, column=2, pady=10) |
99 |
| - |
100 |
| - def toggle_theme(): |
101 |
| - if master.theme == "light": |
102 |
| - set_dark_theme() |
103 |
| - master.theme = "dark" |
104 |
| - else: |
105 |
| - set_light_theme() |
106 |
| - master.theme = "light" |
107 |
| - |
108 |
| - def set_light_theme(): |
109 |
| - master.configure(bg=theme.light["widget_bg"]) |
110 |
| - text.configure(bg=theme.light["pane_bg"], foreground=theme.light["text"], font=theme.default_font) |
111 |
| - theme.apply_light_theme(style) |
112 |
| - change_theme_button.config(text="\u263d") # Sun icon for light theme |
113 |
| - |
114 |
| - def set_dark_theme(): |
115 |
| - master.configure(bg=theme.dark["widget_bg"]) |
116 |
| - text.configure(bg=theme.dark["pane_bg"], foreground=theme.dark["text"], font=theme.default_font) |
117 |
| - theme.apply_dark_theme(style) |
118 |
| - change_theme_button.config(text="\u2600") # Moon icon for dark theme |
119 |
| - |
120 |
| - # Create a frame for the toggle button to position it correctly |
121 |
| - button_frame = ttk.Frame(master, style="PyAEDT.TFrame", relief=tkinter.SUNKEN, borderwidth=2) |
122 |
| - button_frame.grid(row=1, column=2, pady=10, padx=10) # Place it in the second row, third column |
123 |
| - |
124 |
| - # Add the toggle theme button inside the frame |
125 |
| - change_theme_button = ttk.Button(button_frame, text="\u263d", command=toggle_theme, style="PyAEDT.TButton") |
126 |
| - change_theme_button.grid(row=0, column=0, padx=0) |
127 |
| - |
128 |
| - def callback(): |
129 |
| - master.flag = True |
130 |
| - master.asc_path_ui = text.get("1.0", tkinter.END).strip() |
131 |
| - master.destroy() |
132 |
| - |
133 |
| - b3 = ttk.Button(master, text="Import", width=40, command=callback, style="PyAEDT.TButton") |
134 |
| - b3.grid(row=1, column=1, pady=10, padx=10) |
135 |
| - |
136 |
| - tkinter.mainloop() |
137 |
| - |
138 |
| - asc_file_ui = getattr(master, "asc_path_ui", extension_arguments["asc_file"]) |
139 |
| - |
140 |
| - output_dict = {} |
141 |
| - if master.flag: |
142 |
| - output_dict = { |
143 |
| - "asc_file": asc_file_ui, |
144 |
| - } |
145 |
| - return output_dict |
| 72 | + self._text_widget = None |
| 73 | + self.add_extension_content() |
| 74 | + |
| 75 | + def add_extension_content(self): |
| 76 | + """Add UI elements for file selection and import action.""" |
| 77 | + label = ttk.Label( |
| 78 | + self.root, |
| 79 | + text="Browse file:", |
| 80 | + style="PyAEDT.TLabel", |
| 81 | + ) |
| 82 | + label.grid(row=0, column=0, padx=15, pady=10) |
146 | 83 |
|
| 84 | + self._text_widget = tkinter.Text( |
| 85 | + self.root, |
| 86 | + width=40, |
| 87 | + height=1, |
| 88 | + ) |
| 89 | + self._text_widget.grid(row=0, column=1, padx=5, pady=10) |
| 90 | + |
| 91 | + def browse_file(): |
| 92 | + current = self._text_widget.get( |
| 93 | + "1.0", |
| 94 | + tkinter.END, |
| 95 | + ).strip() |
| 96 | + initial = Path(current).parent if current else Path.home() |
| 97 | + filename = filedialog.askopenfilename( |
| 98 | + initialdir=initial, |
| 99 | + title="Select schematic file", |
| 100 | + filetypes=( |
| 101 | + ("LTSPice file", "*.asc"), |
| 102 | + ("Spice file", "*.cir *.sp"), |
| 103 | + ("Qcv file", "*.qcv"), |
| 104 | + ), |
| 105 | + ) |
| 106 | + if filename: |
| 107 | + self._text_widget.delete("1.0", tkinter.END) |
| 108 | + self._text_widget.insert(tkinter.END, filename) |
| 109 | + |
| 110 | + browse_button = ttk.Button( |
| 111 | + self.root, |
| 112 | + text="...", |
| 113 | + width=10, |
| 114 | + command=browse_file, |
| 115 | + style="PyAEDT.TButton", |
| 116 | + ) |
| 117 | + browse_button.grid(row=0, column=2, padx=10, pady=10) |
| 118 | + |
| 119 | + def callback(): |
| 120 | + file_extension = self._text_widget.get( |
| 121 | + "1.0", |
| 122 | + tkinter.END, |
| 123 | + ).strip() |
| 124 | + if not Path(file_extension).exists(): |
| 125 | + raise ValueError("File does not exist.") |
| 126 | + self.data = ImportSchematicData(file_extension=file_extension) |
| 127 | + self.root.destroy() |
| 128 | + |
| 129 | + import_button = ttk.Button( |
| 130 | + self.root, |
| 131 | + text="Import", |
| 132 | + width=40, |
| 133 | + command=callback, |
| 134 | + style="PyAEDT.TButton", |
| 135 | + ) |
| 136 | + import_button.grid(row=1, column=1, padx=10, pady=10) |
147 | 137 |
|
148 |
| -def main(extension_args): |
149 |
| - asc_file = Path(extension_args["asc_file"]) |
150 |
| - if not asc_file.exists(): |
151 |
| - raise Exception("File does not exist.") |
152 | 138 |
|
153 |
| - app = ansys.aedt.core.Desktop( |
| 139 | +def main(data: ImportSchematicData) -> bool: |
| 140 | + """Execute schematic import based on provided data.""" |
| 141 | + file_extension = Path(data.file_extension) |
| 142 | + app = Desktop( |
154 | 143 | new_desktop=False,
|
155 |
| - version=version, |
156 |
| - port=port, |
157 |
| - aedt_process_id=aedt_process_id, |
158 |
| - student_version=is_student, |
| 144 | + version=VERSION, |
| 145 | + port=PORT, |
| 146 | + aedt_process_id=AEDT_PROCESS_ID, |
| 147 | + student_version=IS_STUDENT, |
159 | 148 | )
|
| 149 | + cir = Circuit(design=file_extension.stem) |
160 | 150 |
|
161 |
| - cir = Circuit(design=asc_file.stem) |
| 151 | + if file_extension.suffix == ".asc": |
| 152 | + cir.create_schematic_from_asc_file(str(file_extension)) |
| 153 | + elif file_extension.suffix in {".sp", ".cir"}: |
| 154 | + cir.create_schematic_from_netlist(str(file_extension)) |
| 155 | + elif file_extension.suffix == ".qcv": |
| 156 | + cir.create_schematic_from_mentor_netlist(str(file_extension)) |
162 | 157 |
|
163 |
| - if asc_file.suffix == ".asc": |
164 |
| - cir.create_schematic_from_asc_file(str(asc_file)) |
165 |
| - elif asc_file.suffix in {".sp", ".cir"}: |
166 |
| - cir.create_schematic_from_netlist(str(asc_file)) |
167 |
| - elif asc_file.suffix == ".qcv": |
168 |
| - cir.create_schematic_from_mentor_netlist(str(asc_file)) |
169 |
| - if not extension_args["is_test"]: # pragma: no cover |
| 158 | + if "PYTEST_CURRENT_TEST" not in os.environ: |
170 | 159 | app.release_desktop(False, False)
|
171 | 160 | return True
|
172 | 161 |
|
173 | 162 |
|
174 | 163 | if __name__ == "__main__": # pragma: no cover
|
175 |
| - args = get_arguments(extension_arguments, extension_description) |
176 |
| - |
177 |
| - # Open UI |
178 |
| - if not args["is_batch"]: # pragma: no cover |
179 |
| - output = frontend() |
180 |
| - if output: |
181 |
| - for output_name, output_value in output.items(): |
182 |
| - if output_name in extension_arguments: |
183 |
| - args[output_name] = output_value |
184 |
| - main(args) |
| 164 | + args = get_arguments(EXTENSION_DEFAULT_ARGUMENTS, EXTENSION_TITLE) |
| 165 | + if not args.get("is_batch", False): |
| 166 | + extension = ImportSchematicExtension(withdraw=False) |
| 167 | + tkinter.mainloop() |
| 168 | + if extension.data: |
| 169 | + main(extension.data) |
185 | 170 | else:
|
186 |
| - main(args) |
| 171 | + data = ImportSchematicData() |
| 172 | + for key, value in args.items(): |
| 173 | + setattr(data, key, value) |
| 174 | + main(data) |
0 commit comments