Semapp 1.0.1__py3-none-any.whl → 1.0.3__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.

Potentially problematic release.


This version of Semapp might be problematic. Click here for more details.

@@ -5,10 +5,11 @@
5
5
  import sys
6
6
  import os
7
7
  import time
8
+ import re
8
9
  from PyQt5.QtWidgets import QApplication
9
10
  from PyQt5.QtWidgets import (
10
11
  QWidget, QButtonGroup, QPushButton, QLabel, QGroupBox, QGridLayout,
11
- QFileDialog, QProgressDialog, QRadioButton, QSizePolicy)
12
+ QFileDialog, QProgressDialog, QRadioButton, QSizePolicy, QSlider)
12
13
  from PyQt5.QtGui import QFont
13
14
  from PyQt5.QtCore import Qt
14
15
  from semapp.Processing.processing import Process
@@ -40,6 +41,10 @@ class ButtonFrame(QWidget):
40
41
  self.selected_image = None
41
42
  self.table_data = None
42
43
  self.table_vars = None
44
+ self.image_slider = None # Slider for COMPLUS4T mode
45
+ self.slider_value = 0 # Current slider value
46
+ self.image_group_box = None # Store reference to image type group box
47
+ self.plot_frame = None # Reference to plot frame for updates
43
48
 
44
49
  self.rename = QRadioButton("Rename")
45
50
  self.split_rename = QRadioButton("Split .tif and rename (w/ tag)")
@@ -63,7 +68,7 @@ class ButtonFrame(QWidget):
63
68
  # Example of adding them to a layout
64
69
  self.entries = {}
65
70
  self.dirname = None
66
- # self.dirname = r"C:\Users\TM273821\Desktop\SEM\Amel"
71
+ # self.dirname = r"C:\Users\TM273821\Desktop\SEM\TEst_complus"
67
72
 
68
73
 
69
74
  max_characters = 30 # Set character limit
@@ -91,9 +96,6 @@ class ButtonFrame(QWidget):
91
96
  """Create the directory if it does not exist."""
92
97
  if not os.path.exists(path):
93
98
  os.makedirs(path)
94
- print(f"Directory created: {path}")
95
- else:
96
- print(f"Directory already exists: {path}")
97
99
 
98
100
  def init_ui(self):
99
101
  """Initialize the user interface"""
@@ -169,6 +171,7 @@ class ButtonFrame(QWidget):
169
171
  """Method to select folder and update checkbuttons"""
170
172
  self.select_folder()
171
173
  self.update_wafer()
174
+ self.image_radiobuttons() # Refresh image type widget (radio buttons or slider)
172
175
 
173
176
  def update_wafer(self):
174
177
  """Update the appearance of radio buttons based on the existing
@@ -178,11 +181,16 @@ class ButtonFrame(QWidget):
178
181
  subdirs = [d for d in os.listdir(self.dirname) if
179
182
  os.path.isdir(os.path.join(self.dirname, d))]
180
183
 
184
+ # Si il n'y a pas de sous-dossiers, chercher les wafers dans les fichiers KLARF
185
+ wafer_ids = []
186
+ if not subdirs:
187
+ wafer_ids = self.extract_wafer_ids_from_klarf()
188
+
181
189
  # Update the style of radio buttons based on the subdirectory presence
182
190
  for number in range(1, 27):
183
191
  radio_button = self.radio_vars.get(number)
184
192
  if radio_button:
185
- if str(number) in subdirs:
193
+ if str(number) in subdirs or number in wafer_ids:
186
194
  radio_button.setStyleSheet(WAFER_BUTTON_EXISTING_STYLE)
187
195
  else:
188
196
  radio_button.setStyleSheet(WAFER_BUTTON_MISSING_STYLE)
@@ -192,6 +200,44 @@ class ButtonFrame(QWidget):
192
200
  radio_button = self.radio_vars.get(number)
193
201
  radio_button.setStyleSheet(WAFER_BUTTON_MISSING_STYLE)
194
202
 
203
+ def extract_wafer_ids_from_klarf(self):
204
+ """Extract wafer IDs from KLARF files (.001) that contain COMPLUS4T."""
205
+ wafer_ids = []
206
+
207
+ if not self.dirname:
208
+ return wafer_ids
209
+
210
+ # Chercher les fichiers .001
211
+ try:
212
+ files = [f for f in os.listdir(self.dirname)
213
+ if f.endswith('.001') and os.path.isfile(os.path.join(self.dirname, f))]
214
+
215
+ for file in files:
216
+ file_path = os.path.join(self.dirname, file)
217
+ try:
218
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
219
+ content = f.read()
220
+
221
+ # Check if file contains "COMPLUS4T"
222
+ if 'COMPLUS4T' in content:
223
+ # Search for all lines with WaferID
224
+ # Pattern to extract number in quotes after WaferID
225
+ pattern = r'WaferID\s+"@(\d+)"'
226
+ matches = re.findall(pattern, content)
227
+
228
+ # Add found IDs (converted to int)
229
+ for match in matches:
230
+ wafer_id = int(match)
231
+ if wafer_id not in wafer_ids and 1 <= wafer_id <= 26:
232
+ wafer_ids.append(wafer_id)
233
+ except Exception as e:
234
+ pass # Error reading file
235
+
236
+ except Exception as e:
237
+ pass # Error listing files
238
+
239
+ return wafer_ids
240
+
195
241
  def create_wafer(self):
196
242
  """Create a grid of radio buttons for wafer slots with exclusive selection."""
197
243
  group_box = QGroupBox("Wafer Slots") # Add a title to the group
@@ -237,59 +283,125 @@ class ButtonFrame(QWidget):
237
283
  return self.selected_option
238
284
 
239
285
  def image_radiobuttons(self):
240
- """Create a grid of radio buttons for wafer slots with exclusive selection."""
241
- self.table_data = self.settings_window.get_table_data()
242
- print(self.table_data)
243
- number = len(self.table_data)
244
-
245
- group_box = QGroupBox("Image type") # Add a title to the group
246
- group_box.setStyleSheet(GROUP_BOX_STYLE) # Style the title
247
-
286
+ """Create a grid of radio buttons or slider for image type selection."""
287
+ # Remove old widget if it exists
288
+ if self.image_group_box is not None:
289
+ self.layout.removeWidget(self.image_group_box)
290
+ self.image_group_box.deleteLater()
291
+ self.image_group_box = None
292
+
293
+ # Reset variables
294
+ self.image_slider = None
295
+ self.table_vars = None
296
+ self.slider_value = 0
297
+
298
+ # Check if COMPLUS4T mode
299
+ is_complus4t = self._check_complus4t_in_dirname()
300
+
301
+ # Change title based on mode
302
+ title = "Defect Size (nm)" if is_complus4t else "Image type"
303
+ group_box = QGroupBox(title)
304
+ group_box.setStyleSheet(GROUP_BOX_STYLE)
305
+ self.image_group_box = group_box
306
+
248
307
  wafer_layout = QGridLayout()
249
- wafer_layout.setContentsMargins(2, 20, 2, 2) # Reduce internal margins
250
- wafer_layout.setSpacing(5) # Reduce spacing between widgets
251
-
252
- self.table_vars = {} # Store references to radio buttons
253
-
254
- # Add radio buttons from 1 to 24, with 12 radio buttons per row
255
- for i in range(number):
256
- label = str(self.table_data[i]["Scale"]) + " - " + str(
257
- self.table_data[i]["Image Type"])
258
- radio_button = QRadioButton(label)
259
- radio_button.setStyleSheet(WAFER_BUTTON_DEFAULT_STYLE)
260
-
261
- # Connect the radio button to a handler for exclusive selection
262
- radio_button.toggled.connect(self.get_selected_image)
263
- self.table_vars[i] = radio_button
264
-
265
- # Calculate the row and column for each radio button in the layout
266
- row = (i) // 3 # Row starts at 1 after the label
267
- col = (i) % 3 # Column ranges from 0 to 11
268
-
269
- wafer_layout.addWidget(radio_button, row, col)
270
-
308
+ wafer_layout.setContentsMargins(2, 20, 2, 2)
309
+ wafer_layout.setSpacing(5)
310
+
311
+ if is_complus4t:
312
+ # COMPLUS4T mode: create slider from 0 to 100 nm
313
+ self.image_slider = QSlider(Qt.Horizontal)
314
+ self.image_slider.setMinimum(0)
315
+ self.image_slider.setMaximum(100)
316
+ self.image_slider.setValue(0)
317
+ self.image_slider.setTickPosition(QSlider.TicksBelow)
318
+ self.image_slider.setTickInterval(10)
319
+ self.image_slider.valueChanged.connect(self.on_slider_changed)
320
+
321
+ self.slider_value_label = QLabel("0 nm")
322
+ self.slider_value_label.setStyleSheet("color: black; font-size: 20px; font-weight: bold;")
323
+
324
+ wafer_layout.addWidget(self.image_slider, 0, 0, 1, 2)
325
+ wafer_layout.addWidget(self.slider_value_label, 1, 0, 1, 2)
326
+ else:
327
+ # Normal mode: create radio buttons
328
+ self.table_data = self.settings_window.get_table_data()
329
+ number = len(self.table_data)
330
+
331
+ self.table_vars = {}
332
+
333
+ for i in range(number):
334
+ label = str(self.table_data[i]["Scale"]) + " - " + str(
335
+ self.table_data[i]["Image Type"])
336
+ radio_button = QRadioButton(label)
337
+ radio_button.setStyleSheet(WAFER_BUTTON_DEFAULT_STYLE)
338
+ radio_button.toggled.connect(self.get_selected_image)
339
+ self.table_vars[i] = radio_button
340
+
341
+ row = (i) // 3
342
+ col = (i) % 3
343
+ wafer_layout.addWidget(radio_button, row, col)
344
+
271
345
  group_box.setLayout(wafer_layout)
272
-
273
- # Add the QGroupBox to the main layout
274
346
  self.layout.addWidget(group_box, 1, 4, 1, 1)
347
+
348
+ def _check_complus4t_in_dirname(self):
349
+ """Check if COMPLUS4T is present in .001 files in dirname."""
350
+ if not self.dirname or not os.path.exists(self.dirname):
351
+ return False
352
+
353
+ try:
354
+ files = [f for f in os.listdir(self.dirname)
355
+ if f.endswith('.001') and os.path.isfile(os.path.join(self.dirname, f))]
356
+
357
+ for file in files:
358
+ file_path = os.path.join(self.dirname, file)
359
+ try:
360
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
361
+ content = f.read()
362
+ if 'COMPLUS4T' in content:
363
+ return True
364
+ except Exception as e:
365
+ pass # Error reading file
366
+ except Exception as e:
367
+ pass # Error listing files
368
+
369
+ return False
370
+
371
+ def on_slider_changed(self, value):
372
+ """Handle slider value changes and trigger plot update."""
373
+ self.slider_value = value
374
+ self.slider_value_label.setText(f"{value} nm")
375
+
376
+ # Trigger plot refresh if plot_frame exists
377
+ if self.plot_frame is not None:
378
+ self.plot_frame._update_plot()
275
379
 
276
380
  def refresh_radiobuttons(self):
277
381
  """Recreates the radio buttons after updating the data in Settings."""
278
382
  self.image_radiobuttons() # Call your method to recreate the radio buttons
279
383
 
280
384
  def get_selected_image(self):
281
- """Track the selected radio button."""
282
- selected_number = None # Variable to store the selected radio button number
283
- n_types= len(self.table_vars.items())
284
- # Iterate over all radio buttons
285
- for number, radio_button in self.table_vars.items():
286
-
287
- if radio_button.isChecked():
288
- selected_number = number # Track the currently selected radio button
289
-
290
- if selected_number is not None:
291
- self.selected_image = selected_number # Store the selected option for further use
292
- return self.selected_image, n_types
385
+ """Track the selected radio button or slider value."""
386
+ # Check if in COMPLUS4T mode (slider)
387
+ if self.image_slider is not None:
388
+ # Return slider value (0-100) as image type
389
+ return self.slider_value, 1
390
+
391
+ # Normal mode: radio buttons
392
+ selected_number = None
393
+ if self.table_vars:
394
+ n_types = len(self.table_vars.items())
395
+ # Iterate over all radio buttons
396
+ for number, radio_button in self.table_vars.items():
397
+ if radio_button.isChecked():
398
+ selected_number = number
399
+
400
+ if selected_number is not None:
401
+ self.selected_image = selected_number
402
+ return self.selected_image, n_types
403
+
404
+ return None
293
405
 
294
406
  def create_radiobuttons(self):
295
407
  """Create radio buttons for tools and a settings button."""
@@ -444,7 +556,7 @@ class ButtonFrame(QWidget):
444
556
  QApplication.processEvents() # Ensures the interface is updated
445
557
  task_function(*args, **kwargs)
446
558
  elapsed_time = time.time() - start_time
447
- print(f"{task_name} completed in {elapsed_time:.2f} seconds.")
559
+ pass # Task completed
448
560
 
449
561
  if self.split_rename.isChecked():
450
562
  execute_with_timer("Cleaning of folders", sem_class.clean)
semapp/Plot/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Plot package initialization.
3
- Contains SEM image processing functionality.
3
+ Contains plotting and visualization functionality for SEM data.
4
4
  """
5
5
 
6
- from .utils import*
6
+ from .utils import *
7
7
 
8
- __all__ = ['Process']
8
+ __all__ = []