PaIRS-UniNa 0.2.7__cp311-cp311-win_amd64.whl → 0.2.11__cp311-cp311-win_amd64.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 +40 -24
- PaIRS_UniNa/Changes.txt +50 -0
- PaIRS_UniNa/Explorer.py +257 -77
- 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 +30 -28
- PaIRS_UniNa/Output_Tab.py +1 -3
- PaIRS_UniNa/PaIRS_pypacks.py +171 -67
- 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 +3 -0
- PaIRS_UniNa/TabTools.py +201 -9
- PaIRS_UniNa/Vis_Tab.py +221 -65
- PaIRS_UniNa/Vis_Tab_CalVi.py +139 -12
- PaIRS_UniNa/Whatsnew.py +4 -3
- PaIRS_UniNa/_PaIRS_PIV.pyd +0 -0
- PaIRS_UniNa/__init__.py +3 -3
- PaIRS_UniNa/addwidgets_ps.py +773 -97
- PaIRS_UniNa/calibView.py +5 -2
- PaIRS_UniNa/gPaIRS.py +307 -48
- PaIRS_UniNa/icons/closeAllFloat.png +0 -0
- PaIRS_UniNa/icons/defaultWinSize.png +0 -0
- PaIRS_UniNa/icons/dockVis.png +0 -0
- PaIRS_UniNa/icons/dockVis_disable.png +0 -0
- PaIRS_UniNa/icons/floatingVisSize.png +0 -0
- PaIRS_UniNa/icons/folder_loop_cleanup.png +0 -0
- PaIRS_UniNa/icons/folder_loop_cleanup_off.png +0 -0
- PaIRS_UniNa/icons/fullWinsize.png +0 -0
- PaIRS_UniNa/icons/icon_PaIRS.ico +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/smallWinSize.png +0 -0
- PaIRS_UniNa/icons/spiv_setup_no.png +0 -0
- PaIRS_UniNa/icons/spiv_setup_ok.png +0 -0
- PaIRS_UniNa/icons/undockVis.png +0 -0
- PaIRS_UniNa/procTools.py +46 -1
- PaIRS_UniNa/rqrdpckgs.txt +7 -7
- PaIRS_UniNa/tabSplitter.py +6 -1
- PaIRS_UniNa/ui_Calibration_Tab.py +92 -59
- PaIRS_UniNa/ui_gPairs.py +92 -50
- PaIRS_UniNa/ui_infoPaIRS.py +8 -8
- PaIRS_UniNa/whatsnew.txt +2 -3
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.11.dist-info}/METADATA +6 -8
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.11.dist-info}/RECORD +50 -33
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.11.dist-info}/WHEEL +1 -1
- {pairs_unina-0.2.7.dist-info → pairs_unina-0.2.11.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(gPaIRS_QMenu_style)
|
|
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
|
|
|
@@ -475,13 +476,16 @@ class PaIRSTree(QTreeWidget):
|
|
|
475
476
|
#self.setStyleSheet(f"QTreeWidget::item:selected {{{style}}}")
|
|
476
477
|
#self.setStyleSheet(f"QTreeWidget::item:selected:active {{{style}}}")
|
|
477
478
|
|
|
478
|
-
style = """
|
|
479
|
-
QTreeWidget::item:
|
|
480
|
-
background-color:
|
|
481
|
-
}
|
|
482
|
-
QTreeWidget::item:selected
|
|
483
|
-
background-color:
|
|
484
|
-
}
|
|
479
|
+
style = f"""
|
|
480
|
+
QTreeWidget::item:hover {{
|
|
481
|
+
background-color: {PaIRS_ghostblue};
|
|
482
|
+
}}
|
|
483
|
+
QTreeWidget::item:selected:!active {{
|
|
484
|
+
background-color: {PaIRS_lightblue};
|
|
485
|
+
}}
|
|
486
|
+
QTreeWidget::item:selected:active {{
|
|
487
|
+
background-color: {PaIRS_blue};
|
|
488
|
+
}}
|
|
485
489
|
"""
|
|
486
490
|
self.setStyleSheet(style)
|
|
487
491
|
|
|
@@ -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):
|
|
@@ -2156,7 +2160,7 @@ class ImageTreeWidget(QWidget):
|
|
|
2156
2160
|
self.imTree.spinSelection([r+1,c+1,f+1])
|
|
2157
2161
|
else: #single tree
|
|
2158
2162
|
self.label.setText(self.name + f' (cam: {c+1}, frame: {f+1})')
|
|
2159
|
-
self.label.setStyleSheet('QLabel{color:
|
|
2163
|
+
self.label.setStyleSheet(f'QLabel{{color: {PaIRS_blue}}}')
|
|
2160
2164
|
self.icon_label.setVisible(True)
|
|
2161
2165
|
indTree=1+c*2+f
|
|
2162
2166
|
if indTree!=self.indTree:
|
|
@@ -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(gPaIRS_QMenu_style)
|
|
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,17 +2630,17 @@ 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
|
-
style = """
|
|
2636
|
-
|
|
2637
|
-
background-color:
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
background-color:
|
|
2641
|
-
}
|
|
2634
|
+
style = f"""
|
|
2635
|
+
QTableWidget::item:hover {{
|
|
2636
|
+
background-color: {PaIRS_ghostblue};
|
|
2637
|
+
}}
|
|
2638
|
+
QTableWidget::item:selected:!active {{
|
|
2639
|
+
background-color: {PaIRS_lightblue};
|
|
2640
|
+
}}
|
|
2641
|
+
QTableWidget::item:selected:active {{
|
|
2642
|
+
background-color: {PaIRS_blue};
|
|
2643
|
+
}}
|
|
2642
2644
|
"""
|
|
2643
2645
|
self.setStyleSheet(style)
|
|
2644
2646
|
|
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,74 @@ 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
|
+
PaIRS_blue_transp = 0.8
|
|
154
|
+
PaIRS_blue_transp_str = f"{PaIRS_blue_transp:.2f}"
|
|
155
|
+
PaIRS_blue = f"rgba(0, 116, 255, {PaIRS_blue_transp_str})"
|
|
156
|
+
PaIRS_lightblue = PaIRS_blue.replace(PaIRS_blue_transp_str,f"{PaIRS_blue_transp/2:.2f}")
|
|
157
|
+
PaIRS_faintblue = PaIRS_blue.replace(PaIRS_blue_transp_str,f"{PaIRS_blue_transp/4:.2f}")
|
|
158
|
+
PaIRS_ghostblue = PaIRS_blue.replace(PaIRS_blue_transp_str,f"{PaIRS_blue_transp/8:.2f}")
|
|
159
|
+
PaIRS_pink = "rgb(255,230,230)"
|
|
160
|
+
PaIRS_darkblue = "rgb(33,33,255)"
|
|
161
|
+
PaIRS_gray = "rgb(128,128,128)"
|
|
162
|
+
gPaIRS_QMenu_style=f"""
|
|
163
|
+
QMenu::item:selected{{
|
|
164
|
+
background-color: {PaIRS_blue};
|
|
165
|
+
color: white;
|
|
166
|
+
}}
|
|
167
|
+
QMenu::item:checked,
|
|
168
|
+
QMenu::item:pressed {{
|
|
169
|
+
background-color: {PaIRS_blue};
|
|
170
|
+
color: none;
|
|
171
|
+
}}
|
|
172
|
+
QMenu::item:hover {{
|
|
173
|
+
background-color: {PaIRS_lightblue};
|
|
174
|
+
color: none;
|
|
175
|
+
}}
|
|
176
|
+
"""
|
|
177
|
+
gPaIRS_Completer_style = f"""
|
|
178
|
+
QAbstractItemView {{
|
|
179
|
+
background: transparent,
|
|
180
|
+
}}
|
|
181
|
+
QAbstractItemView::item {{
|
|
182
|
+
padding: 4px 3px;
|
|
183
|
+
}}
|
|
184
|
+
QAbstractItemView::item:hover {{
|
|
185
|
+
background-color: {PaIRS_faintblue};
|
|
186
|
+
color: none;
|
|
187
|
+
}}
|
|
188
|
+
QAbstractItemView::item:focus {{
|
|
189
|
+
background-color: {PaIRS_blue};
|
|
190
|
+
color: white;
|
|
191
|
+
}}
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
def str2QColor(s: str):
|
|
195
|
+
s = s.strip().lower()
|
|
196
|
+
|
|
197
|
+
# Match rgba(r, g, b, a)
|
|
198
|
+
m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([0-9.]+)\s*\)", s)
|
|
199
|
+
if m:
|
|
200
|
+
r, g, b, a = m.groups()
|
|
201
|
+
r, g, b = int(r), int(g), int(b)
|
|
202
|
+
# CSS alpha is 0..1, Qt uses 0..255
|
|
203
|
+
a = int(float(a) * 255)
|
|
204
|
+
return QColor(r, g, b, a)
|
|
205
|
+
|
|
206
|
+
# Match rgb(r, g, b)
|
|
207
|
+
m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)", s)
|
|
208
|
+
if m:
|
|
209
|
+
r, g, b = map(int, m.groups())
|
|
210
|
+
return QColor(r, g, b)
|
|
211
|
+
|
|
212
|
+
raise ValueError(f"Invalid CSS color format: {s}")
|
|
213
|
+
|
|
214
|
+
|
|
150
215
|
from psutil import cpu_count
|
|
151
216
|
NUMTHREADS_MAX=cpu_count(logical=True)#-1
|
|
152
217
|
if NUMTHREADS_MAX<1: NUMTHREADS_MAX=1
|
|
@@ -276,7 +341,7 @@ def activateFlagDebug(Flag=True):
|
|
|
276
341
|
|
|
277
342
|
PaIRS_Header=f'PaIRS - version {__version__}\n'+\
|
|
278
343
|
'Particle Image Reconstruction Software\n'+\
|
|
279
|
-
f'(C) {__year__} Gerardo Paolillo & Tommaso Astarita.\nAll rights reserved.\n'+\
|
|
344
|
+
f'(C) 2023-{__year__} Gerardo Paolillo & Tommaso Astarita.\nAll rights reserved.\n'+\
|
|
280
345
|
f'email: {__mail__}\n'+\
|
|
281
346
|
'****************************************\n'
|
|
282
347
|
|
|
@@ -634,6 +699,13 @@ def myStandardRoot(root):
|
|
|
634
699
|
currroot = re.sub('/+', '/', currroot) # Reduce consecutive slashes to a single slash
|
|
635
700
|
return currroot
|
|
636
701
|
|
|
702
|
+
def relativizePath(currpath:str):
|
|
703
|
+
return currpath
|
|
704
|
+
directory_path = myStandardPath(os.getcwd())
|
|
705
|
+
if directory_path in currpath:
|
|
706
|
+
currpath=currpath.replace(directory_path,'./')
|
|
707
|
+
return currpath
|
|
708
|
+
|
|
637
709
|
def findFiles_sorted(pattern):
|
|
638
710
|
list_files=glob.glob(pattern)
|
|
639
711
|
files=sorted([re.sub(r'\\+',r'/',f) for f in list_files],key=str.lower)
|
|
@@ -758,17 +830,48 @@ def identifierName(typeObject:str='proc'):
|
|
|
758
830
|
name=version_user_info+'_'+typeObject+'_'+id
|
|
759
831
|
return name, username, __version__
|
|
760
832
|
|
|
833
|
+
def fileIdenitifierCheck(id: str, filename: str) -> bool:
|
|
834
|
+
"""
|
|
835
|
+
Extract the date/time from the identifier 'name' and check whether 'filename'
|
|
836
|
+
has been modified after that timestamp.
|
|
837
|
+
Returns True if file is newer, False otherwise.
|
|
838
|
+
"""
|
|
839
|
+
|
|
840
|
+
# --- Extract timestamp from name ---
|
|
841
|
+
# Expected pattern: ..._<date>-<time>_...
|
|
842
|
+
# Example: 'PaIRS-v0.2.8_Linux-user_2025/11/07-12:45:31_proc_...'
|
|
843
|
+
try:
|
|
844
|
+
parts = id.split('_')
|
|
845
|
+
date_str, time_str = parts[2].split('-')[0], parts[2].split('-')[1]
|
|
846
|
+
except Exception:
|
|
847
|
+
pri.Error.red("Identifier format not recognized: cannot extract date and time.")
|
|
848
|
+
return False
|
|
849
|
+
|
|
850
|
+
# Convert date/time strings into QDateTime
|
|
851
|
+
qdate = QDate.fromString(date_str, 'yyyy/MM/dd')
|
|
852
|
+
qtime = QTime.fromString(time_str, 'HH:mm:ss')
|
|
853
|
+
qdt_identifier = QDateTime(qdate, qtime)
|
|
854
|
+
qdt_identifier = qdt_identifier.addSecs(-1) #to be safe
|
|
855
|
+
|
|
856
|
+
if not qdt_identifier.isValid():
|
|
857
|
+
pri.Error.red("Parsed QDateTime is not valid. Check identifier format.")
|
|
858
|
+
return False
|
|
859
|
+
|
|
860
|
+
# --- File timestamp ---
|
|
861
|
+
if not os.path.exists(filename):
|
|
862
|
+
return False
|
|
863
|
+
|
|
864
|
+
file_mtime = os.path.getmtime(filename)
|
|
865
|
+
qdt_file = QDateTime.fromSecsSinceEpoch(int(file_mtime))
|
|
866
|
+
|
|
867
|
+
# True if file was modified after the timestamp stored in name
|
|
868
|
+
return qdt_file > qdt_identifier
|
|
869
|
+
|
|
761
870
|
PlainTextConverter=QtGui.QTextDocument()
|
|
762
871
|
def toPlainText(text):
|
|
763
872
|
PlainTextConverter.setHtml(text) #for safety
|
|
764
873
|
return PlainTextConverter.toPlainText()
|
|
765
874
|
|
|
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
875
|
def clean_tree(tree:QTreeWidget):
|
|
773
876
|
def remove_children(item:QTreeWidgetItem):
|
|
774
877
|
while item.childCount() > 0:
|
|
@@ -1121,59 +1224,6 @@ def checkOutDated(packageName:str,printOutDated):
|
|
|
1121
1224
|
f3=executor.submit(asyncio.run,checkOutDatedInternal(packageName))
|
|
1122
1225
|
f3.add_done_callback(checkOutDatedComplete)
|
|
1123
1226
|
|
|
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
1227
|
import webbrowser
|
|
1178
1228
|
def downloadExampleData(self,url):
|
|
1179
1229
|
Message=f'Test data are available at the following link:\n{url}'
|
|
@@ -1242,6 +1292,8 @@ class PaIRSApp(QApplication):
|
|
|
1242
1292
|
super().__init__(*args)
|
|
1243
1293
|
self.installMessageHandler()
|
|
1244
1294
|
self.setStyle('Fusion')
|
|
1295
|
+
self.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
1296
|
+
self.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
1245
1297
|
|
|
1246
1298
|
def applicationSupportsSecureRestorableState(self):
|
|
1247
1299
|
return True
|
|
@@ -1309,7 +1361,7 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1309
1361
|
else:
|
|
1310
1362
|
pri.Error.red(f"Malformed line: {line}")
|
|
1311
1363
|
|
|
1312
|
-
|
|
1364
|
+
FlagUpdateFile = False
|
|
1313
1365
|
warnings = []
|
|
1314
1366
|
|
|
1315
1367
|
for i, pkg in enumerate(required_packages):
|
|
@@ -1319,9 +1371,10 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1319
1371
|
installed_version = None
|
|
1320
1372
|
|
|
1321
1373
|
# Update current installed version
|
|
1322
|
-
if installed_version is not None
|
|
1323
|
-
vcurr_list[i]
|
|
1324
|
-
|
|
1374
|
+
if installed_version is not None:
|
|
1375
|
+
if installed_version != vcurr_list[i]:
|
|
1376
|
+
vcurr_list[i] = installed_version
|
|
1377
|
+
FlagUpdateFile = True
|
|
1325
1378
|
|
|
1326
1379
|
# Check if within [vmin, vmax]
|
|
1327
1380
|
if not (le_ver(vmin_list[i],installed_version) and le_ver(installed_version,vmax_list[i])):
|
|
@@ -1336,7 +1389,7 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1336
1389
|
|
|
1337
1390
|
# Show warning
|
|
1338
1391
|
if len(warnings)>0: self.FlagPackIssue=True
|
|
1339
|
-
if len(warnings)>0 or FlagForcePrint:
|
|
1392
|
+
if ( (FlagUpdateFile or FlagDisplay) and len(warnings)>0 ) or FlagForcePrint :
|
|
1340
1393
|
message = (
|
|
1341
1394
|
"Some installed packages have a version outside the target range used to develop "
|
|
1342
1395
|
"the current release of the PaIRS_UniNa package.\n\n"
|
|
@@ -1368,9 +1421,60 @@ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False,
|
|
|
1368
1421
|
warningDialog(self, Message="All installed packages are within the expected version range.", flagScreenCenter=True,pixmap=icons_path+'greenv.png')
|
|
1369
1422
|
|
|
1370
1423
|
# Update file if needed
|
|
1371
|
-
if
|
|
1424
|
+
if FlagUpdateFile:
|
|
1372
1425
|
with open(filename, "w") as f:
|
|
1373
1426
|
for pkg, vmin, vmax, vcurr in zip(required_packages, vmin_list, vmax_list, vcurr_list):
|
|
1374
1427
|
f.write(f"{pkg}\t{vmin}\t{vmax}\t{vcurr if vcurr else 0}\n")
|
|
1375
1428
|
|
|
1376
|
-
return required_packages, vmin_list, vmax_list, vcurr_list
|
|
1429
|
+
return required_packages, vmin_list, vmax_list, vcurr_list
|
|
1430
|
+
|
|
1431
|
+
import base64
|
|
1432
|
+
def save_frame_geometry_to_param(self: QMainWindow):
|
|
1433
|
+
print_frame_geometry(self,f"({self.objectName()}) Saving frame geometry ***")
|
|
1434
|
+
fg = self.frameGeometry()
|
|
1435
|
+
payload = {"type": "frame", "x": fg.x(), "y": fg.y(), "w": fg.width(), "h": fg.height()}
|
|
1436
|
+
return base64.b64encode(json.dumps(payload).encode("utf-8")).decode("ascii")
|
|
1437
|
+
|
|
1438
|
+
def print_frame_geometry(self: QMainWindow, tag="", target = ""):
|
|
1439
|
+
fg = self.frameGeometry()
|
|
1440
|
+
g = self.geometry()
|
|
1441
|
+
wh = self.windowHandle()
|
|
1442
|
+
m = wh.frameMargins() if wh else None
|
|
1443
|
+
pri.Callback.yellow(tag,
|
|
1444
|
+
"frame", fg.x(), fg.y(), fg.width(), fg.height(),
|
|
1445
|
+
"client", g.x(), g.y(), g.width(), g.height(),
|
|
1446
|
+
"margins", (m.left(), m.top(), m.right(), m.bottom()) if m else None)
|
|
1447
|
+
if target:
|
|
1448
|
+
pri.Callback.yellow(tag,
|
|
1449
|
+
"target", target.x(), target.y(), target.width(), target.height())
|
|
1450
|
+
|
|
1451
|
+
def restore_frame_geometry_from_param(self: QMainWindow, geometry: str, max_iter: int = 10):
|
|
1452
|
+
try:
|
|
1453
|
+
payload = json.loads(base64.b64decode(geometry).decode("utf-8"))
|
|
1454
|
+
except Exception:
|
|
1455
|
+
return
|
|
1456
|
+
if payload.get("type") != "frame":
|
|
1457
|
+
return
|
|
1458
|
+
|
|
1459
|
+
target = QRect(payload["x"], payload["y"], payload["w"], payload["h"])
|
|
1460
|
+
tag=f"({self.objectName()}) Setting frame geometry | "
|
|
1461
|
+
print_frame_geometry(self,tag,target)
|
|
1462
|
+
spaces=tag[:-3]+'---'
|
|
1463
|
+
|
|
1464
|
+
def step(i=0):
|
|
1465
|
+
cur = self.frameGeometry()
|
|
1466
|
+
dx = target.x() - cur.x()
|
|
1467
|
+
dy = target.y() - cur.y()
|
|
1468
|
+
dw = target.width() - cur.width()
|
|
1469
|
+
dh = target.height() - cur.height()
|
|
1470
|
+
|
|
1471
|
+
if (dx, dy, dw, dh) == (0, 0, 0, 0) or i >= max_iter:
|
|
1472
|
+
return False
|
|
1473
|
+
|
|
1474
|
+
g = self.geometry() # client
|
|
1475
|
+
self.setGeometry(QRect(g.x() + dx, g.y() + dy, g.width() + dw, g.height() + dh))
|
|
1476
|
+
print_frame_geometry(self,spaces)
|
|
1477
|
+
if i>0: QTimer.singleShot(200, lambda: step(i + 1))
|
|
1478
|
+
return True
|
|
1479
|
+
|
|
1480
|
+
if step(0): QTimer.singleShot(200, lambda: step(1))
|
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(gPaIRS_QMenu_style)
|
|
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
|