diff --git a/artiq/gui/dndwidgets.py b/artiq/gui/dndwidgets.py index 03e8a5e63..1f7b3fc07 100644 --- a/artiq/gui/dndwidgets.py +++ b/artiq/gui/dndwidgets.py @@ -1,4 +1,6 @@ -from PyQt5 import QtCore, QtWidgets +from PyQt5 import QtCore, QtWidgets, QtGui + +from artiq.gui.flowlayout import FlowLayout class VDragDropSplitter(QtWidgets.QSplitter): @@ -98,3 +100,65 @@ class VDragScrollArea(QtWidgets.QScrollArea): dy = self._direction * self._speed new_val = min(max_, max(min_, val + dy)) self.verticalScrollBar().setValue(new_val) + + +# Widget with FlowLayout and drag and drop support between widgets +class DragDropFlowLayoutWidget(QtWidgets.QWidget): + def __init__(self): + QtWidgets.QWidget.__init__(self) + self.layout = FlowLayout() + self.setLayout(self.layout) + self.setAcceptDrops(True) + + def _get_index(self, pos): + for i in range(self.layout.count()): + if self.itemAt(i).geometry().contains(pos): + return i + return -1 + + def mousePressEvent(self, event): + if event.buttons() == QtCore.Qt.LeftButton \ + and event.modifiers() == QtCore.Qt.ShiftModifier: + index = self._get_index(event.pos()) + if index == -1: + return + drag = QtGui.QDrag(self) + mime = QtCore.QMimeData() + mime.setData("index", str(index).encode()) + drag.setMimeData(mime) + pixmapi = QtWidgets.QApplication.style().standardIcon( + QtWidgets.QStyle.SP_FileIcon) + drag.setPixmap(pixmapi.pixmap(32)) + drag.exec_(QtCore.Qt.MoveAction) + event.accept() + + def dragEnterEvent(self, event): + event.accept() + + def dropEvent(self, event): + index = self._get_index(event.pos()) + source_layout = event.source() + source_index = int(bytes(event.mimeData().data("index")).decode()) + if source_layout == self: + if index == source_index: + return + widget = self.layout.itemAt(source_index).widget() + self.layout.removeWidget(widget) + self.layout.addWidget(widget) + self.layout.itemList.insert(index, self.layout.itemList.pop()) + else: + widget = source_layout.layout.itemAt(source_index).widget() + source_layout.layout.removeWidget(widget) + self.layout.addWidget(widget) + if index != -1: + self.layout.itemList.insert(index, self.layout.itemList.pop()) + event.accept() + + def addWidget(self, widget): + self.layout.addWidget(widget) + + def count(self): + return self.layout.count() + + def itemAt(self, i): + return self.layout.itemAt(i)