通过在 pyqt5 中拖动边缘来调整自定义小部件的大小 [英] Resizing custom widget by dragging the edges in pyqt5

查看:142
本文介绍了通过在 pyqt5 中拖动边缘来调整自定义小部件的大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了一个类似于 QPushbutton 或标签的自定义小部件.当鼠标位于小部件的边缘时,我想让用户调整小部件的大小.我该怎么做?

I have made a custom widget similar to QPushbutton or label. I would like to let the user resize the widget when the mouse is over the edge of the widget. How can I do this?

(注意:我不是在寻找 Splitter 窗口)

(Note: I am not looking for Splitter window)

推荐答案

一款图片编辑软件,你有专属的空间"对于图像,用户可以在该空间的边界内自由地做她/他想做的任何事情.当小部件放置在可以表示多个问题的布局管理容器中时(通常应该如此).您不仅需要实现整个鼠标交互来调整小部件的大小,还需要通知可能的父小部件有关调整大小的信息.

An image editing software, you have a dedicated "space" for the image, and the user is free to do anything she/he wants within the boundaries of that space. When a widget is placed within a layout-managed container (as it normally should) that can represent multiple issues. Not only you've to implement the whole mouse interaction to resize the widget, but you also need to notify the possible parent widget(s) about the resizing.

也就是说,您要实现的目标可以完成,但需要注意一些事项.

That said, what you're trying to achieve can be done, with some caveats.

以下是标准 QWidget 的一个非常基本的实现,它能够调整自身大小,同时通知其父小部件有关大小提示修改的信息.请注意,这是完整的,只要鼠标移动发生在小部件的顶部或左侧边缘,它的行为就不会正确响应鼠标移动.此外,虽然它(可以)在增加其大小的同时正确调整父小部件的大小,但在缩小时不会发生调整大小.这理论上可以通过设置 minimumSize() 并手动调用 adjustSize() 但是,为了正确提供类似概念所需的所有可能功能,您需要自己完成整个实现.

The following is a very basic implementation of a standard QWidget that is able to resize itself, while notifying its parent widget(s) about the size hint modifications. Note that this is not complete, and its behavior doesn't correctly respond to mouse movements whenever they happen on the top or left edges of the widget. Moreover, while it (could) correctly resize the parent widget(s) while increasing its size, the resize doesn't happen when shrinking. This can theoretically be achieved by setting a minimumSize() and manually calling adjustSize() but, in order to correctly provide all the possible features required by a similar concept, you'll need to do the whole implementation by yourself.

from PyQt5 import QtCore, QtGui, QtWidgets

Left, Right = 1, 2
Top, Bottom = 4, 8
TopLeft = Top|Left
TopRight = Top|Right
BottomRight = Bottom|Right
BottomLeft = Bottom|Left

class ResizableLabel(QtWidgets.QWidget):
    resizeMargin = 4
    # note that the Left, Top, Right, Bottom constants cannot be used as class
    # attributes if you want to use list comprehension for better performance,
    # and that's due to the variable scope behavior on Python 3
    sections = [x|y for x in (Left, Right) for y in (Top, Bottom)]
    cursors = {
        Left: QtCore.Qt.SizeHorCursor, 
        Top|Left: QtCore.Qt.SizeFDiagCursor, 
        Top: QtCore.Qt.SizeVerCursor, 
        Top|Right: QtCore.Qt.SizeBDiagCursor, 
        Right: QtCore.Qt.SizeHorCursor, 
        Bottom|Right: QtCore.Qt.SizeFDiagCursor, 
        Bottom: QtCore.Qt.SizeVerCursor, 
        Bottom|Left: QtCore.Qt.SizeBDiagCursor, 
    }
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.startPos = self.section = None
        self.rects = {section:QtCore.QRect() for section in self.sections}

        # mandatory for cursor updates
        self.setMouseTracking(True)

        # just for demonstration purposes
        background = QtGui.QPixmap(3, 3)
        background.fill(QtCore.Qt.transparent)
        qp = QtGui.QPainter(background)
        pen = QtGui.QPen(QtCore.Qt.darkGray, .5)
        qp.setPen(pen)
        qp.drawLine(0, 2, 2, 0)
        qp.end()
        self.background = QtGui.QBrush(background)

    def updateCursor(self, pos):
        for section, rect in self.rects.items():
            if pos in rect:
                self.setCursor(self.cursors[section])
                self.section = section
                return section
        self.unsetCursor()

    def adjustSize(self):
        del self._sizeHint
        super().adjustSize()

    def minimumSizeHint(self):
        try:
            return self._sizeHint
        except:
            return super().minimumSizeHint()

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            if self.updateCursor(event.pos()):
                self.startPos = event.pos()
                return
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self.startPos is not None:
            delta = event.pos() - self.startPos
            if self.section & Left:
                delta.setX(-delta.x())
            elif not self.section & (Left|Right):
                delta.setX(0)
            if self.section & Top:
                delta.setY(-delta.y())
            elif not self.section & (Top|Bottom):
                delta.setY(0)
            newSize = QtCore.QSize(self.width() + delta.x(), self.height() + delta.y())
            self._sizeHint = newSize
            self.startPos = event.pos()
            self.updateGeometry()
        elif not event.buttons():
            self.updateCursor(event.pos())
        super().mouseMoveEvent(event)
        self.update()

    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        self.updateCursor(event.pos())
        self.startPos = self.section = None
        self.setMinimumSize(0, 0)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        outRect = self.rect()
        inRect = self.rect().adjusted(self.resizeMargin, self.resizeMargin, -self.resizeMargin, -self.resizeMargin)
        self.rects[Left] = QtCore.QRect(outRect.left(), inRect.top(), self.resizeMargin, inRect.height())
        self.rects[TopLeft] = QtCore.QRect(outRect.topLeft(), inRect.topLeft())
        self.rects[Top] = QtCore.QRect(inRect.left(), outRect.top(), inRect.width(), self.resizeMargin)
        self.rects[TopRight] = QtCore.QRect(inRect.right(), outRect.top(), self.resizeMargin, self.resizeMargin)
        self.rects[Right] = QtCore.QRect(inRect.right(), self.resizeMargin, self.resizeMargin, inRect.height())
        self.rects[BottomRight] = QtCore.QRect(inRect.bottomRight(), outRect.bottomRight())
        self.rects[Bottom] = QtCore.QRect(inRect.left(), inRect.bottom(), inRect.width(), self.resizeMargin)
        self.rects[BottomLeft] = QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()

    # ---- optional, mostly for demonstration purposes ----

    def paintEvent(self, event):
        super().paintEvent(event)
        qp = QtGui.QPainter(self)
        if self.underMouse() and self.section:
            qp.save()
            qp.setPen(QtCore.Qt.lightGray)
            qp.setBrush(self.background)
            qp.drawRect(self.rect().adjusted(0, 0, -1, -1))
            qp.restore()
        qp.drawText(self.rect(), QtCore.Qt.AlignCenter, '{}x{}'.format(self.width(), self.height()))

    def enterEvent(self, event):
        self.update()

    def leaveEvent(self, event):
        self.update()


class Test(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QGridLayout(self)

        for row in range(3):
            for column in range(3):
                if (row, column) == (1, 1):
                    continue
                layout.addWidget(QtWidgets.QPushButton(), row, column)

        label = ResizableLabel()
        layout.addWidget(label, 1, 1)

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

这篇关于通过在 pyqt5 中拖动边缘来调整自定义小部件的大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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