本文接下来试图看看 QLayout 与窗口的几何尺寸控制。
注意:本文只是试图解释,QLayout其实没有任何神秘的东西,它所有的功能离开它你也都可以做。但这并不是鼓励大家不使用QLayout。
始终记住一点:要改变一个Widget的大小,只有move()、resize()、setGeometry()这3个东西可用,当然,对于带装饰器的顶级窗口,你还可以通过鼠标等改变大小或移动窗口位置(但这个不在本文讨论范围内)。
几何尺寸
一个QWidget 或其派生类
对于一个窗口(Window)来说,还要区分:
- 带窗口装饰器后的位置和大小
- 不带装饰器(客户区域?绘图区域?)的位置和大小
看起来还蛮复杂的哈,列个表看看。
frameGeometry()
|
几何尺寸(位置+大小)
|
对于窗口,包含窗口装饰器
|
x()
y()
pos()
|
只包含位置信息(左上角坐标)
|
move()
|
只移动位置
|
geometry()
|
几何尺寸(位置+大小)
|
不包含窗口装饰器
|
width()
height()
rect()
size()
|
只包含大小信息
|
setGeometry()
|
改变 位置+大小
|
resize()
|
只改变大小
|
关键记住一点:要程序内改变一个Widget的大小,只有move、resize、setGeometry这3个东西可用。不要被QLayout干扰,它一点都不神秘,它也只能老老实实去调用这类函数。
例子
用个例子看看吧,如果
- 在一个 400X400 的Widget上,放置很多其他Widget(比如64个 45X45 的按钮)
#include <QtGui/QApplication>
#include <QtGui/QPushButton>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.setGeometry(100, 100, 400, 400);
for (int i=0; i<8; ++i) {
for (int j=0; j<8; ++j) {
QPushButton * btn = new QPushButton(QString("(%1,%2)").arg(i).arg(j), &widget);
btn->setGeometry(i*50, j*50, 45, 45);
}
}
widget.show();
return a.exec();
}
只需要挨个设置一下几何尺寸,似乎也不复杂嘛。是吧?
困难是什么呢?
- 如果我们用鼠标拖动来改变窗口Widget的大小(有装饰器,可以拖动),它上面的这些按钮却不会动(我们前面的很黑体字提到的哈)。界面将很难看。
其实这也不是大问题,我们只需要在子类化QWidget,覆盖(override)它的resizeEvent()函数
void Widget::resizeEvent(QResizeEvent *)
{
}
在这儿重新设置它上面的按钮的位置和大小就行了。
- 考虑一个问题,我们如何知道一个widget用多大的大小合适呢?
这是个大问题,单一的widget还好解决,比如一个按钮,你可以根据文字、按钮样式等等计算一个大小。可是对于复合的widget:比如我们例子中的widget中有64个按钮,如果再将这样的64个widget放于另外一个widget中,会怎么样?
没有什么好办法,仍然是需要我们一个一个进行计算。其实不是太难,但是操作特别繁杂。
- 再考虑一个问题,如果我们改变一个按钮上的文字,按钮的最佳尺寸要变化,如何处理?
只能是按钮通知其parent(通过LayoutRequest事件),而后parent重新排布子控件,以获得最佳显示效果。
接下来,我们看看 QLayout 是如何解决这三个问题的。
QLayout
layout 做哪些事情呢?
初始放置
|
将子widget一个一个放置到父widget上
|
layout 计算各个子widget大小,并调用setGeometry() 来设置
|
响应父widget变化
|
父widget大小变化时,子widget相应变化
|
layout 通过监听父widget的QResizeEvent事件来实现
|
响应子widget变化
|
子widget的最佳大小变化时
(比如给按钮设置新的Text)
|
让父对象的layout 重新计算几何尺寸
|
QResizeEvent
当一个widget的大小变化后,会生成QResizeEvent事件(这时widget所关联Layout就开始重新计算喽...)。
我们知道,事件都是通过QWidget::event()派发的:
bool QWidget::event(QEvent *event)
{
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break;
...
case QEvent::Move:
moveEvent((QMoveEvent*)event);
break;
case QEvent::Resize:
resizeEvent((QResizeEvent*)event);
break;
...
但是,Qt对QLayout有特殊照顾,在事件到达接收者的event()函数之前,先送到了接收者对应的layout中:
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
...
if (receiver->isWidgetType()) {
QWidget *widget = static_cast<QWidget *>(receiver);
if (QLayout *layout=widget->d_func()->layout) {
layout->widgetEvent(e);
}
}
bool consumed = receiver->event(e);
...
而后layout开始工作
void QLayout::widgetEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::Resize:
if (d->activated) {
QResizeEvent *r = (QResizeEvent *)e;
d->doResize(r->size());
} else {
activate();
}
break;
...
恩,注意看上面代码:如果reciever是widget而且有layout,该事件先送到layout的widgetEvent()中。然后才会通过event()派发到达大家熟悉的resizeEvent()等函数。
QEvent::LayoutRequest
前面的resize比较容易理解,如果子widget的大小想变化,如何通知layout呢?
通过voidQWidget::updateGeometry()函数。
void QWidgetPrivate::updateGeometry_helper(bool forceUpdate)
{
...
if (!q->isWindow() && !q->isHidden() && (parent = q->parentWidget())) {
if (parent->d_func()->layout)
parent->d_func()->layout->invalidate();
else if (parent->isVisible())
QApplication::postEvent(parent, new QEvent(QEvent::LayoutRequest));
}
看看这段代码,如果parent有layout布局,直接让布局无效(强制Layout重新计算大小)。
而如果parent没有布局呢?恩,思想也比较简单:它给父widget发送 LayoutRequest 事件。注意:如果我们不使用布局的话,面对这种情况,我们就要自己处理这个事件喽。
sizeHint等
这个其实似乎是最有趣的,QLayout如果知道它负责控制的各个widget该有多大的大小呢?
QWidget::sizePolicy()
|
这3个东西为layout提供一些大小信息
对于自定义widget,子类化时你可能需要提供这些信息
|
QWidget::sizeHint()
|
QWidget::minimumSizeHint()
|
熟悉这3个东西,以及各个QLayout派生类的使用,不然,你可能会抱怨——QLayout太难用了
“混用”会如何?
比如:前面的例子,我们64个按钮,如果32个使用QGridLayout进行管理,32个不用layout进行管理。结果会怎么样?
其实不会怎么样。QGridLayout 负责对它管理的widget调用setGeometry,而你负责对自己管理的调用setGeometry。想怎么放就怎么放。(但是你要注意:最好别让它们重合,不然...)
QMainWindow
QMainWindow 上面放置很多的Widget:
菜单栏
|
QMenuBar
|
这些全是QWidget的派生类
|
工具栏
|
QToolBar
|
状态栏
|
QStatusBar
|
停靠窗口
|
QDockWidget
|
中心窗体
|
...
|
其实没有什么神秘的,一堆widget放置到了QMainWindow中,而且还会自动随着QMainWindow变化。你很容易想到它默认就已经设置了一个QLayout!
class QMainWindowLayout : public QLayout
{
Q_OBJECT
...
这是一个私有类,你不必关心细节,但是可以考虑:平时如何使用QLayout的?是不是要将你的widget加入到layout中??
在QMainWindow中,QMenuBar、QToolBar等等都已经加入到了它的layout中,而且layout中为你留了一个位置,就是中心窗体。
在QMainWindow,QMainWindowLayout管理的这些子widget布满了几乎整个窗体。所以:有人抱怨
-
为什么在 MainWindow::paintEvent() 中画的东西总是不成功? 不是不成功,是被上面的菜单栏、中心窗体等挡住了。
-
为什么MainWindow::mousePressEvent()中收不到鼠标事件?? 同上...
-
为什么newQPushButton(this)创建的按钮总是在左上方? 既没有加入到layout中,又没有手动调用setGeometry()或move(),当然如此了
- ...
-
当然还有更隐蔽的,信号槽不起作用? 见http://hi.baidu.com/cyclone
参考
分享到:
相关推荐
中心部件通常是一个继承自 `QWidget` 的自定义部件,但也可以是其他 `QWidget` 派生类的实例。以下是如何在 `QMainWindow` 中使用中心部件的说明和一个简单的实例讲解。 + **中心部件的使用说明:** 1. **创建中心...
QWidget 直接派生类的样式表不起作用的解决办法,相关教程链接如下: http://blog.csdn.net/tennysonsky/article/details/46653571
026 QWidget类分析显示和隐藏接口说明线程类QThread使用方法.7z
自实现的一个翻转QWidget的demo;采用QPainter和QPropertyAnimation实现,是一个学习动画设计的高效demo
qwidge窗口嵌入qml窗口, qml调用qwidget方法 qwidget发送信号传递数据 qml信号处理以及注意事项
QWidget,QMainWindow和QDialog的区别.
QT 2个QWidget UI 来回切换例子,并且ui界面可自行单独处理单独cpp和h文件,相互不影响。
QWidget嵌入qml,实现QWidget与qml的交互、无标题栏、可拉伸、可拖动、可切换qml页面
两个QWidget互相交换显示
QWidget嵌入Qml界面的小Demo,比较简单的代码,可以提供大家参考!但是还是有一些问题需要大家解决,如果有一些BUG能告知最好
Qt QWidget 互斥抽屉模型Qt QWidget 互斥抽屉模型 Qt5.0~Qt6..4均能编译通过
实现把QWidget嵌入到QML中,实现方案已经在博客中说明。
QT 框接的qwidget实现方法
这里帮大家解决个小难题~环境变量需要你的本机配好~直接就能跑~祝君好运~
本程序实现:单击qml页面,更新按钮的文本;单击按钮,更新qml页面的颜色;展示qml嵌入QWidget方法与通信
内容:QWidget功能作用的展示控件,展示了QWidget功能作用中大小位置的一些API,通过测试,能让自己更快地熟悉其功能作用。 问题:有问题多交流
Qt例程源代码QWidget.7z
需要选择特定区域,所以制作了个区域选择框,在QWidget上绘制区域框,可以放大缩小移动,可以是矩形,也可以是椭圆,注释非常完整。
QWidget控件在QML中使用的控件 具体使用方法在我的博客中详细说明
Qt 使用QWidget调用QML打开安卓摄像头可以实时预览,显示QT中使用QWidget调用QML的方法