自定义 QSizeGrip 以调整 QListWidget 的大小

Custom QSizeGrip to resize a QListWidget

我想制作一个底部带有调整大小手柄的 QListWidget(类似于您在此类网页上看到的文本字段)。我看到有几个人在那里问同样的问题,但没有找到完整的例子。

我有一些代码几乎就在那里,但它在调整大小时闪烁,所以我猜我缺少关于调整大小策略或布局或其他东西的东西...

这是我的 "working" 示例。理论非常简单,您只需在小部件的 mousePressEvent 中测量鼠标移动的距离,并相应地 resize/reposition。不幸的是,我缺少一些基本的东西,我不知道是什么:

from PyQt4 import QtGui
import sys

class Grip(QtGui.QLabel):
    def __init__(self, parent, move_widget):
        super(Grip, self).__init__(parent)
        self.move_widget = move_widget
        self.setText("+")
        self.min_height = 50

        self.mouse_start = None
        self.height_start = self.move_widget.height()
        self.resizing = False
        self.setMouseTracking(True)


    def showEvent(self, event):
        super(Grip, self).showEvent(event)
        self.reposition()

    def mousePressEvent(self, event):
        super(Grip, self).mousePressEvent(event)
        self.resizing = True
        self.height_start = self.move_widget.height()
        self.mouse_start = event.pos()

    def mouseMoveEvent(self, event):
        super(Grip, self).mouseMoveEvent(event)
        if self.resizing:
            delta = event.pos() - self.mouse_start
            height = self.height_start + delta.y()
            if height > self.min_height:
                self.move_widget.setFixedHeight(height)
            else:
                self.move_widget.setFixedHeight(self.min_height)

            self.reposition()

    def mouseReleaseEvent(self, event):
        super(Grip, self).mouseReleaseEvent(event)
        self.resizing = False

    def reposition(self):
        rect = self.move_widget.geometry()
        self.move(rect.right(), rect.bottom())


class Dialog(QtGui.QDialog):
    def __init__(self):
        super(Dialog, self).__init__()
        layout = QtGui.QVBoxLayout()
        self.setLayout(layout)
        list_widget = QtGui.QListWidget()
        layout.addWidget(list_widget)
        gripper = Grip(self, list_widget)

        layout.addWidget(QtGui.QLabel("Test"))

        self.setGeometry(200, 500, 200, 500)

试一试:

def mouseMoveEvent(self, event):
    super(Grip, self).mouseMoveEvent(event)
    if self.resizing:
        delta = event.pos() - self.mouse_start
        height = self.height_start + delta.y()
        if height > self.min_height:
            self.move_widget.setFixedHeight(height)
        else:
            self.move_widget.setFixedHeight(self.min_height)

        #self.reposition()                                       # <-  ---

def mouseReleaseEvent(self, event):
    super(Grip, self).mouseReleaseEvent(event)
    self.resizing = False

    self.reposition()                                             # <- +++

我认为你应该使用 QSplitter:

而不是你自己的 QSizeGrip
from PyQt4 import QtCore, QtGui
import sys


class Dialog(QtGui.QDialog):
    def __init__(self):
        super(Dialog, self).__init__()
        layout = QtGui.QVBoxLayout(self)

        splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
        layout.addWidget(splitter)
        list_widget = QtGui.QListWidget()
        splitter.addWidget(list_widget)
        splitter.addWidget(QtGui.QLabel("Test"))


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

对于其他解决方案,您可以使用 QSizeGrip 开箱即用的方式:

from PyQt4 import QtCore, QtGui
import sys

class Dialog(QtGui.QDialog):
    def __init__(self):
        super(Dialog, self).__init__()
        layoutMain = QtGui.QVBoxLayout(self)
        listWidget = QtGui.QListWidget(self)
        gripper = QtGui.QSizeGrip(listWidget)
        l = QtGui.QHBoxLayout(listWidget)

        l.setContentsMargins(0, 0, 0, 0)
        l.addWidget(gripper, 0, QtCore.Qt.AlignRight | QtCore.Qt.AlignBottom)

        layoutMain.addWidget(listWidget)
        layoutMain.addWidget(QtGui.QLabel("Test", self))

        self.setGeometry(200, 500, 200, 500)

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

好吧,事实证明我真的很接近。我 post 在这里为任何想解决类似问题的人提供答案!

我真正需要对原始代码进行更改的是引用 globalPos() 而不是本地 pos()。感谢您的帮助,特别是 S. Nick 发现移动事件导致了问题。

from PyQt4 import QtGui
import sys

class Grip(QtGui.QLabel):
    def __init__(self, parent, move_widget):
        super(Grip, self).__init__(parent)
        self.move_widget = move_widget
        self.setText("+")
        self.min_height = 50

        self.mouse_start = None
        self.height_start = self.move_widget.height()
        self.resizing = False
        self.setMouseTracking(True)

        self.setCursor(QtCore.Q.SizeVerCursor)


    def showEvent(self, event):
        super(Grip, self).showEvent(event)
        self.reposition()

    def mousePressEvent(self, event):
        super(Grip, self).mousePressEvent(event)
        self.resizing = True
        self.height_start = self.move_widget.height()
        self.mouse_start = event.globalPos()

    def mouseMoveEvent(self, event):
        super(Grip, self).mouseMoveEvent(event)
        if self.resizing:
            delta = event.globalPos() - self.mouse_start
            height = self.height_start + delta.y()
            if height > self.min_height:
                self.move_widget.setFixedHeight(height)
            else:
                self.move_widget.setFixedHeight(self.min_height)

            self.reposition()

    def mouseReleaseEvent(self, event):
        super(Grip, self).mouseReleaseEvent(event)
        self.resizing = False

    def reposition(self):
        rect = self.move_widget.geometry()
        self.move(rect.right(), rect.bottom())


class Dialog(QtGui.QDialog):
    def __init__(self):
        super(Dialog, self).__init__()
        layout = QtGui.QVBoxLayout()
        self.setLayout(layout)
        list_widget = QtGui.QListWidget()
        layout.addWidget(list_widget)
        gripper = Grip(self, list_widget)

        layout.addWidget(QtGui.QLabel("Test"))

        self.setGeometry(200, 500, 200, 500)