具有动态变化内容的QScrollArea

时间:2014-01-21 09:17:30

标签: c++ qt desktop-application qscrollarea qlayout

我有一个QScrollArea,里面有一些按钮,如图所示。 enter image description here

布局的想法是:  1.当按钮太宽时,应使用左右按钮滚动按钮

2.滚动区域中的按钮数量可以动态更改  3.应尽可能使用任何可用空间来扩展滚动区域。如果不存在这样的空间,则应使用导航按钮进行滚动。

当我增加按钮时,我的当前实现我有这个: enter image description here

但是右边有可用空间,所以这应该是这样的: enter image description here

如果我再次增加到10,例如,那么应该出现滚动条(因为小部件会构成布局)。

我想知道除了手动调整窗口小部件之外是否还有其他方法(因为ui可以翻译,按钮可以改变大小提示,但实际设计也更复杂:(

以下是我对ScrollAreaTest小部件的实现:

#include "MainWidget.h"

#include <QLineEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QScrollArea>
#include <QPushButton>
#include <QDebug>
#include "ButtonWidget.h"

#include "CheckableButtonGroup.h"

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent),
      m_scrollArea( 0 ),
      m_lineEdit( 0 ),
      m_buttons( 0 )
{
    QVBoxLayout* mainLayout = new QVBoxLayout( this );
    QWidget* firstRow = new QWidget;
    QHBoxLayout* firstRowLayout = new QHBoxLayout( firstRow );

    QPushButton* left  = new QPushButton;
    QPushButton* right = new QPushButton;

    m_buttons = new CheckableButtonGroup( Qt::Horizontal );
    m_buttons->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
    m_buttons->setButtonsCount( 5 );
    m_buttons->setStyleSheet( "border: none" );

    QWidget* const buttonsContainer = new QWidget;
    QHBoxLayout* const buttonsContainerLayout = new QHBoxLayout( buttonsContainer );
    buttonsContainerLayout->setSpacing( 0 );
    buttonsContainerLayout->setSizeConstraint( QLayout::SetMinAndMaxSize );
    buttonsContainerLayout->setMargin( 0 );
    buttonsContainerLayout->addWidget( m_buttons, 0, Qt::AlignLeft );

    qDebug() << m_buttons->buttons()[ 0 ]->size();

    m_scrollArea = new QScrollArea;
    m_scrollArea->setContentsMargins( 0, 0, 0, 0 );
    m_scrollArea->setWidget( buttonsContainer );
    m_scrollArea->setWidgetResizable( true );
    m_scrollArea->setStyleSheet( "border: 1px solid blue" );
    m_scrollArea->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );

    firstRowLayout->addWidget( left        , 0, Qt::AlignLeft );
    firstRowLayout->addWidget( m_scrollArea, 1, Qt::AlignLeft );
    firstRowLayout->addWidget( right       , 0, Qt::AlignLeft );

    m_lineEdit = new QLineEdit;
    QPushButton* button = new QPushButton;
    QHBoxLayout* secondRowLayout = new QHBoxLayout;
    secondRowLayout->addWidget( m_lineEdit );
    secondRowLayout->addWidget( button );

    connect( button, SIGNAL(clicked()), SLOT(setButtonsCount()) );

    mainLayout->addWidget( firstRow, 1, Qt::AlignLeft );
    mainLayout->addLayout( secondRowLayout );

    button->setText( "Set buttons count" );

    buttonsContainer->resize( m_buttons->buttonsOptimalWidth(), buttonsContainer->height() );
    m_buttons->resize( m_buttons->buttonsOptimalWidth(), m_buttons->height() );

    //area->resize( 100, area->height() );
    //area->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
}

MainWidget::~MainWidget()
{
}

void MainWidget::setButtonsCount()
{
    m_buttons->setButtonsCount( m_lineEdit->text().toInt() );
}

这是包含问题的整个Qt项目: https://drive.google.com/file/d/0B-mc4aKkzWlxQzlPMEVuNVNKQjg/edit?usp=sharing

1 个答案:

答案 0 :(得分:8)

基本步骤是:

  1. 包含按钮的容器窗口小部件(您的CheckableButtonGroup)必须设置QLayout::SetMinAndMaxSize大小约束。然后它将足够大,可以按住按钮。它的大小政策无关紧要,因为您只需将其放入QScrollArea,而不是放入另一种布局。

  2. 滚动区域需要根据其保存的窗口小部件的大小设置其最大大小。默认实现不会这样做,因此必须通过监视嵌入式窗口小部件的resize事件来实现它。

  3. 下面的代码是一个在Qt 4.8和5.2下都能运作的最小例子。

    Screenshot with two buttons

    Screenshot with multiple buttons

    // https://github.com/KubaO/stackoverflown/tree/master/questions/scrollgrow-21253755
    #include <QtGui>
    #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
    #include <QtWidgets>
    #endif
    
    class ButtonGroup : public QWidget {
       Q_OBJECT
       QHBoxLayout m_layout{this};
    public:
       ButtonGroup(QWidget * parent = 0) : QWidget{parent} {
          m_layout.setSizeConstraint(QLayout::SetMinAndMaxSize); // <<< Essential
       }
       Q_SLOT void addButton() {
          auto n = m_layout.count();
          m_layout.addWidget(new QPushButton{QString{"Btn #%1"}.arg(n+1)});
       }
    };
    
    class AdjustingScrollArea : public QScrollArea {
       bool eventFilter(QObject * obj, QEvent * ev) {
          if (obj == widget() && ev->type() == QEvent::Resize) {
             // Essential vvv
             setMaximumWidth(width() - viewport()->width() + widget()->width());
          }
          return QScrollArea::eventFilter(obj, ev);
       }
    public:
       AdjustingScrollArea(QWidget * parent = 0) : QScrollArea{parent} {}
       void setWidget(QWidget *w) {
          QScrollArea::setWidget(w);
          // It happens that QScrollArea already filters widget events,
          // but that's an implementation detail that we shouldn't rely on.
          w->installEventFilter(this);
       }
    };
    
    class Window : public QWidget {
       QGridLayout         m_layout{this};
       QLabel              m_left{">>"};
       AdjustingScrollArea m_area;
       QLabel              m_right{"<<"};
       QPushButton         m_add{"Add a widget"};
       ButtonGroup         m_group;
    public:
       Window() {
          m_layout.addWidget(&m_left, 0, 0);
          m_left.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
          m_left.setStyleSheet("border: 1px solid green");
    
          m_layout.addWidget(&m_area, 0, 1);
          m_area.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
          m_area.setStyleSheet("QScrollArea { border: 1px solid blue }");
          m_area.setWidget(&m_group);
          m_layout.setColumnStretch(1, 1);
    
          m_layout.addWidget(&m_right, 0, 2);
          m_right.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
          m_right.setStyleSheet("border: 1px solid green");
    
          m_layout.addWidget(&m_add, 1, 0, 1, 3);
          connect(&m_add, SIGNAL(clicked()), &m_group, SLOT(addButton()));
       }
    };
    
    int main(int argc, char *argv[])
    {
       QApplication a{argc, argv};
       Window w;
       w.show();
       return a.exec();
    }
    
    #include "main.moc"