PyQt:是否可以在 QGridLayout 中拖放 QWidgets 以重新排列它们?

Fre*_*tri 4 drag-and-drop pyqt qwidget qgridlayout pyqt5

我正在寻找一种方法来创建可以拖/放以重新排列顺序的图形网格。我的第一次尝试是使用 QDockWidgets,因为它们允许拖放,但是它们在很多其他方面受到限制。是否可以在 QGridLayout 中实现此功能?

现在我有一个带有 3x3 matplotlib 小部件的 QGridLayout。

这是所需布局结果的示例。

在此处输入图片说明

示例代码:

import sys
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import random

from PyQt5.QtWidgets import QGridLayout, QVBoxLayout, QHBoxLayout, QScrollArea, QWidget, QDialog, QApplication, QFrame

class IndicSelectWindow(QDialog):
    def __init__(self, parent=None):
        super(IndicSelectWindow, self).__init__(parent=parent)
        self.resize(1000, 800)

        self.layout = QtWidgets.QHBoxLayout(self)
        self.scrollArea = QScrollArea(self)
        self.scrollArea.setWidgetResizable(True)
        self.scrollAreaWidgetContents = QWidget()
        self.gridLayout = QGridLayout(self.scrollAreaWidgetContents)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.layout.addWidget(self.scrollArea)

        for i in range(3):
             for j in range(3):
                 self.Frame = QFrame(self)
                 self.Frame.setStyleSheet("background-color: white;")
                 self.Frame.setFrameStyle(QFrame.Panel | QFrame.Raised)
                 self.Frame.setLineWidth(2)
                 self.layout = QHBoxLayout(self.Frame)

                 self.figure = Figure()  # a figure to plot on
                 self.canvas = FigureCanvas(self.figure)
                 self.ax = self.figure.add_subplot(111)  # create an axis
                 data = [random.random() for i in range(10)]
                 self.ax.plot(data, '*-')  # plot data
                 self.canvas.draw()  # refresh canvas

                 self.layout.addWidget(self.canvas)

                 Box = QVBoxLayout()

                 Box.addWidget(self.Frame)

                 self.gridLayout.addLayout(Box, i, j)
                 self.gridLayout.setColumnStretch(i % 3, 1)
                 self.gridLayout.setRowStretch(j, 1)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = IndicSelectWindow()
    w.show()
    sys.exit(app.exec_())
Run Code Online (Sandbox Code Playgroud)

ale*_*lec 6

这是一个实现,它将交换拖/放中涉及的项目的位置。3个主要步骤是:

(1) 重新实现mousePressEvent获取LayoutItem基于鼠标坐标的索引。

(2) 重新实现mouseMoveEvent设置一个QDragFigureCanvas。

(3) 重新实现dropEvent以交换布局中的目标项目。

由于 matplotlib 小部件吸收鼠标事件,因此您还需要重新实现eventFilter以检测它们。

导入系统,随机
从 PyQt5.QtWidgets 导入 *
从 PyQt5.QtCore 导入 *
从 PyQt5.QtGui 导入 *
from matplotlib.figure 导入图
从 matplotlib.backends.backend_qt4agg 导入 FigureCanvasQTAgg 作为 FigureCanvas

类 IndicSelectWindow(QDialog):

    def __init__(self, parent=None):
        super(IndicSelectWindow, self).__init__(parent=parent)
        self.resize(1000, 800)

        self.target = 无
        self.setAcceptDrops(True)
        self.layout = QHBoxLayout(self)
        self.scrollArea = QScrollArea(self)
        self.scrollArea.setWidgetResizable(True)
        self.scrollAreaWidgetContents = QWidget()
        self.gridLayout = QGridLayout(self.scrollAreaWidgetContents)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.layout.addWidget(self.scrollArea)

        对于范围内的 i (3):
            对于范围内的 j(3):
                self.Frame = QFrame(self)
                self.Frame.setStyleSheet("背景色:白色;")
                self.Frame.setFrameStyle(QFrame.Panel | QFrame.Raised)
                self.Frame.setLineWidth(2)
                self.layout = QHBoxLayout(self.Frame)

                self.figure = Figure() # 要绘制的图形
                self.canvas = FigureCanvas(self.figure)
                self.ax = self.figure.add_subplot(111) # 创建一个轴
                数据 = [random.random() for i in range(10)]
                self.ax.plot(data, '*-') # 绘制数据
                self.canvas.draw() # 刷新画布
                self.canvas.installEventFilter(self)

                self.layout.addWidget(self.canvas)

                Box = QVBoxLayout()

                Box.addWidget(self.Frame)

                self.gridLayout.addLayout(Box, i, j)
                self.gridLayout.setColumnStretch(i % 3, 1)
                self.gridLayout.setRowStretch(j, 1)

    def eventFilter(self, watched, event):
        如果 event.type() == QEvent.MouseButtonPress:
            self.mousePressEvent(事件)
        elif event.type() == QEvent.MouseMove:
            self.mouseMoveEvent(事件)
        elif event.type() == QEvent.MouseButtonRelease:
            self.mouseReleaseEvent(事件)
        返回 super().eventFilter(watched, event)

    def get_index(self, pos):
        对于我在范围内(self.gridLayout.count()):
            如果 self.gridLayout.itemAt(i).geometry().contains(pos) 和 i != self.target:
                返回我

    def mousePressEvent(self, event):
        如果 event.button() == Qt.LeftButton:
            self.target = self.get_index(event.windowPos().toPoint())
        别的:
            self.target = 无

    def mouseMoveEvent(self, event):
        如果 event.buttons() & Qt.LeftButton 和 self.target 不是 None:
            拖动 = QDrag(self.gridLayout.itemAt(self.target))
            pix = self.gridLayout.itemAt(self.target).itemAt(0).widget().grab()
            mimedata = QMimeData()
            mimedata.setImageData(pix)
            drag.setMimeData(mimedata)
            drag.setPixmap(pix)
            drag.setHotSpot(event.pos())
            拖动.exec_()

    def mouseReleaseEvent(self, event):
        self.target = 无

    def dragEnterEvent(self, event):
        如果 event.mimeData().hasImage():
            事件.接受()
        别的:
            事件.忽略()

    def dropEvent(self, event):
        如果不是 event.source().geometry().contains(event.pos()):
            source = self.get_index(event.pos())
            如果来源是无:
                返回

            i, j = max(self.target, source), min(self.target, source)
            p1, p2 = self.gridLayout.getItemPosition(i), self.gridLayout.getItemPosition(j)

            self.gridLayout.addItem(self.gridLayout.takeAt(i), *p2)
            self.gridLayout.addItem(self.gridLayout.takeAt(j), *p1)


如果 __name__ == '__main__':
    app = QApplication(sys.argv)
    w = IndicSelectWindow()
    w.show()
    sys.exit(app.exec_())