与 QMainWindow 的 GUI 命令交互时,QDockWidget 无法正确调整大小

QDockWidget does not resize properly when interacting with GUI commands of QMainWindow

我有一个关于 QDockWidget 大小调整不正确的问题。具体来说,当我启动 GUI 时,QDockWidget 如下图所示 错误 。我还在使用 .ui 期间调整 QDockWidget 的大小,但是一旦我与 .ui 交互(例如使用 QPushButton 或使用QCheckBox) QDockWidget 再次变大:

预期行为如下所示,它在与 .ui 交互期间不会突然增加维度,而是保持在如下位置:

下面是我在这个项目中使用的代码中最重要的部分,如果有用的话,我在编译器通知的 3 个调试错误上签名 // <-- ERROR HERE

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
    mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    mNewText = new QPlainTextEdit;
    mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    mDockWidget_A->setWidget(mNewText);

    mDockWidget_A->installEventFilter(new QDockResizeEventFilter(mNewText,dynamic_cast<QFluidGridLayout*>(mNewText->layout())));
    addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);
}

qdockresizeeventfilter.h

#include <QObject>
#include <QLayout>
#include <QEvent>
#include <QDockWidget>
#include <QResizeEvent>
#include <QCoreApplication>
#include <QMouseEvent>
#include "qfluidgridlayout.h"
#include "mainwindow.h"

class QDockResizeEventFilter : public QObject
{

public:
    friend QMainWindow;
    friend QLayoutPrivate;
    QDockResizeEventFilter(QWidget* dockChild, QFluidGridLayout* layout, QObject* parent = nullptr)
        : QObject(parent), m_dockChild(dockChild), m_layout(layout)
    {

    }

protected:

    bool eventFilter(QObject *p_obj, QEvent *p_event)
    {  
        if (p_event->type() == QEvent::Resize)
        {
            QResizeEvent* resizeEvent   = static_cast<QResizeEvent*>(p_event);
            QMainWindow* mainWindow     = dynamic_cast<QMainWindow*>(p_obj->parent());              
            QDockWidget* dock           = static_cast<QDockWidget*>(p_obj);

            // determine resize direction
            if (resizeEvent->oldSize().height() != resizeEvent->size().height())
            {
                // vertical expansion
                QSize fixedSize(m_layout->widthForHeight(m_dockChild->size().height()), m_dockChild->size().height()); // <-- ERROR HERE
                if (dock->size().width() != fixedSize.width())
                {
                    m_dockChild->setFixedWidth(fixedSize.width());
                    dock->setFixedWidth(fixedSize.width());

                    // cause mainWindow dock layout recalculation
                    QDockWidget* dummy = new QDockWidget;
                    mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dummy);
                    mainWindow->removeDockWidget(dummy);

                    // adding dock widgets causes the separator move event to end
                    // restart it by synthesizing a mouse press event
                    QPoint mousePos = mainWindow->mapFromGlobal(QCursor::pos());
                    mousePos.setY(dock->rect().bottom());
                    QCursor::setPos(mainWindow->mapToGlobal(mousePos));
                    QMouseEvent* grabSeparatorEvent = new QMouseEvent(QMouseEvent::MouseButtonPress,mousePos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
                    qApp->postEvent(mainWindow, grabSeparatorEvent);
                }
            }
            if (resizeEvent->oldSize().width() != resizeEvent->size().width())
            {
                // Do nothing
            }           
        }   
        return false;
    }

private:

    QWidget* m_dockChild;
    QFluidGridLayout* m_layout;
};

#endif // QDockResizeEventFilter_h_

最后是qfluidgridlayout.h

#ifndef QFluidGridLayout_h_
#define QFluidGridLayout_h_

#include <QLayout>
#include <QGridLayout>
#include <QRect>
#include <QStyle>
#include <QWidgetItem>

class QFluidGridLayout : public QLayout
{
public:

    enum Direction { downToUp, UpToDown };
    QFluidGridLayout(QWidget *parent = nullptr)
        : QLayout(parent)
    {
        setContentsMargins(8,8,8,8);
        setSizeConstraint(QLayout::SetMinAndMaxSize);
    }

    ~QFluidGridLayout() {
        QLayoutItem *item;
        while ((item = takeAt(0)))
            delete item;
    }

    void addItem(QLayoutItem *item) {
        itemList.append(item);
    }

    Qt::Orientations expandingDirections() const {
        return nullptr;
    }

    bool hasHeightForWidth() const {
        return false;
    }

    int heightForWidth(int width) const {
        int height = doLayout(QRect(0, 0, width, 0), true, true);
        return height;
    }

    bool hasWidthForHeight() const {
        return true;
    }

    int widthForHeight(int height) const {  // <-- ERROR HERE
        int width = doLayout(QRect(0, 0, 0, height), true, false);
        return width;
    }

    int count() const {
        return itemList.size();
    }

    QLayoutItem *itemAt(int index) const {
        return itemList.value(index);
    }

    QSize minimumSize() const {
        QSize size;
        QLayoutItem *item;
        foreach (item, itemList)
            size = size.expandedTo(item->minimumSize());
        size += QSize(2*margin(), 2*margin());
        return size;
    }

    void setGeometry(const QRect &rect) {
        QLayout::setGeometry(rect);
        doLayout(rect); 
    }

    QSize sizeHint() const {
        return minimumSize();
    }

    QLayoutItem *takeAt(int index) {
        if (index >= 0 && index < itemList.size())
            return itemList.takeAt(index);
        else
            return nullptr; }
private:
    int doLayout(const QRect &rect, bool testOnly = false, bool width = false) const
    {
        int left, top, right, bottom;
        getContentsMargins(&left, &top, &right, &bottom); // <-- ERROR HERE
        QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
        int x = effectiveRect.x();
        int y = effectiveRect.y();
        int lineHeight = 0;
        int lineWidth = 0;
        QLayoutItem* item;
        foreach(item,itemList)
        {
            QWidget* widget = item->widget();   
            if (y + item->sizeHint().height() > effectiveRect.bottom() && lineWidth > 0) {
                y = effectiveRect.y();
                x += lineWidth + right;
                lineWidth = 0;
            }
            if (!testOnly) {
                item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
            }
            y += item->sizeHint().height() + top;
            lineHeight = qMax(lineHeight, item->sizeHint().height());
            lineWidth = qMax(lineWidth, item->sizeHint().width());
        } 
        if (width) {
            return y + lineHeight - rect.y() + bottom;
        }
        else {
            return x + lineWidth - rect.x() + right;
        }
    }
    QList<QLayoutItem *> itemList;
    Direction dir;
};

#endif // QFluidGridLayout_h_

我经常阅读有关此问题的文章 and in this post. However I have been reading about the possibility that this specific object may have some bugs,建议覆盖一个 resiveEvent。然而 none 这行得通。

经过大量研究,我终于找到了 this useful post,它几乎重现了我遇到的问题,并且包含 class QFluidGridLayout 和 类 上面的两个 类 和 class QDockResizeEventFilter

虽然我使用相同的方法,但我仍然无法实现此对象的正常行为。

我还附上了调试器的快照:

有人可以解释我做错了什么吗?非常感谢您阐明这个问题。

@Emanuele,post you saw it is mainly for sub-classing the QDockWidget as a child and therefore that solution had to be implemented manually. I think that if you look at this alternative solution 你会发现它很有用。

尝试修改您的构造函数,添加 resizeDocks({dock}, {100}, Qt::Horizontal);,如 post 中那样:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
    mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    mNewText = new QPlainTextEdit;
    mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    mDockWidget_A->setWidget(mNewText);
    mDockWidget_A->installEventFilter(new QDockResizeEventFilter(mNewText,dynamic_cast<QFluidGridLayout*>(mNewText->layout())));
    addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);

    resizeDocks({mDockWidget_A}, {100}, Qt::Horizontal);

}