forked from M-Labs/artiq
waveform: add WaveformArea
This commit is contained in:
parent
081f5062f6
commit
6552a860d9
|
@ -1,12 +1,17 @@
|
|||
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from artiq.tools import exc_to_warning
|
||||
from artiq.gui.models import DictSyncTreeSepModel, LocalModelManager
|
||||
from artiq.gui.dndwidgets import DragDropSplitter, VDragScrollArea
|
||||
from artiq.coredevice import comm_analyzer
|
||||
from artiq.coredevice.comm_analyzer import WaveformType
|
||||
|
||||
import numpy as np
|
||||
import itertools
|
||||
import bisect
|
||||
import pyqtgraph as pg
|
||||
import asyncio
|
||||
import logging
|
||||
import math
|
||||
|
||||
|
@ -378,3 +383,144 @@ class AnalogWaveform(Waveform):
|
|||
else:
|
||||
lbl = str(self.cursor_y)
|
||||
self.cursor_label.setText(lbl)
|
||||
|
||||
|
||||
class WaveformArea(QtWidgets.QWidget):
|
||||
cursorMoved = QtCore.pyqtSignal(float)
|
||||
|
||||
def __init__(self, parent, state, channels_mgr):
|
||||
QtWidgets.QWidget.__init__(self, parent=parent)
|
||||
self._state = state
|
||||
self._channels_mgr = channels_mgr
|
||||
|
||||
self._cursor_visible = True
|
||||
self._cursor_x_pos = 0
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
self.setLayout(layout)
|
||||
|
||||
self._ref_axis = pg.PlotWidget()
|
||||
self._ref_axis.hideAxis("bottom")
|
||||
self._ref_axis.hideAxis("left")
|
||||
self._ref_axis.hideButtons()
|
||||
self._ref_axis.setFixedHeight(45)
|
||||
self._ref_axis.setMenuEnabled(False)
|
||||
self._top = pg.AxisItem("top")
|
||||
self._top.setScale(1e-12)
|
||||
self._top.setLabel(units="s")
|
||||
self._ref_axis.setAxisItems({"top": self._top})
|
||||
layout.addWidget(self._ref_axis)
|
||||
|
||||
self._ref_vb = self._ref_axis.getPlotItem().getViewBox()
|
||||
self._ref_vb.setFixedHeight(0)
|
||||
self._ref_vb.setMouseEnabled(x=True, y=False)
|
||||
self._ref_vb.setLimits(xMin=0)
|
||||
|
||||
scroll_area = VDragScrollArea(self)
|
||||
scroll_area.setWidgetResizable(True)
|
||||
scroll_area.setContentsMargins(0, 0, 0, 0)
|
||||
scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
layout.addWidget(scroll_area)
|
||||
|
||||
self._splitter = DragDropSplitter(parent=scroll_area)
|
||||
self._splitter.setHandleWidth(1)
|
||||
scroll_area.setWidget(self._splitter)
|
||||
|
||||
def _add_waveform(self, channel, waveform_type):
|
||||
num_channels = self._splitter.count()
|
||||
self._splitter.setFixedHeight(
|
||||
(num_channels + 1) * Waveform.PREF_HEIGHT)
|
||||
cw = waveform_type(channel, self._state, parent=self._splitter)
|
||||
self._splitter.addWidget(cw)
|
||||
|
||||
action = QtWidgets.QAction("Toggle cursor visible", cw)
|
||||
action.triggered.connect(self._on_toggle_cursor)
|
||||
cw.addAction(action)
|
||||
action = QtWidgets.QAction("Delete waveform", cw)
|
||||
action.triggered.connect(lambda: self._remove_channel(cw))
|
||||
cw.addAction(action)
|
||||
action = QtWidgets.QAction("Delete all", cw)
|
||||
action.triggered.connect(self.clear_channels)
|
||||
cw.addAction(action)
|
||||
action = QtWidgets.QAction("Reset waveform heights", cw)
|
||||
action.triggered.connect(self._splitter.resetSizes)
|
||||
cw.addAction(action)
|
||||
|
||||
cw.cursorMoved.connect(lambda x: self.on_cursor_move(x))
|
||||
cw.cursorMoved.connect(self.cursorMoved.emit)
|
||||
|
||||
cw.setXLink(self._ref_vb)
|
||||
cw.extract_data_from_state()
|
||||
cw.display()
|
||||
cw.on_cursor_move(self._cursor_x_pos)
|
||||
cw.update_x_max()
|
||||
|
||||
async def _add_waveform_task(self):
|
||||
dialog = _AddChannelDialog(self, self._channels_mgr)
|
||||
fut = asyncio.Future()
|
||||
|
||||
def on_accept(s):
|
||||
fut.set_result(s)
|
||||
dialog.accepted.connect(on_accept)
|
||||
dialog.open()
|
||||
channels = await fut
|
||||
self.update_channels(channels)
|
||||
|
||||
def update_channels(self, channel_list):
|
||||
type_map = {
|
||||
WaveformType.BIT: BitWaveform,
|
||||
WaveformType.VECTOR: BitVectorWaveform,
|
||||
WaveformType.ANALOG: AnalogWaveform,
|
||||
WaveformType.LOG: LogWaveform
|
||||
}
|
||||
for channel in channel_list:
|
||||
ty = channel[1][1]
|
||||
waveform_type = type_map[ty]
|
||||
self._add_waveform(channel, waveform_type)
|
||||
|
||||
def get_channels(self):
|
||||
channels = []
|
||||
for i in range(self._splitter.count()):
|
||||
cw = self._splitter.widget(i)
|
||||
channels.append(cw.channel)
|
||||
return channels
|
||||
|
||||
def _remove_channel(self, cw):
|
||||
num_channels = self._splitter.count() - 1
|
||||
cw.deleteLater()
|
||||
self._splitter.setFixedHeight(num_channels * Waveform.PREF_HEIGHT)
|
||||
self._splitter.refresh()
|
||||
|
||||
def clear_channels(self):
|
||||
for i in reversed(range(self._splitter.count())):
|
||||
cw = self._splitter.widget(i)
|
||||
self._remove_channel(cw)
|
||||
|
||||
def on_add_channel_click(self):
|
||||
asyncio.ensure_future(exc_to_warning(self._add_waveform_task()))
|
||||
|
||||
def on_trace_update(self):
|
||||
self._top.setScale(1e-12 * self._state["timescale"])
|
||||
for i in range(self._splitter.count()):
|
||||
cw = self._splitter.widget(i)
|
||||
cw.extract_data_from_state()
|
||||
cw.display()
|
||||
cw.on_cursor_move(self._cursor_x_pos)
|
||||
cw.update_x_max()
|
||||
maximum = self._state["stopped_x"]
|
||||
self._ref_axis.setLimits(xMax=maximum)
|
||||
self._ref_axis.setRange(xRange=(0, maximum))
|
||||
|
||||
def on_cursor_move(self, x):
|
||||
self._cursor_x_pos = x
|
||||
for i in range(self._splitter.count()):
|
||||
cw = self._splitter.widget(i)
|
||||
cw.on_cursor_move(x)
|
||||
|
||||
def _on_toggle_cursor(self):
|
||||
self._cursor_visible = not self._cursor_visible
|
||||
for i in range(self._splitter.count()):
|
||||
cw = self._splitter.widget(i)
|
||||
cw.set_cursor_visible(self._cursor_visible)
|
||||
|
|
Loading…
Reference in New Issue