waveform: changes to mvc

This commit is contained in:
Simon Renblad 2024-01-22 14:13:37 +08:00
parent f60924f077
commit 4d97cb5426

View File

@ -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(