forked from M-Labs/artiq
waveform: changes to mvc
This commit is contained in:
parent
f60924f077
commit
4d97cb5426
@ -113,8 +113,8 @@ class _AddChannelDialog(QtWidgets.QDialog):
|
||||
for select in selection:
|
||||
key = self._channel_model.index_to_key(select)
|
||||
if key is not None:
|
||||
width = self._channel_model[key].ref
|
||||
channels.append((key, width))
|
||||
width, ty = self._channel_model[key].ref
|
||||
channels.append((key, width, ty, []))
|
||||
self.accepted.emit(channels)
|
||||
self.close()
|
||||
|
||||
@ -136,7 +136,7 @@ class Waveform(pg.PlotWidget):
|
||||
|
||||
cursorMoved = QtCore.pyqtSignal(float)
|
||||
|
||||
def __init__(self, channel, state, parent=None):
|
||||
def __init__(self, name, width, parent=None):
|
||||
pg.PlotWidget.__init__(self,
|
||||
parent=parent,
|
||||
x=None,
|
||||
@ -150,13 +150,12 @@ class Waveform(pg.PlotWidget):
|
||||
self.setMenuEnabled(False)
|
||||
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||
|
||||
self.channel = channel
|
||||
self.name = channel[0]
|
||||
self.width = channel[1][0]
|
||||
self.name = name
|
||||
self.width = width
|
||||
|
||||
self.state = state
|
||||
self.x_data = []
|
||||
self.y_data = []
|
||||
#self.state = state
|
||||
#self.x_data = []
|
||||
#self.y_data = []
|
||||
|
||||
self.plot_item = self.getPlotItem()
|
||||
self.plot_item.hideButtons()
|
||||
@ -193,8 +192,8 @@ class Waveform(pg.PlotWidget):
|
||||
self.label_bg = BackgroundItem(parent=self.plot_item, rect=rect)
|
||||
self.label_bg.anchor(itemPos=(0, 0), parentPos=(0, 0), offset=(0, 0))
|
||||
|
||||
def update_x_max(self):
|
||||
self.view_box.setLimits(xMax=self.state["stopped_x"])
|
||||
#def update_x_max(self):
|
||||
# self.view_box.setLimits(xMax=self.state["stopped_x"])
|
||||
|
||||
def set_cursor_visible(self, visible):
|
||||
if visible:
|
||||
@ -202,31 +201,28 @@ class Waveform(pg.PlotWidget):
|
||||
else:
|
||||
self.removeItem(self.cursor)
|
||||
|
||||
def on_cursor_move(self, x):
|
||||
self.cursor.setValue(x)
|
||||
if len(self.x_data) < 1:
|
||||
return
|
||||
ind = bisect.bisect_left(self.x_data, x) - 1
|
||||
dr = self.plot_data_item.dataRect()
|
||||
if dr is None:
|
||||
self.cursor_y = None
|
||||
elif dr.left() <= x \
|
||||
and 0 <= ind < len(self.y_data):
|
||||
self.cursor_y = self.y_data[ind]
|
||||
elif x >= dr.right():
|
||||
self.cursor_y = self.y_data[-1]
|
||||
else:
|
||||
self.cursor_y = None
|
||||
self.format_cursor_label()
|
||||
#def on_cursor_move(self, x):
|
||||
# self.cursor.setValue(x)
|
||||
# if len(self.x_data) < 1:
|
||||
# return
|
||||
# ind = bisect.bisect_left(self.x_data, x) - 1
|
||||
# dr = self.plot_data_item.dataRect()
|
||||
# if dr is None:
|
||||
# self.cursor_y = None
|
||||
# elif dr.left() <= x \
|
||||
# and 0 <= ind < len(self.y_data):
|
||||
# self.cursor_y = self.y_data[ind]
|
||||
# elif x >= dr.right():
|
||||
# self.cursor_y = self.y_data[-1]
|
||||
# else:
|
||||
# self.cursor_y = None
|
||||
# self.format_cursor_label()
|
||||
|
||||
def extract_data_from_state(self):
|
||||
def onDataChange(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def display(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def format_cursor_label(self):
|
||||
raise NotImplementedError
|
||||
#def format_cursor_label(self):
|
||||
# raise NotImplementedError
|
||||
|
||||
# override
|
||||
def mouseMoveEvent(self, e):
|
||||
@ -254,24 +250,19 @@ class Waveform(pg.PlotWidget):
|
||||
|
||||
|
||||
class LogWaveform(Waveform):
|
||||
def __init__(self, channel, state, parent=None):
|
||||
Waveform.__init__(self, channel, state, parent)
|
||||
def __init__(self, name, width, parent=None):
|
||||
Waveform.__init__(self, name, width, parent)
|
||||
|
||||
def extract_data_from_state(self):
|
||||
try:
|
||||
self.x_data, self.y_data = zip(*self.state['logs'][self.name])
|
||||
except:
|
||||
logger.debug('Error caught when loading waveform: {}'.format(self.name), exc_info=True)
|
||||
|
||||
def display(self):
|
||||
def onDataChange(self, data):
|
||||
try:
|
||||
x_data = zip(*data)[0]
|
||||
self.plot_data_item.setData(
|
||||
x=self.x_data, y=np.ones(len(self.x_data)))
|
||||
x=x_data, y=np.ones(len(x_data)))
|
||||
self.plot_data_item.opts.update(
|
||||
{"connect": np.zeros(2), "symbol": "x"})
|
||||
old_msg = ""
|
||||
old_x = 0
|
||||
for x, msg in zip(self.x_data, self.y_data):
|
||||
for x, msg in data:
|
||||
if x == old_x:
|
||||
old_msg += "\n" + msg
|
||||
else:
|
||||
@ -292,23 +283,17 @@ class LogWaveform(Waveform):
|
||||
|
||||
|
||||
class BitWaveform(Waveform):
|
||||
def __init__(self, channel, state, parent=None):
|
||||
Waveform.__init__(self, channel, state, parent)
|
||||
def __init__(self, name, width, parent=None):
|
||||
Waveform.__init__(self, name, width, parent)
|
||||
self._arrows = []
|
||||
#self.plot_data_item.setDownsampling(ds=1000, method="peak", auto=False)
|
||||
|
||||
def extract_data_from_state(self):
|
||||
try:
|
||||
self.x_data, self.y_data = zip(*self.state['data'][self.name])
|
||||
except:
|
||||
logger.debug('Error caught when loading waveform data: {}'.format(self.name), exc_info=True)
|
||||
|
||||
def display(self):
|
||||
def onDataChange(self, data):
|
||||
try:
|
||||
display_y = []
|
||||
display_x = []
|
||||
previous_y = None
|
||||
for x, y in zip(self.x_data, self.y_data):
|
||||
for x, y in data:
|
||||
if y == "X": # TODO: replace with dictionary mapping
|
||||
dis_y = DISPLAY_MID
|
||||
elif y == "1":
|
||||
@ -335,8 +320,8 @@ class BitWaveform(Waveform):
|
||||
|
||||
|
||||
class BitVectorWaveform(Waveform):
|
||||
def __init__(self, channel, state, parent=None):
|
||||
Waveform.__init__(self, channel, state, parent)
|
||||
def __init__(self, name, width, parent=None):
|
||||
Waveform.__init__(self, name, width, parent)
|
||||
self._labels = []
|
||||
hx = math.ceil(self.width / 4)
|
||||
self._format_string = "{:0=" + str(hx) + "X}" # TODO: change method..
|
||||
@ -350,26 +335,21 @@ class BitVectorWaveform(Waveform):
|
||||
right_label_i = bisect.bisect_right(self.x_data, xmax) + 1
|
||||
for i, j in itertools.pairwise(range(left_label_i, right_label_i)):
|
||||
x1 = self.x_data[i]
|
||||
x2 = self.x_data[j] if j < len(self.x_data) else self.state["stopped_x"]
|
||||
x2 = self.x_data[j] if j < len(self.x_data) else self.x_data[-1]*2
|
||||
lbl = self._labels[i]
|
||||
bounds = lbl.boundingRect()
|
||||
bounds_view = self.view_box.mapSceneToView(bounds)
|
||||
if bounds_view.boundingRect().width() < x2 - x1:
|
||||
self.addItem(lbl)
|
||||
|
||||
def extract_data_from_state(self):
|
||||
try:
|
||||
self.x_data, self.y_data = zip(*self.state['data'][self.name])
|
||||
except:
|
||||
logger.debug('Error caught when loading waveform data: {}'.format(self.name), exc_info=True)
|
||||
|
||||
def display(self):
|
||||
|
||||
# TODO this may need to block sigTransformChanged while changing the labels
|
||||
def onDataChange(self, data):
|
||||
try:
|
||||
display_x, display_y = [], []
|
||||
for x, y in zip(self.x_data, self.y_data):
|
||||
for x, y in data:
|
||||
display_x.append(x)
|
||||
display_y.append(DISPLAY_LOW)
|
||||
if "X" in y: # TODO change to using a dictionary
|
||||
if "X" in y: # TODO change to using a dictionary, remove any ref to "X" it does not happen in current impl
|
||||
display_x.append(x)
|
||||
display_y.append(DISPLAY_MID)
|
||||
elif int(y) != 0:
|
||||
@ -396,28 +376,23 @@ class BitVectorWaveform(Waveform):
|
||||
|
||||
|
||||
class AnalogWaveform(Waveform):
|
||||
def __init__(self, channel, state, parent=None):
|
||||
Waveform.__init__(self, channel, state, parent)
|
||||
def __init__(self, name, width, parent=None):
|
||||
Waveform.__init__(self, name, width, parent)
|
||||
self.plot_data_item.setDownsampling(ds=10, method="peak", auto=True)
|
||||
# TODO: experiment with downsampling values for best performance
|
||||
# TODO: potentially switch to not using a step connect
|
||||
|
||||
def extract_data_from_state(self):
|
||||
try:
|
||||
self.x_data, self.y_data = zip(*self.state['data'][self.name])
|
||||
except:
|
||||
logger.debug('Error caught when loading waveform data: {}'.format(self.name), exc_info=True)
|
||||
|
||||
# TODO: change to using max_, min_
|
||||
def display(self):
|
||||
def onDataChange(self, data):
|
||||
try:
|
||||
self.plot_data_item.setData(x=self.x_data, y=self.y_data)
|
||||
mx = max(self.y_data)
|
||||
mn = min(self.y_data)
|
||||
self.plot_item.setRange(yRange=(mn, mx), padding=0.1)
|
||||
x_data, y_data = zip(*data)
|
||||
self.plot_data_item.setData(x=x_data, y=y_data)
|
||||
max_y = max(y_data)
|
||||
min_y = min(y_data)
|
||||
self.plot_item.setRange(yRange=(min_y, max_y), padding=0.1)
|
||||
except:
|
||||
logger.debug('Error caught when displaying waveform: {}'.format(self.name), exc_info=True)
|
||||
self.plot_data_item.setData(x=[0], y=[0])
|
||||
self.plot_data_item.setData(x=[], y=[])
|
||||
|
||||
# TODO: can also just leave it as None -> simpler
|
||||
def format_cursor_label(self):
|
||||
@ -505,7 +480,7 @@ class _WaveformView(QtWidgets.QWidget):
|
||||
WaveformType.ANALOG: AnalogWaveform,
|
||||
WaveformType.LOG: LogWaveform
|
||||
}[ty]
|
||||
return waveform_cls(name, width)
|
||||
return waveform_cls(name, width, parent=self._splitter)
|
||||
|
||||
def _resize(self):
|
||||
self._splitter.setMinimumHeight(Waveform.MIN_HEIGHT * self._model.rowCount())
|
||||
@ -785,11 +760,12 @@ class WaveformDock(QtWidgets.QDockWidget):
|
||||
QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetFloatable)
|
||||
|
||||
self._channel_model = Model({})
|
||||
self._waveform_model = _WaveformModel()
|
||||
|
||||
self._devices = None
|
||||
self._ddb = None
|
||||
self._dump = None
|
||||
|
||||
self._state = {
|
||||
self._waveform_data = {
|
||||
"timescale": 1,
|
||||
"stopped_x": None,
|
||||
"logs": dict(),
|
||||
@ -821,12 +797,9 @@ class WaveformDock(QtWidgets.QDockWidget):
|
||||
grid.addWidget(self._request_dump_btn, 0, 1)
|
||||
self._request_dump_btn.clicked.connect(
|
||||
lambda: asyncio.ensure_future(self.rpc_client.trigger_proxy_task()))
|
||||
|
||||
self._waveform_area = WaveformArea(self, self._state,
|
||||
self._channel_model)
|
||||
self.traceDataChanged.connect(self._waveform_area.on_trace_update)
|
||||
self.traceDataChanged.connect(self._update_log_channels)
|
||||
grid.addWidget(self._waveform_area, 2, 0, colspan=12)
|
||||
|
||||
self._waveform_view = _WaveformView(self)
|
||||
grid.addWidget(self._waveform_view, 2, 0, colspan=12)
|
||||
|
||||
self._add_btn = QtWidgets.QToolButton()
|
||||
self._add_btn.setToolTip("Add channels...")
|
||||
@ -834,13 +807,15 @@ class WaveformDock(QtWidgets.QDockWidget):
|
||||
QtWidgets.QApplication.style().standardIcon(
|
||||
QtWidgets.QStyle.SP_FileDialogListView))
|
||||
grid.addWidget(self._add_btn, 0, 2)
|
||||
self._add_btn.clicked.connect(self._waveform_area.on_add_channel_click)
|
||||
self._add_btn.clicked.connect(self.on_add_channel_click)
|
||||
|
||||
self._cursor_control = _CursorTimeControl(parent=self, state=self._state)
|
||||
grid.addWidget(self._cursor_control, 0, 3, colspan=3)
|
||||
self._cursor_control.submit.connect(
|
||||
self._waveform_area.on_cursor_move)
|
||||
self._waveform_area.cursorMoved.connect(self._cursor_control.display_value)
|
||||
#self._cursor_control = _CursorTimeControl(parent=self, state=self._state)
|
||||
#grid.addWidget(self._cursor_control, 0, 3, colspan=3)
|
||||
#self._cursor_control.submit.connect(
|
||||
# self._waveform_area.on_cursor_move)
|
||||
#self._waveform_area.cursorMoved.connect(self._cursor_control.display_value)
|
||||
|
||||
self.traceDataChanged.connect(self._update_log_channels)
|
||||
|
||||
self._file_menu = QtWidgets.QMenu()
|
||||
self._add_async_action("Open trace...", self.load_trace)
|
||||
@ -867,6 +842,21 @@ class WaveformDock(QtWidgets.QDockWidget):
|
||||
self._dump = data
|
||||
self.traceDataChanged.emit()
|
||||
|
||||
def on_add_channel_click(self):
|
||||
asyncio.ensure_future(self._add_waveform_task())
|
||||
|
||||
async def _add_waveform_task(self):
|
||||
dialog = _AddChannelDialog(self, self._channel_model)
|
||||
fut = asyncio.Future()
|
||||
|
||||
def on_accept(s):
|
||||
fut.set_result(s)
|
||||
dialog.accepted.connect(on_accept)
|
||||
dialog.open()
|
||||
channels = await fut
|
||||
self._waveform_model.extend(channels)
|
||||
self._waveform_model.update_data(self._waveform_data) # TODO use a specific function for this
|
||||
|
||||
async def load_trace(self):
|
||||
try:
|
||||
filename = await get_open_file_name(
|
||||
|
Loading…
Reference in New Issue
Block a user