自定义可折叠小部件导致尺寸问题。

huangapple go评论67阅读模式
英文:

Custom collapsible widget causing size issues

问题

我在如何在Qt中创建可展开/折叠部分小部件的帖子答案中找到了这个答案,但提出的解决方案并不完美。

事实上,正如您下面所看到的,一旦小部件已经展开并关闭,它不会重置窗口大小,这会导致创建空白空间:

自定义可折叠小部件导致尺寸问题。

以下是一个最小可复制的示例(您需要从帖子答案中导入折叠部件):

QApplication a(argc, argv);
QWidget* widget = new QWidget();
widget->setFixedWidth(500);

QVBoxLayout* mainLayout = new QVBoxLayout(widget);

QFrame* widgetTop = new QFrame();
widgetTop->setFrameShape(QFrame::StyledPanel);
widgetTop->setFixedHeight(50);
QHBoxLayout* layoutTop = new QHBoxLayout(widgetTop);
QLabel* labelTop = new QLabel("widget-top");
labelTop->setAlignment(Qt::AlignCenter);
layoutTop->addWidget(labelTop);
mainLayout->addWidget(widgetTop);

QFrame* widgetMiddle = new QFrame();
widgetMiddle->setFrameShape(QFrame::StyledPanel);
widgetMiddle->setFixedHeight(150);
QHBoxLayout* layoutMiddle = new QHBoxLayout(widgetMiddle);
QLabel* labelMiddle = new QLabel("widget-middle");
labelMiddle->setAlignment(Qt::AlignCenter);
layoutMiddle->addWidget(labelMiddle);
mainLayout->addWidget(widgetMiddle);

QFrame* widgetBottom = new QFrame();
widgetBottom->setFrameShape(QFrame::StyledPanel);
widgetBottom->setFixedHeight(100);
QHBoxLayout* layoutBottom = new QHBoxLayout(widgetBottom);
QLabel* labelBottom = new QLabel("widget-bottom");
labelBottom->setAlignment(Qt::AlignCenter);
layoutBottom->addWidget(labelBottom);

auto* anyLayout = new QVBoxLayout();
anyLayout->addWidget(widgetBottom);
Spoiler* spoiler = new Spoiler("我有问题", 100);
spoiler->setContentLayout(*anyLayout);
mainLayout->addWidget(spoiler);

// 显示主窗口
widget->show();
return a.exec();

我尝试使用adjustSize(),但它与QPropertyAnimation不兼容。

如何使窗口返回到其原始高度?

英文:

I came across this post answer on How to make an expandable/collapsable section widget in Qt, but the proposed solution is not without flaws.

In fact, as you can see below, once the widget has been expanded and closed, it doesn't reset the window size, which has the effect of creating empty spaces:

自定义可折叠小部件导致尺寸问题。

Here is a minimal reproducible example (you need to import the spoiler widget from the post answer):

QApplication a(argc, argv);
QWidget* widget = new QWidget();
widget->setFixedWidth(500);

QVBoxLayout* mainLayout = new QVBoxLayout(widget);

QFrame* widgetTop = new QFrame();
widgetTop->setFrameShape(QFrame::StyledPanel);
widgetTop->setFixedHeight(50);
QHBoxLayout* layoutTop = new QHBoxLayout(widgetTop);
QLabel* labelTop = new QLabel("widget-top");
labelTop->setAlignment(Qt::AlignCenter);
layoutTop->addWidget(labelTop);
mainLayout->addWidget(widgetTop);


QFrame* widgetMiddle = new QFrame();
widgetMiddle->setFrameShape(QFrame::StyledPanel);
widgetMiddle->setFixedHeight(150);
QHBoxLayout* layoutMiddle = new QHBoxLayout(widgetMiddle);
QLabel* labelMiddle = new QLabel("widget-middle");
labelMiddle->setAlignment(Qt::AlignCenter);
layoutMiddle->addWidget(labelMiddle);
mainLayout->addWidget(widgetMiddle);


QFrame* widgetBottom = new QFrame();
widgetBottom->setFrameShape(QFrame::StyledPanel);
widgetBottom->setFixedHeight(100);
QHBoxLayout* layoutBottom = new QHBoxLayout(widgetBottom);
QLabel* labelBottom = new QLabel("widget-bottom");
labelBottom->setAlignment(Qt::AlignCenter);
layoutBottom->addWidget(labelBottom);

auto* anyLayout = new QVBoxLayout();
anyLayout->addWidget(widgetBottom);
Spoiler* spoiler = new Spoiler("I'm buggy", 100);
spoiler->setContentLayout(*anyLayout);
mainLayout->addWidget(spoiler);

// Affichage du widget principal
widget->show();
return a.exec();

I tried using adjustSize() but it doesn't work with the QPropertyAnimation.

How to make the window return to its original height?

答案1

得分: 1

以下是我对Spoiler进行的一些更改,以解决这个问题,您需要将窗口设为spoiler的父部件:

添加了更多成员变量:

bool collapsing = false;
int contentHeight;

在构造函数中进行的更改:

QObject::connect(&toggleButton, &QToolButton::clicked, [this](const bool checked)
{
    toggleButton.setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
    toggleAnimation.setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);

    // 用于检查调整大小事件
    if (toggleAnimation.direction() == QAbstractAnimation::Backward)
    {
        collapsing = true;
    }
    else
    {
        collapsing = false;
    }
    
    toggleAnimation.start();
});

// 在动画完成时将父部件的大小限制恢复为正常值
QObject::connect(&toggleAnimation, &QParallelAnimationGroup::finished, [this]()
{
    parentWidget()->setMaximumHeight(16777215);
    parentWidget()->setMinimumHeight(parentWidget()->minimumSizeHint().height());
});

调整大小事件:

void Spoiler::resizeEvent(QResizeEvent *event)
{
    // 在动画播放时调整父部件的大小
    // 第二个条件是问题行为的解释原因
    if (toggleAnimation.state() == QAbstractAnimation::Running &&
        parentWidget()->minimumHeight() + contentHeight > parentWidget()->height())
    {
        if (collapsing)
        {
            int newHeight = parentWidget()->height() - (event->oldSize().height() - event->size().height());
            parentWidget()->setFixedHeight(newHeight);
            return;
        }

        int newHeight = parentWidget()->height() - (event->oldSize().height() - event->size().height());
        parentWidget()->setFixedHeight(newHeight);
    }
}

这是它初看时的样子:

自定义可折叠小部件导致尺寸问题。

但是如果您快速点击它,它将表现不正常:

自定义可折叠小部件导致尺寸问题。

导致这个问题的原因是调整大小事件被过快地多次调用,但我不知道具体原因。

我尝试过对父部件的大小进行动画处理,但比这个解决方案更丑陋,可能是因为我无法确定起始值和结束值。

我提供这个解决方案更多是为了帮助您识别问题并提出解决方法,我认为不应该使用这个解决方案,除非您可以使其适用于您的用例。

英文:

Here are a few changes I made to Spoiler to deal with this problem, and you have to make your window spoiler's parent widget:

Added more members:

bool collapsing=false;
int contentHeight;

Changes made in ctor:

QObject::connect(&toggleButton, &QToolButton::clicked, [this](const bool checked)
{
    toggleButton.setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
    toggleAnimation.setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);

    //a check for the resize event 
    if(toggleAnimation.direction()==QAbstractAnimation::Backward)
    {
        collapsing=true;
    }
    else
    {
        collapsing=false;
    }
    
    toggleAnimation.start();
});

//get spoiler min and max sizes to normal when animation finishes
QObject::connect(&toggleAnimation, &QParallelAnimationGroup::finished, [this]()
{
    parentWidget()->setMaximumHeight(16777215);
    parentWidget()->setMinimumHeight(parentWidget()->minimumSizeHint().height());
});

Resize event:

void Spoiler::resizeEvent(QResizeEvent *event)
{
    //resize parentWidget while spoiler size is animated
    //second condition is the explanation behind the problematic behavior
    if(toggleAnimation.state()==QAbstractAnimation::Running &&
        parentWidget()->minimumHeight()+contentHeight>parentWidget()->height())
    {
        if(collapsing)
        {
            int newHeight = parentWidget()->height()-(event->oldSize().height()-event->size().height());
            parentWidget()->setFixedHeight(newHeight);
            return;
        }

        int newHeight = parentWidget()->height()-(event->oldSize().height()-event->size().height());
        parentWidget()->setFixedHeight(newHeight);
    }
}

Here is how it looks at first sight:

自定义可折叠小部件导致尺寸问题。

But if you stress it by fast clicking it, it will behave wrong:

自定义可折叠小部件导致尺寸问题。

The resize event getting called so many times too fast is the reason behind that, but I don't know how exactly.

I did try to animate parent widget size, but it's uglier than this solution, perhaps because I couldn't figure the start and end values.

I'm providing this more as a help to identify the problem and suggest a way to deal with it, I don't think this solution should be used, unless you can make it work for your use case.

huangapple
  • 本文由 发表于 2023年6月15日 19:33:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76482048.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定