@@ -817,11 +817,17 @@ class PyDMArchiverTimePlot(PyDMTimePlot):
817
817
The parent of this widget.
818
818
init_y_channels : list
819
819
A list of scalar channels to plot vs time.
820
- background: str
820
+ background : str
821
821
The background color for the plot. Accepts any arguments that
822
822
pyqtgraph.mkColor will accept.
823
- optimized_data_bins: int
823
+ optimized_data_bins : int
824
824
The number of bins of data returned from the archiver when using optimized requests
825
+ request_cooldown : int
826
+ The time, in milliseconds, between requests to the archiver appliance
827
+ cache_data : bool
828
+ Whether curves should retain archive data or fetch new data when the x-axis changes
829
+ show_all : bool
830
+ Shifts the x-axis range to show all data, or stay where the user set the x-axis to
825
831
"""
826
832
827
833
def __init__ (
@@ -830,6 +836,9 @@ def __init__(
830
836
init_y_channels : List [str ] = [],
831
837
background : str = "default" ,
832
838
optimized_data_bins : int = 2000 ,
839
+ request_cooldown : int = 1000 ,
840
+ cache_data : bool = True ,
841
+ show_all : bool = True ,
833
842
):
834
843
super (PyDMArchiverTimePlot , self ).__init__ (
835
844
parent = parent ,
@@ -838,56 +847,104 @@ def __init__(
838
847
background = background ,
839
848
bottom_axis = DateAxisItem ("bottom" ),
840
849
)
850
+ self ._cache_data = None
851
+
841
852
self .optimized_data_bins = optimized_data_bins
853
+ self .request_cooldown = request_cooldown
854
+ self .cache_data = cache_data
855
+ self ._show_all = show_all # Show all plotted data after archiver fetch
856
+
842
857
self ._starting_timestamp = time .time () # The timestamp at which the plot was first rendered
843
858
self ._min_x = self ._starting_timestamp - DEFAULT_TIME_SPAN
844
859
self ._prev_x = self ._min_x # Holds the minimum x-value of the previous update of the plot
845
860
self ._archive_request_queued = False
846
861
self .setTimeSpan (DEFAULT_TIME_SPAN )
847
862
848
- def updateXAxis (self , update_immediately : bool = False ) -> None :
863
+ @property
864
+ def cache_data (self ):
865
+ """Returns if the curves of the plot are caching archive data or
866
+ fetching new archive data on every change to the x-axis"""
867
+ return self ._cache_data
868
+
869
+ @cache_data .setter
870
+ def cache_data (self , enable : bool ):
871
+ """If true, the curves on the plot will keep thier most recently fetched archive data. New
872
+ data will only be fetched when users navigate to an "unseen" section of the plot.
873
+ When false, the curves will fetch new archive data on every change to the x-axis.
874
+ """
875
+ if self ._cache_data == enable :
876
+ return
877
+ if enable :
878
+ try :
879
+ self .plotItem .sigXRangeChanged .disconnect (self .updateXAxis )
880
+ self .plotItem .sigXRangeChangedManually .disconnect (self .updateXAxis )
881
+ except TypeError :
882
+ pass
883
+ else :
884
+ self .plotItem .sigXRangeChanged .connect (self .updateXAxis )
885
+ self .plotItem .sigXRangeChangedManually .connect (self .updateXAxis )
886
+ self ._cache_data = enable
887
+
888
+ def updateXAxis (self ) -> None :
849
889
"""Manages the requests to archiver appliance. When the user pans or zooms the x axis to the left,
850
890
a request will be made for backfill data"""
851
- if len ( self . _curves ) == 0 or self .auto_scroll_timer . isActive () :
891
+ if not self ._curves :
852
892
return
853
893
854
- min_x = self .plotItem .getAxis ("bottom" ).range [0 ] # Gets the leftmost timestamp displayed on the x-axis
855
- max_x = self .plotItem .getAxis ("bottom" ).range [1 ]
856
- max_point = max ([curve .max_x () for curve in self ._curves ])
857
- if min_x == 0 : # This is zero when the plot first renders
858
- self ._max_x = time .time ()
859
- self ._min_x = self ._max_x - DEFAULT_TIME_SPAN
860
- self ._starting_timestamp = self ._max_x
861
- if self .getTimeSpan () != MIN_TIME_SPAN :
862
- # Initialize x-axis based on the time span as well as trigger a call to the archiver below
863
- self ._min_x = self ._min_x - self .getTimeSpan ()
894
+ min_x , max_x = self .plotItem .getAxis ("bottom" ).range # Get current visible x-axis range
895
+ if min_x == 0 : # Initial render case
896
+ self ._initialize_x_axis ()
897
+ elif not self ._cache_data :
898
+ self ._handle_caching_off (min_x , max_x )
899
+ elif not self .plotItem .isAnyXAutoRange ():
900
+ self ._handle_manual_scrolling_or_zoom (min_x , max_x )
901
+
902
+ self ._prev_x = min_x
903
+
904
+ def _initialize_x_axis (self ) -> None :
905
+ """Initializes the x-axis for the first render."""
906
+ self ._max_x = time .time ()
907
+ self ._min_x = self ._max_x - DEFAULT_TIME_SPAN
908
+ self ._starting_timestamp = self ._max_x
909
+
910
+ if self .getTimeSpan () != MIN_TIME_SPAN :
911
+ self ._min_x -= self .getTimeSpan ()
912
+ self ._archive_request_queued = True
913
+ self .requestDataFromArchiver ()
914
+
915
+ blocked = self .plotItem .blockSignals (True )
916
+ self .plotItem .setXRange (self ._min_x , self ._max_x , padding = 0.0 , update = False )
917
+ self .plotItem .blockSignals (blocked )
918
+
919
+ def _handle_caching_off (self , min_x : float , max_x : float ) -> None :
920
+ """Handles the situation when there is no cached data and the user has changed the x-axis range."""
921
+ if min_x != self ._min_x or max_x != self ._max_x :
922
+ self ._min_x = min_x
923
+ self ._max_x = max_x
924
+ self .setTimeSpan (max_x - min_x )
925
+ if not self ._archive_request_queued :
864
926
self ._archive_request_queued = True
865
- self .requestDataFromArchiver ()
866
- self .plotItem .setXRange (
867
- time .time () - DEFAULT_TIME_SPAN , time .time (), padding = 0.0 , update = update_immediately
868
- )
869
- elif min_x < self ._min_x and not self .plotItem .isAnyXAutoRange ():
870
- # This means the user has manually scrolled to the left, so request archived data
927
+ QTimer .singleShot (self .request_cooldown , self .requestDataFromArchiver )
928
+
929
+ def _handle_manual_scrolling_or_zoom (self , min_x : float , max_x : float ) -> None :
930
+ """Handles scenarios of manual scrolling or zooming when autorange is disabled."""
931
+ max_point = max (curve .max_x () for curve in self ._curves )
932
+
933
+ if min_x < self ._min_x :
934
+ # User scrolled to the left, request archived data
871
935
self ._min_x = min_x
872
936
self .setTimeSpan (max_point - min_x )
873
937
if not self ._archive_request_queued :
874
- # Letting the user pan or scroll the plot is convenient, but can generate a lot of events in under
875
- # a second that would trigger a request for data. By using a timer, we avoid this burst of events
876
- # and consolidate what would be many requests to archiver into just one.
877
938
self ._archive_request_queued = True
878
- QTimer .singleShot (1000 , self .requestDataFromArchiver )
879
- # Here we only update the x-axis if the user hasn't asked for autorange and they haven't zoomed in (as
880
- # detected by the max range showing on the plot being less than the data available)
881
- elif not self .plotItem .isAnyXAutoRange () and max_x >= max_point - 10 :
882
- if min_x > (self ._prev_x + 15 ) or min_x < (self ._prev_x - 15 ):
883
- # The plus/minus 15 just makes sure we don't do this on every update tick of the graph
939
+ QTimer .singleShot (self .request_cooldown , self .requestDataFromArchiver )
940
+ elif max_x >= max_point - 10 :
941
+ # Check if we should update the x-axis
942
+ if abs (min_x - self ._prev_x ) > 15 :
884
943
self .setTimeSpan (max_point - min_x )
885
944
else :
886
- # Keep the plot moving with a rolling window based on the current timestamp
887
- self .plotItem .setXRange (
888
- max_point - self .getTimeSpan (), max_point , padding = 0.0 , update = update_immediately
889
- )
890
- self ._prev_x = min_x
945
+ blocked = self .plotItem .blockSignals (True )
946
+ self .plotItem .setXRange (max_point - self .getTimeSpan (), max_point , padding = 0.0 , update = False )
947
+ self .plotItem .blockSignals (blocked )
891
948
892
949
def requestDataFromArchiver (self , min_x : Optional [float ] = None , max_x : Optional [float ] = None ) -> None :
893
950
"""
@@ -911,8 +968,10 @@ def requestDataFromArchiver(self, min_x: Optional[float] = None, max_x: Optional
911
968
if curve .use_archive_data :
912
969
if max_x is None :
913
970
max_x = curve .min_x ()
971
+ if not self ._cache_data :
972
+ max_x = min (max_x , self ._max_x )
914
973
requested_seconds = max_x - min_x
915
- if requested_seconds <= 5 :
974
+ if requested_seconds <= MIN_TIME_SPAN :
916
975
continue # Avoids noisy requests when first rendering the plot
917
976
# Max amount of raw data to return before using optimized data
918
977
max_data_request = int (0.80 * self .getArchiveBufferSize ())
@@ -963,7 +1022,7 @@ def createCurveItem(self, *args, **kwargs) -> ArchivePlotCurveItem:
963
1022
def archive_data_received (self ):
964
1023
"""Take any action needed when this plot receives new data from archiver appliance"""
965
1024
self ._archive_request_queued = False
966
- if self .auto_scroll_timer .isActive ():
1025
+ if self .auto_scroll_timer .isActive () or not self . _show_all :
967
1026
return
968
1027
969
1028
max_x = max ([curve .max_x () for curve in self ._curves ])
0 commit comments