forked from M-Labs/artiq
wip
This commit is contained in:
parent
760d70cf33
commit
781afd9a94
@ -3,6 +3,55 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore
|
from PyQt5 import QtWidgets, QtCore
|
||||||
|
|
||||||
from artiq.gui.models import DictSyncTreeSepModel
|
from artiq.gui.models import DictSyncTreeSepModel
|
||||||
|
from artiq.gui.tools import QRecursiveFilterProxyModel
|
||||||
|
|
||||||
|
|
||||||
|
# Not tested with validators or partial editing (textChanged etc). Intended for simple usage.
|
||||||
|
class BetterLineEdit(QtWidgets.QLineEdit):
|
||||||
|
finished = QtCore.pyqtSignal(bool)
|
||||||
|
|
||||||
|
def __init__(self, init):
|
||||||
|
QtWidgets.QLineEdit.__init__(self, init)
|
||||||
|
self.setFrame(False)
|
||||||
|
self.setReadOnly(True)
|
||||||
|
self.returnPressed.connect(self._return_pressed)
|
||||||
|
self.editingFinished.connect(self._editing_finished)
|
||||||
|
self._text = init
|
||||||
|
self._candidate = None
|
||||||
|
|
||||||
|
def mouseDoubleClickEvent(self, event):
|
||||||
|
if self.isReadOnly():
|
||||||
|
self.setReadOnly(False)
|
||||||
|
self.setFrame(True)
|
||||||
|
QtWidgets.QLineEdit.mouseDoubleClickEvent(self, event)
|
||||||
|
|
||||||
|
def _return_pressed(self):
|
||||||
|
self._candidate = self.text()
|
||||||
|
|
||||||
|
def _editing_finished(self):
|
||||||
|
self.setReadOnly(True)
|
||||||
|
self.setFrame(False)
|
||||||
|
if self._candidate is not None:
|
||||||
|
changed = self._candidate != self._text
|
||||||
|
self._text = self._candidate
|
||||||
|
self.setText(self._text)
|
||||||
|
self._candidate = None
|
||||||
|
self.finished.emit(changed)
|
||||||
|
else:
|
||||||
|
self.setText(self._text)
|
||||||
|
|
||||||
|
def set_text(self, text):
|
||||||
|
self.blockSignals(True)
|
||||||
|
self._text = text
|
||||||
|
self.setText(self._text)
|
||||||
|
self.blockSignals(False)
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
key = event.key()
|
||||||
|
if key == QtCore.Qt.Key_Escape and not self.isReadOnly():
|
||||||
|
self.editingFinished.emit()
|
||||||
|
else:
|
||||||
|
QtWidgets.QLineEdit.keyPressEvent(self, event)
|
||||||
|
|
||||||
|
|
||||||
# MoninjView contains these and adds according to the proper type
|
# MoninjView contains these and adds according to the proper type
|
||||||
@ -12,48 +61,162 @@ from artiq.gui.models import DictSyncTreeSepModel
|
|||||||
# -> this is passed directly to the device manager
|
# -> this is passed directly to the device manager
|
||||||
# suboptimally multiple signals could be exposed
|
# suboptimally multiple signals could be exposed
|
||||||
class _MoninjWidget(QtWidgets.QWidget):
|
class _MoninjWidget(QtWidgets.QWidget):
|
||||||
inject = QtCore.pyqtSignal(tuple)
|
inject = QtCore.pyqtSignal(str, str)
|
||||||
|
nameChanged = QtCore.pyqtSignal()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, name, channel):
|
||||||
QtWidgets.QWidget.__init__(self)
|
QtWidgets.QWidget.__init__(self)
|
||||||
|
self.name = name
|
||||||
|
self.channel = channel
|
||||||
|
self.setMaximumHeight(100)
|
||||||
|
self.layout = QtWidgets.QGridLayout()
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
self.name_label = BetterLineEdit(self.name) # TODO: make this a cancellable line edit -> add cancellable line edit to gui tools
|
||||||
|
self.name_label.finished.connect(self.name_changed)
|
||||||
|
self.layout.addWidget(self.name_label, 0, 0)
|
||||||
|
|
||||||
|
def name_changed(self, changed):
|
||||||
|
if changed:
|
||||||
|
self.name = self.name_label._text
|
||||||
|
self.nameChanged.emit()
|
||||||
|
|
||||||
def refresh(self, value):
|
def refresh(self, value):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class _TTLWidget(_MoninjWidget):
|
class _TTLWidget(_MoninjWidget):
|
||||||
pass
|
def __init__(self, name, channel):
|
||||||
|
_MoninjWidget.__init__(self, name, channel)
|
||||||
|
self.label = QtWidgets.QLabel("0")
|
||||||
|
self.layout.addWidget(self.label, 0, 1)
|
||||||
|
self.button_group = QtWidgets.QButtonGroup()
|
||||||
|
self.button_group.setExclusive(False)
|
||||||
|
self.lvl = QtWidgets.QPushButton("LVL")
|
||||||
|
self.lvl.setCheckable(True)
|
||||||
|
self.layout.addWidget(self.lvl, 0, 2)
|
||||||
|
self.button_group.addButton(self.lvl, 0)
|
||||||
|
self.ovr = QtWidgets.QPushButton("OVR")
|
||||||
|
self.ovr.setCheckable(True)
|
||||||
|
self.layout.addWidget(self.ovr, 0, 3)
|
||||||
|
self.button_group.addButton(self.ovr, 1)
|
||||||
|
self.button_group.idClicked.connect(self._button_clicked)
|
||||||
|
|
||||||
|
def _button_clicked(self, id):
|
||||||
|
lvl = self.lvl.isChecked()
|
||||||
|
ovr = self.ovr.isChecked()
|
||||||
|
if lvl and ovr:
|
||||||
|
self.inject.emit("ttl", "1")
|
||||||
|
elif not lvl and ovr:
|
||||||
|
self.inject.emit("ttl", "0")
|
||||||
|
elif id == 1:
|
||||||
|
self.inject.emit("ttl", "exp")
|
||||||
|
|
||||||
|
def refresh(self, value):
|
||||||
|
self.label.setText(value)
|
||||||
|
|
||||||
|
|
||||||
class _DDSWidget(_MoninjWidget):
|
class _DDSWidget(_MoninjWidget):
|
||||||
pass
|
def __init__(self, name, channel):
|
||||||
|
_MoninjWidget.__init__(self, name, channel)
|
||||||
|
|
||||||
|
def refresh(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class _DACWidget(_MoninjWidget):
|
class _DACWidget(_MoninjWidget):
|
||||||
pass
|
def __init__(self, name, channel):
|
||||||
|
_MoninjWidget.__init__(self, name, channel) # the channel is the actual uid
|
||||||
|
|
||||||
|
def refresh(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
# The main tree / table view of the dock -> probably inherits from QTreeView and connects with the
|
# The main tree / table view of the dock -> probably inherits from QTreeView and connects with the
|
||||||
# 'MoninjModel' MoninjModel and View could be merged as QTreeWidget but probably not a good idea..
|
# 'MoninjModel' MoninjModel and View could be merged as QTreeWidget <-- this is what we will do...
|
||||||
class MoninjView:
|
# dont allow nested groups for now
|
||||||
|
# strongly resembles and borrows from EntryTreeWidget
|
||||||
|
class MoninjTreeWidget(QtWidgets.QTreeWidget):
|
||||||
|
def __init__(self):
|
||||||
|
QtWidgets.QTreeWidget.__init__(self)
|
||||||
|
self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove)
|
||||||
|
|
||||||
|
def add_group(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_channel(self, channel_args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_channels(self, channels):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_channel(self, id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_configuration(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_configuration(self, config):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Like the _AddChannelDialog but should have search function enabled with QRecursiveFilterProxyModel
|
# Like the _AddChannelDialog but should have search function enabled with QRecursiveFilterProxyModel
|
||||||
class MoninjAddChannelDialog(QtWidgets.QDialog):
|
class MoninjAddChannelDialog(QtWidgets.QDialog):
|
||||||
pass
|
def __init__(self, parent):
|
||||||
|
QtWidgets.QDialog.__init__(self, parent=parent)
|
||||||
|
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||||
|
self.setWindowTitle("Add channels")
|
||||||
|
|
||||||
|
layout = QtWidgets.QVBoxLayout()
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
self._search = QtWidgets.QLineEdit()
|
||||||
|
self._search.setPlaceholderText("search...")
|
||||||
|
self._search.editingFinished.connect(self._search_datasets)
|
||||||
|
layout.addWidget(self._search)
|
||||||
|
|
||||||
|
self._model = None
|
||||||
|
self._tree_view = QtWidgets.QTreeView()
|
||||||
|
self._tree_view.setHeaderHidden(True)
|
||||||
|
self._tree_view.setSelectionBehavior(
|
||||||
|
QtWidgets.QAbstractItemView.SelectItems)
|
||||||
|
self._tree_view.setSelectionMode(
|
||||||
|
QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
layout.addWidget(self._tree_view)
|
||||||
|
|
||||||
|
self._button_box = QtWidgets.QDialogButtonBox(
|
||||||
|
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
|
self._button_box.setCenterButtons(True)
|
||||||
|
self._button_box.accepted.connect(self.add_channels)
|
||||||
|
self._button_box.rejected.connect(self.reject)
|
||||||
|
layout.addWidget(self._button_box)
|
||||||
|
|
||||||
|
def setModel(self, model):
|
||||||
|
self._model = model
|
||||||
|
self._model_filter = QRecursiveFilterProxyModel()
|
||||||
|
self._model_filter.setSourceModel(model)
|
||||||
|
self._tree_view.setModel(self._model_filter)
|
||||||
|
|
||||||
|
def _search_datasets(self):
|
||||||
|
if hasattr(self, "_table_filter"):
|
||||||
|
self.table_model_filter.setFilterFixedString(self._search.displayText())
|
||||||
|
|
||||||
|
def add_channels(self):
|
||||||
|
selection = self._tree_view.selectedIndexes()
|
||||||
|
channels = []
|
||||||
|
for select in selection:
|
||||||
|
key = self._model.index_to_key(select)
|
||||||
|
if key is not None:
|
||||||
|
channels.append([key, *self._model[key].ref, []])
|
||||||
|
self.channels = channels
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
# Contains the necessary identifiers for listing all available channels, organized by type and name
|
# Contains the necessary identifiers for listing all available channels, organized by type and name
|
||||||
# does not do any moninj itself, only exists as reference to add channels to 'MoninjModel'
|
# does not do any moninj itself, only exists as reference to add channels to 'MoninjModel'
|
||||||
class MoninjChannelModel(DictSyncTreeSepModel):
|
class MoninjChannelModel(DictSyncTreeSepModel):
|
||||||
pass
|
def __init__(self, init):
|
||||||
|
DictSyncTreeSepModel.__init__(self, "/", ["Channels"], init)
|
||||||
|
|
||||||
# Currently displayed data, should either be AbstractTableModel
|
|
||||||
# extends features to connect widgets with device manager
|
|
||||||
class MoninjModel:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Retains most functionality of old DeviceManager (contain any device interface here)
|
# Retains most functionality of old DeviceManager (contain any device interface here)
|
||||||
@ -69,5 +232,80 @@ class DeviceManager:
|
|||||||
# + search bar (sort options / if not in view itself)
|
# + search bar (sort options / if not in view itself)
|
||||||
# + add channels dialog window
|
# + add channels dialog window
|
||||||
class MoninjDock(QtWidgets.QDockWidget):
|
class MoninjDock(QtWidgets.QDockWidget):
|
||||||
|
def __init__(self, schedule_ctl):
|
||||||
|
QtWidgets.QDockWidget.__init__(self, "MonInj")
|
||||||
|
self.setObjectName("MonInj")
|
||||||
|
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
|
||||||
|
QtWidgets.QDockWidget.DockWidgetFloatable)
|
||||||
|
|
||||||
|
# connect the device manager (it manages ddb + moninj)
|
||||||
|
self.dm = DeviceManager(schedule_ctl)
|
||||||
|
|
||||||
|
# GridLayout
|
||||||
|
layout = QtWidgets.QGridLayout()
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# Options (borrow from waveform)
|
||||||
|
self._menu_btn = QtWidgets.QPushButton()
|
||||||
|
self._menu_btn.setIcon(
|
||||||
|
QtWidgets.QApplication.style().standardIcon(
|
||||||
|
QtWidgets.QStyle.SP_FileDialogStart))
|
||||||
|
layout.addWidget(self._menu_btn, 0, 0)
|
||||||
|
|
||||||
|
self._file_menu = QtWidgets.QMenu()
|
||||||
|
# self._add_async_action("Open configuration...", self.load_configuration)
|
||||||
|
# self._add_async_action("Save configuration...", self.save_configuration)
|
||||||
|
self._menu_btn.setMenu(self._file_menu)
|
||||||
|
|
||||||
|
# Add channels
|
||||||
|
self.channel_model = MoninjChannelModel()
|
||||||
|
self.add_channel_dialog = MoninjAddChannelDialog()
|
||||||
|
self.add_channel_dialog.setModel(self.channel_model)
|
||||||
|
|
||||||
|
self._add_btn = QtWidgets.QToolButton()
|
||||||
|
self._add_btn.setToolTip("Add channels...")
|
||||||
|
self._add_btn.setIcon(
|
||||||
|
QtWidgets.QApplication.style().standardIcon(
|
||||||
|
QtWidgets.QStyle.SP_FileDialogListView))
|
||||||
|
self._add_btn.clicked.connect(self.add_channel_dialog.open)
|
||||||
|
layout.addWidget(self._add_btn, 0, 2)
|
||||||
|
|
||||||
|
# Add new group
|
||||||
|
self._add_group_btn = QtWidgets.QToolButton()
|
||||||
|
self._add_group_btn.setToolTip("Add group...")
|
||||||
|
self._add_group_btn.setIcon(
|
||||||
|
QtWidgets.QApplication.style().standardIcon(
|
||||||
|
QtWidgets.QStyle.SP_FileDialogListView))
|
||||||
|
|
||||||
|
# connect device manager to channel model (potentially separate out this)
|
||||||
|
self.dm.update_channels.connect(self.channel_model.update_channels)
|
||||||
|
|
||||||
|
# MoninjView and model
|
||||||
|
self.moninj_model = MoninjModel()
|
||||||
|
self.moninj_view = MoninjView()
|
||||||
|
self.moninj_view.setModel(self.moninj_model)
|
||||||
|
layout.addWidget(self.moninj_view, 1, 0)
|
||||||
|
|
||||||
|
# connect device manager to moninj model / view
|
||||||
|
self.dm.monitor.connect(self.moninj_view.monitor)
|
||||||
|
self.moninj_view.inject.connect(self.dm.inject)
|
||||||
|
|
||||||
|
# add group
|
||||||
|
self._add_group_btn.clicked.connect(self.moninj_model.new_group)
|
||||||
|
|
||||||
|
# is it acceptable for open/closed knowledge to be dropped? only maintained in state
|
||||||
|
def save_configuration(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def open_configuration(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def restore_state(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
if self.dm is not None:
|
||||||
|
await self.dm.close()
|
||||||
|
Loading…
Reference in New Issue
Block a user