15
15
import math
16
16
import time
17
17
import weakref
18
- from typing import Dict , Tuple , List , Any
18
+ from typing import Dict , Tuple , List , Any , Optional
19
19
20
20
import numpy as np
21
21
import numpy .typing as npt
22
- from PySide6 .QtCore import Signal , QThread , QMutex , QMutexLocker , QThreadPool , QRunnable , QObject
22
+ from PySide6 .QtCore import Signal , QThread , QMutex , QMutexLocker , QThreadPool , QRunnable , QObject , Qt
23
23
from PySide6 .QtWidgets import QTableWidgetItem
24
+ from pydantic import BaseModel
24
25
25
26
from .signals_table import HasRegionSignalsTable
26
- from .util import IdentityCacheDict , not_none
27
+ from .util import IdentityCacheDict , HasSaveLoadDataConfig , not_none
27
28
28
29
29
- class StatsSignalsTable (HasRegionSignalsTable ):
30
+ class StatsTableStateModel (BaseModel ):
31
+ stats_disabled : Optional [bool ] = None
32
+
33
+
34
+ class StatsSignalsTable (HasRegionSignalsTable , HasSaveLoadDataConfig ):
30
35
"""Mixin into SignalsTable with statistics rows. Optional range to specify computation of statistics.
31
36
Values passed into set_data must all be numeric."""
32
37
@@ -44,6 +49,8 @@ class StatsSignalsTable(HasRegionSignalsTable):
44
49
COL_STAT_STDEV ,
45
50
]
46
51
52
+ _MODEL_BASES = [StatsTableStateModel ]
53
+
47
54
_FULL_RANGE = (- float ("inf" ), float ("inf" ))
48
55
49
56
class StatsCalculatorSignals (QObject ):
@@ -80,6 +87,8 @@ def run(self) -> None:
80
87
self ._parent ._last_region = request_region
81
88
82
89
for xs_ys_ref in request_data :
90
+ if self ._parent ._stats_calculation_disabled : # terminate if disabled
91
+ return
83
92
with QMutexLocker (self ._parent ._request_mutex ):
84
93
if self ._parent ._debounce_target_ns != debounce_target_ns :
85
94
return
@@ -125,6 +134,8 @@ def _init_table(self) -> None:
125
134
self .setHorizontalHeaderItem (self .COL_STAT + self .COL_STAT_STDEV , QTableWidgetItem ("StDev" ))
126
135
127
136
def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
137
+ self ._stats_calculation_disabled = False
138
+
128
139
super ().__init__ (* args , ** kwargs )
129
140
# since calculating stats across the full range is VERY EXPENSIVE, cache the results
130
141
self ._full_range_stats = IdentityCacheDict [npt .NDArray [np .float64 ], Dict [int , float ]]() # array -> stats dict
@@ -148,6 +159,43 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
148
159
self ._stats_signals = self .StatsCalculatorSignals ()
149
160
self ._stats_signals .update .connect (self ._on_stats_updated )
150
161
162
+ def _update (self ) -> None :
163
+ super ()._update ()
164
+ self ._update_stats_disabled ()
165
+
166
+ def _write_model (self , model : BaseModel ) -> None :
167
+ assert isinstance (model , StatsTableStateModel )
168
+ super ()._write_model (model )
169
+ model .stats_disabled = self ._stats_calculation_disabled
170
+
171
+ def _load_model (self , model : BaseModel ) -> None :
172
+ assert isinstance (model , StatsTableStateModel )
173
+ super ()._load_model (model )
174
+ if model .stats_disabled is not None :
175
+ self .disable_stats (model .stats_disabled )
176
+
177
+ def stats_disabled (self ) -> bool :
178
+ """Returns whether stats calculation is disabled."""
179
+ return self ._stats_calculation_disabled
180
+
181
+ def disable_stats (self , disable : bool = True ) -> None :
182
+ """Call this to disable stats calculation and to blank the table, or re-enable the calculation."""
183
+ self ._stats_calculation_disabled = disable
184
+ self ._update_stats_disabled ()
185
+ if not disable :
186
+ self ._update_stats_task (0 , True ) # populate the table again
187
+
188
+ def _update_stats_disabled (self ) -> None :
189
+ """Updates the table visuals for stats disabled"""
190
+ for row , name in enumerate (self ._data_items .keys ()):
191
+ for col in self .STATS_COLS :
192
+ item = not_none (self .item (row , self .COL_STAT + col ))
193
+ if self ._stats_calculation_disabled : # clear table on disable
194
+ item .setFlags (item .flags () & ~ Qt .ItemFlag .ItemIsEnabled )
195
+ item .setText ("" )
196
+ else :
197
+ item .setFlags (item .flags () | Qt .ItemFlag .ItemIsEnabled )
198
+
151
199
def _on_stats_updated (
152
200
self , input_arr : npt .NDArray [np .float64 ], input_region : Tuple [float , float ], stats_dict : Dict [int , float ]
153
201
) -> None :
@@ -160,6 +208,9 @@ def _on_stats_updated(
160
208
self ._update_stats_display (False )
161
209
162
210
def _update_stats_task (self , delay_ms : int , clear_table : bool ) -> None :
211
+ if self ._stats_calculation_disabled : # don't create a calculation task if disabled
212
+ return
213
+
163
214
region = HasRegionSignalsTable ._region_of_plot (self ._plots )
164
215
data_items = [ # filter out enum types
165
216
(name , (xs , ys )) for name , (xs , ys ) in self ._plots ._data .items () if np .issubdtype (ys .dtype , np .number )
@@ -183,6 +234,9 @@ def _update_stats_task(self, delay_ms: int, clear_table: bool) -> None:
183
234
self ._update_stats_display (clear_table )
184
235
185
236
def _update_stats_display (self , clear_table : bool ) -> None :
237
+ if self ._stats_calculation_disabled : # don't update the display if disabled
238
+ return
239
+
186
240
for row , name in enumerate (self ._data_items .keys ()):
187
241
xs , ys = self ._plots ._data .get (name , (None , None ))
188
242
if xs is None or ys is None :
0 commit comments