PaIRS-UniNa 0.2.7__cp312-cp312-macosx_11_0_universal2.whl → 0.2.10__cp312-cp312-macosx_11_0_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.
- PaIRS_UniNa/Calibration_Tab.py +16 -0
- PaIRS_UniNa/Changes.txt +39 -0
- PaIRS_UniNa/Explorer.py +311 -75
- PaIRS_UniNa/FolderLoop.py +196 -6
- PaIRS_UniNa/Input_Tab.py +160 -53
- PaIRS_UniNa/Input_Tab_CalVi.py +11 -12
- PaIRS_UniNa/Input_Tab_tools.py +17 -15
- PaIRS_UniNa/Output_Tab.py +1 -3
- PaIRS_UniNa/PaIRS_pypacks.py +63 -65
- PaIRS_UniNa/Process_Tab.py +19 -15
- PaIRS_UniNa/Process_Tab_Disp.py +8 -1
- PaIRS_UniNa/SPIVCalHelp.py +155 -0
- PaIRS_UniNa/Saving_tools.py +2 -0
- PaIRS_UniNa/TabTools.py +165 -6
- PaIRS_UniNa/Vis_Tab.py +50 -22
- PaIRS_UniNa/Vis_Tab_CalVi.py +1 -2
- PaIRS_UniNa/Whatsnew.py +4 -3
- PaIRS_UniNa/_PaIRS_PIV.so +0 -0
- PaIRS_UniNa/__init__.py +3 -3
- PaIRS_UniNa/addwidgets_ps.py +570 -70
- PaIRS_UniNa/gPaIRS.py +118 -17
- PaIRS_UniNa/icons/folder_loop_cleanup.png +0 -0
- PaIRS_UniNa/icons/folder_loop_cleanup_off.png +0 -0
- PaIRS_UniNa/icons/information.png +0 -0
- PaIRS_UniNa/icons/information2.png +0 -0
- PaIRS_UniNa/icons/scan_path_loop.png +0 -0
- PaIRS_UniNa/icons/scan_path_loop_off.png +0 -0
- PaIRS_UniNa/icons/spiv_setup_no.png +0 -0
- PaIRS_UniNa/icons/spiv_setup_ok.png +0 -0
- PaIRS_UniNa/procTools.py +46 -1
- PaIRS_UniNa/rqrdpckgs.txt +7 -7
- PaIRS_UniNa/ui_Calibration_Tab.py +92 -59
- PaIRS_UniNa/ui_gPairs.py +8 -8
- PaIRS_UniNa/whatsnew.txt +2 -3
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.10.dist-info}/METADATA +7 -8
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.10.dist-info}/RECORD +38 -30
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.10.dist-info}/WHEEL +0 -0
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.10.dist-info}/top_level.txt +0 -0
PaIRS_UniNa/Input_Tab_CalVi.py
CHANGED
|
@@ -34,6 +34,8 @@ button_tips={
|
|
|
34
34
|
}
|
|
35
35
|
combo_tips={}
|
|
36
36
|
class INPpar_CalVi(TABpar):
|
|
37
|
+
pathCompleter=basefold_DEBUGOptions
|
|
38
|
+
|
|
37
39
|
def __init__(self,Process=ProcessTypes.null,Step=StepTypes.null):
|
|
38
40
|
self.setup(Process,Step)
|
|
39
41
|
super().__init__('INPpar_CalVi','Input_CalVi')
|
|
@@ -79,8 +81,6 @@ class INPpar_CalVi(TABpar):
|
|
|
79
81
|
|
|
80
82
|
self.errorMessage = ''
|
|
81
83
|
self.FlagReadCalib = False
|
|
82
|
-
|
|
83
|
-
self.pathCompleter=basefold_DEBUGOptions
|
|
84
84
|
|
|
85
85
|
class Input_Tab_CalVi(gPaIRS_Tab):
|
|
86
86
|
|
|
@@ -178,6 +178,7 @@ class Input_Tab_CalVi(gPaIRS_Tab):
|
|
|
178
178
|
|
|
179
179
|
def listContextMenuEvent(self, list_images:QTableWidget, event):
|
|
180
180
|
menu=QMenu(list_images)
|
|
181
|
+
menu.setStyleSheet(self.gui.ui.menu.styleSheet())
|
|
181
182
|
buttons=['import', 'import_plane',
|
|
182
183
|
-1,'down','up',
|
|
183
184
|
-1,'delete','clean']
|
|
@@ -290,13 +291,13 @@ class Input_Tab_CalVi(gPaIRS_Tab):
|
|
|
290
291
|
def line_edit_path_preaction(self):
|
|
291
292
|
currpath=myStandardPath(self.ui.line_edit_path.text())
|
|
292
293
|
self.FlagScanPath=os.path.normpath(self.INPpar.path)!=currpath
|
|
293
|
-
|
|
294
|
-
if
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
currpath=relativizePath(currpath)
|
|
295
|
+
if os.path.exists(currpath) and currpath!='./':
|
|
296
|
+
pathCompleter=INPpar_CalVi.pathCompleter
|
|
297
|
+
if currpath in pathCompleter: pathCompleter.remove(currpath)
|
|
298
|
+
pathCompleter.insert(0,currpath)
|
|
299
|
+
if len(pathCompleter)>pathCompleterLength:
|
|
300
|
+
INPpar_CalVi.pathCompleter=pathCompleter[:pathCompleterLength]
|
|
300
301
|
self.ui.line_edit_path.setText(currpath)
|
|
301
302
|
|
|
302
303
|
def button_path_action(self):
|
|
@@ -691,9 +692,7 @@ class Input_Tab_CalVi(gPaIRS_Tab):
|
|
|
691
692
|
|
|
692
693
|
def line_edit_path_out_preaction(self):
|
|
693
694
|
currpath=myStandardPath(self.ui.line_edit_path_out.text())
|
|
694
|
-
|
|
695
|
-
if directory_path in currpath:
|
|
696
|
-
currpath=currpath.replace(directory_path,'./')
|
|
695
|
+
currpath=relativizePath(currpath)
|
|
697
696
|
self.ui.line_edit_path_out.setText(currpath)
|
|
698
697
|
|
|
699
698
|
def button_path_out_action(self):
|
PaIRS_UniNa/Input_Tab_tools.py
CHANGED
|
@@ -188,7 +188,8 @@ class ImageSet(TABpar):
|
|
|
188
188
|
pri.Error.red(f'Trying to access a non-existing index position ({k}) in the image set structure ({self.count} sets identified)')
|
|
189
189
|
return []
|
|
190
190
|
f=i+npairs*step
|
|
191
|
-
if k>-1
|
|
191
|
+
if k>-1 and step>0:
|
|
192
|
+
return [self.nameF(self.fname[k],j) for j in range(i,f,step)]
|
|
192
193
|
else:
|
|
193
194
|
return ['' for _ in range(i,f,step)] if step else []
|
|
194
195
|
|
|
@@ -476,6 +477,9 @@ class PaIRSTree(QTreeWidget):
|
|
|
476
477
|
#self.setStyleSheet(f"QTreeWidget::item:selected:active {{{style}}}")
|
|
477
478
|
|
|
478
479
|
style = """
|
|
480
|
+
QTreeWidget::item:hover {
|
|
481
|
+
background-color: rgba(0, 116, 255, 0.1);
|
|
482
|
+
}
|
|
479
483
|
QTreeWidget::item:selected:!active {
|
|
480
484
|
background-color: rgba(0, 116, 255, 0.4);
|
|
481
485
|
}
|
|
@@ -1208,7 +1212,7 @@ class GlobalImageTree(AsynPaIRSTree):
|
|
|
1208
1212
|
header.setSectionResizeMode(2, QHeaderView.ResizeMode.Interactive)
|
|
1209
1213
|
self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
|
1210
1214
|
self.setUniformRowHeights(True)
|
|
1211
|
-
self.setStyleSheet(f"QTreeView::item {{ height: {self.default_row_height} px; }}")
|
|
1215
|
+
self.setStyleSheet(self.styleSheet()+f"QTreeView::item {{ height: {self.default_row_height} px; }}")
|
|
1212
1216
|
|
|
1213
1217
|
self.path=''
|
|
1214
1218
|
self.ncam=1
|
|
@@ -1319,11 +1323,11 @@ class GlobalImageTree(AsynPaIRSTree):
|
|
|
1319
1323
|
topLevelItem.setIcon(0,self.icon_warning)
|
|
1320
1324
|
topLevelItem.setToolTip(0,'Files (!) missing')
|
|
1321
1325
|
topLevelItem.setStatusTip(0,'Files (!) missing')
|
|
1322
|
-
|
|
1326
|
+
elif k in self.warns:
|
|
1323
1327
|
self.warns.remove(k)
|
|
1324
1328
|
topLevelItem.setIcon(0,QIcon())
|
|
1325
1329
|
topLevelItem.setToolTip(0,'')
|
|
1326
|
-
topLevelItem.setStatusTip(0,'
|
|
1330
|
+
topLevelItem.setStatusTip(0,'')
|
|
1327
1331
|
return
|
|
1328
1332
|
for k in range(self.topLevelItemCount()): scan_items(k)
|
|
1329
1333
|
self.resetImNumber()
|
|
@@ -1816,7 +1820,7 @@ class ImageTreeWidget(QWidget):
|
|
|
1816
1820
|
self.imTree.setVisible(True)
|
|
1817
1821
|
self.imTree.keyPressEvent=lambda e: self.treeKeyPressEvent(e)
|
|
1818
1822
|
self.imTree.installEventFilter(self)
|
|
1819
|
-
|
|
1823
|
+
|
|
1820
1824
|
self.FlagSettingPar=None
|
|
1821
1825
|
|
|
1822
1826
|
def eventFilter(self, obj, event):
|
|
@@ -2416,6 +2420,7 @@ class ImageTreeWidget(QWidget):
|
|
|
2416
2420
|
item=tree.currentItem()
|
|
2417
2421
|
if not item: return
|
|
2418
2422
|
menu=QMenu(tree)
|
|
2423
|
+
menu.setStyleSheet(self.window().ui.menu.styleSheet())
|
|
2419
2424
|
name=[]
|
|
2420
2425
|
act=[]
|
|
2421
2426
|
fun=[]
|
|
@@ -2461,14 +2466,11 @@ class CalibrationTree(PaIRSTree):
|
|
|
2461
2466
|
def __init__(self, parent: QWidget=None,listDim=2,listDepth=1):
|
|
2462
2467
|
super().__init__(parent,listDim,listDepth)
|
|
2463
2468
|
|
|
2464
|
-
columns=["#","filename"]
|
|
2465
|
-
self.setColumnCount(len(columns))
|
|
2466
|
-
self.setHeaderLabels(columns)
|
|
2467
2469
|
header=self.header()
|
|
2468
|
-
self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
|
2469
2470
|
header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
|
|
2470
2471
|
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
|
|
2471
|
-
self.setStyleSheet(f"QTreeView::item {{ height: {self.default_row_height} px; }}")
|
|
2472
|
+
self.setStyleSheet(self.styleSheet()+f"QTreeView::item {{ height: {self.default_row_height} px; }}")
|
|
2473
|
+
self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
|
2472
2474
|
|
|
2473
2475
|
self.ncam=1
|
|
2474
2476
|
self.calList=create_empty_list_of_dimension(1)
|
|
@@ -2628,15 +2630,15 @@ class PaIRSTable(QTableWidget):
|
|
|
2628
2630
|
|
|
2629
2631
|
self.pen = QPen(qRgba(127,227,255,0.9))
|
|
2630
2632
|
self.pen.setWidth(3)
|
|
2631
|
-
#style="background-color: rgba(173,216,230,0.1); color: rgba(128,128,128,0.25);"
|
|
2632
|
-
#self.setStyleSheet(f"QTreeWidget::item:selected {{{style}}}")
|
|
2633
|
-
#self.setStyleSheet(f"QTreeWidget::item:selected:active {{{style}}}")
|
|
2634
2633
|
|
|
2635
2634
|
style = """
|
|
2636
|
-
|
|
2635
|
+
QTableWidget::item:hover {
|
|
2636
|
+
background-color: rgba(0, 116, 255, 0.1);
|
|
2637
|
+
}
|
|
2638
|
+
QTableWidget::item:selected:!active {
|
|
2637
2639
|
background-color: rgba(0, 116, 255, 0.4);
|
|
2638
2640
|
}
|
|
2639
|
-
|
|
2641
|
+
QTableWidget::item:selected:active {
|
|
2640
2642
|
background-color: rgba(0, 116, 255, 0.8);
|
|
2641
2643
|
}
|
|
2642
2644
|
"""
|
PaIRS_UniNa/Output_Tab.py
CHANGED
|
@@ -810,9 +810,7 @@ class Output_Tab(gPaIRS_Tab):
|
|
|
810
810
|
|
|
811
811
|
def line_edit_path_preaction(self):
|
|
812
812
|
currpath=myStandardPath(self.ui.line_edit_path.text())
|
|
813
|
-
|
|
814
|
-
if directory_path in currpath:
|
|
815
|
-
currpath=currpath.replace(directory_path,'./')
|
|
813
|
+
currpath=relativizePath(currpath)
|
|
816
814
|
self.ui.line_edit_path.setText(currpath)
|
|
817
815
|
|
|
818
816
|
def button_path_action(self):
|
PaIRS_UniNa/PaIRS_pypacks.py
CHANGED
|
@@ -80,8 +80,10 @@ if currentID in (developerIDs['GP_Win_Office'],developerIDs['GP_Win_Office_New']
|
|
|
80
80
|
'C:/desk/PIV_Img/img1/',
|
|
81
81
|
'C:/desk/PIV_Img/_data/PIV_data/virtual_case/',
|
|
82
82
|
'C:/desk/PIV_Img/_data/PIV_data/real_case/',
|
|
83
|
+
'C:/desk/PIV_Img/_data/SPIV_data/real_case/img/',
|
|
83
84
|
'C:/desk/PIV_Img/_data/Calibration_data/pinhole/',
|
|
84
85
|
'C:/desk/PIV_Img/_data/Calibration_data/cylinder/',
|
|
86
|
+
'C:/desk/PIV_Img/_data/SPIV_data/real_case/calib/',
|
|
85
87
|
]
|
|
86
88
|
basefold_DEBUG_VIS='C:/desk/PIV_Img/_data/PIV_data/real_case/'
|
|
87
89
|
elif currentID==developerIDs['GP_WSL']:
|
|
@@ -142,11 +144,21 @@ f_empty_width=250 #blank space in scrollable area within the main window
|
|
|
142
144
|
time_ScrollBar=250 #time of animation of scroll area
|
|
143
145
|
time_callback2_async=0 #time to test async callbacks
|
|
144
146
|
time_showSplashOnTop=250
|
|
147
|
+
pathCompleterLength=10
|
|
145
148
|
|
|
146
149
|
fileChanges='Changes.txt'
|
|
147
150
|
fileWhatsNew=['whatsnew.txt','whatwasnew.txt']
|
|
148
151
|
icons_path="icons/"
|
|
149
152
|
|
|
153
|
+
gPaIRS_QMenu_style="""
|
|
154
|
+
QMenu::item:selected,
|
|
155
|
+
QMenu::item:checked,
|
|
156
|
+
QMenu::item:pressed {
|
|
157
|
+
background-color: rgba(0, 116, 255, 0.8);
|
|
158
|
+
color: white;
|
|
159
|
+
}
|
|
160
|
+
"""
|
|
161
|
+
|
|
150
162
|
from psutil import cpu_count
|
|
151
163
|
NUMTHREADS_MAX=cpu_count(logical=True)#-1
|
|
152
164
|
if NUMTHREADS_MAX<1: NUMTHREADS_MAX=1
|
|
@@ -634,6 +646,13 @@ def myStandardRoot(root):
|
|
|
634
646
|
currroot = re.sub('/+', '/', currroot) # Reduce consecutive slashes to a single slash
|
|
635
647
|
return currroot
|
|
636
648
|
|
|
649
|
+
def relativizePath(currpath:str):
|
|
650
|
+
return currpath
|
|
651
|
+
directory_path = myStandardPath(os.getcwd())
|
|
652
|
+
if directory_path in currpath:
|
|
653
|
+
currpath=currpath.replace(directory_path,'./')
|
|
654
|
+
return currpath
|
|
655
|
+
|
|
637
656
|
def findFiles_sorted(pattern):
|
|
638
657
|
list_files=glob.glob(pattern)
|
|
639
658
|
files=sorted([re.sub(r'\\+',r'/',f) for f in list_files],key=str.lower)
|
|
@@ -758,17 +777,48 @@ def identifierName(typeObject:str='proc'):
|
|
|
758
777
|
name=version_user_info+'_'+typeObject+'_'+id
|
|
759
778
|
return name, username, __version__
|
|
760
779
|
|
|
780
|
+
def fileIdenitifierCheck(id: str, filename: str) -> bool:
|
|
781
|
+
"""
|
|
782
|
+
Extract the date/time from the identifier 'name' and check whether 'filename'
|
|
783
|
+
has been modified after that timestamp.
|
|
784
|
+
Returns True if file is newer, False otherwise.
|
|
785
|
+
"""
|
|
786
|
+
|
|
787
|
+
# --- Extract timestamp from name ---
|
|
788
|
+
# Expected pattern: ..._<date>-<time>_...
|
|
789
|
+
# Example: 'PaIRS-v0.2.8_Linux-user_2025/11/07-12:45:31_proc_...'
|
|
790
|
+
try:
|
|
791
|
+
parts = id.split('_')
|
|
792
|
+
date_str, time_str = parts[2].split('-')[0], parts[2].split('-')[1]
|
|
793
|
+
except Exception:
|
|
794
|
+
pri.Error.red("Identifier format not recognized: cannot extract date and time.")
|
|
795
|
+
return False
|
|
796
|
+
|
|
797
|
+
# Convert date/time strings into QDateTime
|
|
798
|
+
qdate = QDate.fromString(date_str, 'yyyy/MM/dd')
|
|
799
|
+
qtime = QTime.fromString(time_str, 'HH:mm:ss')
|
|
800
|
+
qdt_identifier = QDateTime(qdate, qtime)
|
|
801
|
+
qdt_identifier = qdt_identifier.addSecs(-1) #to be safe
|
|
802
|
+
|
|
803
|
+
if not qdt_identifier.isValid():
|
|
804
|
+
pri.Error.red("Parsed QDateTime is not valid. Check identifier format.")
|
|
805
|
+
return False
|
|
806
|
+
|
|
807
|
+
# --- File timestamp ---
|
|
808
|
+
if not os.path.exists(filename):
|
|
809
|
+
return False
|
|
810
|
+
|
|
811
|
+
file_mtime = os.path.getmtime(filename)
|
|
812
|
+
qdt_file = QDateTime.fromSecsSinceEpoch(int(file_mtime))
|
|
813
|
+
|
|
814
|
+
# True if file was modified after the timestamp stored in name
|
|
815
|
+
return qdt_file > qdt_identifier
|
|
816
|
+
|
|
761
817
|
PlainTextConverter=QtGui.QTextDocument()
|
|
762
818
|
def toPlainText(text):
|
|
763
819
|
PlainTextConverter.setHtml(text) #for safety
|
|
764
820
|
return PlainTextConverter.toPlainText()
|
|
765
821
|
|
|
766
|
-
def showTip(obj,message):
|
|
767
|
-
toolTipDuration=obj.toolTipDuration()
|
|
768
|
-
obj.setToolTipDuration(3000)
|
|
769
|
-
QToolTip.showText(QCursor.pos(),message)
|
|
770
|
-
obj.setToolTipDuration(toolTipDuration)
|
|
771
|
-
|
|
772
822
|
def clean_tree(tree:QTreeWidget):
|
|
773
823
|
def remove_children(item:QTreeWidgetItem):
|
|
774
824
|
while item.childCount() > 0:
|
|
@@ -1121,59 +1171,6 @@ def checkOutDated(packageName:str,printOutDated):
|
|
|
1121
1171
|
f3=executor.submit(asyncio.run,checkOutDatedInternal(packageName))
|
|
1122
1172
|
f3.add_done_callback(checkOutDatedComplete)
|
|
1123
1173
|
|
|
1124
|
-
def changes(self,TabType,filename,title=" Changes"):
|
|
1125
|
-
FlagShow=False
|
|
1126
|
-
if self.logChanges:
|
|
1127
|
-
if self.logChanges.isVisible():
|
|
1128
|
-
FlagShow=True
|
|
1129
|
-
if FlagShow:
|
|
1130
|
-
self.logChanges.hide()
|
|
1131
|
-
self.logChanges.show()
|
|
1132
|
-
else:
|
|
1133
|
-
self.logChanges=TabType(self,True)
|
|
1134
|
-
self.logChanges.resize(720,720)
|
|
1135
|
-
self.logChanges.show()
|
|
1136
|
-
self.logChanges.ui.progress_Proc.hide()
|
|
1137
|
-
self.logChanges.ui.button_close_tab.hide()
|
|
1138
|
-
icon=QPixmap(''+ icons_path +'news.png')
|
|
1139
|
-
self.logChanges.ui.icon.setPixmap(icon)
|
|
1140
|
-
self.logChanges.setWindowIcon(self.windowIcon())
|
|
1141
|
-
self.logChanges.setWindowTitle(title)
|
|
1142
|
-
self.logChanges.ui.name_tab.setText(title)
|
|
1143
|
-
|
|
1144
|
-
self.logChanges.ui.log.setLineWrapColumnOrWidth(self.logChanges.ui.log.width()-20)
|
|
1145
|
-
self.logChanges.ui.log.setStyleSheet("")
|
|
1146
|
-
|
|
1147
|
-
def setFontPixelSize(logChanges:type(self.logChanges),fPixSize):
|
|
1148
|
-
logfont=self.font()
|
|
1149
|
-
logfont.setFamily(fontName)
|
|
1150
|
-
logfont.setPixelSize(fPixSize+2)
|
|
1151
|
-
logChanges.ui.log.setFont(logfont)
|
|
1152
|
-
fPixSize_TabNames=min([fPixSize*2,30])
|
|
1153
|
-
lab=logChanges.ui.name_tab
|
|
1154
|
-
font=lab.font()
|
|
1155
|
-
font.setPixelSize(fPixSize_TabNames)
|
|
1156
|
-
lab.setFont(font)
|
|
1157
|
-
self.logChanges.setFontPixelSize=lambda fS: setFontPixelSize(self.logChanges,fS)
|
|
1158
|
-
self.logChanges.setFontPixelSize(self.TABpar.fontPixelSize)
|
|
1159
|
-
def logResizeEvent(logChanges:type(self.logChanges),e):
|
|
1160
|
-
super(type(logChanges),logChanges).resizeEvent(e)
|
|
1161
|
-
logChanges.ui.log.setLineWrapColumnOrWidth(logChanges.ui.log.width()-20)
|
|
1162
|
-
self.logChanges.ui.log.resizeEvent=lambda e: logResizeEvent(self.logChanges,e)
|
|
1163
|
-
|
|
1164
|
-
self.logChanges.ui.icon.addfuncclick['whatsnew']=self.whatsNew
|
|
1165
|
-
self.logChanges.ui.icon.setCustomCursor()
|
|
1166
|
-
|
|
1167
|
-
try:
|
|
1168
|
-
file = open(filename, "rb")
|
|
1169
|
-
content = file.read().decode("utf-8")
|
|
1170
|
-
self.logChanges.ui.log.setText(content)
|
|
1171
|
-
file.close()
|
|
1172
|
-
except Exception as inst:
|
|
1173
|
-
pri.Error.red(f'There was a problem while reading the file {filename}:\n{inst}')
|
|
1174
|
-
self.logChanges.ui.log.setText(f'No information about PaIRS-UniNa updates available!\n\nSorry for this, try to reinstall PaIRS-UniNa or alternatively contact the authors at {__mail__}.')
|
|
1175
|
-
return
|
|
1176
|
-
|
|
1177
1174
|
import webbrowser
|
|
1178
1175
|
def downloadExampleData(self,url):
|
|
1179
1176
|
Message=f'Test data are available at the following link:\n{url}'
|
|
@@ -1309,7 +1306,7 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1309
1306
|
else:
|
|
1310
1307
|
pri.Error.red(f"Malformed line: {line}")
|
|
1311
1308
|
|
|
1312
|
-
|
|
1309
|
+
FlagUpdateFile = False
|
|
1313
1310
|
warnings = []
|
|
1314
1311
|
|
|
1315
1312
|
for i, pkg in enumerate(required_packages):
|
|
@@ -1319,9 +1316,10 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1319
1316
|
installed_version = None
|
|
1320
1317
|
|
|
1321
1318
|
# Update current installed version
|
|
1322
|
-
if installed_version is not None
|
|
1323
|
-
vcurr_list[i]
|
|
1324
|
-
|
|
1319
|
+
if installed_version is not None:
|
|
1320
|
+
if installed_version != vcurr_list[i]:
|
|
1321
|
+
vcurr_list[i] = installed_version
|
|
1322
|
+
FlagUpdateFile = True
|
|
1325
1323
|
|
|
1326
1324
|
# Check if within [vmin, vmax]
|
|
1327
1325
|
if not (le_ver(vmin_list[i],installed_version) and le_ver(installed_version,vmax_list[i])):
|
|
@@ -1336,7 +1334,7 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1336
1334
|
|
|
1337
1335
|
# Show warning
|
|
1338
1336
|
if len(warnings)>0: self.FlagPackIssue=True
|
|
1339
|
-
if len(warnings)>0 or FlagForcePrint:
|
|
1337
|
+
if ( (FlagUpdateFile or FlagDisplay) and len(warnings)>0 ) or FlagForcePrint :
|
|
1340
1338
|
message = (
|
|
1341
1339
|
"Some installed packages have a version outside the target range used to develop "
|
|
1342
1340
|
"the current release of the PaIRS_UniNa package.\n\n"
|
|
@@ -1368,7 +1366,7 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1368
1366
|
warningDialog(self, Message="All installed packages are within the expected version range.", flagScreenCenter=True,pixmap=icons_path+'greenv.png')
|
|
1369
1367
|
|
|
1370
1368
|
# Update file if needed
|
|
1371
|
-
if
|
|
1369
|
+
if FlagUpdateFile:
|
|
1372
1370
|
with open(filename, "w") as f:
|
|
1373
1371
|
for pkg, vmin, vmax, vcurr in zip(required_packages, vmin_list, vmax_list, vcurr_list):
|
|
1374
1372
|
f.write(f"{pkg}\t{vmin}\t{vmax}\t{vcurr if vcurr else 0}\n")
|
PaIRS_UniNa/Process_Tab.py
CHANGED
|
@@ -735,13 +735,12 @@ class Process_Tab(gPaIRS_Tab):
|
|
|
735
735
|
message="Please, insert at least one element!"
|
|
736
736
|
else:
|
|
737
737
|
message="Items must be inserted in decreasing order!"
|
|
738
|
-
wlab.setToolTip(message)
|
|
739
|
-
wlab.setStatusTip(message)
|
|
740
|
-
"""
|
|
741
|
-
QToolTip.showText(QCursor.pos(),wlab.toolTip(),wedit,QRect(),3000)
|
|
742
|
-
"""
|
|
743
738
|
else:
|
|
744
739
|
wlab.setPixmap(QPixmap())
|
|
740
|
+
message=""
|
|
741
|
+
show_mouse_tooltip(wedit,message)
|
|
742
|
+
wlab.setToolTip(message)
|
|
743
|
+
wlab.setStatusTip(message)
|
|
745
744
|
self.PROpar.VectFlag[self.Vect_widgets.index(wedit)]=not FlagError
|
|
746
745
|
return split_text, vect, FlagError
|
|
747
746
|
|
|
@@ -1292,18 +1291,25 @@ class Process_Tab(gPaIRS_Tab):
|
|
|
1292
1291
|
def line_edit_IW_action(self):
|
|
1293
1292
|
text=self.ui.line_edit_IW.text()
|
|
1294
1293
|
split_text=re.split(r'(\d+)', text)[1:-1:2]
|
|
1295
|
-
|
|
1296
|
-
|
|
1294
|
+
if len(split_text)!=4:
|
|
1295
|
+
message="Please insert four distinct values to edit the current PIV process iteration!"
|
|
1296
|
+
show_mouse_tooltip(self,message)
|
|
1297
|
+
self.line_edit_IW_set()
|
|
1298
|
+
else:
|
|
1299
|
+
vect=[int(split_text[i]) for i in (0,2,1,3)]
|
|
1297
1300
|
k=self.PROpar.row
|
|
1298
1301
|
FlagValid=True
|
|
1299
1302
|
if k>0: FlagValid=FlagValid and all([vect[i]<=self.PROpar.Vect[i][k-1] for i in range(4)])
|
|
1300
1303
|
if k<self.PROpar.Nit-1 and FlagValid: FlagValid=FlagValid and all([vect[i]>=self.PROpar.Vect[i][k+1] for i in range(4)])
|
|
1301
1304
|
if FlagValid:
|
|
1305
|
+
message=""
|
|
1306
|
+
show_mouse_tooltip(self,message)
|
|
1302
1307
|
for i in range(4):
|
|
1303
1308
|
self.PROpar.Vect[i][k]=vect[i] #np.array([vect[i]])
|
|
1304
1309
|
else:
|
|
1305
|
-
message='IW sizes
|
|
1306
|
-
|
|
1310
|
+
message='IW sizes or spacings were assigned inconsistently! They must be inserted in decreasing order across iterations. Please, retry!'
|
|
1311
|
+
show_mouse_tooltip(self,message)
|
|
1312
|
+
self.line_edit_IW_set()
|
|
1307
1313
|
self.PROpar.flag_rect_wind=any([v!=w for v,w in zip(self.PROpar.Vect[0],self.PROpar.Vect[2])]) or any([v!=w for v,w in zip(self.PROpar.Vect[1],self.PROpar.Vect[3])])
|
|
1308
1314
|
return
|
|
1309
1315
|
|
|
@@ -1329,6 +1335,7 @@ class Process_Tab(gPaIRS_Tab):
|
|
|
1329
1335
|
item=table_iter.currentItem()
|
|
1330
1336
|
if not item: return
|
|
1331
1337
|
menu=QMenu(table_iter)
|
|
1338
|
+
menu.setStyleSheet(self.gui.ui.menu.styleSheet())
|
|
1332
1339
|
buttons=['add', 'delete']
|
|
1333
1340
|
name=[]
|
|
1334
1341
|
tips=['Add new iteration to the PIV process','Delete current iteration from the PIV process']
|
|
@@ -1361,10 +1368,7 @@ class Process_Tab(gPaIRS_Tab):
|
|
|
1361
1368
|
item.setStatusTip('')
|
|
1362
1369
|
|
|
1363
1370
|
message='No context menu available! Please, pause processing.'
|
|
1364
|
-
|
|
1365
|
-
self.setToolTipDuration(3000)
|
|
1366
|
-
QToolTip.showText(QCursor.pos(),message)
|
|
1367
|
-
self.setToolTipDuration(toolTipDuration)
|
|
1371
|
+
show_mouse_tooltip(self,message)
|
|
1368
1372
|
item.setToolTip(toolTip)
|
|
1369
1373
|
item.setStatusTip(toolTip)
|
|
1370
1374
|
|
|
@@ -1688,13 +1692,13 @@ class Process_Tab(gPaIRS_Tab):
|
|
|
1688
1692
|
FlagStable=FlagStable and not flagUnstable
|
|
1689
1693
|
if k==self.PROpar.Nit-1:
|
|
1690
1694
|
if j==0:
|
|
1691
|
-
if self.father and hasattr(self.father,'
|
|
1695
|
+
if self.father and hasattr(self.father,'w_Output'): Res=self.father.w_Output.OUTpar.xres
|
|
1692
1696
|
else: Res=0
|
|
1693
1697
|
self.MTF=[lam,MTF.T,f'IW size-spacing: {Wa:d}-{Wc:d}. Vel.-correl. windowing: {VelWin}-{CorrWin}.',Res]
|
|
1694
1698
|
|
|
1695
1699
|
else:
|
|
1696
1700
|
if self.PROpar.Vect[j*2][kVect]>self.PROpar.Vect[(j-1)*2][kVect]:
|
|
1697
|
-
if self.father: Res=self.father.
|
|
1701
|
+
if self.father: Res=self.father.w_Output.OUTpar.xres*self.father.w_Output.OUTpar.pixAR
|
|
1698
1702
|
else: Res=0
|
|
1699
1703
|
self.MTF=[lam,MTF.T,f'IW size-spacing: {Wa:d}-{Wc:d}. Vel.-correl. windowing: {VelWin}-{CorrWin}.',Res]
|
|
1700
1704
|
if FlagStable:
|
PaIRS_UniNa/Process_Tab_Disp.py
CHANGED
|
@@ -134,15 +134,22 @@ class Process_Tab_Disp(gPaIRS_Tab):
|
|
|
134
134
|
def line_edit_IW_action(self):
|
|
135
135
|
text=self.ui.line_edit_IW.text()
|
|
136
136
|
split_text=re.split(r'(\d+)', text)[1:-1:2]
|
|
137
|
+
if len(split_text)==0:
|
|
138
|
+
message="Please insert at least one value!"
|
|
139
|
+
show_mouse_tooltip(self,message)
|
|
140
|
+
self.line_edit_IW_set()
|
|
141
|
+
return
|
|
137
142
|
split_num=[int(t) for t in split_text]
|
|
138
143
|
if len(split_num)<4: split_num+=[split_num[-1]]*(4-len(split_num))
|
|
139
144
|
vect=[int(split_num[i]) for i in (0,2,1,3)]
|
|
140
145
|
FlagValid=len(vect)==4 and all([v>0 for v in vect])
|
|
141
146
|
if FlagValid:
|
|
142
147
|
self.PROpar.Vect=vect
|
|
148
|
+
message=""
|
|
143
149
|
else:
|
|
144
150
|
message='IW sizes or spacings were assigned inconsistently! Please, retry!'
|
|
145
|
-
|
|
151
|
+
show_mouse_tooltip(self,message)
|
|
152
|
+
self.line_edit_IW_set()
|
|
146
153
|
return
|
|
147
154
|
|
|
148
155
|
#******************** Settings
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from PySide6 import QtWidgets, QtGui, QtCore
|
|
2
|
+
import sys
|
|
3
|
+
from .PaIRS_pypacks import icons_path, fontPixelSize
|
|
4
|
+
|
|
5
|
+
def showSPIVCalHelp(parent=None,disable_callback=None):
|
|
6
|
+
"""
|
|
7
|
+
Shows an informational dialog explaining the correct calibration setup
|
|
8
|
+
for stereoscopic PIV in PaIRS, ensuring compatibility with disparity
|
|
9
|
+
correction and full stereoscopic reconstruction.
|
|
10
|
+
"""
|
|
11
|
+
dlg = QtWidgets.QDialog(parent)
|
|
12
|
+
dlg.setWindowTitle("Guidelines for stereoscopic PIV calibration")
|
|
13
|
+
#dlg.resize(900, 750)
|
|
14
|
+
dlg.setMinimumWidth(900)
|
|
15
|
+
dlg.setMinimumHeight(750)
|
|
16
|
+
|
|
17
|
+
main_layout = QtWidgets.QVBoxLayout(dlg)
|
|
18
|
+
|
|
19
|
+
# --- Explanatory text (English, corrected axes) ---
|
|
20
|
+
text = (
|
|
21
|
+
"For stereoscopic PIV, PaIRS assumes that:<br><br>"
|
|
22
|
+
|
|
23
|
+
" • the <b>calibration plate defines "
|
|
24
|
+
"the x–y plane</b> of the calibration coordinate system;<br>"
|
|
25
|
+
|
|
26
|
+
" • the <b>x-axis</b> is <b>aligned with the stereoscopic baseline</b>, i.e. "
|
|
27
|
+
"the direction along which the projections of the two camera viewing rays diverge "
|
|
28
|
+
"on the calibration plate (the dominant disparity direction);<br>"
|
|
29
|
+
|
|
30
|
+
" • the <b>y-axis</b> is then defined as the axis <b>perpendicular to the plane containing "
|
|
31
|
+
"the two cameras</b> (i.e. perpendicular to the triangulation plane formed by the two "
|
|
32
|
+
"optical axes);<br>"
|
|
33
|
+
|
|
34
|
+
" • the <b>z-axis is normal to the plate</b> (typically pointing towards the cameras).<br><br>"
|
|
35
|
+
|
|
36
|
+
"To ensure full compatibility with the operations performed in the disparity correction step and the stereoscopic reconstruction,"
|
|
37
|
+
" the calibration procedure must always adhere to the above coordinate convention.<br>"
|
|
38
|
+
" The example below shows a <b>correct</b> configuration (left) and an <b>incorrect</b> one (right).<br>"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
text_label = QtWidgets.QLabel()
|
|
42
|
+
text_label.setWordWrap(True)
|
|
43
|
+
text_label.setTextFormat(QtCore.Qt.RichText)
|
|
44
|
+
text_label.setText(f"<div>{text}</div>")
|
|
45
|
+
main_layout.addWidget(text_label)
|
|
46
|
+
font=dlg.font()
|
|
47
|
+
font.setPixelSize(fontPixelSize+4)
|
|
48
|
+
text_label.setFont(font)
|
|
49
|
+
|
|
50
|
+
# --- Side-by-side images ---
|
|
51
|
+
img_layout = QtWidgets.QHBoxLayout()
|
|
52
|
+
img_layout.setSpacing(10)
|
|
53
|
+
main_layout.addLayout(img_layout)
|
|
54
|
+
|
|
55
|
+
# Paths to images (adjust to match PaIRS resources folder)
|
|
56
|
+
img_ok_path = icons_path+"spiv_setup_ok.png"
|
|
57
|
+
img_no_path = icons_path+"spiv_setup_no.png"
|
|
58
|
+
|
|
59
|
+
# --- Correct configuration image ---
|
|
60
|
+
ok_widget = QtWidgets.QVBoxLayout()
|
|
61
|
+
ok_caption = QtWidgets.QLabel()
|
|
62
|
+
caption_text = "<b>Correct configuration (x–z stereo plane)</b>"
|
|
63
|
+
ok_caption.setText(f"<div>{caption_text}</div>")
|
|
64
|
+
ok_caption.setFont(font)
|
|
65
|
+
ok_caption.setTextFormat(QtCore.Qt.RichText)
|
|
66
|
+
ok_caption.setAlignment(QtCore.Qt.AlignCenter)
|
|
67
|
+
|
|
68
|
+
ok_label_img = QtWidgets.QLabel()
|
|
69
|
+
ok_label_img.setAlignment(QtCore.Qt.AlignCenter)
|
|
70
|
+
ok_label_img.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
|
71
|
+
ok_label_img.setScaledContents(True)
|
|
72
|
+
|
|
73
|
+
ok_pix = QtGui.QPixmap(img_ok_path)
|
|
74
|
+
if not ok_pix.isNull():
|
|
75
|
+
ok_pix = ok_pix.scaledToWidth(400, QtCore.Qt.SmoothTransformation)
|
|
76
|
+
ok_label_img.setPixmap(ok_pix)
|
|
77
|
+
ok_label_img.setFixedSize(ok_pix.width(),ok_pix.height())
|
|
78
|
+
|
|
79
|
+
ok_widget.addWidget(ok_caption)
|
|
80
|
+
ok_widget.addWidget(ok_label_img)
|
|
81
|
+
img_layout.addLayout(ok_widget)
|
|
82
|
+
|
|
83
|
+
# --- Incorrect configuration image ---
|
|
84
|
+
no_widget = QtWidgets.QVBoxLayout()
|
|
85
|
+
no_caption = QtWidgets.QLabel()
|
|
86
|
+
caption_text = "<b>Incorrect configuration</b>"
|
|
87
|
+
no_caption.setText(f"<div'>{caption_text}</div>")
|
|
88
|
+
no_caption.setFont(font)
|
|
89
|
+
no_caption.setTextFormat(QtCore.Qt.RichText)
|
|
90
|
+
no_caption.setAlignment(QtCore.Qt.AlignCenter)
|
|
91
|
+
|
|
92
|
+
no_label_img = QtWidgets.QLabel()
|
|
93
|
+
no_label_img.setAlignment(QtCore.Qt.AlignCenter)
|
|
94
|
+
no_label_img.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
|
95
|
+
no_label_img.setScaledContents(True)
|
|
96
|
+
|
|
97
|
+
no_pix = QtGui.QPixmap(img_no_path)
|
|
98
|
+
if not no_pix.isNull():
|
|
99
|
+
no_pix = no_pix.scaledToWidth(400, QtCore.Qt.SmoothTransformation)
|
|
100
|
+
no_label_img.setPixmap(no_pix)
|
|
101
|
+
no_label_img.setFixedSize(no_pix.width(),no_pix.height())
|
|
102
|
+
|
|
103
|
+
no_widget.addWidget(no_caption)
|
|
104
|
+
no_widget.addWidget(no_label_img)
|
|
105
|
+
img_layout.addLayout(no_widget)
|
|
106
|
+
|
|
107
|
+
# Prevent bottom clipping: allow images row to shrink/expand as needed
|
|
108
|
+
main_layout.setStretch(0, 1)
|
|
109
|
+
main_layout.setStretch(1, 0)
|
|
110
|
+
|
|
111
|
+
# Create a horizontal layout for buttons
|
|
112
|
+
button_layout = QtWidgets.QHBoxLayout()
|
|
113
|
+
|
|
114
|
+
# Spacer pushes OK to the right
|
|
115
|
+
button_layout.addStretch(1)
|
|
116
|
+
|
|
117
|
+
# Left button
|
|
118
|
+
if disable_callback is not None:
|
|
119
|
+
button_disable = QtWidgets.QPushButton("Don't show this message again")
|
|
120
|
+
def on_disable():
|
|
121
|
+
disable_callback()
|
|
122
|
+
dlg.accept()
|
|
123
|
+
button_disable.clicked.connect(on_disable)
|
|
124
|
+
button_layout.addWidget(button_disable)
|
|
125
|
+
|
|
126
|
+
# Right button (OK)
|
|
127
|
+
button_ok = QtWidgets.QPushButton("OK")
|
|
128
|
+
def on_ok():
|
|
129
|
+
dlg.accept()
|
|
130
|
+
button_ok.clicked.connect(on_ok)
|
|
131
|
+
button_layout.addWidget(button_ok)
|
|
132
|
+
|
|
133
|
+
# Add the layout to the dialog
|
|
134
|
+
main_layout.addSpacing(12)
|
|
135
|
+
main_layout.addLayout(button_layout)
|
|
136
|
+
|
|
137
|
+
button_ok.setDefault(True)
|
|
138
|
+
button_ok.setFocus()
|
|
139
|
+
|
|
140
|
+
main_layout.setContentsMargins(20, 25, 20, 25) # slightly larger bottom margin to avoid macOS clipping
|
|
141
|
+
|
|
142
|
+
dlg.exec()
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
# QApplication MUST be created before any QWidget
|
|
146
|
+
app = QtWidgets.QApplication(sys.argv)
|
|
147
|
+
|
|
148
|
+
# Parent window (optional)
|
|
149
|
+
main_window = QtWidgets.QMainWindow()
|
|
150
|
+
main_window.show()
|
|
151
|
+
|
|
152
|
+
# Show the SPIV calibration dialog
|
|
153
|
+
showSPIVCalHelp(parent=main_window)
|
|
154
|
+
|
|
155
|
+
sys.exit(app.exec())
|
PaIRS_UniNa/Saving_tools.py
CHANGED