forked from M-Labs/artiq
entries: add EntryTreeWidget
This commit is contained in:
parent
c4323e1179
commit
4002ed0a25
|
@ -1,9 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from artiq.gui.tools import LayoutWidget, disable_scroll_wheel
|
from artiq.gui.tools import LayoutWidget, disable_scroll_wheel, WheelFilter
|
||||||
from artiq.gui.scanwidget import ScanWidget
|
from artiq.gui.scanwidget import ScanWidget
|
||||||
from artiq.gui.scientific_spinbox import ScientificSpinBox
|
from artiq.gui.scientific_spinbox import ScientificSpinBox
|
||||||
|
|
||||||
|
@ -11,6 +12,158 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EntryTreeWidget(QtWidgets.QTreeWidget):
|
||||||
|
def __init__(self):
|
||||||
|
QtWidgets.QTreeWidget.__init__(self)
|
||||||
|
self.setColumnCount(3)
|
||||||
|
self.header().setStretchLastSection(False)
|
||||||
|
if hasattr(self.header(), "setSectionResizeMode"):
|
||||||
|
set_resize_mode = self.header().setSectionResizeMode
|
||||||
|
else:
|
||||||
|
set_resize_mode = self.header().setResizeMode
|
||||||
|
set_resize_mode(0, QtWidgets.QHeaderView.ResizeToContents)
|
||||||
|
set_resize_mode(1, QtWidgets.QHeaderView.Stretch)
|
||||||
|
self.header().setVisible(False)
|
||||||
|
self.setSelectionMode(self.NoSelection)
|
||||||
|
self.setHorizontalScrollMode(self.ScrollPerPixel)
|
||||||
|
self.setVerticalScrollMode(self.ScrollPerPixel)
|
||||||
|
|
||||||
|
self.setStyleSheet("QTreeWidget {background: " +
|
||||||
|
self.palette().midlight().color().name() + " ;}")
|
||||||
|
|
||||||
|
self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
|
||||||
|
|
||||||
|
self._groups = dict()
|
||||||
|
self._arg_to_widgets = dict()
|
||||||
|
self._arguments = dict()
|
||||||
|
|
||||||
|
self.gradient = QtGui.QLinearGradient(
|
||||||
|
0, 0, 0, QtGui.QFontMetrics(self.font()).lineSpacing()*2.5)
|
||||||
|
self.gradient.setColorAt(0, self.palette().base().color())
|
||||||
|
self.gradient.setColorAt(1, self.palette().midlight().color())
|
||||||
|
|
||||||
|
self.bottom_item = QtWidgets.QTreeWidgetItem()
|
||||||
|
self.addTopLevelItem(self.bottom_item)
|
||||||
|
self.bottom_item.setHidden(True)
|
||||||
|
|
||||||
|
def set_argument(self, key, argument):
|
||||||
|
self._arguments[key] = argument
|
||||||
|
widgets = dict()
|
||||||
|
self._arg_to_widgets[key] = widgets
|
||||||
|
entry_class = procdesc_to_entry(argument["desc"])
|
||||||
|
argument["state"] = entry_class.default_state(argument["desc"])
|
||||||
|
entry = entry_class(argument)
|
||||||
|
widget_item = QtWidgets.QTreeWidgetItem([key])
|
||||||
|
if argument["tooltip"]:
|
||||||
|
widget_item.setToolTip(0, argument["tooltip"])
|
||||||
|
widgets["entry"] = entry
|
||||||
|
widgets["widget_item"] = widget_item
|
||||||
|
|
||||||
|
if len(self._arguments) > 1:
|
||||||
|
self.bottom_item.setHidden(False)
|
||||||
|
|
||||||
|
for col in range(3):
|
||||||
|
widget_item.setBackground(col, self.gradient)
|
||||||
|
font = widget_item.font(0)
|
||||||
|
font.setBold(True)
|
||||||
|
widget_item.setFont(0, font)
|
||||||
|
|
||||||
|
if argument["group"] is None:
|
||||||
|
self.insertTopLevelItem(self.indexFromItem(self.bottom_item).row(), widget_item)
|
||||||
|
else:
|
||||||
|
self._get_group(argument["group"]).addChild(widget_item)
|
||||||
|
self.bottom_item.setHidden(False)
|
||||||
|
fix_layout = LayoutWidget()
|
||||||
|
widgets["fix_layout"] = fix_layout
|
||||||
|
fix_layout.addWidget(entry)
|
||||||
|
self.setItemWidget(widget_item, 1, fix_layout)
|
||||||
|
|
||||||
|
reset_entry = QtWidgets.QToolButton()
|
||||||
|
reset_entry.setToolTip("Reset to default value")
|
||||||
|
reset_entry.setIcon(
|
||||||
|
QtWidgets.QApplication.style().standardIcon(
|
||||||
|
QtWidgets.QStyle.SP_BrowserReload))
|
||||||
|
reset_entry.clicked.connect(partial(self.reset_entry, key))
|
||||||
|
|
||||||
|
disable_other_scans = QtWidgets.QToolButton()
|
||||||
|
widgets["disable_other_scans"] = disable_other_scans
|
||||||
|
disable_other_scans.setIcon(
|
||||||
|
QtWidgets.QApplication.style().standardIcon(
|
||||||
|
QtWidgets.QStyle.SP_DialogResetButton))
|
||||||
|
disable_other_scans.setToolTip("Disable other scans")
|
||||||
|
disable_other_scans.clicked.connect(
|
||||||
|
partial(self._disable_other_scans, key))
|
||||||
|
if not isinstance(entry, ScanEntry):
|
||||||
|
disable_other_scans.setVisible(False)
|
||||||
|
|
||||||
|
tool_buttons = LayoutWidget()
|
||||||
|
tool_buttons.layout.setRowStretch(0, 1)
|
||||||
|
tool_buttons.layout.setRowStretch(3, 1)
|
||||||
|
tool_buttons.addWidget(reset_entry, 1)
|
||||||
|
tool_buttons.addWidget(disable_other_scans, 2)
|
||||||
|
self.setItemWidget(widget_item, 2, tool_buttons)
|
||||||
|
|
||||||
|
def _get_group(self, key):
|
||||||
|
if key in self._groups:
|
||||||
|
return self._groups[key]
|
||||||
|
group = QtWidgets.QTreeWidgetItem([key])
|
||||||
|
for col in range(3):
|
||||||
|
group.setBackground(col, self.palette().mid())
|
||||||
|
group.setForeground(col, self.palette().brightText())
|
||||||
|
font = group.font(col)
|
||||||
|
font.setBold(True)
|
||||||
|
group.setFont(col, font)
|
||||||
|
self.insertTopLevelItem(self.indexFromItem(self.bottom_item).row(), group)
|
||||||
|
self._groups[key] = group
|
||||||
|
return group
|
||||||
|
|
||||||
|
def _disable_other_scans(self, current_key):
|
||||||
|
for key, widgets in self._arg_to_widgets.items():
|
||||||
|
if (key != current_key
|
||||||
|
and isinstance(widgets["entry"], ScanEntry)):
|
||||||
|
widgets["entry"].disable()
|
||||||
|
|
||||||
|
def update_argument(self, key, argument):
|
||||||
|
widgets = self._arg_to_widgets[key]
|
||||||
|
|
||||||
|
# Qt needs a setItemWidget() to handle layout correctly,
|
||||||
|
# simply replacing the entry inside the LayoutWidget
|
||||||
|
# results in a bug.
|
||||||
|
|
||||||
|
widgets["entry"].deleteLater()
|
||||||
|
widgets["entry"] = procdesc_to_entry(argument["desc"])(argument)
|
||||||
|
widgets["disable_other_scans"].setVisible(
|
||||||
|
isinstance(widgets["entry"], ScanEntry))
|
||||||
|
widgets["fix_layout"].deleteLater()
|
||||||
|
widgets["fix_layout"] = LayoutWidget()
|
||||||
|
widgets["fix_layout"].addWidget(widgets["entry"])
|
||||||
|
self.setItemWidget(widgets["widget_item"], 1, widgets["fix_layout"])
|
||||||
|
self.updateGeometries()
|
||||||
|
|
||||||
|
def reset_entry(self, key):
|
||||||
|
procdesc = self._arguments[key]["desc"]
|
||||||
|
self._arguments[key]["state"] = procdesc_to_entry(procdesc).default_state(procdesc)
|
||||||
|
self.update_argument(key, self._arguments[key])
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
expanded = []
|
||||||
|
for k, v in self._groups.items():
|
||||||
|
if v.isExpanded():
|
||||||
|
expanded.append(k)
|
||||||
|
return {
|
||||||
|
"expanded": expanded,
|
||||||
|
"scroll": self.verticalScrollBar().value()
|
||||||
|
}
|
||||||
|
|
||||||
|
def restore_state(self, state):
|
||||||
|
for e in state["expanded"]:
|
||||||
|
try:
|
||||||
|
self._groups[e].setExpanded(True)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
self.verticalScrollBar().setValue(state["scroll"])
|
||||||
|
|
||||||
|
|
||||||
class StringEntry(QtWidgets.QLineEdit):
|
class StringEntry(QtWidgets.QLineEdit):
|
||||||
def __init__(self, argument):
|
def __init__(self, argument):
|
||||||
QtWidgets.QLineEdit.__init__(self)
|
QtWidgets.QLineEdit.__init__(self)
|
||||||
|
|
Loading…
Reference in New Issue