深度探索QT窗口系统——几何篇
深度探索QT窗口系统——几何篇
窗口作为界面编程中最重要的部分,没有窗口就没有界面,是窗口让我们摆脱了DOS时代,按钮是一个窗口,文本框是一个窗口,标签页是一个窗口。一个窗口可以由多个窗口组成,每天我们都在与窗口打交道,当你打开windows时,桌面就是一个窗口,你打开浏览器时,你也正在访问窗口。QT窗口系统,QT是一个跨平台的框架,类似微软的MFC,Borland的OWL,前者只适用于windows,而使用QT你可以开发windows平台应用程序,你也可以开发linux应用程序,当然了你也可以用于Symbian、Meego的开发,据说,QT未来还会支持Android,iOS等操作系统。更让人欣喜的是,QT是开源的,你可以看到所有QT的源代码,而且帮助文档很详细,可以说,QT是一个很有前途的开发框架,如果大家想要转到QT平台做开发,深入了解QT的窗口系统是很有必要的。
QWidget的是所有窗体的基类,按钮,文本框,对话框都派生自它,所以了解它的特性后,所有窗体的基本属性都了解了。
1. Qt窗体的几何属性
下图是典型的QT窗口:
灰色部分是窗体的边框,白色区域为窗口的内容区域,绿色区域为窗口标题,QWidget提供1组接口访问窗口的几何特性:
- 包含窗口边框的函数: x(), y(), frameGeometry(), pos(), 和 move()
- 不包含窗口边框的函数: geometry(), width(), height(), rect(), 和size()
下面用一个实例来说明窗口这几个几何特性:
void print(QWidget *pWidget)
{
qDebug() << "this includes the window frame";
qDebug() << "frameGeometry() = " << pWidget->frameGeometry();
qDebug() << "pos() = " << pWidget->pos();
qDebug() << "x() = " << pWidget->x() << "y() = " << pWidget->y();
qDebug() << "frameSize() = " << pWidget->frameSize();
qDebug() << "this excludes the window frame";
qDebug() << "geometry() = " << pWidget->geometry();
qDebug() << "rect() = " << pWidget->rect();
qDebug() << "contentRect() = " << pWidget->contentsRect();
qDebug() << "width() = " << pWidget->width() << "height() = " << pWidget->height();
qDebug() << "size() = " << pWidget->size();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.resize(200, 300);
widget.show();
print(&widget);
return a.exec();
}
显示的窗体大小为,宽为200,高为300,注意,这里的尺寸是相对电脑的屏幕分辨率来说的,也即单位为像素,下面是输出结果:
this includes the window frame
frameGeometry() = QRect(88,116 208x334)
pos() = QPoint(88,116)
x() = 88 y() = 116
frameSize() = QSize(208, 334)
this excludes the window frame
geometry() = QRect(92,146 200x300)
rect() = QRect(0,0 200x300)
contentRect() = QRect(0,0 200x300)
width() = 200 height() = 300
size() = QSize(200, 300)
可以看到包括边框的矩形宽度为208,高度为334;不包括边框的矩形宽度为200,高度为300,这正是我们通过resize设置的尺寸,由上图的窗口图形,不难得出,边框的宽度为4,标题的高度为26。
通过上面的介绍,我们知道了窗体的基本几何属性,也知道怎么去获取这些属性,QWidget有没有提供设置这些属性的方法呢?边框的宽度始终是4吗?标题的高度始终是26吗?有没有办法设置边框的宽度呢?有没有办法设置标题的高度呢?
如何设置窗口的几何特性,窗口的平移,窗口的缩放,如何去掉窗口的边框,如何去掉窗口的标题,如何限定窗口的尺寸,下面逐一介绍:
1. 窗口平移可以用如下两种方式来实现,一是通过move的方法,二是通过setGeometry的方法:
void move(int x, int y)
void move(const QPoint &pos)
void setGeometry(int x, int y, int w, int h )
void setGeometry(const QRect &rect);
对于采用move的方法来移动窗口,只能改变窗口的位置,不能改变窗口的大小,窗口的位置包括窗口边框,若窗口无父窗口,则位置坐标(x,y),相对于电脑屏幕来说的,若窗口有父窗口,则位置坐标(x,y)是相对于父窗口来说的;对于采用setGeometry的方法来移动窗口,可以改变窗口的位置的同时,改变窗口的尺寸,窗口的位置不包括窗口边框,若窗口无父窗口,则位置坐标(x,y)相对于电脑屏幕来说的,若窗口有父窗口,则位置坐标(x,y)相对于父窗口来说的;我们来看实例:
(1) 采用move的方法,窗口无父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "pos() = " << pWidget->pos();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.show();
widget.resize(200, 300);
widget.move(0, 0);
print(&widget);
widget.move(100, 100);
print(&widget);
return a.exec();
}
widget先move(0,0)到屏幕的左上角,窗口系统的坐标轴x轴正向向右,y轴的正向向下,所以坐标原点在左上角;然后widget移动到move(100,100)的位置,下面是打印出的位置:
pos() = QPoint(0,0)
pos() = QPoint(100,100)
(2) 采用setGeometry 的方法,窗口无父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "pos() = " << pWidget->pos();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.show();
widget.resize(200, 300);
widget.setGeometry(0, 0, 200, 300);
print(&widget);
widget.setGeometry(100, 100, 200, 300);
print(&widget);
return a.exec();
}
我们先来看打印出的结果:
pos() = QPoint(-4,-30)
pos() = QPoint(96,70)
为什么第一次不是移动到(0, 0),第二次不是移动到(100,100)呢?因为通过setGeometry设置的移动位置不包括边框,而我们打印出来的是边框的位置,参照几何篇1,很快你就知道,4是边框宽度,30是窗口标题高度+边框宽度。
(3) 采用move的方法,窗口有父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "pos() = " << pWidget->pos();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget parentWidget;
QPushButton *ppbTest = new QPushButton("test button", &parentWidget);
parentWidget.show();
parentWidget.resize(200, 300);
ppbTest->move(100, 100);
print(ppbTest);
ppbTest->move(0, 0);
print(ppbTest);
return a.exec();
}
ppbTest是parentWidget的子窗口,这次移动ppbTest,首先移动位置(100,100)处,然后移动到(0,0)处,先来看看输出结果:
pos() = QPoint(100,100)
pos() = QPoint(0,0)
通过上面的输出结果,我们不难得出结论,ppbTest子窗口move位置是相对父窗口parentWidget来说的,且是相对父窗口的内容矩形来讲的。
(4) 采用setGeometry的方法,窗口有父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "pos() = " << pWidget->pos();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget parentWidget;
QPushButton *ppbTest = new QPushButton("test button", &parentWidget);
parentWidget.show();
parentWidget.resize(200, 300);
ppbTest->setGeometry(100, 100, ppbTest->width(), ppbTest->height());
print(ppbTest);
ppbTest->setGeometry(0, 0, ppbTest->width(), ppbTest->height());
print(ppbTest);
return a.exec();
}
输出结果同(3),为什么呢?不是说setGeometry的移动起始位置不包括边框吗?怎么输出结果和(3)相同呢?这是在这里ppbTest没有边框的原因。
窗口缩放方法,然后介绍一个窗口慢慢展开和慢慢隐藏的实例,窗口缩放的方法有2种,一种是采用resize的方式,另一种是采用setGeometry的方式,下面是QT提供的对应函数接口:
void resize(const QSize &size)
void resize(int w, int h)
void setGeometry(int x, int y, int w, int h)
void setGeometry(const QRect &rect)
(1) resize方法,窗口没有父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "geometry() = " << pWidget->geometry();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.show();
widget.resize(10, 10);
print(&widget);
widget.resize(116, 10);
print(&widget);
widget.resize(200, 300);
print(&widget);
return a.exec();
}
下面是输出结果:
geometry() = QRect(48,88 115x10)
geometry() = QRect(48,88 116x10)
geometry() = QRect(48,88 200x300)
为什么resize(10, 10)以后,geometry的宽度没有变为10呢,这是因为对有边框窗口,窗口上有图标,最小化,最大化,关闭按钮也需要空间,而这里的这下所需的最小空间为115,因此小于115,宽度都会是115。
(2) resize方法,无边框窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "geometry() = " << pWidget->geometry();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget(NULL, Qt::FramelessWindowHint);
widget.show();
widget.resize(10, 10);
print(&widget);
widget.resize(116, 10);
print(&widget);
widget.resize(200, 300);
print(&widget);
return a.exec();
}
下面是输出结果:
geometry() = QRect(256,231 10x10)
geometry() = QRect(256,231 116x10)
geometry() = QRect(256,231 200x300)
(3) resize方法,窗口有父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "geometry() = " << pWidget->geometry();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
QPushButton *ppbTest = new QPushButton("test button", &widget);
widget.show();
widget.resize(200, 300);
ppbTest->resize(10, 10);
print(ppbTest);
ppbTest->resize(116, 10);
print(ppbTest);
ppbTest->resize(200, 300);
print(ppbTest);
return a.exec();
}
下面是输出结果:
geometry() = QRect(256,231 10x10)
geometry() = QRect(256,231 116x10)
geometry() = QRect(256,231 200x300)
(4) setGeometry方法,窗口无父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "geometry() = " << pWidget->geometry();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.show();
int x = widget.geometry().x();
int y = widget.geometry().y();
widget.setGeometry(x, y, 10, 10);
print(&widget);
widget.setGeometry(x, y, 116, 10);
print(&widget);
widget.setGeometry(x, y, 200, 300);
print(&widget);
return a.exec();
}
下面是输出结果:
geometry() = QRect(92,146 115x10)
geometry() = QRect(92,146 116x10)
geometry() = QRect(92,146 200x300)
(5) setGeometry方法,无边框窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "geometry() = " << pWidget->geometry();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget(NULL, Qt::FramelessWindowHint);
widget.show();
int x = widget.geometry().x();
int y = widget.geometry().y();
widget.setGeometry(x, y, 10, 10);
print(&widget);
widget.setGeometry(x, y, 116, 10);
print(&widget);
widget.setGeometry(x, y, 200, 300);
print(&widget);
return a.exec();
}
下面是输出结果:
geometry() = QRect(256,231 10x10)
geometry() = QRect(256,231 116x10)
geometry() = QRect(256,231 200x300)
(6) setGeometry方法,有父窗口
#include <QtGui/QApplication>
#include <qwidget.h>
#include <qdebug.h>
void print(QWidget *pWidget)
{
qDebug() << "geometry() = " << pWidget->geometry();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
QPushButton *ppbTest = new QPushButton("test button", &widget);
widget.show();
int x = ppbTest->geometry().x();
int y = ppbTest->geometry().y();
widget.resize(200, 300);
ppbTest->setGeometry(x, y, 10, 10);
print(ppbTest);
ppbTest->setGeometry(x, y, 116, 10);
print(ppbTest);
ppbTest->setGeometry(x, y, 200, 300);
print(ppbTest);
return a.exec();
}
下面是输出结果:
geometry() = QRect(0,0 10x10)
geometry() = QRect(0,0 116x10)
geometry() = QRect(0,0 200x300)
前面从6个方面讲了通过resize和setGeometry对窗口缩放的方法,接下来给1个窗口展开和收缩的实例:
#ifndef _TEST_H
#define _TEST_H
#include <qwidget.h>
#include <qpushbutton.h>
#include <QTimerEvent>
class CExpandCollapseWidget : public QWidget {
Q_OBJECT
public:
CExpandCollapseWidget(QWidget *parent = NULL) : QWidget(parent, Qt::FramelessWindowHint)
{
timerId = 0;
bExpand = true;
ppbExpandCollapse = new QPushButton("Expand", this);
connect(ppbExpandCollapse, SIGNAL(clicked()), this, SLOT(rxClicked()));
}
protected:
void timerEvent(QTimerEvent *event)
{
if (event->timerId() == timerId)
{
int x = geometry().x();
int y = geometry().y();
int w = geometry().width();
int h = geometry().height();
setGeometry(x, y, w, h + increment);
if (bExpand)
{
if (h > 300)
{
killTimer(timerId);
timerId = 0;
}
}
else
{
if (h < 40)
{
killTimer(timerId);
timerId = 0;
}
}
}
}
private slots:
void rxClicked()
{
if (timerId == 0)
timerId = startTimer(10);
if (ppbExpandCollapse->text() == "Expand")
{
bExpand = true;
ppbExpandCollapse->setText("Collapse");
increment = 10;
//setSizeIncrement(0, 10);
}
else
{
bExpand = false;
ppbExpandCollapse->setText("Expand");
increment = -10;
}
}
private:
QPushButton *ppbExpandCollapse;
bool bExpand;
int timerId;
int increment;
};
#endif
上面是实现展开和收缩的例子,给出调用:
#include <QtGui/QApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CExpandCollapseWidget widget;
widget.show();
return a.exec();
}
下面是运行结果:
当单击Expand按钮时,窗口呈现右边展开时的状态,当单击右边Collapse按钮时,窗口呈现左边收缩时候的状态。
如何去掉窗口边框?怎么限定窗口的大小?
首先来看第一个问题:
1. 有父窗口的的窗口,自动变为无边框窗口
2. 无父窗口,加Qt::FramelessWindowHint标志(Qt3.0以上的版本)
QWidget widget(NULL, Qt::FramelessWindowHint);
或
widget.setWindowFlags(Qt::FramelessWindowHint);
3. 无父窗口,加标志Qt::ToolTip标志,方法同2
4. 无父窗口,加标志Qt::SplashScreen
对于有父窗口,通过加上Qt::Window能不能变成有边框窗口,答案是不行的,那要加边框怎么办,只有自定义了。
接下来我们来看第二个问题,怎么限定窗口的大小?
有时候因为设计的需要,我们往往会限定窗口的大小,QT提供了如下限定窗口尺寸的方法:
/*限定窗口的宽度和高度*/
void setFixedSize(int w, int h)
/*限定窗口的宽度*/
void setFixedWidth(int w)
/*限定窗口的高度*/
void setFixedHeight(int h)
/*限定窗口的最大尺寸,窗口的范围(0, 0)到(w,h)*/
void setMaximumSize (const QSize & size)
void setMaximumSize (int maxw, int maxh)
/*限定窗口的最大高度,高度范围[0,maxh]*/
void setMaximumHeight(int maxh)
/*限定窗口的最大宽度,宽度范围[0, maxw]*/
void setMaximumWidth(int maxw)
/*限定窗口的最小尺寸,窗口的范围>=(w, h)*/
void setMinimumSize (const QSize & size)
void setMinimumSize (int maxw, int maxh)
/*限定窗口的最小高度,高度>=maxh*/
void setMinimumHeight(int maxh)
/*限定窗口的最小宽度,宽度>=maxw*/
void setMinimumWidth(int maxw)