Qt窗口大小不正确,直到用户事件发生

9
我正在创建一个屏幕,用户可以在编辑器中添加特定的瓷砖,但是当添加瓷砖时,窗口没有正确地调整大小以适应内容。但是,当我拖动窗口或稍微调整大小时,它立即自动调整到正确的大小。
enter image description here
而且当我拖动窗口时,它也会自动调整到正确的大小。

enter image description here

我尝试使用resize(sizeHint()),但出现了错误和不正确的大小。当重新调整大小/拖动时,仍会自动调整到正确的大小。
QWindowsWindow::setGeometry: Unable to set geometry 299x329+991+536 on QWidgetWindow/'TileSetterWindow'. Resulting geometry:  299x399+991+536 (frame: 8, 31, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 259x329, maximum size: 16777215x16777215).

我还尝试使用updateGeometry() 和 update(),但好像并没有什么作用。
当将窗口设置为fixedSize时,它会立即调整大小,但是用户无法再调整窗口大小了。我在这里做错了什么,从哪里开始解决呢?
编辑 最小可验证示例.ui文件。 selected_layout的类型为Flowlayout flowlayout_placeholder_1只是因为我不能直接将flowlayout放入设计器中。
编辑2 这是一个最小Visual Studio示例。我在Qt开发中使用Visual Studio。我尝试在Qt Creator中创建项目,但没能成功。

编辑3 添加了一个小视频(80 KB)。

编辑4 这里是更新的Visual Studio示例。它包含jpo38提出的新更改。它修复了不良调整大小的问题。尝试缩小窗口现在会导致问题。即使有更多行的空间,如果您尝试减少水平空间,它们也不再正确地填充垂直空间。


2
提供一个 [mcve]。 - eyllanesc
分享 .ui 文件,而不是由 .ui 生成的 .h 文件,因为这样更容易管理。 - eyllanesc
@Evgeny 我试了一下,出现了“QWindowsWindow :: setGeometry:无法在QWidgetWindow /'TileSetterWindow'上设置几何形状299x329 + 671 + 417。结果几何形状为:299x399 + 671 + 417(框架:8、31、8、8,自定义边距:0、0、0、0,最小尺寸:259x329,最大尺寸:16777215x16777215)。” 它似乎没有做太多事情,只是使初始窗口大小比设计师设置的宽度小。 - Eejin
请提供 .pro 文件,这样示例才算完整。 - karol
不确定,但我会尝试 dialog->layout()->setSizeConstraint(QLayout::SetMinimumSize);。默认的大小约束似乎有点奇怪,因为它不会更新对话框的 minimumSize(),除非是第一次。对于您的情况这将是无用的。 - Johannes Schaub - litb
显示剩余7条评论
2个回答

5

非常好的MCVE,正是需要轻松调查问题的内容。

看起来这个FlowLayout类不是设计用于在用户操作时更改其最小尺寸。当窗口移动时,布局只是通过QWidget内核'偶然'更新。

我可以通过修改FlowLayout :: minimumSize()的行为使其智能地工作,以下是我所做的更改:

  • FlowLayout类中添加QSize minSize;属性
  • 修改FlowLayout::minimumSize(),简单地返回此属性
  • doLayout函数添加第三个参数QSize * pMinSize。这将用于更新此minSize属性
  • 如果指定了pMinSize参数,则修改doLayout以将计算出的大小保存到该参数
  • FlowLayout::setGeometryminSize属性传递给doLayout并在最小尺寸更改时使布局无效

然后布局就能按预期工作了。

int FlowLayout::heightForWidth(int width) const {
    const int height = doLayout(QRect(0, 0, width, 0), true,NULL); // jpo38: set added parameter to NULL here
    return height;
}

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

    // jpo38: update minSize from here, force layout to consider it if it changed
    QSize oldSize = minSize;
    doLayout(rect, false,&minSize);
    if ( oldSize != minSize )
    {
        // force layout to consider new minimum size!
        invalidate();
    }
}

QSize FlowLayout::minimumSize() const {
    // jpo38: Simply return computed min size
    return minSize;
}

int FlowLayout::doLayout(const QRect &rect, bool testOnly,QSize* pMinSize) const {
    int left, top, right, bottom;
    getContentsMargins(&left, &top, &right, &bottom);
    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
    int x = effectiveRect.x();
    int y = effectiveRect.y();
    int lineHeight = 0;

    // jpo38: store max X
    int maxX = 0;

    for (auto&& item : itemList) {
        QWidget *wid = item->widget();
        int spaceX = horizontalSpacing();
        if (spaceX == -1)
            spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
        int spaceY = verticalSpacing();
        if (spaceY == -1)
            spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
        int nextX = x + item->sizeHint().width() + spaceX;
        if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
            x = effectiveRect.x();
            y = y + lineHeight + spaceY;
            nextX = x + item->sizeHint().width() + spaceX;
            lineHeight = 0;
        }

        if (!testOnly)
            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));

        // jpo38: update max X based on current position
        maxX = qMax( maxX, x + item->sizeHint().width() - rect.x() + left );

        x = nextX;
        lineHeight = qMax(lineHeight, item->sizeHint().height());
    }

    // jpo38: save height/width as max height/xidth in pMinSize is specified
    int height = y + lineHeight - rect.y() + bottom;
    if ( pMinSize )
    {
        pMinSize->setHeight( height );
        pMinSize->setWidth( maxX );
    }
    return height;
}

嘿jpo38,干得好。现在它可以自动增加所需的大小,这很棒。但是现在尝试缩小窗口会导致问题。如果您尝试减少水平空间,则它们不再正确填充垂直空间。我已经更新了主贴,提供了更新解决方案的新链接。 - Eejin
@Eejin:现在你看到问题来自于minimumSize,你可能需要对其进行调整,以使其完全按预期工作,包括缩小情况(因为minimumSize肯定会防止宽度被缩小)。 - jpo38

1
我遇到了完全相同的问题(虽然是在PySide2而不是C ++上)。
@jpo38上面的答案并没有直接起作用,但它通过给我一个新的方法来解决了我的问题。
解决方法是存储最后一个几何图形,并使用该几何图形的宽度来计算最小高度。
以下是基于jpo38答案中的代码的未经测试的C++实现(如果某些语法有误,敬请谅解,因为我不经常编写C++代码):
int FlowLayout::heightForWidth(int width) const {
    const int height = doLayout(QRect(0, 0, width, 0), true); 
    return height;
}

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

    // e-l: update lastSize from here
    lastSize = rect.size();
    doLayout(rect, false);
}

QSize FlowLayout::minimumSize() const {
    // e-l: Call heightForWidth from here, my doLayout is doing things a bit differently with regards to margins, so might have to add or not add the margins here to the height
    QSize size;
    for (const QLayoutItem *item : qAsConst(itemList))
        size = size.expandedTo(item->minimumSize());

    const QMargins margins = contentsMargins();
    size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
    size.setHeight(heightForWidth(qMax(lastSize.width(), size.width())));
    return size;
}

int FlowLayout::doLayout(const QRect &rect, bool testOnly) const {
    int left, top, right, bottom;
    getContentsMargins(&left, &top, &right, &bottom);
    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
    int x = effectiveRect.x();
    int y = effectiveRect.y();
    int lineHeight = 0;

    for (auto&& item : itemList) {
        QWidget *wid = item->widget();
        int spaceX = horizontalSpacing();
        if (spaceX == -1)
            spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
        int spaceY = verticalSpacing();
        if (spaceY == -1)
            spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
        int nextX = x + item->sizeHint().width() + spaceX;
        if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
            x = effectiveRect.x();
            y = y + lineHeight + spaceY;
            nextX = x + item->sizeHint().width() + spaceX;
            lineHeight = 0;
        }

        if (!testOnly)
            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));

        x = nextX;
        lineHeight = qMax(lineHeight, item->sizeHint().height());
    }

    int height = y + lineHeight - rect.y() + bottom;

    return height;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接