@@ -433,20 +433,29 @@ def plot_series(
433433
434434 Pass *row_start*/*row_stop* to zoom into a sub-range (always read
435435 exactly; ``x`` stays in absolute row coordinates). The series is a
436- CTable column (*column* is its name; an active row filter is honored) or
437- an array (*column* is the global index along the column dimension of
438- *layout*, or None for 1-D arrays).
436+ CTable column (*column* is its name; a locked row window takes
437+ precedence, otherwise an active row filter is honored) or an array
438+ (*column* is the global index along the column dimension of *layout*,
439+ or None for 1-D arrays).
439440 """
440441 path = self .normalize_path (path )
441442 obj = self ._get_object (path )
442443 kind = object_kind (obj )
443444
444445 if kind == "ctable" :
445- filtered = path in self ._filter_views
446- view = self ._filter_views .get (path , obj )
446+ # A locked row window (set by 'v') takes precedence over any row
447+ # filter, mirroring preview()/read_cell(): a plot shows exactly the
448+ # rows the grid is showing. The SUMMARY fast-path spans the whole
449+ # column, so it is only valid when neither narrows the series.
450+ if path in self ._window_views :
451+ view = self ._window_views [path ]
452+ narrowed = True
453+ else :
454+ view = self ._filter_views .get (path , obj )
455+ narrowed = path in self ._filter_views
447456 n = len (view )
448457 start , stop = self ._clamp_range (row_start , row_stop , n )
449- if start == 0 and stop == n and not filtered :
458+ if start == 0 and stop == n and not narrowed :
450459 env = self ._column_summary_envelope (obj , column , n , max_points )
451460 if env is not None :
452461 return {** env , "n" : n , "row_start" : start , "row_stop" : stop , "method" : "summary" }
@@ -514,8 +523,9 @@ def read_series(
514523 ) -> dict [str , Any ]:
515524 """Return the *raw* values of one series over ``[row_start, row_stop)``.
516525
517- Same series selection as :meth:`plot_series` (CTable column honoring an
518- active filter, or an array column via *layout*) but with no bucketing —
526+ Same series selection as :meth:`plot_series` (CTable column honoring a
527+ locked row window then an active filter, or an array column via
528+ *layout*) but with no bucketing —
519529 every value is read exactly, for the high-res ``h`` view. The result is
520530 ``{"x", "y", "n", "row_start", "row_stop"}`` with ``x`` in absolute row
521531 coordinates. This reads exactly what is asked, so callers must bound the
@@ -526,7 +536,12 @@ def read_series(
526536 kind = object_kind (obj )
527537
528538 if kind == "ctable" :
529- view = self ._filter_views .get (path , obj )
539+ # Honor a locked row window first, then any row filter, matching
540+ # preview()/read_cell() so the hi-res view tracks the visible grid.
541+ if path in self ._window_views :
542+ view = self ._window_views [path ]
543+ else :
544+ view = self ._filter_views .get (path , obj )
530545 n = len (view )
531546 start , stop = self ._clamp_range (row_start , row_stop , n )
532547 y = safe_asarray (view [column ][start :stop ])
0 commit comments