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

Posted

技术标签:

【中文标题】与 QMainWindow 的 GUI 命令交互时,QDockWidget 无法正确调整大小【英文标题】:QDockWidget does not resize properly when interacting with GUI commands of QMainWindow 【发布时间】:2019-03-12 00:39:08 【问题描述】:

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

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

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

ma​​inwindow.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_

我经常在here 和in this post 阅读有关此问题的信息。但是我一直在阅读有关此特定对象可能有一些bugs 的可能性,建议覆盖resiveEvent。然而,这些都没有奏效。

经过大量研究,我终于找到了this useful post,它几乎复制了我遇到的问题,并且承载了class QFluidGridLayoutclass QDockResizeEventFilter 以上两个类中的大部分。

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

我还包括调试器的快照:

谁能解释我做错了什么?非常感谢您阐明这个问题。

【问题讨论】:

【参考方案1】:

@Emanuele,您看到的 post 主要用于将 QDockWidget 子类化为子类,因此必须手动实施该解决方案。我认为如果您查看this alternative solution,您会发现它很有用。

尝试修改您的构造函数,添加resizeDocks(dock, 100, Qt::Horizontal);,如帖子中所示:

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);


【讨论】:

@Linds,感谢您抽出宝贵时间阅读问题。是的,这行得通!这比我想象的要容易得多:)

以上是关于与 QMainWindow 的 GUI 命令交互时,QDockWidget 无法正确调整大小的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5:调用长时间运行的函数时 QMainWindow 冻结

day23 GUI

Python从菜鸟到高手:获取用户输入函数与注释

Python从菜鸟到高手:获取用户输入函数与注释

第1课 - GUI 程序原理分析

将 QMainWindow 中的 enterPressed() 连接到 Button clicked()