spiceitup 2.0.0__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spiceitup-2.0.0 → spiceitup-2.0.1}/PKG-INFO +7 -2
- {spiceitup-2.0.0 → spiceitup-2.0.1}/setup.py +15 -4
- spiceitup-2.0.1/spiceitup/__init__.py +7 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup/__main__.py +2 -1
- spiceitup-2.0.1/spiceitup/layout/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/layout/flow_layout.py +91 -0
- spiceitup-2.0.1/spiceitup/layout/flow_layout_edited.py +98 -0
- spiceitup-2.0.1/spiceitup/layout/flow_layout_old.py +96 -0
- spiceitup-2.0.1/spiceitup/logs/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/logs/log.py +41 -0
- spiceitup-2.0.1/spiceitup/main/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/main/footer.py +229 -0
- spiceitup-2.0.1/spiceitup/main/header.py +154 -0
- spiceitup-2.0.1/spiceitup/main/menu.py +624 -0
- spiceitup-2.0.1/spiceitup/main/menu_bar.py +184 -0
- spiceitup-2.0.1/spiceitup/main/w.py +28 -0
- spiceitup-2.0.1/spiceitup/main/winc.py +508 -0
- spiceitup-2.0.1/spiceitup/plot/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/plot/plot_line.py +182 -0
- spiceitup-2.0.1/spiceitup/plot/plot_map.py +539 -0
- spiceitup-2.0.1/spiceitup/processing/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/processing/dh.py +376 -0
- spiceitup-2.0.1/spiceitup/processing/fit.py +55 -0
- spiceitup-2.0.1/spiceitup/processing/threads/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/processing/threads/run_thread.py +137 -0
- spiceitup-2.0.1/spiceitup/processing/threads/thread_worker.py +20 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/compute_average.py +34 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/generate_fit_maps.py +65 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/load_plot_map.py +21 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/make_process_available.py +17 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/out_of_sync.py +30 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/update_levels.py +37 -0
- spiceitup-2.0.1/spiceitup/processing/threads/workers/update_profile.py +77 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup/run.py +2 -2
- spiceitup-2.0.1/spiceitup/settings/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/settings/const.py +131 -0
- spiceitup-2.0.1/spiceitup/style/style.css +32 -0
- spiceitup-2.0.1/spiceitup/style/style_debug.css +13 -0
- spiceitup-2.0.1/spiceitup/toolbox/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/toolbox/binning.py +60 -0
- spiceitup-2.0.1/spiceitup/toolbox/clickable_qlabel.py +9 -0
- spiceitup-2.0.1/spiceitup/toolbox/filter2D_tests.py +27 -0
- spiceitup-2.0.1/spiceitup/toolbox/level_methods.py +23 -0
- spiceitup-2.0.1/spiceitup/toolbox/multithreading.py +22 -0
- spiceitup-2.0.1/spiceitup/toolbox/pyqtgraph_examples.py +10 -0
- spiceitup-2.0.1/spiceitup/toolbox/pyqtgraph_plot_example.py +104 -0
- spiceitup-2.0.1/spiceitup/toolbox/scipy_fit.py +29 -0
- spiceitup-2.0.1/spiceitup/toolbox/scipy_fit_gaussian.py +39 -0
- spiceitup-2.0.1/spiceitup/toolbox/show_icons.py +29 -0
- spiceitup-2.0.1/spiceitup/toolbox/signal_thread_ex.py +16 -0
- spiceitup-2.0.1/spiceitup/toolbox/small_pyqtgraph.py +91 -0
- spiceitup-2.0.1/spiceitup/toolbox/small_pyqtgraph2.py +94 -0
- spiceitup-2.0.1/spiceitup/toolbox/test.py +63 -0
- spiceitup-2.0.1/spiceitup/utils/__init__.py +0 -0
- spiceitup-2.0.1/spiceitup/utils/internal_utils.py +188 -0
- spiceitup-2.0.1/spiceitup/utils/special_chars.py +5 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup.egg-info/PKG-INFO +7 -2
- spiceitup-2.0.1/spiceitup.egg-info/SOURCES.txt +65 -0
- spiceitup-2.0.1/spiceitup.egg-info/requires.txt +6 -0
- spiceitup-2.0.0/spiceitup.egg-info/SOURCES.txt +0 -12
- spiceitup-2.0.0/spiceitup.egg-info/requires.txt +0 -1
- {spiceitup-2.0.0 → spiceitup-2.0.1}/README.md +0 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/pyproject.toml +0 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/setup.cfg +0 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup/main_call.py +0 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup.egg-info/dependency_links.txt +0 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup.egg-info/entry_points.txt +0 -0
- {spiceitup-2.0.0 → spiceitup-2.0.1}/spiceitup.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spiceitup
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: Quicklook tool for level 2 SPICE imaging
|
|
5
5
|
Home-page: https://git.ias.u-psud.fr/spice/data_quicklook
|
|
6
6
|
Author: David Picard
|
|
@@ -9,7 +9,12 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Requires-Python: >=3.7
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist:
|
|
12
|
+
Requires-Dist: PyQt6
|
|
13
|
+
Requires-Dist: sunraster
|
|
14
|
+
Requires-Dist: pyqtgraph
|
|
15
|
+
Requires-Dist: qdarkgraystyle
|
|
16
|
+
Requires-Dist: scipy
|
|
17
|
+
Requires-Dist: opencv-python-headless
|
|
13
18
|
Dynamic: author
|
|
14
19
|
Dynamic: author-email
|
|
15
20
|
Dynamic: classifier
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import os
|
|
2
|
-
|
|
2
|
+
from pathlib import Path
|
|
3
3
|
from importlib_metadata import entry_points
|
|
4
4
|
from setuptools import setup, find_packages
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='spiceitup',
|
|
8
|
-
version='2.0.
|
|
8
|
+
version='2.0.1',
|
|
9
9
|
description='Quicklook tool for level 2 SPICE imaging',
|
|
10
10
|
long_description=open('README.md').read(),
|
|
11
11
|
long_description_content_type='text/markdown',
|
|
12
12
|
author='David Picard',
|
|
13
13
|
author_email='david.picard@universite-paris-saclay.fr',
|
|
14
14
|
url='https://git.ias.u-psud.fr/spice/data_quicklook',
|
|
15
|
-
packages=
|
|
16
|
-
|
|
15
|
+
packages=find_packages(),
|
|
16
|
+
include_package_data=True,
|
|
17
|
+
package_data={
|
|
18
|
+
"spiceitup": ["style/*.css"],
|
|
19
|
+
},
|
|
20
|
+
install_requires=[
|
|
21
|
+
"PyQt6",
|
|
22
|
+
"sunraster",
|
|
23
|
+
"pyqtgraph",
|
|
24
|
+
"qdarkgraystyle",
|
|
25
|
+
"scipy",
|
|
26
|
+
"opencv-python-headless"
|
|
27
|
+
],
|
|
17
28
|
classifiers=[
|
|
18
29
|
'Programming Language :: Python :: 3',
|
|
19
30
|
'License :: OSI Approved :: MIT License',
|
|
File without changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from PyQt6.QtCore import QPoint, QRect, QSize, Qt
|
|
2
|
+
from PyQt6.QtWidgets import (QApplication, QLayout, QPushButton, QSizePolicy, QWidget, QGridLayout)
|
|
3
|
+
|
|
4
|
+
class FlowLayout(QLayout):
|
|
5
|
+
|
|
6
|
+
def __init__(self, parent = None, margin = 0, spacing = -1):
|
|
7
|
+
super(FlowLayout, self).__init__(parent)
|
|
8
|
+
|
|
9
|
+
if parent is not None:
|
|
10
|
+
self.setContentsMargins(margin, margin, margin, margin)
|
|
11
|
+
|
|
12
|
+
self.setSpacing(spacing)
|
|
13
|
+
|
|
14
|
+
self.itemList = []
|
|
15
|
+
|
|
16
|
+
def __del__(self):
|
|
17
|
+
item = self.takeAt(0)
|
|
18
|
+
while item:
|
|
19
|
+
item = self.takeAt(0)
|
|
20
|
+
|
|
21
|
+
def addItem(self, item):
|
|
22
|
+
self.itemList.append(item)
|
|
23
|
+
|
|
24
|
+
def count(self):
|
|
25
|
+
return len(self.itemList)
|
|
26
|
+
|
|
27
|
+
def itemAt(self, index):
|
|
28
|
+
if index >= 0 and index < len(self.itemList):
|
|
29
|
+
return self.itemList[index]
|
|
30
|
+
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
def takeAt(self, index):
|
|
34
|
+
if index >= 0 and index < len(self.itemList):
|
|
35
|
+
return self.itemList.pop(index)
|
|
36
|
+
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def expandingDirections(self):
|
|
40
|
+
return Qt.Orientations(Qt.Orientation(0))
|
|
41
|
+
|
|
42
|
+
def hasHeightForWidth(self):
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
def heightForWidth(self, width):
|
|
46
|
+
height = self.doLayout(QRect(0, 0, width, 0), True)
|
|
47
|
+
return height
|
|
48
|
+
|
|
49
|
+
def setGeometry(self, rect):
|
|
50
|
+
super(FlowLayout, self).setGeometry(rect)
|
|
51
|
+
self.doLayout(rect, False)
|
|
52
|
+
|
|
53
|
+
def sizeHint(self):
|
|
54
|
+
return self.minimumSize()
|
|
55
|
+
|
|
56
|
+
def minimumSize(self):
|
|
57
|
+
size = QSize()
|
|
58
|
+
|
|
59
|
+
for item in self.itemList:
|
|
60
|
+
size = size.expandedTo(item.minimumSize())
|
|
61
|
+
|
|
62
|
+
margin, _, _, _ = self.getContentsMargins()
|
|
63
|
+
|
|
64
|
+
size += QSize(2 * margin, 2 * margin)
|
|
65
|
+
return size
|
|
66
|
+
|
|
67
|
+
def doLayout(self, rect, testOnly):
|
|
68
|
+
x = rect.x()
|
|
69
|
+
y = rect.y()
|
|
70
|
+
lineHeight = 0
|
|
71
|
+
|
|
72
|
+
for item in self.itemList:
|
|
73
|
+
wid = item.widget()
|
|
74
|
+
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.ControlType.PushButton,
|
|
75
|
+
QSizePolicy.ControlType.PushButton, Qt.Orientation.Horizontal)
|
|
76
|
+
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.ControlType.PushButton,
|
|
77
|
+
QSizePolicy.ControlType.PushButton, Qt.Orientation.Vertical)
|
|
78
|
+
nextX = x + item.sizeHint().width() + spaceX
|
|
79
|
+
if nextX - spaceX > rect.right() and lineHeight > 0:
|
|
80
|
+
x = rect.x()
|
|
81
|
+
y = y + lineHeight + spaceY
|
|
82
|
+
nextX = x + item.sizeHint().width() + spaceX
|
|
83
|
+
lineHeight = 0
|
|
84
|
+
|
|
85
|
+
if not testOnly:
|
|
86
|
+
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
|
87
|
+
|
|
88
|
+
x = nextX
|
|
89
|
+
lineHeight = max(lineHeight, item.sizeHint().height())
|
|
90
|
+
|
|
91
|
+
return y + lineHeight - rect.y()
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from PyQt6.QtCore import QPoint, QRect, QSize, Qt
|
|
2
|
+
from PyQt6.QtWidgets import (QApplication, QLayout, QPushButton, QSizePolicy, QWidget, QGridLayout)
|
|
3
|
+
|
|
4
|
+
class FlowLayout(QLayout):
|
|
5
|
+
q_plot_size = None
|
|
6
|
+
|
|
7
|
+
def __init__(self, parent = None, margin = 0, spacing = -1, q_plot_size = None):
|
|
8
|
+
# plot_size must be a QSize object
|
|
9
|
+
super(FlowLayout, self).__init__(parent)
|
|
10
|
+
|
|
11
|
+
if q_plot_size is not None:
|
|
12
|
+
self.q_plot_size = q_plot_size
|
|
13
|
+
self.q_plot_size.setHeight(int(self.q_plot_size.width() * 1.3)) # because of win title
|
|
14
|
+
self.q_plot_size.setWidth(self.q_plot_size.width() * 2) # because 2 plots x,y and lambda,y
|
|
15
|
+
|
|
16
|
+
if parent is not None:
|
|
17
|
+
self.setContentsMargins(margin, margin, margin, margin)
|
|
18
|
+
|
|
19
|
+
self.setSpacing(spacing)
|
|
20
|
+
|
|
21
|
+
self.itemList = []
|
|
22
|
+
|
|
23
|
+
def __del__(self):
|
|
24
|
+
item = self.takeAt(0)
|
|
25
|
+
while item:
|
|
26
|
+
item = self.takeAt(0)
|
|
27
|
+
|
|
28
|
+
def addItem(self, item):
|
|
29
|
+
self.itemList.append(item)
|
|
30
|
+
|
|
31
|
+
def count(self):
|
|
32
|
+
return len(self.itemList)
|
|
33
|
+
|
|
34
|
+
def itemAt(self, index):
|
|
35
|
+
if index >= 0 and index < len(self.itemList):
|
|
36
|
+
return self.itemList[index]
|
|
37
|
+
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
def takeAt(self, index):
|
|
41
|
+
if index >= 0 and index < len(self.itemList):
|
|
42
|
+
return self.itemList.pop(index)
|
|
43
|
+
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def expandingDirections(self):
|
|
47
|
+
return Qt.Orientations(Qt.Orientation(0))
|
|
48
|
+
|
|
49
|
+
def hasHeightForWidth(self):
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
def heightForWidth(self, width):
|
|
53
|
+
height = self.doLayout(QRect(0, 0, width, 0), True)
|
|
54
|
+
return height
|
|
55
|
+
|
|
56
|
+
def setGeometry(self, rect):
|
|
57
|
+
super(FlowLayout, self).setGeometry(rect)
|
|
58
|
+
self.doLayout(rect, False)
|
|
59
|
+
|
|
60
|
+
def sizeHint(self):
|
|
61
|
+
return self.minimumSize()
|
|
62
|
+
|
|
63
|
+
def minimumSize(self):
|
|
64
|
+
size = QSize()
|
|
65
|
+
|
|
66
|
+
for item in self.itemList:
|
|
67
|
+
size = size.expandedTo(item.minimumSize())
|
|
68
|
+
|
|
69
|
+
margin, _, _, _ = self.getContentsMargins()
|
|
70
|
+
|
|
71
|
+
size += QSize(2 * margin, 2 * margin)
|
|
72
|
+
return size
|
|
73
|
+
|
|
74
|
+
def doLayout(self, rect, testOnly):
|
|
75
|
+
x = rect.x()
|
|
76
|
+
y = rect.y()
|
|
77
|
+
lineHeight = 0
|
|
78
|
+
|
|
79
|
+
for item in self.itemList:
|
|
80
|
+
wid = item.widget()
|
|
81
|
+
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.ControlType.PushButton,
|
|
82
|
+
QSizePolicy.ControlType.PushButton, Qt.Orientation.Horizontal)
|
|
83
|
+
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.ControlType.PushButton,
|
|
84
|
+
QSizePolicy.ControlType.PushButton, Qt.Orientation.Vertical)
|
|
85
|
+
nextX = x + self.q_plot_size.width() + spaceX #nextX = x + item.sizeHint().width() + spaceX
|
|
86
|
+
if nextX - spaceX > rect.right() and lineHeight > 0:
|
|
87
|
+
x = rect.x()
|
|
88
|
+
y = y + lineHeight + spaceY
|
|
89
|
+
nextX = x + self.q_plot_size.width() + spaceX #nextX = x + item.sizeHint().width() + spaceX
|
|
90
|
+
lineHeight = 0
|
|
91
|
+
|
|
92
|
+
if not testOnly:
|
|
93
|
+
item.setGeometry(QRect(QPoint(x, y), self.q_plot_size)) #item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
|
94
|
+
|
|
95
|
+
x = nextX
|
|
96
|
+
lineHeight = max(lineHeight, self.q_plot_size.height()) #lineHeight = max(lineHeight, item.sizeHint().height())
|
|
97
|
+
|
|
98
|
+
return y + lineHeight - rect.y()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from PyQt6.QtCore import QPoint, QRect, QSize, Qt
|
|
2
|
+
from PyQt6.QtWidgets import (QApplication, QLayout, QPushButton, QSizePolicy, QWidget, QGridLayout)
|
|
3
|
+
|
|
4
|
+
class FlowLayout(QLayout):
|
|
5
|
+
q_plot_size = None
|
|
6
|
+
|
|
7
|
+
def __init__(self, parent = None, margin = 0, spacing = -1, q_plot_size = None):
|
|
8
|
+
# plot_size must be a QSize object
|
|
9
|
+
super(FlowLayout, self).__init__(parent)
|
|
10
|
+
|
|
11
|
+
if q_plot_size is not None:
|
|
12
|
+
self.q_plot_size = q_plot_size
|
|
13
|
+
|
|
14
|
+
if parent is not None:
|
|
15
|
+
self.setContentsMargins(margin, margin, margin, margin)
|
|
16
|
+
|
|
17
|
+
self.setSpacing(spacing)
|
|
18
|
+
|
|
19
|
+
self.itemList = []
|
|
20
|
+
|
|
21
|
+
def __del__(self):
|
|
22
|
+
item = self.takeAt(0)
|
|
23
|
+
while item:
|
|
24
|
+
item = self.takeAt(0)
|
|
25
|
+
|
|
26
|
+
def addItem(self, item):
|
|
27
|
+
self.itemList.append(item)
|
|
28
|
+
|
|
29
|
+
def count(self):
|
|
30
|
+
return len(self.itemList)
|
|
31
|
+
|
|
32
|
+
def itemAt(self, index):
|
|
33
|
+
if index >= 0 and index < len(self.itemList):
|
|
34
|
+
return self.itemList[index]
|
|
35
|
+
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
def takeAt(self, index):
|
|
39
|
+
if index >= 0 and index < len(self.itemList):
|
|
40
|
+
return self.itemList.pop(index)
|
|
41
|
+
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def expandingDirections(self):
|
|
45
|
+
return Qt.Orientations(Qt.Orientation(0))
|
|
46
|
+
|
|
47
|
+
def hasHeightForWidth(self):
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
def heightForWidth(self, width):
|
|
51
|
+
height = self.doLayout(QRect(0, 0, width, 0), True)
|
|
52
|
+
return height
|
|
53
|
+
|
|
54
|
+
def setGeometry(self, rect):
|
|
55
|
+
super(FlowLayout, self).setGeometry(rect)
|
|
56
|
+
self.doLayout(rect, False)
|
|
57
|
+
|
|
58
|
+
def sizeHint(self):
|
|
59
|
+
return self.minimumSize()
|
|
60
|
+
|
|
61
|
+
def minimumSize(self):
|
|
62
|
+
size = QSize()
|
|
63
|
+
|
|
64
|
+
for item in self.itemList:
|
|
65
|
+
size = size.expandedTo(item.minimumSize())
|
|
66
|
+
|
|
67
|
+
margin, _, _, _ = self.getContentsMargins()
|
|
68
|
+
|
|
69
|
+
size += QSize(2 * margin, 2 * margin)
|
|
70
|
+
return size
|
|
71
|
+
|
|
72
|
+
def doLayout(self, rect, testOnly):
|
|
73
|
+
x = rect.x()
|
|
74
|
+
y = rect.y()
|
|
75
|
+
lineHeight = 0
|
|
76
|
+
|
|
77
|
+
for item in self.itemList:
|
|
78
|
+
wid = item.widget()
|
|
79
|
+
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.ControlType.PushButton,
|
|
80
|
+
QSizePolicy.ControlType.PushButton, Qt.Orientation.Horizontal)
|
|
81
|
+
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.ControlType.PushButton,
|
|
82
|
+
QSizePolicy.ControlType.PushButton, Qt.Orientation.Vertical)
|
|
83
|
+
nextX = x + item.sizeHint().width() + spaceX
|
|
84
|
+
if nextX - spaceX > rect.right() and lineHeight > 0:
|
|
85
|
+
x = rect.x()
|
|
86
|
+
y = y + lineHeight + spaceY
|
|
87
|
+
nextX = x + item.sizeHint().width() + spaceX
|
|
88
|
+
lineHeight = 0
|
|
89
|
+
|
|
90
|
+
if not testOnly:
|
|
91
|
+
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
|
92
|
+
|
|
93
|
+
x = nextX
|
|
94
|
+
lineHeight = max(lineHeight, item.sizeHint().height())
|
|
95
|
+
|
|
96
|
+
return y + lineHeight - rect.y()
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from settings.const import Const
|
|
5
|
+
|
|
6
|
+
from utils.internal_utils import get_ms
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Log:
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def p(*msgs, msg_type='info'):
|
|
13
|
+
"""Prints in log"""
|
|
14
|
+
prefix = 'INFO'
|
|
15
|
+
if msg_type == 'warn':
|
|
16
|
+
prefix = 'WARNING'
|
|
17
|
+
elif msg_type == 'error':
|
|
18
|
+
prefix = 'ERROR'
|
|
19
|
+
|
|
20
|
+
today_dt = datetime.now()
|
|
21
|
+
today_str = today_dt.strftime(Const.date_format)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
with open(Const.log_file, 'a') as f:
|
|
25
|
+
for msg in msgs:
|
|
26
|
+
f.write(today_str + ' ' + prefix + ': ' + str(msg) + '\n')
|
|
27
|
+
except PermissionError:
|
|
28
|
+
print('Permission denined when appening in ' + Const.log_file)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def p_ms(msg, first_time_ms=0):
|
|
33
|
+
"""Prints current time in milliseconds in log file.
|
|
34
|
+
A first_time_ms can be removed in order to investigate more easily the time
|
|
35
|
+
latency between instructions"""
|
|
36
|
+
Log.p(str(get_ms() - first_time_ms) + ': ' + msg)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def p_duration(first_time_ms):
|
|
41
|
+
Log.p('Duration since first time: ' + str(get_ms() - first_time_ms) + ' ms')
|
|
File without changes
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from functools import partial
|
|
5
|
+
|
|
6
|
+
from PyQt6.QtCore import Qt
|
|
7
|
+
from PyQt6.QtWidgets import *
|
|
8
|
+
|
|
9
|
+
from logs.log import Log
|
|
10
|
+
from processing.threads.run_thread import RunThread
|
|
11
|
+
from settings.const import Const
|
|
12
|
+
|
|
13
|
+
class Footer(QFrame):
|
|
14
|
+
main = None
|
|
15
|
+
f_container = None
|
|
16
|
+
progress_lines = []
|
|
17
|
+
|
|
18
|
+
def __init__(self, main):
|
|
19
|
+
super(Footer, self).__init__()
|
|
20
|
+
self.main = main
|
|
21
|
+
self.setFrameShape(QFrame.Shape.StyledPanel)
|
|
22
|
+
|
|
23
|
+
l_footer = QVBoxLayout(self)
|
|
24
|
+
self.f_container = QFrame()
|
|
25
|
+
|
|
26
|
+
w_scroll = QScrollArea()
|
|
27
|
+
w_scroll.setWidget(self.f_container)
|
|
28
|
+
w_scroll.setWidgetResizable(True)
|
|
29
|
+
l_footer.addWidget(w_scroll)
|
|
30
|
+
|
|
31
|
+
self.l_grid = QGridLayout()
|
|
32
|
+
self.l_grid.setVerticalSpacing(5)
|
|
33
|
+
|
|
34
|
+
self.f_container.setLayout(self.l_grid)
|
|
35
|
+
|
|
36
|
+
def show_progress_line(self, index):
|
|
37
|
+
for key, widget in self.progress_lines[index].items():
|
|
38
|
+
if not isinstance(widget, int) and not isinstance(widget, str): # exclude index/line and title text
|
|
39
|
+
widget.show()
|
|
40
|
+
|
|
41
|
+
def hide_progress_line(self, index):
|
|
42
|
+
for key, widget in self.progress_lines[index].items():
|
|
43
|
+
if not isinstance(widget, int) and not isinstance(widget, str): # exclude index/line and title text
|
|
44
|
+
widget.hide()
|
|
45
|
+
|
|
46
|
+
def get_color(self, val):
|
|
47
|
+
color = Const.progress_bar_color
|
|
48
|
+
if val == 100:
|
|
49
|
+
color = Const.progress_bar_color_done
|
|
50
|
+
return color
|
|
51
|
+
|
|
52
|
+
def get_text_color(self, val):
|
|
53
|
+
color = Const.progress_bar_color
|
|
54
|
+
if val == 100:
|
|
55
|
+
color = Const.green_text
|
|
56
|
+
return color
|
|
57
|
+
|
|
58
|
+
def get_progress_line_index(self, id_):
|
|
59
|
+
for index, pl in enumerate(self.progress_lines):
|
|
60
|
+
if pl['id'] == id_:
|
|
61
|
+
return index
|
|
62
|
+
|
|
63
|
+
def update_progress_line_by_id(self, id_, val):
|
|
64
|
+
index = self.get_progress_line_index(id_)
|
|
65
|
+
self.update_progress_line(index, val)
|
|
66
|
+
|
|
67
|
+
def interrupt_progress_line_by_id(self, id_):
|
|
68
|
+
index = self.get_progress_line_index(id_)
|
|
69
|
+
self.interrupt_progress_line(index)
|
|
70
|
+
|
|
71
|
+
def interrupt_processes(self):
|
|
72
|
+
for index, pl in enumerate(self.progress_lines):
|
|
73
|
+
if pl['val'] < 100:
|
|
74
|
+
self.interrupt_progress_line(index)
|
|
75
|
+
|
|
76
|
+
def interrupt_progress_line(self, index):
|
|
77
|
+
color = Const.progress_bar_interrupt_color
|
|
78
|
+
text_color = Const.red_text
|
|
79
|
+
self.progress_lines[index]['interrupted'] = True
|
|
80
|
+
self.progress_lines[index]['pb'].setStyleSheet('QProgressBar::chunk { background-color: ' +
|
|
81
|
+
color + ';}')
|
|
82
|
+
self.progress_lines[index]['pb2'].setStyleSheet('QProgressBar::chunk { background-color: ' +
|
|
83
|
+
color + ';}')
|
|
84
|
+
self.progress_lines[index]['q_title'].setText('<font color="' + text_color + '">' +
|
|
85
|
+
self.progress_lines[index]['title'] + '</font>')
|
|
86
|
+
val = self.progress_lines[index]['val']
|
|
87
|
+
val_str = '-'
|
|
88
|
+
if val != 1:
|
|
89
|
+
val_str = str(val) + ' %'
|
|
90
|
+
self.progress_lines[index]['q_pc'].setText('<font color="' + text_color + '"><b>' +
|
|
91
|
+
val_str + '</b></font>')
|
|
92
|
+
self.progress_lines[index]['b_cancel'].setText('Cancelled')
|
|
93
|
+
|
|
94
|
+
def update_progress_line(self, index, val, new_line=False): # Use update_progress_line_by_id from outside instead
|
|
95
|
+
# because lines are continuously changing here (adding one moves the other ones)
|
|
96
|
+
if self.progress_lines[index]['interrupted'] and not new_line and val < 100:
|
|
97
|
+
color = Const.progress_bar_interrupt_color
|
|
98
|
+
text_color = Const.red_text
|
|
99
|
+
else:
|
|
100
|
+
color = self.get_color(val)
|
|
101
|
+
text_color = self.get_text_color(val)
|
|
102
|
+
self.progress_lines[index]['pb'].setStyleSheet('QProgressBar::chunk { background-color: ' +
|
|
103
|
+
color + ';}')
|
|
104
|
+
self.progress_lines[index]['pb2'].setStyleSheet('QProgressBar::chunk { background-color: ' +
|
|
105
|
+
color + ';}')
|
|
106
|
+
if val > 100:
|
|
107
|
+
Log.p('Warning: value greater than 100% given (' + str(val) + '), set to 100.')
|
|
108
|
+
val = 100
|
|
109
|
+
|
|
110
|
+
# Setting up both sliders
|
|
111
|
+
val1 = val * 2
|
|
112
|
+
if val1 > 100:
|
|
113
|
+
val1 = 100
|
|
114
|
+
val2 = 2 * (val - 50)
|
|
115
|
+
if val2 < 0:
|
|
116
|
+
val2 = 0
|
|
117
|
+
val_str = '-'
|
|
118
|
+
if val != 1:
|
|
119
|
+
val_str = str(val) + ' %'
|
|
120
|
+
self.progress_lines[index]['pb'].setValue(val1)
|
|
121
|
+
self.progress_lines[index]['pb2'].setValue(val2)
|
|
122
|
+
self.progress_lines[index]['q_title'].setText('<font color="' + text_color + '">' +
|
|
123
|
+
self.progress_lines[index]['title'] + '</font>')
|
|
124
|
+
self.progress_lines[index]['q_pc'].setText('<font color="' + text_color + '"><b>' +
|
|
125
|
+
val_str + '</b></font>')
|
|
126
|
+
self.progress_lines[index]['val'] = val
|
|
127
|
+
|
|
128
|
+
if not new_line:
|
|
129
|
+
pl_from = self.progress_lines[index]
|
|
130
|
+
pl_from['val'] = val
|
|
131
|
+
self.update_cancel_button(pl_from, self.progress_lines[index])
|
|
132
|
+
rt = self.progress_lines[index]['rt']
|
|
133
|
+
b_cancel = self.progress_lines[index]['b_cancel']
|
|
134
|
+
try:
|
|
135
|
+
b_cancel.clicked.disconnect()
|
|
136
|
+
except TypeError: # this one was not connected
|
|
137
|
+
pass
|
|
138
|
+
b_cancel.clicked.connect(partial(Footer.interrupt,
|
|
139
|
+
b_cancel,
|
|
140
|
+
rt))
|
|
141
|
+
|
|
142
|
+
def change_line(self, index, pl):
|
|
143
|
+
val = self.progress_lines[index]['val']
|
|
144
|
+
if pl['interrupted']:
|
|
145
|
+
text_color = Const.red_text
|
|
146
|
+
else:
|
|
147
|
+
text_color = self.get_text_color(val)
|
|
148
|
+
self.progress_lines[index]['title'] = pl['title']
|
|
149
|
+
self.progress_lines[index]['q_title'].setText('<font color="' + text_color + '">' +
|
|
150
|
+
pl['title'] + '</font>')
|
|
151
|
+
self.progress_lines[index]['interrupted'] = pl['interrupted']
|
|
152
|
+
self.progress_lines[index]['with_cancel'] = pl['with_cancel']
|
|
153
|
+
self.update_cancel_button(pl, self.progress_lines[index])
|
|
154
|
+
self.progress_lines[index]['rt'] = pl['rt']
|
|
155
|
+
|
|
156
|
+
def update_cancel_button(self, pl_from, pl_to):
|
|
157
|
+
if pl_to['val'] is not None:
|
|
158
|
+
is_done = pl_to['val'] >= 100
|
|
159
|
+
must_be_enabled = pl_from['with_cancel'] and not pl_from['interrupted'] and not is_done
|
|
160
|
+
pl_to['b_cancel'].setEnabled(must_be_enabled)
|
|
161
|
+
cancel_text = 'Cancel'
|
|
162
|
+
if is_done:
|
|
163
|
+
cancel_text = 'Done'
|
|
164
|
+
elif pl_from['interrupted']:
|
|
165
|
+
cancel_text = 'Cancelled'
|
|
166
|
+
button_css = Const.footer_button + ' '
|
|
167
|
+
button_css += Const.button_enabled if must_be_enabled else Const.button_disabled
|
|
168
|
+
pl_to['b_cancel'].setStyleSheet(button_css)
|
|
169
|
+
pl_to['b_cancel'].setText(cancel_text)
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def interrupt(b_cancel, rt):
|
|
173
|
+
b_cancel.setText('Cancelled')
|
|
174
|
+
b_cancel.setEnabled(False)
|
|
175
|
+
b_cancel.setStyleSheet(Const.footer_button + ' ' + Const.button_disabled)
|
|
176
|
+
rt.interrupt()
|
|
177
|
+
|
|
178
|
+
def create_progress_line(self, rt, title, with_cancel=True):
|
|
179
|
+
now = datetime.now()
|
|
180
|
+
title = 'At ' + now.strftime('%H:%M:%S') + ' - ' + title
|
|
181
|
+
q_title = QLabel()
|
|
182
|
+
q_pc = QLabel('')
|
|
183
|
+
q_pc.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
184
|
+
pb = QProgressBar()
|
|
185
|
+
pb.setFixedHeight(Const.progress_bar_height)
|
|
186
|
+
pb.setTextVisible(False)
|
|
187
|
+
pb2 = QProgressBar()
|
|
188
|
+
pb2.setFixedHeight(Const.progress_bar_height)
|
|
189
|
+
pb2.setTextVisible(False)
|
|
190
|
+
b_cancel = QPushButton('Cancel')
|
|
191
|
+
if with_cancel:
|
|
192
|
+
b_cancel.setStyleSheet(Const.footer_button + ' ' + Const.button_enabled)
|
|
193
|
+
else:
|
|
194
|
+
b_cancel.setStyleSheet(Const.footer_button + ' ' + Const.button_disabled)
|
|
195
|
+
b_cancel.setEnabled(False)
|
|
196
|
+
|
|
197
|
+
new_index = len(self.progress_lines)
|
|
198
|
+
|
|
199
|
+
self.l_grid.addWidget(q_title, new_index, 0)
|
|
200
|
+
self.l_grid.addWidget(pb, new_index, 1)
|
|
201
|
+
self.l_grid.addWidget(q_pc, new_index, 2)
|
|
202
|
+
self.l_grid.addWidget(pb2, new_index, 3)
|
|
203
|
+
self.l_grid.addWidget(b_cancel, new_index, 4)
|
|
204
|
+
|
|
205
|
+
# will be erased but create permanent objects (bars, labels, button)
|
|
206
|
+
self.progress_lines.append({'id': '', 'title': '', 'q_title': q_title, 'with_cancel': None,
|
|
207
|
+
'pb': pb, 'q_pc': q_pc, 'pb2': pb2, 'val': None,
|
|
208
|
+
'interrupted': None, 'b_cancel': b_cancel, 'rt': None})
|
|
209
|
+
|
|
210
|
+
if new_index > 0:
|
|
211
|
+
# Push other lines to free the first line
|
|
212
|
+
for i in reversed(range(new_index)):
|
|
213
|
+
# Copying i into (i + 1)
|
|
214
|
+
self.progress_lines[i + 1]['id'] = self.progress_lines[i]['id']
|
|
215
|
+
self.change_line(i + 1, self.progress_lines[i])
|
|
216
|
+
self.update_progress_line(i + 1, self.progress_lines[i]['val'])
|
|
217
|
+
|
|
218
|
+
# Now line 0 is available
|
|
219
|
+
self.update_progress_line(0, 0, True)
|
|
220
|
+
self.change_line(0, {'title': title, 'interrupted': False, 'with_cancel': with_cancel,
|
|
221
|
+
'b_cancel': b_cancel, 'rt': rt})
|
|
222
|
+
self.progress_lines[0]['id'] = new_index
|
|
223
|
+
if with_cancel:
|
|
224
|
+
self.progress_lines[0]['b_cancel'].clicked.connect(partial(Footer.interrupt,
|
|
225
|
+
self.progress_lines[0]['b_cancel'],
|
|
226
|
+
rt))
|
|
227
|
+
|
|
228
|
+
return new_index # created id
|
|
229
|
+
|