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.

@@ -36,19 +36,20 @@ class Process:
36
36
  self.output_dir = None
37
37
  self.load_json()
38
38
  def load_json(self):
39
+
39
40
  """Load the settings data from a JSON file."""
40
41
  try:
41
42
  with open(self.scale_data, "r", encoding="utf-8") as file:
42
43
  self.settings = json.load(file)
43
- print("Settings data loaded successfully.")
44
+ pass # Settings loaded
44
45
  except FileNotFoundError:
45
- print("Settings file not found. Starting fresh.")
46
+ # Settings file not found, starting fresh
46
47
  self.settings = []
47
48
  except json.JSONDecodeError as error:
48
- print(f"JSON decoding error: {error}")
49
+ # JSON decoding error
49
50
  self.settings = []
50
51
  except OSError as error:
51
- print(f"OS error when reading file: {error}")
52
+ # OS error when reading file
52
53
  self.settings = []
53
54
  def extract_positions(self, filepath):
54
55
  '''Function to extract positions from a 001 file.'''
@@ -113,13 +114,13 @@ class Process:
113
114
  val3 = d["val3"]
114
115
  val4_scaled = d["val4"] * pitch_x - x_center
115
116
  val5_scaled = d["val5"] * pitch_y - y_center
116
-
117
+ defect_size = d["val8"]
117
118
  x_corr = round((val2 + val4_scaled) / 10000, 1)
118
119
  y_corr = round((val3 + val5_scaled) / 10000, 1)
119
120
 
120
- corrected_positions.append({"defect_id": val1, "X": x_corr,"Y": y_corr})
121
+ corrected_positions.append({"defect_id": val1, "X": x_corr,"Y": y_corr, "defect_size": defect_size})
121
122
 
122
- self.coordinates = pd.DataFrame(corrected_positions, columns=["X", "Y"])
123
+ self.coordinates = pd.DataFrame(corrected_positions, columns=["X", "Y", "defect_size"])
123
124
 
124
125
  return self.coordinates
125
126
  def rename(self):
@@ -132,7 +133,7 @@ class Process:
132
133
  self.output_dir = os.path.join(self.dirname, self.wafer_number)
133
134
 
134
135
  if not os.path.exists(self.output_dir):
135
- print(f"Directory not found: {self.output_dir}")
136
+ pass # Directory not found
136
137
  return
137
138
 
138
139
  matching_files = glob.glob(os.path.join(self.output_dir, '*.001'))
@@ -153,8 +154,6 @@ class Process:
153
154
  for file in tiff_files:
154
155
  # Extract page number from the file name (e.g., data_page_1.tiff)
155
156
  file_number = int(file.split('_')[2].split('.')[0])
156
- print(f"Processing {file}: Page number {file_number}, Total "
157
- f"settings {len(self.settings)}")
158
157
 
159
158
  # Calculate the corresponding row in the CSV
160
159
  csv_row_index = (file_number - 1) // len(self.settings)
@@ -176,7 +175,6 @@ class Process:
176
175
 
177
176
  # Rename the file
178
177
  os.rename(old_path, new_path)
179
- print(f"Renamed: {file} -> {new_name}")
180
178
 
181
179
  def split_tiff(self):
182
180
  """
@@ -193,7 +191,6 @@ class Process:
193
191
  output_files = []
194
192
  page_index = 0
195
193
  if not os.path.exists(self.tiff_path):
196
- print(f"TIFF file not found: {self.tiff_path}")
197
194
  return []
198
195
 
199
196
  try:
@@ -224,7 +221,6 @@ class Process:
224
221
  self.output_dir = os.path.join(self.dirname, self.wafer_number)
225
222
 
226
223
  if not os.path.exists(self.output_dir):
227
- print(f"Error: Directory does not exist: {self.output_dir}")
228
224
  return
229
225
 
230
226
  tiff_files = [f for f in os.listdir(self.output_dir)
@@ -235,7 +231,6 @@ class Process:
235
231
  if not file_name.startswith("data") or "page" in file_name.lower() or file_name.endswith("001"):
236
232
  file_path = os.path.join(self.output_dir, file_name)
237
233
  os.remove(file_path)
238
- print(f"Deleted: {file_path}")
239
234
 
240
235
  def split_tiff_all(self):
241
236
  """
@@ -248,13 +243,10 @@ class Process:
248
243
  for subdir, _, _ in os.walk(self.dirname):
249
244
  if subdir != self.dirname:
250
245
  self.tiff_path = os.path.join(subdir, "data.tif")
251
- print(f"Processing directory: {subdir}, "
252
- f"TIFF path: {self.tiff_path}")
253
246
 
254
247
  output_files = []
255
248
  page_index = 0
256
249
  if not os.path.exists(self.tiff_path):
257
- print(f"TIFF file not found: {self.tiff_path}")
258
250
  continue
259
251
 
260
252
  try:
@@ -287,7 +279,6 @@ class Process:
287
279
  if subdir != self.dirname:
288
280
  self.output_dir = os.path.join(self.dirname,
289
281
  os.path.basename(subdir))
290
- print(f"Renaming files in: {self.output_dir}")
291
282
 
292
283
  matching_files = glob.glob(os.path.join(self.output_dir, '*.001'))
293
284
 
@@ -307,8 +298,6 @@ class Process:
307
298
 
308
299
  for file in tiff_files:
309
300
  file_number = int(file.split('_')[2].split('.')[0])
310
- print(f"Processing {file}: Page number {file_number}, "
311
- f"Total settings {len(self.settings)}")
312
301
 
313
302
  csv_row_index = (file_number - 1) // len(self.settings)
314
303
  remainder = (file_number - 1) % len(self.settings)
@@ -324,7 +313,6 @@ class Process:
324
313
  new_path = os.path.join(self.output_dir, new_name)
325
314
 
326
315
  os.rename(old_path, new_path)
327
- print(f"Renamed: {file} -> {new_name}")
328
316
 
329
317
  def clean_all(self):
330
318
  """
@@ -337,10 +325,8 @@ class Process:
337
325
  if subdir != self.dirname:
338
326
  self.output_dir = os.path.join(self.dirname,
339
327
  os.path.basename(subdir))
340
- print(f"Cleaning directory: {self.output_dir}")
341
328
 
342
329
  if not os.path.exists(self.output_dir):
343
- print(f"Error: Directory does not exist: {self.output_dir}")
344
330
  continue
345
331
 
346
332
  tiff_files = [f for f in os.listdir(self.output_dir)
@@ -350,7 +336,6 @@ class Process:
350
336
  "page" in file_name.lower() or file_name.endswith("001"):
351
337
  file_path = os.path.join(self.output_dir, file_name)
352
338
  os.remove(file_path)
353
- print(f"Deleted: {file_path}")
354
339
 
355
340
  def organize_and_rename_files(self):
356
341
  """
@@ -359,16 +344,22 @@ class Process:
359
344
  and rename the files to 'data.tif' in their respective subfolders.
360
345
  """
361
346
  if not os.path.exists(self.dirname):
362
- print(f"Error: The folder {self.dirname} does not exist.")
363
347
  return
364
348
 
349
+ # Check if there are subdirectories
350
+ subdirs = [d for d in os.listdir(self.dirname) if
351
+ os.path.isdir(os.path.join(self.dirname, d))]
352
+
353
+ # Check if there are .tif files
354
+ tif_files = [f for f in os.listdir(self.dirname)
355
+ if f.lower().endswith(".tif") and os.path.isfile(os.path.join(self.dirname, f))]
356
+
365
357
  # Iterate through files in the directory
366
358
  for file_name in os.listdir(self.dirname):
367
359
  if file_name.lower().endswith(".tif"):
368
360
  parts = file_name.rsplit("_", 1)
369
361
  if len(parts) < 2:
370
- print(
371
- f"Skipping file with unexpected format: {file_name}")
362
+ # Skip file with unexpected format
372
363
  continue
373
364
 
374
365
  # Use the last part (before extension) as the subfolder name
@@ -383,14 +374,11 @@ class Process:
383
374
  destination_path = os.path.join(subfolder_path, "data.tif")
384
375
  shutil.move(source_path, destination_path)
385
376
 
386
- print(
387
- f"Moved and renamed: {file_name} -> {destination_path}")
388
377
 
389
378
  if file_name.lower().endswith(".001"):
390
379
  parts = file_name.rsplit("_", 1)
391
380
  if len(parts) < 2:
392
- print(
393
- f"Skipping file with unexpected format: {file_name}")
381
+ # Skip file with unexpected format
394
382
  continue
395
383
 
396
384
  # Use the last part (before extension) as the subfolder name
@@ -405,8 +393,53 @@ class Process:
405
393
  destination_path = os.path.join(subfolder_path, file_name)
406
394
  shutil.move(source_path, destination_path)
407
395
 
408
- print(
409
- f"Moved and renamed: {file_name} -> {destination_path}")
396
+
397
+ # If no subdirectories and no .tif files, create folders from KLARF files
398
+ if not subdirs and not tif_files:
399
+ wafer_ids = self.extract_wafer_ids_from_klarf()
400
+
401
+ if wafer_ids:
402
+ for wafer_id in wafer_ids:
403
+ subfolder_path = os.path.join(self.dirname, str(wafer_id))
404
+ os.makedirs(subfolder_path, exist_ok=True)
405
+
406
+ def extract_wafer_ids_from_klarf(self):
407
+ """Extract wafer IDs from KLARF files (.001) that contain COMPLUS4T."""
408
+ wafer_ids = []
409
+
410
+ if not self.dirname:
411
+ return wafer_ids
412
+
413
+ # Chercher les fichiers .001
414
+ try:
415
+ files = [f for f in os.listdir(self.dirname)
416
+ if f.endswith('.001') and os.path.isfile(os.path.join(self.dirname, f))]
417
+
418
+ for file in files:
419
+ file_path = os.path.join(self.dirname, file)
420
+ try:
421
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
422
+ content = f.read()
423
+
424
+ # Check if file contains "COMPLUS4T"
425
+ if 'COMPLUS4T' in content:
426
+ # Search for all lines with WaferID
427
+ # Pattern to extract number in quotes after WaferID
428
+ pattern = r'WaferID\s+"@(\d+)"'
429
+ matches = re.findall(pattern, content)
430
+
431
+ # Add found IDs (converted to int)
432
+ for match in matches:
433
+ wafer_id = int(match)
434
+ if wafer_id not in wafer_ids and 1 <= wafer_id <= 26:
435
+ wafer_ids.append(wafer_id)
436
+ except Exception as e:
437
+ pass # Error reading file
438
+
439
+ except Exception as e:
440
+ pass # Error listing files
441
+
442
+ return wafer_ids
410
443
 
411
444
  def rename_wo_legend_all(self):
412
445
  """
@@ -421,19 +454,15 @@ class Process:
421
454
  if file.endswith(".001"):
422
455
  folder_names.append(subdir)
423
456
 
424
- print(folder_names) # debug print
425
457
 
426
458
  # Process each folder found
427
459
  for folder in folder_names:
428
460
  for subdir, _, files in os.walk(folder):
429
461
  for file in files:
430
462
  if file.endswith(".tiff"):
431
- print(subdir)
432
- print(f"Processing: {file}")
433
463
  old_filepath = os.path.join(subdir, file)
434
464
 
435
465
  matching_files = glob.glob(os.path.join(folder, '*.001'))
436
- print("Found .001 files:", matching_files) # debug print
437
466
 
438
467
  if matching_files:
439
468
  recipe_path = matching_files[0]
@@ -442,14 +471,11 @@ class Process:
442
471
  return
443
472
 
444
473
 
445
- print(self.coordinates)
446
474
 
447
475
  try:
448
476
  defect_part = int(file.split("_")[1]) - 1
449
- print(f"Defect part index: {defect_part}")
450
477
  except (IndexError, ValueError):
451
- print(
452
- f"Skipping file due to unexpected format: {file}")
478
+ # Error parsing defect part index, skip file
453
479
  continue
454
480
 
455
481
  # Check if defect part is within the valid range
@@ -459,31 +485,28 @@ class Process:
459
485
 
460
486
 
461
487
  if defect_part >= len(self.coordinates):
462
- print(
463
- f"Skipping file {file} due to out-of-bounds "
464
- f"defect part.")
488
+ # Error: defect part out of bounds, skip file
465
489
  continue
466
490
 
467
491
  x = self.coordinates.iloc[defect_part, 0]
468
492
  y = self.coordinates.iloc[defect_part, 1]
469
- new_filename = f"{x}_{y}"
470
-
471
- # Add specific suffix based on the file type
472
- if "_Class_1_Internal" in file:
473
- new_filename += "_BSE.tif"
474
- elif "_Class_1_Topography" in file:
475
- topo_number = file.split("_Class_1_Topography")[1][
476
- 0] # Extract Topography number
477
- new_filename += f"_SE{topo_number}.tiff"
478
- else:
479
- print(
480
- f"Skipping file due to unexpected format: {file}")
481
- continue
493
+ new_filename = f"{x}_{y}.tiff"
494
+
495
+ # # Add specific suffix based on the file type
496
+ # if "_Class_1_Internal" in file:
497
+ # new_filename += "_BSE.tif"
498
+ # elif "_Class_1_Topography" in file:
499
+ # topo_number = file.split("_Class_1_merged")[1][
500
+ # 0] # Extract Topography number
501
+ # new_filename += f"_SE{topo_number}.tiff"
502
+ # else:
503
+ # print(
504
+ # f"Skipping file due to unexpected format: {file}")
505
+ # continue
482
506
 
483
507
  # Construct the new file path and rename
484
508
  new_filepath = os.path.join(subdir, new_filename)
485
509
  os.rename(old_filepath, new_filepath)
486
- print(f"Renamed: {old_filepath} -> {new_filepath}")
487
510
 
488
511
  def rename_wo_legend(self):
489
512
  """
@@ -493,27 +516,22 @@ class Process:
493
516
  """
494
517
  wafer_path = os.path.join(self.dirname, self.wafer_number)
495
518
  if not os.path.exists(wafer_path):
496
- print(f"Error: The wafer folder {wafer_path} does not exist.")
497
519
  return
498
520
 
499
521
  for subdir, _, files in os.walk(wafer_path):
500
522
  for file in files:
501
523
  if file.endswith(".tiff"):
502
- print(f"Processing: {file}")
503
524
 
504
525
  old_filepath = os.path.join(subdir, file)
505
526
 
506
527
  try:
507
528
  defect_part = int(file.split("_")[1]) - 1
508
- print(f"Defect part index: {defect_part}")
509
529
  except (IndexError, ValueError):
510
- print(
511
- f"Skipping file due to unexpected format: {file}")
530
+ # Error parsing defect part, skip file
512
531
  continue
513
532
 
514
533
  matching_files = glob.glob(os.path.join(wafer_path, '*.001'))
515
534
 
516
- print("Found .001 files:", matching_files) # debug print
517
535
 
518
536
  if matching_files:
519
537
  recipe_path = matching_files[0]
@@ -523,9 +541,7 @@ class Process:
523
541
 
524
542
  # Check if defect part is within the valid range
525
543
  if defect_part >= len(self.coordinates):
526
- print(
527
- f"Skipping file {file} "
528
- f"due to out-of-bounds defect part.")
544
+ # Error: defect part out of bounds, skip file
529
545
  continue
530
546
 
531
547
  x = self.coordinates.iloc[defect_part, 0]
@@ -540,14 +556,12 @@ class Process:
540
556
  0] # Extract Topography number
541
557
  new_filename += f"_SE{topo_number}.tiff"
542
558
  else:
543
- print(
544
- f"Skipping file due to unexpected format: {file}")
559
+ # Error: unexpected file format, skip file
545
560
  continue
546
561
 
547
562
  # Construct the new file path and rename
548
563
  new_filepath = os.path.join(subdir, new_filename)
549
564
  os.rename(old_filepath, new_filepath)
550
- print(f"Renamed: {old_filepath} -> {new_filepath}")
551
565
 
552
566
  def clean_folders_and_files(self):
553
567
  """
@@ -565,21 +579,59 @@ class Process:
565
579
  # Avoid name conflicts
566
580
  if not os.path.exists(new_path):
567
581
  os.rename(old_path, new_path)
568
- print(f"Renamed: {old_path} -> {new_path}")
569
582
  else:
570
- print(f"Conflict: {new_path} already exists. Skipped renaming.")
583
+ pass # Conflict: file already exists, skip
571
584
 
572
585
  for folder, subfolders, files in os.walk(self.dirname):
573
586
  # Delete .tiff files that contain "Raw" in their name
574
587
  for file in files:
575
588
  if file.endswith(".tiff") and "Raw" in file:
576
- print(file)
577
589
  file_path = os.path.join(folder, file)
578
590
  os.remove(file_path)
579
- print(f"Deleted: {file_path}")
591
+
592
+
593
+ def ajouter_extension_tiff(self):
594
+ """
595
+ Add .tiff extension to all files in parent folder and subfolders,
596
+ EXCEPT those ending with .001.
597
+ Uses os.walk to traverse all subfolders.
598
+ """
599
+ fichiers_renommes = 0
600
+
601
+ # Traverse all subfolders with os.walk
602
+ for root, dirs, files in os.walk(self.dirname):
603
+ for file in files:
604
+ # Ignore files ending with .001
605
+ if file.endswith('.001'):
606
+ continue
607
+
608
+ # Check if file has no extension or has different extension than .tiff
609
+ if '.' not in file or not file.endswith('.tiff'):
610
+ old_filepath = os.path.join(root, file)
611
+
612
+ # If file has no extension, add .tiff
613
+ if '.' not in file:
614
+ new_filename = file + '.tiff'
615
+ else:
616
+ # If file has different extension, replace with .tiff
617
+ base_name = file.rsplit('.', 1)[0] # Take everything except last extension
618
+ new_filename = base_name + '.tiff'
619
+
620
+ new_filepath = os.path.join(root, new_filename)
621
+
622
+ # Check that new name doesn't already exist
623
+ if not os.path.exists(new_filepath):
624
+ try:
625
+ os.rename(old_filepath, new_filepath)
626
+ fichiers_renommes += 1
627
+ except Exception as e:
628
+ pass # Error renaming file
629
+
630
+ return fichiers_renommes
631
+
580
632
 
581
633
  if __name__ == "__main__":
582
- DIRNAME = r"C:\Users\TM273821\Desktop\SEM\RAW"
634
+ DIRNAME = r"C:\Users\TM273821\Desktop\SEM\D25S2039_200_MOS2_SIO2_API"
583
635
  SCALE = r"C:\Users\TM273821\SEM\settings_data.json"
584
636
 
585
637
  processor = Process(DIRNAME, wafer=18, scale=SCALE)
@@ -588,11 +640,14 @@ if __name__ == "__main__":
588
640
  # processor.organize_and_rename_files() # Organize and rename files
589
641
  # processor.rename_wo_legend_all() # Preprocess all files in the directory
590
642
  # processor.clean_folders_and_files()
591
- processor.rename_wo_legend() # Preprocess specific wafer
643
+ # processor.rename_wo_legend() # Preprocess specific wafer
592
644
 
593
645
  # processor.split_tiff_all() # Preprocess specific wafer
594
646
  # processor.split_tiff_all() # Preprocess specific wafer
595
647
  # processor.split_tiff() # Preprocess specific wafer
596
- # processor.rename_all() # Preprocess specific wafer
648
+ processor.rename_all() # Preprocess specific wafer
649
+
650
+ # # Ajouter l'extension .tiff aux fichiers qui n'en ont pas
651
+ # processor.ajouter_extension_tiff()
597
652
 
598
653
 
semapp/__init__.py CHANGED
@@ -0,0 +1,10 @@
1
+ """
2
+ SEMapp - SEM Data Visualization Application
3
+
4
+ A PyQt5-based application for visualizing and analyzing Scanning Electron Microscope (SEM) data.
5
+ Supports both standard and COMPLUS4T KLARF file formats.
6
+ """
7
+
8
+ __version__ = "1.0.2"
9
+ __author__ = "Your Name"
10
+
semapp/main.py CHANGED
@@ -6,8 +6,13 @@ for the SEM data visualization application.
6
6
  """
7
7
 
8
8
  import sys
9
+ import os
9
10
  from PyQt5.QtCore import QTimer
10
11
  from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout
12
+
13
+ # Add parent directory to path to allow imports
14
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
15
+
11
16
  from semapp.Layout.main_window_att import LayoutFrame
12
17
  from semapp.Layout.create_button import ButtonFrame
13
18
  from semapp.Plot.frame_attributes import PlotFrame
@@ -76,7 +81,11 @@ class MainWindow(QWidget): # pylint: disable=R0903
76
81
 
77
82
 
78
83
  def main():
79
- print("SEMapp launched")
84
+ """
85
+ Main entry point for the SEMapp application.
86
+
87
+ Creates and displays the main window, then starts the Qt event loop.
88
+ """
80
89
  app = QApplication(sys.argv)
81
90
  window = MainWindow()
82
91
  window.show()