与 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);
}
我有一个关于 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_
我经常阅读有关此问题的文章 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);
}