本文通过多个案例,详细说明关于Qt窗体尺寸的一些重要问题
默认尺寸
对于一个Qt的窗口(继承于QWidget),获取其窗体尺寸的方法size();
以一个Qt创建Qt Widgets Application项目的默认生成代码为基础,做如下测试
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{qDebug()<<"MainWindow::MainWindow:"<<this->size();this->resize(1200,800);qDebug()<<"MainWindow::MainWindow:"<<this->size();
}
第一个this->size()输出QSize(640, 480),即MainWindow具有默认的尺寸640*480
第二个this->size()输出QSize(1200, 800)
实际上,继承于QWidget的控件,例如QPushButton,QLineEdit等也会获得默认的尺寸640*480
QWidget* page=new QWidget;qDebug()<<".."<<page->size();QPushButton* btn=new QPushButton;qDebug()<<".."<<btn->size();QLineEdit* edt=new QLineEdit;qDebug()<<".."<<edt->size();
以上输出都是QSize(640, 480)
布局之后的尺寸
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{qDebug()<<"MainWindow::MainWindow:"<<this->size();this->resize(1200,800);QWidget* page=new QWidget;qDebug()<<".."<<page->size();QVBoxLayout* layout=new QVBoxLayout(page);QPushButton* btn=new QPushButton;qDebug()<<".."<<btn->size();QLineEdit* edt=new QLineEdit;qDebug()<<".."<<edt->size();layout->addWidget(btn);layout->addWidget(edt);this->setCentralWidget(page);qDebug()<<".."<<page->size();qDebug()<<".."<<btn->size();qDebug()<<".."<<edt->size();qDebug()<<"MainWindow::MainWindow:"<<this->size();//当界面显示出来之后,点击按钮查看控件尺寸connect(btn,&QPushButton::clicked,[=]{qDebug()<<".."<<page->size();qDebug()<<".."<<btn->size();qDebug()<<".."<<edt->size();});
}
以上代码this->setCentralWidget(page);之后的三个打印对应的尺寸应该是多少?
依然还是 QSize(640, 480)
而connect()方法中的三个打印对应的值则分别为
.. QSize(1200, 800)
.. QSize(1178, 28)
.. QSize(1178, 24)
以上代码说明两个问题:
(1)将控件加入到布局,然后放入了父窗体中,则控件的尺寸会根据父窗体的布局重新设置
(2)控件尺寸的重新设置发生在窗体显示出来后(绘制时)
固定尺寸
以下代码实现在主窗体上布局多个按钮和子窗体,实现点击按钮切换每个子窗体的隐藏和可见。
但是存在问题:
(1)4个子窗体平分了主窗体的尺寸;
(2)隐藏一个子窗体后,其他的子窗体尺寸被更改(窗体重绘时会重新设置尺寸)
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{qDebug()<<"MainWindow::MainWindow:"<<this->size();this->resize(1200,800);QWidget* page=new QWidget;QVBoxLayout* layout=new QVBoxLayout(page);QScrollArea* contentArea=new QScrollArea;contentArea->setWidgetResizable(true);contentArea->setWidget(page);for(int i=0;i<4;i++){QWidget* p=new QWidget;//设置窗体背景色,使得区分背景,可见p->setStyleSheet("background-color:green");QPushButton* b=new QPushButton;layout->addWidget(b);layout->addWidget(p);connect(b,&QPushButton::clicked,[p]{//点击按钮后窗体切换显示和隐藏效果p->setVisible(!p->isVisible());});}this->setCentralWidget(contentArea);
}
如果需要设置子窗体在重绘时不改变尺寸,那么需要设置其尺寸
例如在QWidget* p=new QWidget;下方增加 p->setFixedHeight(300);
这样隐藏和显示一个子窗体不会对任意其他的子窗体的尺寸造成影响。
更改后会发现,当隐藏完所有的子窗体后,所有的按钮被均匀布局在了主窗体,如果想要的效果是按钮从主窗体从上往下紧密排列,可以在layout添加完所有的控件和窗口后,添加一个stretch,即在this->setCentralWidget(contentArea);上一行添加layout->addStretch();
窗体实际尺寸
如果MainWindow设置的高度为600,而它的中心窗体高度为800,该怎样显示?
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{qDebug()<<"MainWindow::MainWindow:"<<this->size();this->resize(800,600);qDebug()<<"MainWindow::MainWindow:"<<this->size();//默认情况下MainWindow没有centralWidgetQWidget* center=new QWidget;qDebug()<<"center:"<<center->size();QVBoxLayout* layout=new QVBoxLayout(center);QWidget* page1=new QWidget;page1->setStyleSheet("background-color:green");page1->setFixedHeight(400);QWidget* page2=new QWidget;page2->setFixedHeight(400);page2->setStyleSheet("background-color:yellow");layout->addWidget(page1);layout->addWidget(page2);qDebug()<<"center:"<<center->size();this->setCentralWidget(center);//此时输出QSize(800, 600)qDebug()<<"MainWindow::MainWindow:"<<this->size();
}
int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();qDebug()<<"w size:"<<w.size();return a.exec();
}
以上代码执行时,MainWindow构造函数中的输出mainwindow的窗体高度为600,但是在窗口显示(执行了w.show())之后,它的高度却是829
给窗体设置某个尺寸,实际显示的时候窗体根据布局,不一定恰好按照给定的尺寸来显示
尺寸不够引起重叠的窗体
如果主窗体设置了固定高度600,而它的中心窗体设置固定高度1200?
此时,肯定主窗体显示不全page1和page2
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{qDebug()<<"MainWindow::MainWindow:"<<this->size();this->resize(800,600);this->setFixedHeight(600);qDebug()<<"MainWindow::MainWindow:"<<this->size();//默认情况下MainWindow没有centralWidgetQWidget* center=new QWidget;center->resize(800,500);qDebug()<<"center:"<<center->size();QVBoxLayout* layout=new QVBoxLayout(center);QWidget* page1=new QWidget;page1->setStyleSheet("background-color:green");page1->setFixedHeight(600);QWidget* page2=new QWidget;page2->setFixedHeight(600);page2->setStyleSheet("background-color:yellow");layout->addWidget(page1);layout->addWidget(page2);qDebug()<<"center:"<<center->size();this->setCentralWidget(center);//此时输出QSize(800, 600)qDebug()<<"MainWindow::MainWindow:"<<this->size();QTimer::singleShot(1000,[=](){//为什么page1和page2的高度是600,而实际显示的界面没有600?//事实上page1和page2的高度都是600,界面上也是600,但是page1和page2重叠了一部分qDebug()<<"page1:"<<page1->size();qDebug()<<"page2:"<<page2->size();qDebug()<<"center:"<<center->size();qDebug()<<"sizeHint:"<<layout->sizeHint();qDebug()<<"MainWindow::MainWindow:"<<this->size();});
}
这里输出了layout的sizeHit(),为1229,layout上放置了page1和page2,他们的高度600+600=1200,29是布局周边留下的空白部分。
最小高度限制
这里展示了一个实际项目中遇到的问题,需要在一个页面中放置多个表格,并且每个表格要直接显示至少5行,更多行的时候滚动条显示。
以下代码是从项目中抽取的问题特征部分:
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{this->resize(800,600);QWidget* page=new QWidget;QVBoxLayout* layout=new QVBoxLayout(page);QScrollArea* contentArea=new QScrollArea;contentArea->setWidgetResizable(true);contentArea->setWidget(page);for(int i=0;i<4;i++){TableWidget* p=new TableWidget;QPushButton* b=new QPushButton;layout->addWidget(b);layout->addWidget(p);connect(b,&QPushButton::clicked,[p]{//点击按钮后窗体切换显示和隐藏效果p->setVisible(!p->isVisible());qDebug()<<"p size:"<<p->size();});}layout->addStretch();this->setCentralWidget(contentArea);
}
TableWidget只是一个平凡的QTableWidget实例,它设置了默认表格有6行8列,
TableWidget::TableWidget()
{this->setRowCount(6);this->setColumnCount(8);
}
上述代码的效果:
目前的问题是每个表格高度都被挤压了,只显示了一行。
一种解决问题的思路是,在表格初始化时,设置最小高度限制:
TableWidget::TableWidget()
{this->setMinimumHeight(250);this->setRowCount(6);this->setColumnCount(8);
}
一个更复杂的示例
以下是一个更复杂一些的示例,这个示例有助于理解Qt在布局控件的过程中对尺寸的更改。
代码目的是在主界面上加载多个Drawer页面,Drawer页面是一种类似抽屉的盒子,可以打开关闭(显示隐藏)
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{this->resize(800,600);QWidget* page=new QWidget;page->setMinimumHeight(1000);//layout用来装载drawerQVBoxLayout* layout=new QVBoxLayout(page);Drawer* d1=new Drawer;Drawer* d2=new Drawer;Drawer* d3=new Drawer;Drawer* d4=new Drawer;QList<Drawer*> drawers{d1,d2,d3,d4};for(int i=0;i<1;i++){QVBoxLayout* lay=new QVBoxLayout;lay->addWidget(new QLineEdit);lay->addWidget(new QLineEdit);lay->addWidget(new QLineEdit);lay->addWidget(new QLineEdit);lay->addStretch();drawers.at(i)->setContentLayout(lay);}for(int i=1;i<4;i++){QVBoxLayout* lay=new QVBoxLayout;lay->addWidget(new TableWidget);drawers.at(i)->setContentLayout(lay);}//以下的stretch使得drawer可以往页面上面顶for(int i=0;i<4;i++)layout->addWidget(drawers.at(i));layout->addStretch();QScrollArea* contentArea=new QScrollArea;contentArea->setWidgetResizable(true);contentArea->setWidget(page);this->setCentralWidget(contentArea);QTimer::singleShot(1000,[=](){qDebug()<<"MainWindow::MainWindow:"<<this->size()<<","<<this->sizeHint();qDebug()<<"d2:size:"<<d2->size()<<","<<d2->sizeHint();});}
Drawer提供一个按钮,用于交互显示隐藏页面,和一个接口用于设置页面显示的内容(设置一个布局)
Drawer::Drawer(QWidget *parent): QWidget{parent}
{QVBoxLayout* layout=new QVBoxLayout;layout->addSpacing(0);layout->setContentsMargins(0,0,0,0);QToolButton* btn=new QToolButton;layout->addWidget(btn);area=new QScrollArea;area->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);layout->addWidget(area);this->setLayout(layout);connect(btn,&QToolButton::clicked,[=]{area->setVisible(!area->isVisible());qDebug()<<"drawer size:"<<this->size()<<this->minimumHeight();
// if(!area->isVisible())
// {
// this->setFixedHeight(30);
// }
// else
// {
// this->setFixedHeight(minHeight);
// }});
}void Drawer::setContentLayout(QLayout *lay)
{area->setLayout(lay);
// minHeight=this->minimumHeight();
}
其中TableWidget依然是QTableWidget的简单实例
TableWidget::TableWidget()
{this->setRowCount(6);this->setColumnCount(8);
}
以上代码的效果如下:
存在的问题是:
(1)表格默认只展示了两行,
(2)并且关闭了一个Drawer后,其他Drawer的尺寸会发生变化
其中,前面对(2)问题的处理是设置固定的尺寸p->setFixedHeight(300);
对应这里就是在Drawer的构造函数第一行加 this->setFixedHeight(300);但是这样一来,会发现每个Drawer之间重叠了,就是尺寸不够引起了窗体重叠,300*400=1200>1000,主窗体中的滚动区域的窗体设置的最小高度1000.
另外隐藏所有的Drawer页面后,剩余的按钮又均匀分布在了主界面。
这里的效果和前面固定尺寸一节遇到的问题看似相同,而实际上却不一样,前面的问题通过在布局的最后增加stretch解决,这里显然不能。
这里遇到的问题是由于按钮点击后隐藏的不是整个Drawer,而只是Drawer中的area,Drawer依然是可见的,因此处于整个主界面的布局之中。这里当Drawer中的area被隐藏后,Drawer页面本身被重新布局了,即只有一个QToolButton参与了布局,所以能看到按钮被布局在了Drawer的中央。
那么,这种情况下该怎样实现效果:
(1)隐藏所有的Drawer的area后,只剩下按钮顶格显示在主页面
(2)表格默认显示至少5行
(3)隐藏一个Drawer不会对其他的Drawer尺寸产生影响
一种方式是要在Drawer显示隐藏切换的时候同时设置固定的尺寸;这个固定的尺寸要使得表格至少显示5行
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{this->resize(800,600);QWidget* page=new QWidget;//给page设置了最小高度后,每个drawer的高度会高一些page->setMinimumHeight(1200);//layout用来装载drawerQVBoxLayout* layout=new QVBoxLayout(page);Drawer* d1=new Drawer;Drawer* d2=new Drawer;Drawer* d3=new Drawer;Drawer* d4=new Drawer;QList<Drawer*> drawers{d1,d2,d3,d4};for(int i=0;i<1;i++){QVBoxLayout* lay=new QVBoxLayout;lay->addWidget(new QLineEdit);lay->addWidget(new QLineEdit);lay->addWidget(new QLineEdit);lay->addWidget(new QLineEdit);lay->addStretch();//需要提前设置Drawer的最小高度drawers.at(i)->setMinimumHeight(200);drawers.at(i)->setContentLayout(lay);}for(int i=1;i<4;i++){QVBoxLayout* lay=new QVBoxLayout;lay->addWidget(new TableWidget);//需要提前设置Drawer的最小高度drawers.at(i)->setMinimumHeight(300);drawers.at(i)->setContentLayout(lay);}//以下的stretch使得drawer可以往页面上面顶for(int i=0;i<4;i++)layout->addWidget(drawers.at(i));layout->addStretch();QScrollArea* contentArea=new QScrollArea;contentArea->setWidgetResizable(true);contentArea->setWidget(page);this->setCentralWidget(contentArea);QTimer::singleShot(1000,[=](){qDebug()<<"MainWindow::MainWindow:"<<this->size()<<","<<this->sizeHint();qDebug()<<"d2:size:"<<d2->size()<<","<<d2->sizeHint();});}
TableWidget::TableWidget()
{this->setRowCount(6);this->setColumnCount(8);
}
Drawer::Drawer(QWidget *parent): QWidget{parent}
{QVBoxLayout* layout=new QVBoxLayout;layout->addSpacing(0);layout->setContentsMargins(0,0,0,0);QToolButton* btn=new QToolButton;layout->addWidget(btn);area=new QScrollArea;area->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);layout->addWidget(area);this->setLayout(layout);connect(btn,&QToolButton::clicked,[=]{area->setVisible(!area->isVisible());qDebug()<<"drawer size:"<<this->size()<<this->minimumHeight();//这里根据area显示和隐藏的状态动态调整整个drawer的高度if(!area->isVisible()){this->setFixedHeight(30);}else{this->setFixedHeight(minHeight);}});
}void Drawer::setContentLayout(QLayout *lay)
{area->setLayout(lay);//这里将预设的最小高度记录下来,用于在area显示隐藏切换时设置drawer的高度minHeight=this->minimumHeight();
}
以上代码的效果如下: