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.
- semapp/Layout/create_button.py +163 -51
- semapp/Plot/__init__.py +3 -3
- semapp/Plot/frame_attributes.py +255 -79
- semapp/Processing/processing.py +132 -77
- semapp/__init__.py +10 -0
- semapp/main.py +10 -1
- semapp-1.0.3.dist-info/METADATA +226 -0
- semapp-1.0.3.dist-info/RECORD +19 -0
- {semapp-1.0.1.dist-info → semapp-1.0.3.dist-info}/WHEEL +1 -1
- semapp-1.0.1.dist-info/METADATA +0 -20
- semapp-1.0.1.dist-info/RECORD +0 -19
- {semapp-1.0.1.dist-info → semapp-1.0.3.dist-info}/entry_points.txt +0 -0
- {semapp-1.0.1.dist-info → semapp-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {semapp-1.0.1.dist-info → semapp-1.0.3.dist-info}/top_level.txt +0 -0
semapp/Layout/create_button.py
CHANGED
|
@@ -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\
|
|
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
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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)
|
|
250
|
-
wafer_layout.setSpacing(5)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
self.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
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