PyQt:是否可以将QWidget拖放到QGridLayout中以重新排列它们? [英] PyQt: Is it possible to drag/drop QWidgets in a QGridLayout to rearrange them?

查看:191
本文介绍了PyQt:是否可以将QWidget拖放到QGridLayout中以重新排列它们?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am looking for a way to create a grid of graphs that can be dragged/dropped to rearrange the order. My first try was using QDockWidgets as they allow for drag/drop, however they were limited in a lot of other ways. Would it be possible to implement this function in a QGridLayout?

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

For now I have a QGridLayout with 3x3 matplotlib widgets.

这是所需布局结果的一个示例.

Here is an example of the desired layout outcome.

示例代码:

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_())

推荐答案

此处是一种实现,它将交换拖放中涉及的项目的位置.三个主要步骤是:

Here is an implementation that will swap the positions of the items involved in a drag/drop. The 3 main steps are:

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

(1) Reimplement mousePressEvent to get the index of the LayoutItem based on mouse coordinates.

(2)重新实现mouseMoveEvent以设置FigureCanvas的QDrag.

(2) Reimplement mouseMoveEvent to set up a QDrag of the FigureCanvas.

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

(3) Reimplement dropEvent to swap the target items in the layout.

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

Since the matplotlib widgets absorb mouse events you also need to reimplement eventFilter to detect them.


import sys, random
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class IndicSelectWindow(QDialog):

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

        self.target = None
        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)

        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.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):
        if event.type() == QEvent.MouseButtonPress:
            self.mousePressEvent(event)
        elif event.type() == QEvent.MouseMove:
            self.mouseMoveEvent(event)
        elif event.type() == QEvent.MouseButtonRelease:
            self.mouseReleaseEvent(event)
        return super().eventFilter(watched, event)

    def get_index(self, pos):
        for i in range(self.gridLayout.count()):
            if self.gridLayout.itemAt(i).geometry().contains(pos) and i != self.target:
                return i

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.target = self.get_index(event.windowPos().toPoint())
        else:
            self.target = None

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton and self.target is not None:
            drag = 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())
            drag.exec_()

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

    def dragEnterEvent(self, event):
        if event.mimeData().hasImage():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if not event.source().geometry().contains(event.pos()):
            source = self.get_index(event.pos())
            if source is None:
                return

            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)


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

这篇关于PyQt:是否可以将QWidget拖放到QGridLayout中以重新排列它们?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆