<b>Saving and export</b>

Export writes the <i>visible range</i> of every plotted trace to a file: NumPy <tt>.npy</tt> or
<tt>.csv</tt>, chosen by the extension you type.

<b>How to reach it</b>

<b>Export visible range…</b> in the overflow menu, or <tt>Ctrl+E</tt>. A file chooser asks where;
the format follows the path's extension.

<b>Worth knowing</b>

• The table is collected at the moment you click, not when the chooser returns. A live view keeps
  scrolling while a dialog is up, so a late collection would write a window you never saw.
• The rows are the <b>stored samples</b>, never the LOD buckets the plot draws from. A row count
  far below the span times the stored rate means something cut the table from the pyramid.
• The stored rate is not always the wire rate. A window long enough to need decimation is stored
  averaged at a lower rate, and the export carries <i>that</i> — read the status bar's ⓘ popover
  for the figure before trusting a sample count.
• Two time columns. <tt>time</tt> is the display timeline — negative ages ending at 0 while
  streaming, offsets from the trigger edge while an event is held — so it is the x you read a
  value at on screen. <tt>abs_seconds</tt> is the source's own clock for the same sample, which
  is what lets two exports taken in different modes line up. Both are strictly increasing and
  evenly spaced; duplicate or backwards stamps would mean the file held two clocks.
• An export taken <i>mid-capture</i> writes the partly filled window that is on screen, which is
  shorter than the event span. Export outside a capture if you want the whole event.

<b>Example</b>

A 2 s window at 10 kS/s over 3 traces writes 20001 rows and 5 columns
(<tt>time</tt>, <tt>abs_seconds</tt>, three signals). The <tt>.npy</tt> is
<tt>64·⌈(10+len(header)+1)/64⌉ + rows·(16 + 4·cols)</tt> bytes. Read it back with
<tt>pd.DataFrame(np.load(path))</tt>.
