PaIRS-UniNa 0.2.4__cp311-cp311-macosx_10_9_universal2.whl → 0.2.6__cp311-cp311-macosx_10_9_universal2.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 PaIRS-UniNa might be problematic. Click here for more details.

Files changed (60) hide show
  1. PaIRS_UniNa/Changes.txt +35 -0
  2. PaIRS_UniNa/Custom_Top.py +1 -1
  3. PaIRS_UniNa/Explorer.py +3063 -3049
  4. PaIRS_UniNa/FolderLoop.py +371 -371
  5. PaIRS_UniNa/Input_Tab.py +717 -709
  6. PaIRS_UniNa/Input_Tab_CalVi.py +4 -4
  7. PaIRS_UniNa/Input_Tab_tools.py +3018 -3009
  8. PaIRS_UniNa/Output_Tab.py +2 -2
  9. PaIRS_UniNa/PaIRS.py +17 -17
  10. PaIRS_UniNa/PaIRS_PIV.py +56 -1
  11. PaIRS_UniNa/PaIRS_pypacks.py +323 -60
  12. PaIRS_UniNa/Process_Tab.py +8 -13
  13. PaIRS_UniNa/Process_Tab_Disp.py +9 -4
  14. PaIRS_UniNa/Saving_tools.py +277 -277
  15. PaIRS_UniNa/TabTools.py +63 -21
  16. PaIRS_UniNa/Vis_Tab.py +293 -115
  17. PaIRS_UniNa/Whatsnew.py +13 -0
  18. PaIRS_UniNa/_PaIRS_PIV.so +0 -0
  19. PaIRS_UniNa/__init__.py +3 -3
  20. PaIRS_UniNa/gPaIRS.py +3825 -3600
  21. PaIRS_UniNa/icons/flaticon_PaIRS_download_warning.png +0 -0
  22. PaIRS_UniNa/icons/pylog.png +0 -0
  23. PaIRS_UniNa/icons/python_warning.png +0 -0
  24. PaIRS_UniNa/icons/queue.png +0 -0
  25. PaIRS_UniNa/icons/uninitialized.png +0 -0
  26. PaIRS_UniNa/icons/window.png +0 -0
  27. PaIRS_UniNa/listLib.py +301 -301
  28. PaIRS_UniNa/parForMulti.py +433 -433
  29. PaIRS_UniNa/parForWorkers.py +46 -1
  30. PaIRS_UniNa/pivParFor.py +1 -1
  31. PaIRS_UniNa/procTools.py +17 -7
  32. PaIRS_UniNa/rqrdpckgs.txt +9 -0
  33. PaIRS_UniNa/stereo.py +683 -683
  34. PaIRS_UniNa/stereoPivParFor.py +1 -1
  35. PaIRS_UniNa/tabSplitter.py +606 -606
  36. PaIRS_UniNa/ui_Calibration_Tab.py +542 -542
  37. PaIRS_UniNa/ui_Custom_Top.py +294 -294
  38. PaIRS_UniNa/ui_Input_Tab.py +1098 -1098
  39. PaIRS_UniNa/ui_Input_Tab_CalVi.py +1280 -1280
  40. PaIRS_UniNa/ui_Log_Tab.py +261 -261
  41. PaIRS_UniNa/ui_Output_Tab.py +2360 -2360
  42. PaIRS_UniNa/ui_Process_Tab.py +3808 -3808
  43. PaIRS_UniNa/ui_Process_Tab_CalVi.py +1547 -1547
  44. PaIRS_UniNa/ui_Process_Tab_Disp.py +1139 -968
  45. PaIRS_UniNa/ui_Process_Tab_Min.py +435 -435
  46. PaIRS_UniNa/ui_ResizePopup.py +203 -203
  47. PaIRS_UniNa/ui_Vis_Tab.py +1626 -1533
  48. PaIRS_UniNa/ui_Vis_Tab_CalVi.py +1249 -1249
  49. PaIRS_UniNa/ui_Whatsnew.py +131 -131
  50. PaIRS_UniNa/ui_gPairs.py +873 -849
  51. PaIRS_UniNa/ui_infoPaIRS.py +550 -428
  52. PaIRS_UniNa/whatsnew.txt +4 -4
  53. {PaIRS_UniNa-0.2.4.dist-info → pairs_unina-0.2.6.dist-info}/METADATA +47 -30
  54. {PaIRS_UniNa-0.2.4.dist-info → pairs_unina-0.2.6.dist-info}/RECORD +56 -54
  55. {PaIRS_UniNa-0.2.4.dist-info → pairs_unina-0.2.6.dist-info}/WHEEL +1 -1
  56. PaIRS_UniNa/icons/order.png +0 -0
  57. PaIRS_UniNa/icons/order_reverse.png +0 -0
  58. PaIRS_UniNa/icons/run_piv.png +0 -0
  59. PaIRS_UniNa-0.2.4.dist-info/LICENSE +0 -19
  60. {PaIRS_UniNa-0.2.4.dist-info → pairs_unina-0.2.6.dist-info}/top_level.txt +0 -0
@@ -15,11 +15,14 @@ basefold_DEBUG='./'
15
15
  basefold_DEBUG_VIS=''
16
16
  #basefold='B:/dl/apairs/jetcross'
17
17
 
18
+
18
19
  developerIDs={
19
20
  'GP_Win_Office': '231128824800632', #'0x7824af430781',
20
21
  'GP_Win_Office_New': '140626882900161', #'0x7824af430781',
21
22
  'GP_Mac_Laptop': 'V94LRP93FV', #'0xa275dd445ab0',
23
+ 'GP_WSL' : 'b44ec1c0e5a74ffd97bb050c39ef6cb1',
22
24
  'TA_Win_Office': '160983906000941', #'0xccb0da8c896e'
25
+ 'TA_Win_Office_New': '231128824801036', #??
23
26
  }
24
27
 
25
28
  import psutil,subprocess
@@ -28,9 +31,32 @@ def getCurrentID():
28
31
  serial_number=None
29
32
  try:
30
33
  if psutil.LINUX:
34
+ def get_linux_serial():
35
+ from pathlib import Path
36
+ candidates = [
37
+ Path("/sys/class/dmi/id/board_serial"),
38
+ Path("/sys/class/dmi/id/product_uuid"),
39
+ Path("/etc/machine-id"), Path("/var/lib/dbus/machine-id") #WSL
40
+ ]
41
+ for p in candidates:
42
+ try:
43
+ if p.is_file():
44
+ val = p.read_text(errors="ignore").strip()
45
+ if val and val.lower() not in {
46
+ "none", "unknown", "not specified", "to be filled by o.e.m."
47
+ }:
48
+ return val
49
+ except Exception as e:
50
+ if Flag_DEBUG:
51
+ print(f"Error while retrieving motherboard serial number: {e}")
52
+ continue
53
+ return None
54
+ serial_number = get_linux_serial()
55
+ """"
31
56
  # On Linux, the motherboard serial number can be obtained from the /sys/class/dmi/id/board_serial file
32
57
  with open('/sys/class/dmi/id/board_serial', 'r') as f:
33
58
  serial_number = f.read().strip()
59
+ """
34
60
  elif psutil.WINDOWS:
35
61
  # On Windows, the motherboard serial number can be obtained using WMI
36
62
  output = subprocess.check_output(["wmic", "baseboard", "get", "SerialNumber"]).decode('utf-8')
@@ -42,7 +68,8 @@ def getCurrentID():
42
68
  if b'Serial Number (system)' in line:
43
69
  serial_number = line.split(b':')[1].strip().decode('utf-8')
44
70
  except Exception as e:
45
- print(f"Error while retrieving motherboard serial number: {e}")
71
+ if Flag_DEBUG:
72
+ print(f"Error while retrieving motherboard serial number: {e}")
46
73
  return serial_number
47
74
 
48
75
  currentID=getCurrentID()
@@ -57,6 +84,16 @@ if currentID in (developerIDs['GP_Win_Office'],developerIDs['GP_Win_Office_New']
57
84
  'C:/desk/PIV_Img/_data/Calibration_data/cylinder/',
58
85
  ]
59
86
  basefold_DEBUG_VIS='C:/desk/PIV_Img/_data/PIV_data/real_case/'
87
+ elif currentID==developerIDs['GP_WSL']:
88
+ basefold_DEBUG='/mnt/c/desk/PIV_Img/_data/PIV_data/virtual_case/'
89
+ basefold_DEBUGOptions=[
90
+ '/mnt/c/desk/PIV_Img/img1/',
91
+ '/mnt/c/desk/PIV_Img/_data/PIV_data/virtual_case/',
92
+ '/mnt/c/desk/PIV_Img/_data/PIV_data/real_case/',
93
+ '/mnt/c/desk/PIV_Img/_data/Calibration_data/pinhole/',
94
+ '/mnt/c/desk/PIV_Img/_data/Calibration_data/cylinder/',
95
+ ]
96
+ basefold_DEBUG_VIS='/mnt/c/desk/PIV_Img/_data/PIV_data/real_case/'
60
97
  elif currentID==developerIDs['GP_Mac_Laptop']: #gerardo mac
61
98
  basefold_DEBUG='/Users/gerardo/Desktop/PIV_Img/swirler_png/' #'/Users/gerardo/Desktop/PIV_Img/img1/'
62
99
  basefold_DEBUGOptions=[
@@ -64,18 +101,19 @@ elif currentID==developerIDs['GP_Mac_Laptop']: #gerardo mac
64
101
  '/Users/gerardo/Desktop/PaIRS_examples/PIV_data/virtual_case/',
65
102
  #'/Users/gerardo/Desktop/PaIRS_examples/PIV_data/virtual_case_2/',
66
103
  '/Users/gerardo/Desktop/PaIRS_examples/PIV_data/real_case/',
104
+ '/Users/gerardo/Desktop/PaIRS_examples/SPIV_data/real_case/img/',
67
105
  '/Users/gerardo/Desktop/PaIRS_examples/Calibration_data/pinhole/',
68
106
  '/Users/gerardo/Desktop/PaIRS_examples/Calibration_data/cylinder/'
69
107
  ]
70
108
  basefold_DEBUG_VIS='/Users/gerardo/Desktop/PaIRS_examples/PIV_data/real_case/'
71
109
  basefold_DEBUG_VIS='/Users/gerardo/Desktop/PIV_Img/img1/'
72
- elif currentID==developerIDs['TA_Win_Office']: #TA Ufficio
73
- basefold_DEBUG='C:\desk\Attuali\PythonLibC\PIV\img'
110
+ elif currentID in (developerIDs['TA_Win_Office'],developerIDs['TA_Win_Office_New']): #TA windows
111
+ basefold_DEBUG=r'C:\desk\Attuali\PythonLibC\PIV\img'
74
112
  basefold_DEBUGOptions=[
75
113
  'C:/desk/PIV_Img/img1/',
76
114
  'C:/desk/PIV_Img/swirler_png/',
77
115
  '../../img/calib/',
78
- 'C:\desk\Attuali\PythonLibC\PIV\img',
116
+ r'C:\desk\Attuali\PythonLibC\PIV\img',
79
117
  ]
80
118
  basefold_DEBUG_VIS=''
81
119
  else:
@@ -147,6 +185,37 @@ import sys
147
185
  import concurrent.futures
148
186
  import asyncio
149
187
 
188
+ _old_init_QAction = QAction.__init__
189
+ def _new_init_QAction(self, *args, **kwargs):
190
+ _old_init_QAction(self, *args, **kwargs)
191
+ try:
192
+ self.setIconVisibleInMenu(True)
193
+ except Exception:
194
+ pass
195
+ QAction.__init__ = _new_init_QAction
196
+ _old_init_QMenu = QMenu.__init__
197
+ def _new_init_QMenu(self, *args, **kwargs):
198
+ _old_init_QMenu(self, *args, **kwargs)
199
+ try:
200
+ self.menuAction().setIconVisibleInMenu(True)
201
+ except Exception:
202
+ pass
203
+ QMenu.__init__ = _new_init_QMenu
204
+
205
+ # --- Patch dei metodi QMenu che CREANO/AGGIUNGONO azioni (copre gli overload C++) ---
206
+ _old_addAction = QMenu.addAction
207
+ def _new_addAction(self, *args, **kwargs):
208
+ act:QAction = _old_addAction(self, *args, **kwargs) # può essere creato lato C++
209
+ try:
210
+ if isinstance(act, QAction):
211
+ act.setIconVisibleInMenu(True)
212
+ except Exception:
213
+ pass
214
+ return act
215
+ QMenu.addAction = _new_addAction
216
+
217
+ Flag_ISEXE=getattr(sys, 'frozen', False) #made by pyInstaller
218
+ EXEurl='https://www.pairs.unina.it/#download'
150
219
 
151
220
  class ColorPrint:
152
221
  def __init__(self,flagTime=False,prio=PrintTAPriority.medium,faceStd=PrintTA.faceStd,flagFullDebug=False):
@@ -182,6 +251,7 @@ class GPaIRSPrint:
182
251
  self.Info=ColorPrint(prio=PrintTAPriority.medium)
183
252
  self.Time=ColorPrint(prio=PrintTAPriority.medium if FlagPrintTime else PrintTAPriority.veryLow,flagTime=True,faceStd=PrintTA.faceUnderline)
184
253
  self.Error=ColorPrint(prio=PrintTAPriority.medium,faceStd=PrintTA.faceBold)
254
+ self.IOError=ColorPrint(prio=PrintTAPriority.veryLow,faceStd=PrintTA.faceBold)
185
255
  self.Process=ColorPrint(prio=PrintTAPriority.veryLow)
186
256
  self.Callback=ColorPrint(prio=PrintTAPriority.veryLow)
187
257
  self.TABparDiff=ColorPrint(prio=PrintTAPriority.veryLow)
@@ -235,7 +305,6 @@ if __package__ or "." in __name__:
235
305
  foldPaIRS = foldPaIRS.replace('\\', '/')
236
306
  else:
237
307
  foldPaIRS='./'
238
-
239
308
  class ProcessTypes:
240
309
  null=None
241
310
  min=0
@@ -245,7 +314,7 @@ class ProcessTypes:
245
314
  cal=10
246
315
 
247
316
  singleCamera=[piv]
248
- threeCameras=[tpiv]
317
+ threeCameras=[min,tpiv]
249
318
 
250
319
  class StepTypes:
251
320
  null=None
@@ -289,15 +358,24 @@ class outExt:
289
358
  cfg_calvi='.calvi_cfg'
290
359
  pla='.pairs_pla'
291
360
 
292
-
293
361
 
294
362
  lastcfgname='lastWorkSpace'+outExt.wksp
295
-
296
363
  fileChanges=foldPaIRS+'Changes.txt'
297
- fileWhatsNew=[foldPaIRS+f for f in fileWhatsNew]
298
364
  icons_path=foldPaIRS+icons_path
299
- lastcfgname=foldPaIRS+lastcfgname
300
- pro_path=foldPaIRS+"pro/"
365
+
366
+ if not Flag_ISEXE:
367
+ fileWhatsNew=[foldPaIRS+f for f in fileWhatsNew]
368
+
369
+ lastcfgname=foldPaIRS+lastcfgname
370
+ pro_path=foldPaIRS+"pro/"
371
+ else:
372
+ from pathlib import Path
373
+ exe_dir = str(Path(sys.argv[0]).resolve().parent)+'/'
374
+ fileWhatsNew=[foldPaIRS+fileWhatsNew[0],exe_dir+fileWhatsNew[1]]
375
+ lastcfgname = exe_dir + lastcfgname
376
+ pro_path = exe_dir + "pro/"
377
+
378
+
301
379
  if not os.path.exists(pro_path):
302
380
  try:
303
381
  os.mkdir(pro_path)
@@ -319,7 +397,7 @@ if Flag_NATIVEDIALOGS:
319
397
  else:
320
398
  optionNativeDialog=QFileDialog.Option.DontUseNativeDialog
321
399
 
322
- def warningDialog(self:QWidget,Message,time_milliseconds=0,flagScreenCenter=False,icon:QIcon=QIcon(),palette=None,pixmap=None,title='Warning!',flagRichText=False,flagNoButtons=False,addButton:dict=None,FlagStayOnTop=False): #addButton=['Print Message',lambda: print(Message)]
400
+ def warningDialog(self:QWidget,Message,time_milliseconds=0,flagScreenCenter=False,icon:QIcon=QIcon(),palette=None,pixmap=None,title='Warning!',flagRichText=False,flagNoButtons=False,addButton:dict=None,FlagStayOnTop=False,pixmapSize=64): #addButton=['Print Message',lambda: print(Message)]
323
401
  dlg=None
324
402
  if Message:
325
403
  if isinstance(self,QMainWindow) and hasattr(self,'w_Input'):
@@ -328,6 +406,7 @@ def warningDialog(self:QWidget,Message,time_milliseconds=0,flagScreenCenter=Fals
328
406
  dlg = QMessageBox(self)
329
407
  dlg.setWindowTitle(title)
330
408
  dlg.setText(str(Message))
409
+
331
410
  if flagRichText: dlg.setTextFormat(Qt.TextFormat.RichText)
332
411
  if flagNoButtons:
333
412
  dlg.setStandardButtons(QMessageBox.StandardButton.NoButton)
@@ -361,7 +440,7 @@ def warningDialog(self:QWidget,Message,time_milliseconds=0,flagScreenCenter=Fals
361
440
  if palette:
362
441
  dlg.setPalette(palette)
363
442
  if pixmap:
364
- dlg.setIconPixmap(QPixmap(pixmap).scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio,Qt.SmoothTransformation))
443
+ dlg.setIconPixmap(QPixmap(pixmap).scaled(pixmapSize, pixmapSize, Qt.AspectRatioMode.KeepAspectRatio,Qt.SmoothTransformation))
365
444
  if self:
366
445
  dlg.setFont(self.font())
367
446
  c=dlg.findChildren(QObject)
@@ -685,10 +764,9 @@ def toPlainText(text):
685
764
  return PlainTextConverter.toPlainText()
686
765
 
687
766
  def showTip(obj,message):
688
- tip=QToolTip(obj)
689
767
  toolTipDuration=obj.toolTipDuration()
690
768
  obj.setToolTipDuration(3000)
691
- tip.showText(QCursor.pos(),message)
769
+ QToolTip.showText(QCursor.pos(),message)
692
770
  obj.setToolTipDuration(toolTipDuration)
693
771
 
694
772
  def clean_tree(tree:QTreeWidget):
@@ -780,12 +858,16 @@ def runPaIRS(self,command='',flagQuestion=True):
780
858
  def run(self):
781
859
  try:
782
860
  import subprocess
783
- if Flag: #launched from package
784
- pri.Info.white(sys.executable+' -m PaIRS_UniNa '+command)
785
- subprocess.call(sys.executable+' -m PaIRS_UniNa '+command,shell=True)
861
+ if Flag_ISEXE:
862
+ pri.Info.white(sys.executable+' '+command)
863
+ subprocess.call(sys.executable+' '+command,shell=True)
786
864
  else:
787
- pri.Info.white(sys.executable+' -c '+'"'+f"import os; os.chdir('{os.getcwd()}'); {pyCommands[command]}"+'"')
788
- subprocess.call(sys.executable+' -c '+'"'+f"import os; os.chdir('{os.getcwd()}'); {pyCommands[command]}"+'"',shell=True)
865
+ if Flag: #launched from package
866
+ pri.Info.white(sys.executable+' -m PaIRS_UniNa '+command)
867
+ subprocess.call(sys.executable+' -m PaIRS_UniNa '+command,shell=True)
868
+ else:
869
+ pri.Info.white(sys.executable+' -c '+'"'+f"import os; os.chdir('{os.getcwd()}'); {pyCommands[command]}"+'"')
870
+ subprocess.call(sys.executable+' -c '+'"'+f"import os; os.chdir('{os.getcwd()}'); {pyCommands[command]}"+'"',shell=True)
789
871
  self.isRunning=False
790
872
  except Exception as inst:
791
873
  pri.Error.red(inst)
@@ -814,12 +896,12 @@ def showSplash(filename=''+ icons_path +'logo_PaIRS_completo.png'):
814
896
  splash.show()
815
897
  return splash
816
898
 
817
- def checkLatestVersion(self,version,app:QApplication=None,splash:QLabel=None):
899
+ def checkLatestVersion(self,version,app:QApplication=None,splash:QLabel=None,flagWarning=1):
818
900
  flagStopAndDownload=False
819
901
  var=self.TABpar
820
902
  #var.FlagOutDated=0 if currentVersion==var.latestVersion else var.FlagOutDated
821
903
  if abs(var.FlagOutDated)==1:
822
- warningLatestVersion(self,app,flagExit=0,flagWarning=1,FlagStayOnTop=True)
904
+ warningLatestVersion(self,app,flagExit=0,flagWarning=flagWarning,FlagStayOnTop=True)
823
905
  var.FlagOutDated=2 if var.FlagOutDated==1 else -2
824
906
  """
825
907
  flagStopAndDownload=questionDialog(self,f'A new version of the PaIRS_UniNa package is available. Do you want to download it before starting the current istance of {self.name}?')
@@ -850,7 +932,7 @@ def checkLatestVersion(self,version,app:QApplication=None,splash:QLabel=None):
850
932
  var.FlagOutDated=-2 if var.FlagOutDated==-2 else -1
851
933
  elif flagOutDated==-1000:
852
934
  sOut=f'Error from pip: it was not possible to check for a new version of the {packageName} package!'
853
- var.FlagOutDated=0
935
+ var.FlagOutDated=-1000
854
936
  else:
855
937
  sOut=f'{packageName} The current version ({currentVersion}) of {packageName} is up-to-date! Enjoy it!'
856
938
  var.FlagOutDated=0
@@ -870,13 +952,18 @@ def warningLatestVersion(self,app,flagExit=0,flagWarning=0,time_milliseconds=0,F
870
952
  py=myStandardRoot(sys.executable).split('/')[-1].split('.')[0]
871
953
  command=f'{py} -m pip install --upgrade PaIRS_UniNa'
872
954
  if self.TABpar.FlagOutDated>0:
873
- Message=f'A new version of the PaIRS_UniNa package is available (current: {self.TABpar.currentVersion}, latest: {self.TABpar.latestVersion}).\nPlease, {exitSuggestion}install it with the following command:\n{command}'
955
+ if Flag_ISEXE:
956
+ Message=f'A new version of the PaIRS_UniNa package is available (current: {self.TABpar.currentVersion}, latest: {self.TABpar.latestVersion}).\nPlease, download it from the following link:\n{EXEurl}'
957
+ else:
958
+ Message=f'A new version of the PaIRS_UniNa package is available (current: {self.TABpar.currentVersion}, latest: {self.TABpar.latestVersion}).\nPlease, {exitSuggestion}install it with the following command:\n{command}'
959
+ elif self.TABpar.FlagOutDated==-1000:
960
+ Message = ("Unable to check for the latest official release of PaIRS_UniNa. Please check the PyPI page manually for updates:\n""https://pypi.org/project/PaIRS-UniNa/")
874
961
  else:
875
962
  Message=f'The version of the current instance of PaIRS_UniNa ({self.TABpar.currentVersion}) is newer than the latest official releas ({self.TABpar.latestVersion})!\nYou should contact Tommaso and Gerardo if you are a developer and some relevant change is made by yourself!\nIf you are a user, enjoy this beta version and please report any issue!'
876
963
  if flagExit:
877
964
  print(f"\n{'*'*100}\n"+Message+f"\n{'*'*100}\n")
878
965
  if flagWarning:
879
- warningDialog(self,Message,time_milliseconds=time_milliseconds,flagScreenCenter=True,pixmap=''+ icons_path +'flaticon_PaIRS_download.png' if self.TABpar.FlagOutDated>0 else ''+ icons_path +'flaticon_PaIRS_beta.png',FlagStayOnTop=FlagStayOnTop,addButton={"See what's new!": lambda: QDesktopServices.openUrl(QUrl("https://pypi.org/project/PaIRS-UniNa/"))} if self.TABpar.FlagOutDated>0 else {})
966
+ warningDialog(self,Message,time_milliseconds=time_milliseconds,flagScreenCenter=True,pixmap=''+ icons_path +'flaticon_PaIRS_download.png' if self.TABpar.FlagOutDated>0 else ''+ icons_path +'flaticon_PaIRS_download_warning.png' if self.TABpar.FlagOutDated==-1000 else ''+ icons_path +'flaticon_PaIRS_beta.png',FlagStayOnTop=FlagStayOnTop,addButton={"Go to the download page!": lambda: QDesktopServices.openUrl(QUrl(EXEurl))} if Flag_ISEXE else {"See what's new!": lambda: QDesktopServices.openUrl(QUrl("https://pypi.org/project/PaIRS-UniNa/"))} if self.TABpar.FlagOutDated>0 else {})
880
967
 
881
968
  def downloadLatestVersion(self,app):
882
969
  try:
@@ -900,6 +987,7 @@ def downloadLatestVersion(self,app):
900
987
 
901
988
  def button_download_PaIRS_action(self,app):
902
989
  warningLatestVersion(self,app,flagExit=0,flagWarning=1)
990
+ checkLatestVersion(self,__version__,self.app,splash=None,flagWarning=0)
903
991
  return
904
992
  flagStopAndDownload=questionDialog(self,f'A new version of the PaIRS_UniNa package is available. Do you want to close the current instance of {self.name} and download it?')
905
993
  if not flagStopAndDownload: return
@@ -911,6 +999,20 @@ def button_download_PaIRS_action(self,app):
911
999
  else: command=''
912
1000
  subprocess.call(sys.executable+' -m PaIRS_UniNa '+command,shell=True)
913
1001
  #runPaIRS(self,flagQuestion=False)
1002
+
1003
+ import urllib.request, json, ssl
1004
+
1005
+ def get_package_version_urllib(package_name):
1006
+ """Get package version using only standard library"""
1007
+ try:
1008
+ import certifi
1009
+ url = f"https://pypi.org/pypi/{package_name}/json"
1010
+ context = ssl.create_default_context(cafile=certifi.where())
1011
+ with urllib.request.urlopen(url, context=context, timeout=10) as response:
1012
+ data = json.loads(response.read().decode())
1013
+ return True, data['info']['version']
1014
+ except Exception as e:
1015
+ return False, f"Error: {e}"
914
1016
 
915
1017
  def checkOutDated(packageName:str,printOutDated):
916
1018
  '''
@@ -942,48 +1044,60 @@ def checkOutDated(packageName:str,printOutDated):
942
1044
  currentVersion='none'
943
1045
  latestVersion=''
944
1046
  try:
945
- if Flag_DEBUG:
1047
+ if Flag_ISEXE:
946
1048
  currentVersion=__version__+'.'+__subversion__ if int(__subversion__) else __version__
947
1049
  else:
948
- command=[sys.executable, '-m', 'pip', 'show', packageName]
949
- reqs = subprocess.run(command,capture_output=True)
950
- if reqs.returncode:
951
- pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
952
- return flagOutDated,currentVersion,latestVersion
953
- printing=reqs.stdout.decode("utf-8")
954
- pri.Info.cyan( printing )
955
- r=reqs.stdout.decode("utf-8").replace('\r','').split('\n')
956
- currentVersion='none'
957
- for s in r:
958
- if 'Version: ' in s:
959
- currentVersion=s.replace('Version: ','')
960
- break
1050
+ if Flag_DEBUG:
1051
+ currentVersion=__version__+'.'+__subversion__ if int(__subversion__) else __version__
1052
+ else:
1053
+ command=[sys.executable, '-m', 'pip', 'show', packageName]
1054
+ reqs = subprocess.run(command,capture_output=True)
1055
+ if reqs.returncode:
1056
+ pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
1057
+ return flagOutDated,currentVersion,latestVersion
1058
+ printing=reqs.stdout.decode("utf-8")
1059
+ pri.Info.cyan( printing )
1060
+ r=reqs.stdout.decode("utf-8").replace('\r','').split('\n')
1061
+ currentVersion='none'
1062
+ for s in r:
1063
+ if 'Version: ' in s:
1064
+ currentVersion=s.replace('Version: ','')
1065
+ break
961
1066
  if currentVersion!=__version__:
962
1067
  message=f'Greetings, developer!\nThe version of the current instance of PaIRS_UniNa ({__version__}) is different from that installed in the present Python environment ({currentVersion})!\nYou should contact Tommaso and Gerardo if some relevant change is made by yourself!'
963
1068
  pri.Info.yellow(f'{"-"*50}\n{message}\n{"-"*50}\n')
964
- command=[sys.executable, '-m', 'pip', 'index','versions',packageName]
965
- reqs = subprocess.run(command,capture_output=True)
966
- if not reqs.returncode:
967
- printing=reqs.stdout.decode("utf-8")
968
- pri.Info.cyan( printing )
969
- r=reqs.stdout.decode("utf-8").replace('\r','').split('\n')
970
- #currentVersion=r[0].replace(packageName,'').replace('(','').replace(')','').replace(' ','')
971
- latestVersion=r[1].replace('Available versions: ','').split(',')[0]
1069
+ if Flag_ISEXE:
1070
+ _, latestVersion = get_package_version_urllib("PaIRS_UniNa")
972
1071
  else:
973
- pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
974
-
975
- command=[sys.executable, '-m', 'pip', 'list','--outdated']
1072
+ command=[sys.executable, '-m', 'pip', 'index', 'versions', packageName]
976
1073
  reqs = subprocess.run(command,capture_output=True)
977
- if reqs.returncode:
978
- pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
979
- return flagOutDated,currentVersion,latestVersion
980
- outDated = [r.decode().split('==')[0] for r in reqs.stdout.split()]
981
- if packageName in outDated:
982
- i=outDated.index(packageName)
983
- latestVersion=outDated[i+2]
1074
+ if not reqs.returncode:
1075
+ printing=reqs.stdout.decode("utf-8")
1076
+ pri.Info.cyan( printing )
1077
+ r=reqs.stdout.decode("utf-8").replace('\r','').split('\n')
1078
+ #currentVersion=r[0].replace(packageName,'').replace('(','').replace(')','').replace(' ','')
1079
+ latestVersion=r[1].replace('Available versions: ','').split(',')[0]
984
1080
  else:
985
- latestVersion=currentVersion
986
- pri.Info.cyan(f'{packageName} ({currentVersion}). Latest version available: {latestVersion}')
1081
+ flagOk,latestVersion=get_package_version_urllib(packageName)
1082
+ if not flagOk:
1083
+ pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
1084
+ pri.Error.red(latestVersion)
1085
+ latestVersion='none'
1086
+
1087
+ """
1088
+ command=[sys.executable, '-m', 'pip', 'list','--outdated']
1089
+ reqs = subprocess.run(command,capture_output=True)
1090
+ if reqs.returncode:
1091
+ pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
1092
+ return flagOutDated,currentVersion,latestVersion
1093
+ outDated = [r.decode().split('==')[0] for r in reqs.stdout.split()]
1094
+ if packageName in outDated:
1095
+ i=outDated.index(packageName)
1096
+ latestVersion=outDated[i+2]
1097
+ else:
1098
+ latestVersion=currentVersion
1099
+ pri.Info.cyan(f'{packageName} ({currentVersion}). Latest version available: {latestVersion}')
1100
+ """
987
1101
  #flagOutDated=1 if currentVersion!=latestVersion else 0
988
1102
  cV_parts=[int(c) for c in currentVersion.split('.')]
989
1103
  lV_parts=[int(c) for c in latestVersion.split('.')]
@@ -1095,6 +1209,34 @@ def optimalPivCores(totCore,nImgs,penCore=1):
1095
1209
  #nPivMax=floor(totCore/nCoreMax)
1096
1210
  return nPivMax,nCoreMax
1097
1211
 
1212
+ from PySide6.QtCore import qInstallMessageHandler, QtMsgType
1213
+ def custom_qt_message_handler(mode, context, message):
1214
+ if ("QPainter" in message or "paintEngine" in message):
1215
+ return #Silenzia questi messaggi
1216
+ print(message) #Altrimenti stampali normalmente (oppure loggali)
1217
+ qInstallMessageHandler(custom_qt_message_handler)
1218
+
1219
+ """
1220
+ def custom_qt_message_handler(mode, context, message):
1221
+ if "QPainter" in message or "paintEngine" in message:
1222
+ print("\n!!! Intercepted Qt message:")
1223
+ print(message)
1224
+ print("\n*** Current Python stacktrace:")
1225
+ traceback.print_stack() # Questo stampa lo stack in cui è stato generato il messaggio
1226
+ else:
1227
+ print(message)
1228
+ qInstallMessageHandler(custom_qt_message_handler)
1229
+ import functools
1230
+ import traceback
1231
+ def log_qpainter_usage(func):
1232
+ @functools.wraps(func)
1233
+ def wrapper(*args, **kwargs):
1234
+ print(f"\n°°° Execution of {func.__name__} in {func.__module__}")
1235
+ traceback.print_stack(limit=4) # Mostra solo lo stack alto
1236
+ return func(*args, **kwargs)
1237
+ return wrapper
1238
+ """
1239
+
1098
1240
  class PaIRSApp(QApplication):
1099
1241
  def __init__(self,*args):
1100
1242
  super().__init__(*args)
@@ -1110,4 +1252,125 @@ class PaIRSApp(QApplication):
1110
1252
 
1111
1253
  def installMessageHandler(self):
1112
1254
  qInstallMessageHandler(self.message_handler)
1113
-
1255
+
1256
+ rqrdpckgs_filename=foldPaIRS+"rqrdpckgs.txt"
1257
+ from packaging.version import Version
1258
+ import importlib.metadata
1259
+
1260
+ def resetRequiredPackagesFile(filename=rqrdpckgs_filename):
1261
+ # Leggi il contenuto esistente
1262
+ try:
1263
+ with open(filename, "r") as f:
1264
+ lines = f.readlines()
1265
+ except FileNotFoundError:
1266
+ pri.Error.red(f"resetRequiredPackagesFile: File {filename} not found.")
1267
+ return
1268
+
1269
+ with open(filename, "w") as f:
1270
+ for line in lines:
1271
+ parts = line.strip().split()
1272
+ if len(parts) >= 3:
1273
+ pkg = parts[0]
1274
+ vmin = parts[1]
1275
+ vmax = parts[2]
1276
+ f.write(f"{pkg}\t{vmin}\t{vmax}\t0\n")
1277
+ else:
1278
+ pri.Error.red(f"resetRequiredPackagesFile: Skipping malformed line: {line}")
1279
+
1280
+ def to_triplet(v: Version) -> tuple[int,int,int]:
1281
+ r = v.release or (0,)
1282
+ return (r[0], r[1] if len(r) > 1 else 0, r[2] if len(r) > 2 else 0)
1283
+
1284
+ def le_ver(a: Version, b: Version) -> bool:
1285
+ a1,a2,a3 = to_triplet(a)
1286
+ b1,b2,b3 = to_triplet(b)
1287
+ if a1 > b1: return False
1288
+ if a1 < b1: return True
1289
+ if a2 > b2: return False
1290
+ if a2 < b2: return True
1291
+ return a3 <= b3
1292
+
1293
+ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False, FlagForcePrint=False):
1294
+ required_packages = []
1295
+ vmin_list = []
1296
+ vmax_list = []
1297
+ vcurr_list = []
1298
+
1299
+ # Read file
1300
+ with open(filename, "r") as f:
1301
+ for line in f:
1302
+ #pri.Info.white(line)
1303
+ parts = line.strip().split()
1304
+ if len(parts) >= 4:
1305
+ required_packages.append(parts[0])
1306
+ vmin_list.append(Version(parts[1]))
1307
+ vmax_list.append(Version(parts[2]))
1308
+ vcurr_list.append(Version(parts[3]) if parts[3] != "0" else None)
1309
+ else:
1310
+ pri.Error.red(f"Malformed line: {line}")
1311
+
1312
+ Flag = False
1313
+ warnings = []
1314
+
1315
+ for i, pkg in enumerate(required_packages):
1316
+ try:
1317
+ installed_version = Version(importlib.metadata.version(pkg))
1318
+ except importlib.metadata.PackageNotFoundError:
1319
+ installed_version = None
1320
+
1321
+ # Update current installed version
1322
+ if installed_version is not None and (installed_version != vcurr_list[i] or FlagDisplay):
1323
+ vcurr_list[i] = installed_version
1324
+ Flag = True
1325
+
1326
+ # Check if within [vmin, vmax]
1327
+ if not (le_ver(vmin_list[i],installed_version) and le_ver(installed_version,vmax_list[i])):
1328
+ """
1329
+ warnings.append(
1330
+ f"- {pkg}: installed = {installed_version}, target range = [{vmin_list[i]}, {vmax_list[i]}]"
1331
+ )
1332
+ """
1333
+ warnings.append(
1334
+ f"- {pkg} {installed_version} not in [{vmin_list[i]}, {vmax_list[i]}]"
1335
+ )
1336
+
1337
+ # Show warning
1338
+ if len(warnings)>0: self.FlagPackIssue=True
1339
+ if len(warnings)>0 or FlagForcePrint:
1340
+ message = (
1341
+ "Some installed packages have a version outside the target range used to develop "
1342
+ "the current release of the PaIRS_UniNa package.\n\n"
1343
+ "This may lead to compatibility issues. If you experience unexpected behavior, "
1344
+ "it is recommended to either reinstall the last tested compatible versions or "
1345
+ f"download the executable at {EXEurl}."
1346
+ f" If any issue occurs, please contact the authors at {__mail__}.\n\n"
1347
+ #"or use the standalone executable available at:\n"
1348
+ #"https://pairs.unina.it/#download\n\n"
1349
+ "Incompatible packages:\n"
1350
+ + "\n".join(warnings) +
1351
+ "\n\nYou may reinstall the last compatible versions using the following commands:\n\n"
1352
+ )
1353
+ for i, pkg in enumerate(required_packages):
1354
+ if vcurr_list[i] is not None and (not (vmin_list[i] <= vcurr_list[i] <= vmax_list[i]) or FlagForcePrint):
1355
+ message += (
1356
+ f"python -m pip uninstall {pkg}\n"
1357
+ f"python -m pip install {pkg}=={vmax_list[i]}\n"
1358
+ )
1359
+
1360
+ warningDialog(
1361
+ self,
1362
+ Message=message,
1363
+ flagScreenCenter=True,
1364
+ pixmap=icons_path + 'python_warning.png',
1365
+ pixmapSize=96
1366
+ )
1367
+ elif FlagDisplay:
1368
+ warningDialog(self, Message="All installed packages are within the expected version range.", flagScreenCenter=True,pixmap=icons_path+'greenv.png')
1369
+
1370
+ # Update file if needed
1371
+ if Flag:
1372
+ with open(filename, "w") as f:
1373
+ for pkg, vmin, vmax, vcurr in zip(required_packages, vmin_list, vmax_list, vcurr_list):
1374
+ f.write(f"{pkg}\t{vmin}\t{vmax}\t{vcurr if vcurr else 0}\n")
1375
+
1376
+ return required_packages, vmin_list, vmax_list, vcurr_list
@@ -724,9 +724,8 @@ class Process_Tab(gPaIRS_Tab):
724
724
  #******************** Actions
725
725
  def edit_Wind_vectors(self,wedit:QLineEdit,wlab:QLabel):
726
726
  text=wedit.text()
727
- split_text=re.split('(\d+)', text)[1:-1:2]
727
+ split_text=re.split(r'(\d+)', text)[1:-1:2]
728
728
  vect=[int(i) for i in split_text]
729
- tip=QToolTip(wedit)
730
729
  FlagEmpty=len(vect)==0
731
730
  if FlagEmpty: FlagError=True
732
731
  else: FlagError=not all([v>=w for v,w in zip(vect[:-1],vect[1:])])
@@ -739,21 +738,18 @@ class Process_Tab(gPaIRS_Tab):
739
738
  wlab.setToolTip(message)
740
739
  wlab.setStatusTip(message)
741
740
  """
742
- tip=QToolTip(wedit)
743
- tip.showText(QCursor.pos(),wlab.toolTip(),wedit,QRect(),3000)
741
+ QToolTip.showText(QCursor.pos(),wlab.toolTip(),wedit,QRect(),3000)
744
742
  """
745
743
  else:
746
744
  wlab.setPixmap(QPixmap())
747
- tip.hideText()
748
745
  self.PROpar.VectFlag[self.Vect_widgets.index(wedit)]=not FlagError
749
- return split_text, vect, tip, FlagError
746
+ return split_text, vect, FlagError
750
747
 
751
748
  def set_Wind_vectors(self,wedit:QLineEdit,wlab:QLabel,i):
752
- _, vect, tip, FlagError=self.edit_Wind_vectors(wedit,wlab)
753
- self.set_Wind_vectors_new(i,vect,tip,FlagError)
749
+ _, vect, FlagError=self.edit_Wind_vectors(wedit,wlab)
750
+ self.set_Wind_vectors_new(i,vect,FlagError)
754
751
 
755
- def set_Wind_vectors_new(self,i,vect,tip=QToolTip(),FlagError=False):
756
- #tip.hideText()
752
+ def set_Wind_vectors_new(self,i,vect,FlagError=False):
757
753
  if not FlagError:
758
754
  Nit_i=len(vect)
759
755
  if Nit_i>self.PROpar.Nit:
@@ -1295,7 +1291,7 @@ class Process_Tab(gPaIRS_Tab):
1295
1291
 
1296
1292
  def line_edit_IW_action(self):
1297
1293
  text=self.ui.line_edit_IW.text()
1298
- split_text=re.split('(\d+)', text)[1:-1:2]
1294
+ split_text=re.split(r'(\d+)', text)[1:-1:2]
1299
1295
  vect=[int(split_text[i]) for i in (0,2,1,3)]
1300
1296
  if len(vect)==4:
1301
1297
  k=self.PROpar.row
@@ -1365,10 +1361,9 @@ class Process_Tab(gPaIRS_Tab):
1365
1361
  item.setStatusTip('')
1366
1362
 
1367
1363
  message='No context menu available! Please, pause processing.'
1368
- tip=QToolTip(self)
1369
1364
  toolTipDuration=self.toolTipDuration()
1370
1365
  self.setToolTipDuration(3000)
1371
- tip.showText(QCursor.pos(),message)
1366
+ QToolTip.showText(QCursor.pos(),message)
1372
1367
  self.setToolTipDuration(toolTipDuration)
1373
1368
  item.setToolTip(toolTip)
1374
1369
  item.setStatusTip(toolTip)