forked from M-Labs/artiq
1
0
Fork 0

Compare commits

...

10 Commits

7 changed files with 390 additions and 76 deletions

View File

@ -0,0 +1,311 @@
# Sketching up a potential overhaul for the moninj GUI interface
from PyQt5 import QtWidgets, QtCore
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
# (the class should be resolved with a helper and the interface generic)
# All widgets should take a refresh hook (slot) for monitoring
# All widgets should expose an inject signal with the "command" and "value"
# -> this is passed directly to the device manager
# suboptimally multiple signals could be exposed
class _MoninjWidget(QtWidgets.QWidget):
inject = QtCore.pyqtSignal(str, str)
nameChanged = QtCore.pyqtSignal()
def __init__(self, name, channel):
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):
raise NotImplementedError
class _TTLWidget(_MoninjWidget):
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):
def __init__(self, name, channel):
_MoninjWidget.__init__(self, name, channel)
def refresh(self, value):
raise NotImplementedError
class _DACWidget(_MoninjWidget):
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
# 'MoninjModel' MoninjModel and View could be merged as QTreeWidget <-- this is what we will do...
# 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
# Like the _AddChannelDialog but should have search function enabled with QRecursiveFilterProxyModel
class MoninjAddChannelDialog(QtWidgets.QDialog):
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
# does not do any moninj itself, only exists as reference to add channels to 'MoninjModel'
class MoninjChannelModel(DictSyncTreeSepModel):
def __init__(self, init):
DictSyncTreeSepModel.__init__(self, "/", ["Channels"], init)
# Retains most functionality of old DeviceManager (contain any device interface here)
# What previously connected directly with widgets should now connect with 'MoninjModel'
class DeviceManager:
pass
# A standalone Dock
# Should include
# Toolbar header:
# + save and open configurations
# + search bar (sort options / if not in view itself)
# + add channels dialog window
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
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()

View File

@ -8,7 +8,6 @@ from sipyco import pyon
from artiq.tools import scale_from_metadata, short_format, exc_to_warning
from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel
from artiq.gui.models import DictSyncTreeSepModel
from artiq.gui.scientific_spinbox import ScientificSpinBox
logger = logging.getLogger(__name__)

View File

@ -5,6 +5,7 @@ from PyQt5 import QtCore, QtWidgets, QtGui
from artiq.gui.models import DictSyncModel
from artiq.gui.entries import EntryTreeWidget, procdesc_to_entry
from artiq.gui.tools import LayoutWidget
logger = logging.getLogger(__name__)
@ -29,32 +30,34 @@ class Model(DictSyncModel):
return k
class _InteractiveArgsRequest(QtWidgets.QWidget):
class _InteractiveArgsRequest(EntryTreeWidget):
supplied = QtCore.pyqtSignal(int, dict)
cancelled = QtCore.pyqtSignal(int)
def __init__(self, rid, arglist_desc):
QtWidgets.QWidget.__init__(self)
EntryTreeWidget.__init__(self)
self.rid = rid
self.arguments = dict()
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.entry_tree = EntryTreeWidget()
self.entry_tree.quickStyleClicked.connect(self.supply)
layout.addWidget(self.entry_tree, 0, 0, 1, 2)
for key, procdesc, group, tooltip in arglist_desc:
self.arguments[key] = {"desc": procdesc, "group": group, "tooltip": tooltip}
self.entry_tree.set_argument(key, self.arguments[key])
self.cancel_btn = QtWidgets.QPushButton("Cancel")
self.cancel_btn.setIcon(QtWidgets.QApplication.style().standardIcon(
self.set_argument(key, self.arguments[key])
self.quickStyleClicked.connect(self.supply)
cancel_btn = QtWidgets.QPushButton("Cancel")
cancel_btn.setIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_DialogCancelButton))
self.cancel_btn.clicked.connect(self.cancel)
layout.addWidget(self.cancel_btn, 1, 0, 1, 1)
self.supply_btn = QtWidgets.QPushButton("Supply")
self.supply_btn.setIcon(QtWidgets.QApplication.style().standardIcon(
cancel_btn.clicked.connect(self.cancel)
supply_btn = QtWidgets.QPushButton("Supply")
supply_btn.setIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_DialogOkButton))
self.supply_btn.clicked.connect(self.supply)
layout.addWidget(self.supply_btn, 1, 1, 1, 1)
supply_btn.clicked.connect(self.supply)
buttons = LayoutWidget()
buttons.addWidget(cancel_btn, 1, 1)
buttons.addWidget(supply_btn, 1, 2)
buttons.layout.setColumnStretch(0, 1)
buttons.layout.setColumnStretch(1, 0)
buttons.layout.setColumnStretch(2, 0)
buttons.layout.setColumnStretch(3, 1)
self.setItemWidget(self.bottom_item, 1, buttons)
def supply(self):
argument_values = dict()
@ -67,18 +70,27 @@ class _InteractiveArgsRequest(QtWidgets.QWidget):
self.cancelled.emit(self.rid)
class _InteractiveArgsView(QtWidgets.QTabWidget):
class _InteractiveArgsView(QtWidgets.QStackedWidget):
supplied = QtCore.pyqtSignal(int, dict)
cancelled = QtCore.pyqtSignal(int)
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
QtWidgets.QStackedWidget.__init__(self)
self.tabs = QtWidgets.QTabWidget()
self.default_label = QtWidgets.QLabel("No pending interactive arguments requests.")
self.default_label.setAlignment(QtCore.Qt.AlignCenter)
font = QtGui.QFont(self.default_label.font())
font.setItalic(True)
self.default_label.setFont(font)
self.addWidget(self.tabs)
self.addWidget(self.default_label)
self.model = Model({})
def setModel(self, model):
for i in range(self.count()):
widget = self.widget(i)
self.removeTab(i)
self.setCurrentIndex(1)
for i in range(self.tabs.count()):
widget = self.tabs.widget(i)
self.tabs.removeTab(i)
widget.deleteLater()
self.model = model
self.model.rowsInserted.connect(self.rowsInserted)
@ -93,17 +105,20 @@ class _InteractiveArgsView(QtWidgets.QTabWidget):
inter_args_request = _InteractiveArgsRequest(rid, arglist_desc)
inter_args_request.supplied.connect(self.supplied)
inter_args_request.cancelled.connect(self.cancelled)
self.insertTab(row, inter_args_request, title)
self.tabs.insertTab(row, inter_args_request, title)
def rowsInserted(self, parent, first, last):
assert first == last
self.setCurrentIndex(0)
self._insert_widget(first)
def rowsRemoved(self, parent, first, last):
assert first == last
widget = self.widget(first)
self.removeTab(first)
widget = self.tabs.widget(first)
self.tabs.removeTab(first)
widget.deleteLater()
if self.tabs.count() == 0:
self.setCurrentIndex(1)
class InteractiveArgsDock(QtWidgets.QDockWidget):

View File

@ -3,20 +3,17 @@ import logging
import textwrap
from collections import namedtuple
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5 import QtCore, QtWidgets
from artiq.coredevice.comm_moninj import *
from artiq.coredevice.ad9910 import (
_AD9910_REG_PROFILE0, _AD9910_REG_PROFILE7,
_AD9910_REG_FTW, _AD9910_REG_CFR1
)
from artiq.coredevice.ad9912_reg import AD9912_POW1, AD9912_SER_CONF
from artiq.coredevice.comm_moninj import CommMonInj, TTLOverride, TTLProbe
from artiq.coredevice.ad9912_reg import AD9912_SER_CONF
from artiq.gui.tools import LayoutWidget
from artiq.gui.flowlayout import FlowLayout
logger = logging.getLogger(__name__)
class _CancellableLineEdit(QtWidgets.QLineEdit):
def escapePressedConnect(self, cb):
self.esc_cb = cb
@ -131,7 +128,7 @@ class _TTLWidget(QtWidgets.QFrame):
else:
color = ""
self.value.setText("<font size=\"5\"{}>{}</font>".format(
color, value_s))
color, value_s))
oe = self.cur_oe or self.force_out
direction = "OUT" if oe else "IN"
self.direction.setText("<font size=\"2\">" + direction + "</font>")
@ -189,7 +186,7 @@ class _DDSModel:
self.cur_reg = 0
self.dds_type = dds_type
self.is_urukul = dds_type in ["AD9910", "AD9912"]
if dds_type == "AD9914":
self.ftw_per_hz = 2**32 / ref_clk
else:
@ -281,7 +278,7 @@ class _DDSWidget(QtWidgets.QFrame):
set_btn.setText("Set")
set_btn.setToolTip("Set frequency")
set_grid.addWidget(set_btn, 0, 1, 1, 1)
# for urukuls also allow switching off RF
if self.dds_model.is_urukul:
off_btn = QtWidgets.QToolButton()
@ -325,18 +322,17 @@ class _DDSWidget(QtWidgets.QFrame):
def set_clicked(self, set):
self.data_stack.setCurrentIndex(1)
self.button_stack.setCurrentIndex(1)
self.value_edit.setText("{:.7f}"
.format(self.cur_frequency/1e6))
self.value_edit.setText("{:.7f}".format(self.cur_frequency / 1e6))
self.value_edit.setFocus()
self.value_edit.selectAll()
def off_clicked(self, set):
self.dm.dds_channel_toggle(self.dds_name, self.dds_model, sw=False)
def apply_changes(self, apply):
self.data_stack.setCurrentIndex(0)
self.button_stack.setCurrentIndex(0)
frequency = float(self.value_edit.text())*1e6
frequency = float(self.value_edit.text()) * 1e6
self.dm.dds_set_frequency(self.dds_name, self.dds_model, frequency)
def cancel_changes(self, cancel):
@ -345,10 +341,8 @@ class _DDSWidget(QtWidgets.QFrame):
def refresh_display(self):
self.cur_frequency = self.dds_model.cur_frequency
self.value_label.setText("<font size=\"4\">{:.7f}</font>"
.format(self.cur_frequency/1e6))
self.value_edit.setText("{:.7f}"
.format(self.cur_frequency/1e6))
self.value_label.setText("<font size=\"4\">{:.7f}</font>".format(self.cur_frequency / 1e6))
self.value_edit.setText("{:.7f}".format(self.cur_frequency / 1e6))
def sort_key(self):
return (self.bus_channel, self.channel)
@ -363,7 +357,7 @@ class _DACWidget(_SimpleDisplayWidget):
def refresh_display(self):
self.value.setText("<font size=\"4\">{:.3f}</font><font size=\"2\"> %</font>"
.format(self.cur_value*100/2**16))
.format(self.cur_value * 100 / 2**16))
def sort_key(self):
return (self.spi_channel, self.channel)
@ -390,18 +384,16 @@ def setup_from_ddb(ddb):
force_out = v["class"] == "TTLOut"
widget = _WidgetDesc(k, comment, _TTLWidget, (channel, force_out, k))
description.add(widget)
elif (v["module"] == "artiq.coredevice.ad9914"
and v["class"] == "AD9914"):
elif (v["module"] == "artiq.coredevice.ad9914" and v["class"] == "AD9914"):
bus_channel = v["arguments"]["bus_channel"]
channel = v["arguments"]["channel"]
dds_sysclk = v["arguments"]["sysclk"]
model = _DDSModel(v["class"], dds_sysclk)
widget = _WidgetDesc(k, comment, _DDSWidget, (k, bus_channel, channel, model))
widget = _WidgetDesc(k, comment, _DDSWidget,
(k, bus_channel, channel, model))
description.add(widget)
elif (v["module"] == "artiq.coredevice.ad9910"
and v["class"] == "AD9910") or \
(v["module"] == "artiq.coredevice.ad9912"
and v["class"] == "AD9912"):
elif (v["module"] == "artiq.coredevice.ad9910" and v["class"] == "AD9910") or \
(v["module"] == "artiq.coredevice.ad9912" and v["class"] == "AD9912"):
channel = v["arguments"]["chip_select"] - 4
if channel < 0:
continue
@ -411,18 +403,20 @@ def setup_from_ddb(ddb):
pll = v["arguments"]["pll_n"]
refclk = ddb[dds_cpld]["arguments"]["refclk"]
clk_div = v["arguments"].get("clk_div", 0)
model = _DDSModel( v["class"], refclk, dds_cpld, pll, clk_div)
widget = _WidgetDesc(k, comment, _DDSWidget, (k, bus_channel, channel, model))
description.add(widget)
elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53xx")
or (v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino")):
model = _DDSModel(v["class"], refclk, dds_cpld, pll, clk_div)
widget = _WidgetDesc(k, comment, _DDSWidget,
(k, bus_channel, channel, model))
description.add(widget)
elif (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53xx") or \
(v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino"):
spi_device = v["arguments"]["spi_device"]
spi_device = ddb[spi_device]
while isinstance(spi_device, str):
spi_device = ddb[spi_device]
spi_channel = spi_device["arguments"]["channel"]
for channel in range(32):
widget = _WidgetDesc((k, channel), comment, _DACWidget, (spi_channel, channel, k))
widget = _WidgetDesc((k, channel), comment, _DACWidget,
(spi_channel, channel, k))
description.add(widget)
elif v["type"] == "controller" and k == "core_moninj":
mi_addr = v["host"]
@ -483,7 +477,7 @@ class _DeviceManager:
self.setup_dac_monitoring(False, widget.spi_channel, widget.channel)
widget.deleteLater()
del self.dac_widgets[(widget.spi_channel, widget.channel)]
self.dac_cb()
self.dac_cb()
else:
raise ValueError
@ -562,7 +556,7 @@ class _DeviceManager:
cpld_dev = """self.setattr_device("core_cache")
self.setattr_device("{}")""".format(dds_model.cpld)
# `sta`/`rf_sw`` variables are guaranteed for urukuls
# `sta`/`rf_sw`` variables are guaranteed for urukuls
# so {action} can use it
# if there's no RF enabled, CPLD may have not been initialized
# but if there is, it has been initialised - no need to do again
@ -621,8 +615,8 @@ class _DeviceManager:
channel_init=channel_init))
asyncio.ensure_future(
self._submit_by_content(
dds_exp,
title,
dds_exp,
title,
log_msg))
def dds_set_frequency(self, dds_channel, dds_model, freq):
@ -637,8 +631,8 @@ class _DeviceManager:
dds_channel,
dds_model,
action,
"SetDDS",
"Set DDS {} {}MHz".format(dds_channel, freq/1e6))
"SetDDS",
"Set DDS {} {}MHz".format(dds_channel, freq / 1e6))
def dds_channel_toggle(self, dds_channel, dds_model, sw=True):
# urukul only
@ -658,7 +652,7 @@ class _DeviceManager:
dds_channel,
dds_model,
action,
"ToggleDDS",
"ToggleDDS",
"Toggle DDS {} {}".format(dds_channel, "on" if sw else "off"))
def setup_ttl_monitoring(self, enable, channel):
@ -716,11 +710,12 @@ class _DeviceManager:
await self.mi_connection.close()
self.mi_connection = None
new_mi_connection = CommMonInj(self.monitor_cb, self.injection_status_cb,
self.disconnect_cb)
self.disconnect_cb)
try:
await new_mi_connection.connect(self.mi_addr, self.mi_port)
except Exception:
logger.error("failed to connect to moninj. Is aqctl_moninj_proxy running?", exc_info=True)
logger.error("failed to connect to moninj. Is aqctl_moninj_proxy running?",
exc_info=True)
await asyncio.sleep(10.)
self.reconnect_mi.set()
else:
@ -773,12 +768,9 @@ class MonInj:
self.dac_dock = _MonInjDock("DAC")
self.dm = _DeviceManager(schedule_ctl)
self.dm.ttl_cb = lambda: self.ttl_dock.layout_widgets(
self.dm.ttl_widgets.values())
self.dm.dds_cb = lambda: self.dds_dock.layout_widgets(
self.dm.dds_widgets.values())
self.dm.dac_cb = lambda: self.dac_dock.layout_widgets(
self.dm.dac_widgets.values())
self.dm.ttl_cb = lambda: self.ttl_dock.layout_widgets(self.dm.ttl_widgets.values())
self.dm.dds_cb = lambda: self.dds_dock.layout_widgets(self.dm.dds_widgets.values())
self.dm.dac_cb = lambda: self.dac_dock.layout_widgets(self.dm.dac_widgets.values())
async def stop(self):
if self.dm is not None:

View File

@ -3,8 +3,6 @@ from functools import partial
from PyQt5 import QtCore, QtWidgets
from artiq.gui.tools import LayoutWidget
logger = logging.getLogger(__name__)

View File

@ -17,7 +17,7 @@ from artiq.tools import exc_to_warning, short_format
from artiq.coredevice import comm_analyzer
from artiq.coredevice.comm_analyzer import WaveformType
from artiq.gui.tools import LayoutWidget, get_open_file_name, get_save_file_name
from artiq.gui.models import DictSyncTreeSepModel, LocalModelManager
from artiq.gui.models import DictSyncTreeSepModel
from artiq.gui.dndwidgets import VDragScrollArea, VDragDropSplitter

View File

@ -6,7 +6,6 @@ import atexit
import importlib
import os
import logging
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from qasync import QEventLoop