setupEM 0.1.2__tar.gz → 0.1.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: setupEM
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Python tool for configuration of gds2palace workflow with GUI.
5
5
  Author-email: Volker Muehlhaus <volker@muehlhaus.com>
6
6
  Requires-Python: >=3.9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "setupEM"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "Python tool for configuration of gds2palace workflow with GUI."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -17,7 +17,7 @@
17
17
  ########################################################################
18
18
 
19
19
 
20
- import sys, json, os, tempfile, pathlib, ast
20
+ import sys, json, os, tempfile, pathlib, ast, webbrowser
21
21
  from PySide6.QtWidgets import (
22
22
  QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout,
23
23
  QLabel, QLineEdit,QComboBox,QTableWidget,QHeaderView,
@@ -123,31 +123,65 @@ def simulation_ports_to_struct (simulation_ports):
123
123
  return all_ports_list
124
124
 
125
125
 
126
- '''
127
- def add_struct_to_simulation_ports (all_ports_list):
128
- # import structure from JSON to all_simulation_ports
129
-
130
- for port in all_ports_list:
131
- portnumber = port.get(portnumber)
132
- source_layernum = port.get(source_layernum)
133
- target_layername = port.get(target_layername)
134
- from_layername = port.get(from_layername)
135
- to_layername = port.get(to_layername)
136
- direction = port.get(direction)
137
- port_Z0 = port.get(port_Z0)
138
- voltage = port.get(voltage)
139
-
140
- sim_port = simulation_setup.simulation_port(portnumber,
141
- voltage,
142
- port_Z0,
143
- source_layernum,
144
- target_layername=target_layername,
145
- from_layername=from_layername,
146
- to_layername=to_layername,
147
- direction=direction)
148
-
149
- simulation_ports.add_port(sim_port)
150
- '''
126
+
127
+ # ----------------------------------------
128
+
129
+ class FileDropLineEdit(QLineEdit):
130
+ def __init__(self, allowed_extensions=None, on_file_dropped=None):
131
+ super().__init__()
132
+ self.setAcceptDrops(True)
133
+
134
+ # Example: [".png", ".txt"]
135
+ self.allowed_extensions = allowed_extensions or []
136
+
137
+ # Function to execute after a successful drop
138
+ # Signature: callback(path: str)
139
+ self.on_file_dropped = on_file_dropped
140
+
141
+ def dragEnterEvent(self, event):
142
+ if event.mimeData().hasUrls() and self._contains_valid_files(event):
143
+ event.acceptProposedAction()
144
+ else:
145
+ event.ignore()
146
+
147
+ def dragMoveEvent(self, event):
148
+ if event.mimeData().hasUrls() and self._contains_valid_files(event):
149
+ event.acceptProposedAction()
150
+ else:
151
+ event.ignore()
152
+
153
+ def dropEvent(self, event):
154
+ if event.mimeData().hasUrls():
155
+ files = [url.toLocalFile() for url in event.mimeData().urls()]
156
+ valid_files = [f for f in files if self._is_valid(f)]
157
+
158
+ if valid_files:
159
+ file_path = valid_files[0]
160
+ self.setText(file_path)
161
+ event.acceptProposedAction()
162
+
163
+ # 🚀 Call the user function if set
164
+ if self.on_file_dropped:
165
+ self.on_file_dropped(file_path)
166
+
167
+ else:
168
+ self.setText("Invalid file type")
169
+ event.ignore()
170
+
171
+ def _contains_valid_files(self, event):
172
+ for url in event.mimeData().urls():
173
+ if self._is_valid(url.toLocalFile()):
174
+ return True
175
+ return False
176
+
177
+ def _is_valid(self, path):
178
+ if not self.allowed_extensions:
179
+ return True
180
+ ext = os.path.splitext(path)[1].lower()
181
+ return ext in self.allowed_extensions
182
+
183
+
184
+
151
185
 
152
186
  # ---------- FILE INPUT TAB ----------
153
187
  class FileInputTab(QWidget):
@@ -165,7 +199,8 @@ class FileInputTab(QWidget):
165
199
  self.gds_layout = QVBoxLayout()
166
200
 
167
201
  self.gds_file_layout = QHBoxLayout()
168
- self.gds_file_edit = QLineEdit("Please choose a file ===>")
202
+ self.gds_file_edit = FileDropLineEdit([".gds",".GDS"], self.set_gds_file)
203
+ self.gds_file_edit.setText( "Please choose a file ===>")
169
204
  self.gds_file_edit.setStyleSheet(EDIT_STYLE_REQUIRED)
170
205
 
171
206
  self.browse_gds_btn = QPushButton("Browse ...")
@@ -222,7 +257,8 @@ class FileInputTab(QWidget):
222
257
  self.XML_layout = QVBoxLayout()
223
258
 
224
259
  self.XML_file_layout = QHBoxLayout()
225
- self.XML_file_edit = QLineEdit("Please choose a file ===>")
260
+ self.XML_file_edit = FileDropLineEdit([".xml",".XML"], self.set_XML_file)
261
+ self.XML_file_edit.setText("Please choose a file ===>")
226
262
  self.XML_file_edit.setStyleSheet(EDIT_STYLE_REQUIRED)
227
263
 
228
264
  self.browse_XML_btn = QPushButton("Browse ...")
@@ -248,13 +284,18 @@ class FileInputTab(QWidget):
248
284
  previous_directory = os.path.dirname(previous_file)
249
285
  if not os.path.isdir(previous_directory):
250
286
  previous_directory = ""
287
+
288
+ filename, _ = QFileDialog.getOpenFileName(self, "Select GDSII File",previous_directory,"*.gds;;*.*")
289
+ if filename:
290
+ self.set_gds_file(filename)
251
291
 
252
- file_path, _ = QFileDialog.getOpenFileName(self, "Select GDSII File",previous_directory,"*.gds;;*.*")
253
- if file_path:
254
- self.gds_file_edit.setText(file_path)
255
- saved_values ["GdsFile"] = file_path.replace('\\','/')
256
292
 
257
-
293
+ def set_gds_file(self, filename):
294
+ # clear model name and target dir if they were auto-generated from previous model
295
+ self.MainWindow.clear_modelname_and_targetdir()
296
+ self.gds_file_edit.setText(filename)
297
+ saved_values ["GdsFile"] = filename.replace('\\','/')
298
+ # file is read when leaving the files tab
258
299
 
259
300
  def browse_XML_file(self):
260
301
  # start browsing from previous file location, if valid
@@ -265,9 +306,13 @@ class FileInputTab(QWidget):
265
306
 
266
307
  filename, _ = QFileDialog.getOpenFileName(self, "Select XML Stackup File",previous_directory,"*.xml;;*.*")
267
308
  if filename:
268
- self.XML_file_edit.setText(filename)
269
- saved_values ["SubstrateFile"] = filename.replace('\\','/')
270
- # file is read when leaving this tab
309
+ self.set_XML_file(filename)
310
+
311
+
312
+ def set_XML_file (self, filename):
313
+ self.XML_file_edit.setText(filename)
314
+ saved_values ["SubstrateFile"] = filename.replace('\\','/')
315
+ # file is read when leaving the files tab
271
316
 
272
317
 
273
318
  def load_values(self):
@@ -1734,6 +1779,8 @@ class CodeEditor(QPlainTextEdit):
1734
1779
 
1735
1780
 
1736
1781
  # ---------- MAIN WINDOW ----------
1782
+
1783
+
1737
1784
  class MainWindow(QMainWindow):
1738
1785
  TAB_HEADER_COLORS = ["#FFCDD2", "#C8E6C9", "#BBDEFB", "#FFF9C4", "#D1C4E9"]
1739
1786
 
@@ -1823,6 +1870,18 @@ class MainWindow(QMainWindow):
1823
1870
  file_menu.addSeparator()
1824
1871
  file_menu.addAction(exit_action)
1825
1872
 
1873
+ help_menu = menu_bar.addMenu("&Help")
1874
+ self.web_gds2palace_action = QAction("gds2palace", self)
1875
+ self.web_manual_action = QAction("Documentation", self)
1876
+ self.web_examples_action = QAction("Examples", self)
1877
+ self.web_gds2palace_action.triggered.connect(lambda: webbrowser.open("https://github.com/VolkerMuehlhaus/gds2palace_ihp_sg13g2"))
1878
+ self.web_manual_action.triggered.connect(lambda: webbrowser.open("https://github.com/VolkerMuehlhaus/gds2palace_ihp_sg13g2/tree/main/doc"))
1879
+ self.web_examples_action.triggered.connect(lambda: webbrowser.open("https://github.com/VolkerMuehlhaus/gds2palace_ihp_sg13g2/tree/main/workflow"))
1880
+ help_menu.addAction(self.web_gds2palace_action)
1881
+ help_menu.addAction(self.web_manual_action)
1882
+ help_menu.addAction(self.web_examples_action)
1883
+
1884
+
1826
1885
  # ---------- Menu actions ----------
1827
1886
 
1828
1887
 
@@ -2021,7 +2080,6 @@ class MainWindow(QMainWindow):
2021
2080
  QMessageBox.information(self, f"Could not load file {file_path}")
2022
2081
 
2023
2082
 
2024
-
2025
2083
  def save_user_inputs_to_file(self, filename):
2026
2084
  # make sure all tabs save their values
2027
2085
  self.save_all_tabs()
@@ -2066,6 +2124,16 @@ class MainWindow(QMainWindow):
2066
2124
  QMessageBox.warning(self, "Error", f"Failed to export model code to {file_path}: {e}")
2067
2125
 
2068
2126
 
2127
+ def clear_modelname_and_targetdir(self):
2128
+ # clear model name in output settings, to avoid overwriting when changing data
2129
+ saved_values['model_basename']=''
2130
+ # clear target directory if is the gds directory, but keep if other value
2131
+ gds_dir = os.path.dirname(saved_values['GdsFile'])
2132
+ target_dir = saved_values['sim_path']
2133
+ if target_dir.upper()==gds_dir.upper():
2134
+ saved_values['sim_path']=''
2135
+
2136
+
2069
2137
 
2070
2138
  def parse_assignments(file_path):
2071
2139
  # parse lines from a Python model code for variable assigments
@@ -2132,12 +2200,11 @@ def parse_python_ports_definitions (file_path):
2132
2200
 
2133
2201
  def main():
2134
2202
  app = QApplication(sys.argv)
2203
+
2135
2204
  if sys.platform.startswith("win"):
2136
- app.setStyle("Windows")
2137
- else:
2138
- # macOS + Linux
2139
- app.setStyle("Fusion")
2140
- win = MainWindow()
2205
+ app.setStyle(QStyleFactory.create("Windows"))
2206
+
2207
+ win = MainWindow()
2141
2208
  win.show()
2142
2209
  sys.exit(app.exec())
2143
2210
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: setupEM
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Python tool for configuration of gds2palace workflow with GUI.
5
5
  Author-email: Volker Muehlhaus <volker@muehlhaus.com>
6
6
  Requires-Python: >=3.9
File without changes
File without changes
File without changes