Semapp 1.0.0__tar.gz → 1.0.2__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.

Potentially problematic release.


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

Files changed (25) hide show
  1. {semapp-1.0.0 → semapp-1.0.2}/PKG-INFO +8 -9
  2. {semapp-1.0.0 → semapp-1.0.2}/SEMapp.egg-info/PKG-INFO +8 -9
  3. semapp-1.0.2/SEMapp.egg-info/requires.txt +6 -0
  4. {semapp-1.0.0 → semapp-1.0.2}/pyproject.toml +8 -9
  5. {semapp-1.0.0 → semapp-1.0.2}/semapp/Layout/create_button.py +1 -1
  6. {semapp-1.0.0 → semapp-1.0.2}/semapp/Plot/frame_attributes.py +69 -48
  7. {semapp-1.0.0 → semapp-1.0.2}/semapp/Processing/processing.py +76 -20
  8. {semapp-1.0.0 → semapp-1.0.2}/semapp/main.py +1 -1
  9. semapp-1.0.0/SEMapp.egg-info/requires.txt +0 -6
  10. {semapp-1.0.0 → semapp-1.0.2}/LICENSE +0 -0
  11. {semapp-1.0.0 → semapp-1.0.2}/README.md +0 -0
  12. {semapp-1.0.0 → semapp-1.0.2}/SEMapp.egg-info/SOURCES.txt +0 -0
  13. {semapp-1.0.0 → semapp-1.0.2}/SEMapp.egg-info/dependency_links.txt +0 -0
  14. {semapp-1.0.0 → semapp-1.0.2}/SEMapp.egg-info/entry_points.txt +0 -0
  15. {semapp-1.0.0 → semapp-1.0.2}/SEMapp.egg-info/top_level.txt +0 -0
  16. {semapp-1.0.0 → semapp-1.0.2}/semapp/Layout/__init__.py +0 -0
  17. {semapp-1.0.0 → semapp-1.0.2}/semapp/Layout/main_window_att.py +0 -0
  18. {semapp-1.0.0 → semapp-1.0.2}/semapp/Layout/settings.py +0 -0
  19. {semapp-1.0.0 → semapp-1.0.2}/semapp/Layout/styles.py +0 -0
  20. {semapp-1.0.0 → semapp-1.0.2}/semapp/Plot/__init__.py +0 -0
  21. {semapp-1.0.0 → semapp-1.0.2}/semapp/Plot/styles.py +0 -0
  22. {semapp-1.0.0 → semapp-1.0.2}/semapp/Plot/utils.py +0 -0
  23. {semapp-1.0.0 → semapp-1.0.2}/semapp/Processing/__init__.py +0 -0
  24. {semapp-1.0.0 → semapp-1.0.2}/semapp/__init__.py +0 -0
  25. {semapp-1.0.0 → semapp-1.0.2}/setup.cfg +0 -0
@@ -1,21 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Semapp
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Package for SEM visualization
5
5
  Author-email: Thibaut Meyer <thibaut.meyer3@gmail.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/pypa/sampleproject
8
- Project-URL: Issues, https://github.com/pypa/sampleproject/issues
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/thi-mey/SEMapp
9
8
  Keywords: SEM,GUI
10
9
  Classifier: Programming Language :: Python :: 3
11
10
  Classifier: Operating System :: OS Independent
12
11
  Requires-Python: >=3.9
13
12
  Description-Content-Type: text/markdown
14
13
  License-File: LICENSE
15
- Requires-Dist: matplotlib==3.8.3
16
- Requires-Dist: numpy<2.0,>=1.21
17
- Requires-Dist: pandas>=2.2.0
18
- Requires-Dist: Pillow>=11.0.0
14
+ Requires-Dist: matplotlib==3.10.3
15
+ Requires-Dist: numpy==2.2.5
16
+ Requires-Dist: pandas==2.2.3
17
+ Requires-Dist: Pillow==11.2.1
19
18
  Requires-Dist: PyQt5==5.15.11
20
- Requires-Dist: PyQt5_sip<13.0.0,>=12.15.0
19
+ Requires-Dist: PyQt5_sip==12.17.0
21
20
  Dynamic: license-file
@@ -1,21 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Semapp
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Package for SEM visualization
5
5
  Author-email: Thibaut Meyer <thibaut.meyer3@gmail.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/pypa/sampleproject
8
- Project-URL: Issues, https://github.com/pypa/sampleproject/issues
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/thi-mey/SEMapp
9
8
  Keywords: SEM,GUI
10
9
  Classifier: Programming Language :: Python :: 3
11
10
  Classifier: Operating System :: OS Independent
12
11
  Requires-Python: >=3.9
13
12
  Description-Content-Type: text/markdown
14
13
  License-File: LICENSE
15
- Requires-Dist: matplotlib==3.8.3
16
- Requires-Dist: numpy<2.0,>=1.21
17
- Requires-Dist: pandas>=2.2.0
18
- Requires-Dist: Pillow>=11.0.0
14
+ Requires-Dist: matplotlib==3.10.3
15
+ Requires-Dist: numpy==2.2.5
16
+ Requires-Dist: pandas==2.2.3
17
+ Requires-Dist: Pillow==11.2.1
19
18
  Requires-Dist: PyQt5==5.15.11
20
- Requires-Dist: PyQt5_sip<13.0.0,>=12.15.0
19
+ Requires-Dist: PyQt5_sip==12.17.0
21
20
  Dynamic: license-file
@@ -0,0 +1,6 @@
1
+ matplotlib==3.10.3
2
+ numpy==2.2.5
3
+ pandas==2.2.3
4
+ Pillow==11.2.1
5
+ PyQt5==5.15.11
6
+ PyQt5_sip==12.17.0
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = "Semapp"
7
- version = "1.0.0"
7
+ version = "1.0.2"
8
8
  authors = [
9
9
  { name="Thibaut Meyer", email="thibaut.meyer3@gmail.com" },
10
10
  ]
@@ -16,23 +16,22 @@ keywords = [
16
16
  "GUI",
17
17
  ]
18
18
  dependencies = [
19
- "matplotlib==3.8.3",
20
- "numpy>=1.21,<2.0",
21
- "pandas>=2.2.0",
22
- "Pillow>=11.0.0",
19
+ "matplotlib==3.10.3",
20
+ "numpy==2.2.5",
21
+ "pandas==2.2.3",
22
+ "Pillow==11.2.1",
23
23
  "PyQt5==5.15.11",
24
- "PyQt5_sip>=12.15.0,<13.0.0"
24
+ "PyQt5_sip==12.17.0",
25
25
  ]
26
26
  classifiers = [
27
27
  "Programming Language :: Python :: 3",
28
28
  "Operating System :: OS Independent",
29
29
  ]
30
- license = "MIT"
30
+ license = "GPL-3.0-or-later"
31
31
  license-files = ["LICENSE*"]
32
32
 
33
33
  [project.gui-scripts]
34
34
  Semapp = "semapp.main:main"
35
35
 
36
36
  [project.urls]
37
- Homepage = "https://github.com/pypa/sampleproject"
38
- Issues = "https://github.com/pypa/sampleproject/issues"
37
+ Homepage = "https://github.com/thi-mey/SEMapp"
@@ -63,7 +63,7 @@ class ButtonFrame(QWidget):
63
63
  # Example of adding them to a layout
64
64
  self.entries = {}
65
65
  self.dirname = None
66
- # self.dirname = r"C:\Users\TM273821\Desktop\SEM\Amel"
66
+ # self.dirname = r"C:\Users\TM273821\Desktop\SEM\TEst_complus"
67
67
 
68
68
 
69
69
  max_characters = 30 # Set character limit
@@ -114,42 +114,49 @@ class PlotFrame(QWidget):
114
114
  dans_defect_list = False
115
115
 
116
116
  with open(filepath, "r", encoding="utf-8") as f:
117
- for ligne in f:
118
- ligne = ligne.strip()
119
-
120
- if ligne.startswith("SampleSize"):
121
- match = re.search(r"SampleSize\s+1\s+(\d+)", ligne)
122
- if match:
123
- data["SampleSize"] = int(match.group(1))
124
-
125
- elif ligne.startswith("DiePitch"):
126
- match = re.search(r"DiePitch\s+([0-9.]+)\s+([0-9.]+);", ligne)
127
- if match:
128
- data["DiePitch"]["X"] = float(match.group(1))
129
- data["DiePitch"]["Y"] = float(match.group(2))
130
-
131
- elif ligne.startswith("DieOrigin"):
132
- match = re.search(r"DieOrigin\s+([0-9.]+)\s+([0-9.]+);", ligne)
133
- if match:
134
- data["DieOrigin"]["X"] = float(match.group(1))
135
- data["DieOrigin"]["Y"] = float(match.group(2))
136
-
137
- elif ligne.startswith("SampleCenterLocation"):
138
- match = re.search(r"SampleCenterLocation\s+([0-9.]+)\s+([0-9.]+);", ligne)
139
- if match:
140
- data["SampleCenterLocation"]["X"] = float(match.group(1))
141
- data["SampleCenterLocation"]["Y"] = float(match.group(2))
142
-
143
- elif ligne.startswith("DefectList"):
144
- dans_defect_list = True
145
- continue
146
-
147
- elif dans_defect_list:
148
- if re.match(r"^\d+\s", ligne):
149
- valeurs = ligne.split()
150
- if len(valeurs) >= 18:
151
- defect = {f"val{i+1}": float(val) for i, val in enumerate(valeurs[:18])}
152
- data["Defects"].append(defect)
117
+ lines = f.readlines()
118
+
119
+ for i, line in enumerate(lines):
120
+ line = line.strip()
121
+
122
+ if line.startswith("SampleSize"):
123
+ match = re.search(r"SampleSize\s+1\s+(\d+)", line)
124
+ if match:
125
+ data["SampleSize"] = int(match.group(1))
126
+
127
+ elif line.startswith("DiePitch"):
128
+ match = re.search(r"DiePitch\s+([0-9.e+-]+)\s+([0-9.e+-]+);", line)
129
+ if match:
130
+ data["DiePitch"]["X"] = float(match.group(1))
131
+ data["DiePitch"]["Y"] = float(match.group(2))
132
+
133
+ elif line.startswith("DieOrigin"):
134
+ match = re.search(r"DieOrigin\s+([0-9.e+-]+)\s+([0-9.e+-]+);", line)
135
+ if match:
136
+ data["DieOrigin"]["X"] = float(match.group(1))
137
+ data["DieOrigin"]["Y"] = float(match.group(2))
138
+
139
+ elif line.startswith("SampleCenterLocation"):
140
+ match = re.search(r"SampleCenterLocation\s+([0-9.e+-]+)\s+([0-9.e+-]+);", line)
141
+ if match:
142
+ data["SampleCenterLocation"]["X"] = float(match.group(1))
143
+ data["SampleCenterLocation"]["Y"] = float(match.group(2))
144
+
145
+ elif line.startswith("DefectList"):
146
+ dans_defect_list = True
147
+ continue
148
+
149
+ elif dans_defect_list:
150
+ if re.match(r"^\d+\s", line):
151
+ value = line.split()
152
+ if len(value) >= 12:
153
+ # Vérifier si la ligne suivante a exactement 2 colonnes
154
+ if i + 1 < len(lines):
155
+ next_line = lines[i + 1].strip()
156
+ next_values = next_line.split()
157
+ if len(next_values) == 2:
158
+ defect = {f"val{i+1}": float(val) for i, val in enumerate(value[:10])}
159
+ data["Defects"].append(defect)
153
160
 
154
161
  pitch_x = data["DiePitch"]["X"]
155
162
  pitch_y = data["DiePitch"]["Y"]
@@ -164,6 +171,7 @@ class PlotFrame(QWidget):
164
171
  val3 = d["val3"]
165
172
  val4_scaled = d["val4"] * pitch_x - Xcenter
166
173
  val5_scaled = d["val5"] * pitch_y - Ycenter
174
+ defect_size = d["val9"]
167
175
 
168
176
  x_corr = round((val2 + val4_scaled) / 10000, 1)
169
177
  y_corr = round((val3 + val5_scaled) / 10000, 1)
@@ -171,11 +179,20 @@ class PlotFrame(QWidget):
171
179
  corrected_positions.append({
172
180
  "defect_id": val1,
173
181
  "X": x_corr,
174
- "Y": y_corr
182
+ "Y": y_corr,
183
+ "defect_size": defect_size
184
+
175
185
  })
176
186
 
177
187
 
178
- self.coordinates = pd.DataFrame(corrected_positions, columns=["X", "Y"])
188
+ self.coordinates = pd.DataFrame(corrected_positions, columns=["X", "Y", "defect_size"])
189
+
190
+ # Enregistrer le mapping en CSV dans le même dossier que filepath
191
+ import os
192
+ file_dir = os.path.dirname(filepath)
193
+ csv_path = os.path.join(file_dir, "mapping.csv")
194
+ self.coordinates.to_csv(csv_path, index=False)
195
+ print(f"Mapping saved to: {csv_path}")
179
196
 
180
197
  return self.coordinates
181
198
 
@@ -204,15 +221,15 @@ class PlotFrame(QWidget):
204
221
  folder_path = os.path.join(self.button_frame.folder_var_changed(),
205
222
  str(self.selected_wafer))
206
223
 
207
- # Recherche du fichier qui se termine par .001 dans le dossier
224
+ # Find the first .001 file in the selected folder
208
225
  matching_files = glob.glob(os.path.join(folder_path, '*.001'))
209
226
 
210
- # Si au moins un fichier correspond, on prend le premier
227
+ # Sort the files to ensure consistent ordering
211
228
  if matching_files:
212
229
  recipe_path = matching_files[0]
213
230
  else:
214
- recipe_path = None # Ou tu peux lever une exception ou afficher un message d’erreur
215
- # Charger les coordonnées depuis le fichier CSV (recipe)
231
+ recipe_path = None
232
+
216
233
  self.coordinates = self.extract_positions(recipe_path)
217
234
 
218
235
  tiff_path = os.path.join(folder_path, "data.tif")
@@ -223,9 +240,8 @@ class PlotFrame(QWidget):
223
240
  return
224
241
 
225
242
  self._load_tiff(tiff_path)
226
- self._update_plot() # Maintenant les coordonnées seront disponibles pour le plot
243
+ self._update_plot()
227
244
 
228
- # Pop-up stylisé
229
245
  msg = QMessageBox()
230
246
  msg.setIcon(QMessageBox.Information)
231
247
  msg.setText(f"Wafer {self.selected_wafer} opened successfully")
@@ -303,8 +319,10 @@ class PlotFrame(QWidget):
303
319
  if self.coordinates is not None:
304
320
  x_coords = self.coordinates.iloc[:, 0]
305
321
  y_coords = self.coordinates.iloc[:, 1]
322
+ defect_size = self.coordinates.iloc[:, 2]
323
+ print(f"defect_size: {defect_size}")
306
324
 
307
- # Calcul de la valeur maximale absolue parmi toutes les coordonnées
325
+ # Calculate the maximum value for scaling
308
326
  max_val = max(abs(x_coords).max(), abs(y_coords).max())
309
327
 
310
328
  if max_val <= 5:
@@ -316,14 +334,17 @@ class PlotFrame(QWidget):
316
334
  elif max_val <= 15:
317
335
  radius = 15
318
336
  else:
319
- radius = max_val # fallback pour les cas supérieurs à 30
337
+ radius = max_val # fallback for > 15
320
338
 
321
339
  self.radius = radius
322
340
 
323
- ax.scatter(x_coords, y_coords, color='blue', marker='o',
341
+ # Create color array based on defect_size (values are in scientific notation)
342
+ colors = ['red' if size > 1.0e+01 else 'blue' for size in defect_size]
343
+
344
+ ax.scatter(x_coords, y_coords, color=colors, marker='o',
324
345
  s=100, label='Positions')
325
346
 
326
- # Mise à l'échelle du graphique en fonction du radius
347
+ # Set limits based on the radius
327
348
  ax.set_xlim(-radius - 1, radius + 1)
328
349
  ax.set_ylim(-radius - 1, radius + 1)
329
350
 
@@ -113,13 +113,13 @@ class Process:
113
113
  val3 = d["val3"]
114
114
  val4_scaled = d["val4"] * pitch_x - x_center
115
115
  val5_scaled = d["val5"] * pitch_y - y_center
116
-
116
+ defect_size = d["val8"]
117
117
  x_corr = round((val2 + val4_scaled) / 10000, 1)
118
118
  y_corr = round((val3 + val5_scaled) / 10000, 1)
119
119
 
120
- corrected_positions.append({"defect_id": val1, "X": x_corr,"Y": y_corr})
120
+ corrected_positions.append({"defect_id": val1, "X": x_corr,"Y": y_corr, "defect_size": defect_size})
121
121
 
122
- self.coordinates = pd.DataFrame(corrected_positions, columns=["X", "Y"])
122
+ self.coordinates = pd.DataFrame(corrected_positions, columns=["X", "Y", "defect_size"])
123
123
 
124
124
  return self.coordinates
125
125
  def rename(self):
@@ -217,7 +217,6 @@ class Process:
217
217
  def clean(self):
218
218
  """
219
219
  Clean up the output directory by deleting any non-conforming TIFF files.
220
-
221
220
  This method deletes any files that do not follow the expected naming
222
221
  conventions (files not starting with "data" or
223
222
  containing the word "page").
@@ -467,19 +466,19 @@ class Process:
467
466
 
468
467
  x = self.coordinates.iloc[defect_part, 0]
469
468
  y = self.coordinates.iloc[defect_part, 1]
470
- new_filename = f"{x}_{y}"
471
-
472
- # Add specific suffix based on the file type
473
- if "_Class_1_Internal" in file:
474
- new_filename += "_BSE.tif"
475
- elif "_Class_1_Topography" in file:
476
- topo_number = file.split("_Class_1_Topography")[1][
477
- 0] # Extract Topography number
478
- new_filename += f"_SE{topo_number}.tiff"
479
- else:
480
- print(
481
- f"Skipping file due to unexpected format: {file}")
482
- continue
469
+ new_filename = f"{x}_{y}.tiff"
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_merged")[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
483
482
 
484
483
  # Construct the new file path and rename
485
484
  new_filepath = os.path.join(subdir, new_filename)
@@ -579,21 +578,78 @@ class Process:
579
578
  os.remove(file_path)
580
579
  print(f"Deleted: {file_path}")
581
580
 
581
+
582
+ def ajouter_extension_tiff(self):
583
+ """
584
+ Ajoute l'extension .tiff à tous les fichiers du dossier parent et ses sous-dossiers,
585
+ SAUF ceux qui se terminent par .001.
586
+ Utilise os.walk pour parcourir tous les sous-dossiers.
587
+ """
588
+ fichiers_renommes = 0
589
+
590
+ print(f"Scan du dossier parent: {self.dirname}")
591
+
592
+ # Parcourir tous les sous-dossiers avec os.walk
593
+ for root, dirs, files in os.walk(self.dirname):
594
+ print(f"\nTraitement du dossier: {root}")
595
+
596
+ for file in files:
597
+ # Ignorer les fichiers qui se terminent par .001
598
+ if file.endswith('.001'):
599
+ print(f"Fichier ignoré (extension .001): {file}")
600
+ continue
601
+
602
+ # Vérifier si le fichier n'a pas d'extension ou a une extension différente de .tiff
603
+ if '.' not in file or not file.endswith('.tiff'):
604
+ old_filepath = os.path.join(root, file)
605
+
606
+ # Si le fichier n'a pas d'extension, ajouter .tiff
607
+ if '.' not in file:
608
+ new_filename = file + '.tiff'
609
+ else:
610
+ # Si le fichier a une extension différente, remplacer par .tiff
611
+ base_name = file.rsplit('.', 1)[0] # Prendre tout sauf la dernière extension
612
+ new_filename = base_name + '.tiff'
613
+
614
+ new_filepath = os.path.join(root, new_filename)
615
+
616
+ # Vérifier que le nouveau nom n'existe pas déjà
617
+ if not os.path.exists(new_filepath):
618
+ try:
619
+ os.rename(old_filepath, new_filepath)
620
+ print(f"Extension ajoutée: '{file}' -> '{new_filename}'")
621
+ fichiers_renommes += 1
622
+ except Exception as e:
623
+ print(f"Erreur lors du renommage de '{file}': {e}")
624
+ else:
625
+ print(f"Erreur: Le fichier '{new_filename}' existe déjà, impossible de renommer '{file}'")
626
+
627
+ print(f"\n{'='*50}")
628
+ print(f"RÉSUMÉ DE L'AJOUT D'EXTENSIONS")
629
+ print(f"{'='*50}")
630
+ print(f"Fichiers renommés: {fichiers_renommes}")
631
+
632
+ return fichiers_renommes
633
+
634
+
582
635
  if __name__ == "__main__":
583
- DIRNAME = r"C:\Users\TM273821\Desktop\SEM\RAW"
636
+ DIRNAME = r"C:\Users\TM273821\Desktop\SEM\lD25S1556"
584
637
  SCALE = r"C:\Users\TM273821\SEM\settings_data.json"
585
638
 
586
639
  processor = Process(DIRNAME, wafer=18, scale=SCALE)
587
640
 
588
641
  # Process files
589
642
  # processor.organize_and_rename_files() # Organize and rename files
590
- # processor.rename_wo_legend_all() # Preprocess all files in the directory
643
+ processor.rename_wo_legend_all() # Preprocess all files in the directory
591
644
  # processor.clean_folders_and_files()
592
- processor.rename_wo_legend() # Preprocess specific wafer
645
+ # processor.rename_wo_legend() # Preprocess specific wafer
593
646
 
594
647
  # processor.split_tiff_all() # Preprocess specific wafer
595
648
  # processor.split_tiff_all() # Preprocess specific wafer
596
649
  # processor.split_tiff() # Preprocess specific wafer
597
650
  # processor.rename_all() # Preprocess specific wafer
598
651
 
652
+ # Ajouter l'extension .tiff aux fichiers qui n'en ont pas
653
+ processor.ajouter_extension_tiff()
654
+
599
655
 
@@ -52,7 +52,7 @@ class MainWindow(QWidget): # pylint: disable=R0903
52
52
  Configures the main layout, creates frames for different sections,
53
53
  and initializes the update timer.
54
54
  """
55
- self.setWindowTitle("Data Visualization")
55
+ self.setWindowTitle("SEMapp")
56
56
  self.setStyleSheet(f"background-color: {BACKGROUND_COLOR};")
57
57
 
58
58
  # Create the main layout (canvas_layout)
@@ -1,6 +0,0 @@
1
- matplotlib==3.8.3
2
- numpy<2.0,>=1.21
3
- pandas>=2.2.0
4
- Pillow>=11.0.0
5
- PyQt5==5.15.11
6
- PyQt5_sip<13.0.0,>=12.15.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes