c++ 在Qt中制作可折叠的GroupBox-什么决定了折叠的大小?

wswtfjt7  于 2022-11-27  发布在  其他
关注(0)|答案(3)|浏览(279)

我试图用一个Collapse/Expand按钮来创建GroupBox,希望它在Qt中有一个显而易见的功能。我已经用绘制按钮来创建QGroupBox的子类。当单击时,我的代码将对GroupBox的所有子级调用setVisible(false)。此外,它还遍历GroupBox中的所有QLayouts,并将它们的contentMargins设置为零。但是,在折叠状态下,某些GroupBox仍然比其他GroupBox大,我不知道是什么原因导致了这种情况。
这是我到目前为止所想到的(我知道可能存在nullptr问题):

void CollapsibleGroupBox::onVisibilityChanged()
{
  CollapseExpandButton::State s;

  s = m_clExpButton->state();

  QLayout *master = this->layout();
  QList<QObject *> children = this->children();

  switch (s) {
  case CollapseExpandButton::State::COLLAPSED:
    for (QObject *o : children) {
      QWidget *w = qobject_cast<QWidget *>(o);
      if (w != nullptr) {
        if (w != m_clExpButton)
          w->setVisible(false);

        continue;
      }

      if (o == master) {
        m_layoutMargins.clear();
        collapseLayout(master);
      }
    }
    break;
  case CollapseExpandButton::State::EXPANDED:
    for (QObject *o : children) {
      QWidget *w = qobject_cast<QWidget *>(o);
      if (w != nullptr) {
        w->setVisible(true);

        continue;
      }

      if (o == master)
        expandLayout(master);

    }
    break;
  }
}

void CollapsibleGroupBox::collapseLayout(QLayout *layout)
{
  for (QObject *o : layout->children()) {
    QLayout *l= qobject_cast<QLayout *>(o);
    if (l == nullptr)
      continue;

    collapseLayout(l);
  }
  if (m_layoutMargins.contains(layout))
    return;

  QMargins m = layout->contentsMargins();
  m_layoutMargins[layout] = m;
  layout->setContentsMargins(0, 0, 0, 0);
}

void CollapsibleGroupBox::expandLayout(QLayout *layout)
{
  for (QObject *o : layout->children()) {
    QLayout *l = qobject_cast<QLayout *>(o);
    if (l == nullptr)
      continue;

    if (m_layoutMargins.contains(l))
      expandLayout(l);
  }
  if (m_layoutMargins.contains(layout)) {
    QMargins m = m_layoutMargins[layout];
    layout->setContentsMargins(m);
  }
}
vc9ivgsu

vc9ivgsu1#

这是由于您提到的组框中的分隔符造成的。请将它们的高度设置为零;在组框展开时不应该影响它们的行为。

anhgbhbe

anhgbhbe2#

我回到我的旧代码前一段时间,似乎我终于想出了一个合理的解决方案。完整的代码如下:

可折叠组框. h

#ifndef COLLAPSIBLEGROUPBOX_H
#define COLLAPSIBLEGROUPBOX_H

#include <QGroupBox>
#include <QMap>
#include <QMargins>
#include <QPair>

class QResizeEvent;
class CollapseExpandButton;
class QSpacerItem;

class CollapsibleGroupBox : public QGroupBox
{
public:
  explicit CollapsibleGroupBox(QWidget *parent = nullptr);

protected:
  void resizeEvent(QResizeEvent *);

private:
  void resizeCollapseButton();
  void collapseLayout(QLayout *layout);
  void collapseSpacer(QSpacerItem *spacer);
  void expandLayout(QLayout *layout);
  void expandSpacer(QSpacerItem *spacer);

  CollapseExpandButton *m_clExpButton;
  QMap<const void *, QMargins> m_layoutMargins;
  QMap<const void *, QPair<QSize, QSizePolicy>> m_spacerSizes;

private slots:
  void onScreenChanged();
  void onVisibilityChanged();

};

#endif // COLLAPSIBLEGROUPBOX_H

可折叠组框. cpp

#include "collapsiblegroupbox.h"
#include "collapseexpandbutton.h"
#include <QApplication>
#include <QGuiApplication>
#include <QLayout>
#include <QResizeEvent>
#include <QScreen>
#include <QStyle>
#include <QTimer>
#include <QWindow>
#include <cassert>
#include <cmath>

inline
QWindow *findWindowForWidget(const QWidget *widget)
{
  for (;;) {
    QWindow *wh = widget->window()->windowHandle();
    if (wh != nullptr)
      return wh;

    widget = qobject_cast<const QWidget *>(widget->parent());
    if (widget == nullptr)
      return nullptr;
  }
}

inline
QScreen * findScreenForWidget(const QWidget *widget)
{
  for (;;) {
    QWindow *wh = widget->window()->windowHandle();
    if (wh != nullptr) {
      QScreen *scr = wh->screen();
      if (scr != nullptr)
        return scr;
    }

    widget = qobject_cast<const QWidget *>(widget->parent());
    if (widget == nullptr)
      return nullptr;
  }
}

CollapsibleGroupBox::CollapsibleGroupBox(QWidget *parent) :
  QGroupBox(parent)
{
  m_clExpButton = new CollapseExpandButton(this);

  connect(m_clExpButton, &CollapseExpandButton::clicked, this, &CollapsibleGroupBox::onVisibilityChanged);

  QTimer::singleShot(0, this, [this] {
    auto wh = findWindowForWidget(this);
    if (wh != nullptr)
      connect(wh, &QWindow::screenChanged, this, &CollapsibleGroupBox::onScreenChanged);
  });

  QTimer::singleShot(0, this, &CollapsibleGroupBox::resizeCollapseButton);
}

void CollapsibleGroupBox::collapseLayout(QLayout *lay)
{
  assert(!m_layoutMargins.contains(lay));

  const int cnt = lay->count();
  for (int idx = 0; idx < cnt; idx++) {
    auto lit = lay->itemAt(idx);

    if (lit->widget()) {
      auto w = lit->widget();
      if (w != m_clExpButton)
        w->setVisible(false);
    }
    else if (lit->spacerItem())
      collapseSpacer(lit->spacerItem());
    else if (lit->layout())
      collapseLayout(lit->layout());
  }

  m_layoutMargins[lay] = lay->contentsMargins();
  lay->setContentsMargins(0, 0, 0, 0);
}

void CollapsibleGroupBox::collapseSpacer(QSpacerItem *spacer)
{
  assert(!m_spacerSizes.contains(spacer));

  m_spacerSizes[spacer] = {spacer->sizeHint(), spacer->sizePolicy()};
  spacer->changeSize(0, 0);
}

void CollapsibleGroupBox::expandLayout(QLayout *lay)
{
  assert(m_layoutMargins.contains(lay));

  const int cnt = lay->count();
  for (int idx = 0; idx < cnt; idx++) {
    auto lit = lay->itemAt(idx);

    if (lit->widget())
      lit->widget()->setVisible(true);
    else if (lit->spacerItem())
      expandSpacer(lit->spacerItem());
    else if (lit->layout())
      expandLayout(lit->layout());
  }

  lay->setContentsMargins(m_layoutMargins[lay]);
}

void CollapsibleGroupBox::expandSpacer(QSpacerItem *spacer)
{
  assert(m_spacerSizes.contains(spacer));

  const auto &sz = m_spacerSizes[spacer].first;
  const auto &pol = m_spacerSizes[spacer].second;

  spacer->changeSize(sz.width(), sz.height(), pol.horizontalPolicy(), pol.verticalPolicy());
}

void CollapsibleGroupBox::onScreenChanged()
{
  resizeCollapseButton();
}

void CollapsibleGroupBox::onVisibilityChanged()
{
  assert(this->layout() != nullptr);

  CollapseExpandButton::State s = m_clExpButton->state();

  switch (s) {
  case CollapseExpandButton::State::COLLAPSED:
    m_layoutMargins.clear();
    m_spacerSizes.clear();

    collapseLayout(this->layout());
    break;
  case CollapseExpandButton::State::EXPANDED:
    expandLayout(this->layout());
    break;
  }
}

void CollapsibleGroupBox::resizeCollapseButton()
{
  const QScreen *scr = findScreenForWidget(this);

  if (scr == nullptr)
    return;

  const auto &size = this->size();

#ifdef Q_OS_WIN
  qreal baseSize = 15.0;
  int yOffset = 5;
#else
  qreal baseSize = 22.0;
  int yOffset = 0;
#endif

  if (scr == nullptr)
    return;

  if (QString::compare(QApplication::style()->objectName(), "fusion") == 0)
    baseSize = 15.0;

  const qreal dpi = scr->logicalDotsPerInchX();
  const qreal btnSize = floor((baseSize * dpi / 96.0) + 0.5);

  m_clExpButton->setGeometry(size.width() - btnSize, yOffset, btnSize, btnSize);
}

void CollapsibleGroupBox::resizeEvent(QResizeEvent *)
{
  resizeCollapseButton();
}

这似乎崩溃和恢复的方块完全像我所期望的。

vh0rcniy

vh0rcniy3#

我无法解释分组框的明显不可复制的高度。但您也可以这样做:

void CollapsibleGroupBox::onVisibilityChanged(bool state)
{
    switch (state) {
    case true:
        m_originalMaxHeight = this->maximumHeight();
        this->setMaximumHeight(this->fontMetrics().height());
        break;
    case false:
        this->setMaximumHeight(m_originalMaxHeight);
        break;
    }
}

并向包含分组框的布局添加一个拉伸。当动态添加或删除小部件时,它可以很好地调整大小。

相关问题