这个程序里面我们用到了需要自己编写类来实现一个控件了。
而且这个类还被独立为单独的文件。
不多说先,直接贴代码。
- #include<QApplication>
- #include"analogclock.h"
- /* 包含头文件 */
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- /* 定义一个类的实例 */
- AnalogClock clock;
- /* 显示这个实例 */
- clock.show();
- return app.exec();
- }
- #ifndef ANALOGCLOCK_H
- #define ANALOGCLOCK_H
- #include<QWidget>
- /* 创建了一个类,用公有的派生类型,从QWidget派生来的 */
- class AnalogClock:public QWidget
- {
- Q_OBJECT
- public:
- /* 构造函数 */
- AnalogClock(QWidget *parent = 0);
- protected:
- /* 号称是覆盖掉了原来Widget中的这个函数,重新实现了功能 */
- void paintEvent(QPaintEvent *event);
- };
- #endif // ANALOGCLOCK_H
- #include<QtGui>
- #include "analogclock.h"
- /* 构造函数的实现,当创建新的对象的时候就会调用构造函数 */
- AnalogClock::AnalogClock(QWidget *parent):QWidget(parent)
- {
- /* 建造一个定时器 */
- QTimer *timer = new QTimer(this);
- /* 把定时器的超时中断连接到这个类的更新动作上 */
- connect(timer, SIGNAL(timeout()),this,SLOT(update()));
- /* 设置定时器的属性 */
- timer->start(1000);
- setWindowTitle(tr("Analog Clock"));
- resize(200, 200);
- }
- /* paintEvent事件实现函数,触发条件为需要重绘的时候,
- 比如从被遮挡到需要显示,或者被调用update的时候 */
- void AnalogClock::paintEvent(QPaintEvent *)
- {
- /* 时针和分针的点坐标 */
- static const QPoint hourHand[3] = {
- QPoint(7, 8),
- QPoint(-7, 8),
- QPoint(0, -40)
- };
- static const QPoint minuteHand[3] = {
- QPoint(7, 8),
- QPoint(-7, 8),
- QPoint(0, -70)
- };
- /* 设置时针和分针的颜色 */
- QColor hourColor(127, 0, 127);
- QColor minuteColor(0, 127, 127, 191);
- /* QT自带函数,返回两个数的最小值 */
- int side = qMin(width(), height());
- /* 获取当前时间 */
- QTime time = QTime::currentTime();
- /*
- 在this这个可绘画设备上创建一个绘画者
- 设置渲染,用反锯齿效果
- 用一个重载函数转换一下坐标系
- 缩放一下这个坐标系
- */
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.translate(width()/2, height()/2);
- painter.scale(side/200.0, side/200.0);
- /* 用一个重载函数设置一下画笔的类型,NoPen就是没有线
- 再用一个重载函数设置一下笔刷的颜色,用默认类型
- */
- painter.setPen(Qt::NoPen);
- painter.setBrush(hourColor);
- painter.save(); //保存当前的绘画者状态
- //用给出的角度旋转坐标系
- painter.rotate(30.0*((time.hour()+time.minute()/60.0)));
- //用线把点连起来
- painter.drawConvexPolygon(hourHand, 3);
- //恢复当前的绘画者状态
- painter.restore();
- painter.setPen(hourColor);
- /* 画代表小时的线 */
- for (int i = 0; i < 12; ++i) {
- painter.drawLine(88, 0, 96, 0);
- painter.rotate(30.0);
- }
- //下面画分钟的过程是上面的重复,只是分针的颜色用到了透明度
- painter.setPen(Qt::NoPen);
- painter.setBrush(minuteColor);
- painter.save();
- painter.rotate(6.0*(time.minute()+time.second()/60.0));
- painter.drawConvexPolygon(minuteHand, 3);
- painter.restore();
- painter.setPen(minuteColor);
- /* 画代表分钟的线 */
- for (int j = 0; j < 60; ++j) {
- if((j % 5) != 0) {
- painter.drawLine(92, 0, 96,0);
- }
- painter.rotate(6.0);
- }
- }
模拟时钟的程序中用到了6个我们新接触的东西:
1. QPaintEvent 绘画事件
2. QTimer 定时器
3. QPoint 点
4. QColor 颜色
5. QTime 时间
6. QPainter 绘画者
下面我们针对每一个分别作出粗浅的理解解释分析……
QPaintEvent
QPaintEvent是个类,继承于QEvent,包含了绘画的事件。
当控件本身更新的时候,绘画事件会发送到控件,对于实例来讲,这种事情一般是遮住他的控件移动了,导致其需要重绘。
事件包含了一个region()需要更新,还有一一个rect(),rect是这个区域的边框。这两个都是需要提供的,因为许多控件不能使用region,这样的话rect就比rgion().boudingRect()更快了。
下面提到了自动剪切的事情
在一个paint event过程中,绘画被剪切到region()中。Qt的绘画系统执行剪切,并且独立于任何一个可能应用于QPainter用来在绘画设备上进行绘画的剪切。
结果就是由QPainter::clipRegion()返回给新构建的QPainter的值不会影响到绘画系统使用的剪切区域
QTimer
QTimer是个类,提供了重复的和一次性的定时器
QTimer类为定时器提供了一个高层编程接口。使用它的时候,创建一个QTimer,连接它的timeout()信号到适当的槽上,然后调用start(),然后它就会按照不变的间隔区触发timerout()信号。模拟时钟上的例子:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
然后update()槽每秒被触发一次。
我们也可以设置定时器让它只被触发一次,调用setSingleShot(true).也可以使用静态的QTimer::SingleShot()函数来调用一个槽,在指定一个间隔之后:
QTimer::singleShot(200, this, SLOT(updateCaption()));
在多线程程序中,你可以使用QTimer在任一个具有事件循环的线程中。为了在非GUI的线程中开始一个事件循环,可以使用QThread::exec(),QT使用定时器的线程亲和力(thread affinity)来检测哪个线程需要触发timeout()信号。因为如此,你必须在一个线程里开始,结束定时器;它不能在另外的线程中开始一个定时器。
作为一个特殊的例子,一个QTimer用一个值为0的timeout将会马上time out,在窗口系统时间队列中被处理的所有事件。这可以用来做很繁重的工作,当提供一个很短暂的用户界面的时候:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timerout()), this, SLOT(processOneThing()));
timer->start();
processOneThing()将会从此以后被重复的调用。它应给能够快速的返回,以便Qt可以处理分配事件给控件并且当它完成工作的时候马上停掉定时器。这是在GUI应用程序中实现大量工作的一种传统解决方式;多线程现在许多平台上都使用了,我们期望零毫秒QTimer逐步的能被QThread替代掉。
下面的帮助文档还讲到了精度以及它的替代函数,这里就不慢慢翻译了。
QPoint
QPoint是一个类,定义了一个点,用整数。
一个点指定了x坐标和y坐标,可以通过x(),y()函数来访问。isNull()函数返回真值,当x,y都为0的时候。坐标可以通过set(),setY()函数设置,或者rx(),ry()函数将会返回坐标系的引用。(允许直接修改)
给出一个点p,下面的语句是等价的
QPoint p;
p.setX(p.x() + 1);
p += QPoint(1,0)
p.rx()++;
一个QPoint对象也可以被用作一个向量:加减都定义为向量运算。也可以被int或qreal进行乘除。
另外,QPoint类提供了manhattanLength()函数,这个曼哈顿函数返回两数的平方和的平方根。
最后QPoint对象可以像流一样的被比较。
QColor
QColor是一个类,提供颜色,基于RGB,HSV或者CMYK的值。
颜色系统就不解释了,东西太复杂了,QColor构造函数是基于RGB的,要转换成其他的颜色制式,用toHsv()或者toCmyk()函数。
QTime
QTime是一个类,提供时钟时间。
QTime对象包含一个时钟,比如时分秒毫秒等。它可以从当前i同事中读取当前时间,测量一段时间。它还提供了比较时间的函数,通过添加毫秒更改时间的函数。
QTime使用24小时制的时间格式,没有上午下午的概念。不想QDateTime,QTime不知道时区或者DST时间。DST是daylight savings time 就是夏时制,改作息时间用的。
QTime对象一般通过给出的时分秒毫秒来创建,或者使用静态函数currentTime(),创建一个QTime对象,包含了系统本地时间。注意,精度取决于系统,不是所有的系统都提供1毫秒的精度。
hour(),minute(),second()和msec()函数用来访问时间的时分秒毫秒。同样的信息由toString()函数提供文本格式。
QTime提供了全部的操作来比较两个QTime对象。一个早一些的时间被认为是小的。
addSecs(),addMSecs()函数可以使用。于此对应的两个时间之间可以用secsTo(),mescsTo()。
QTime可以用来测量时间间隔,使用start(),restart(),elapsed()函数。
QPainter
QPainter是一个类,提供了底层的在控件或者其他可绘制设备上的绘画能力。
QPainter提供了高性能函数来为大多数的GUI绘图函数使用。它可以画所有的东西,从简单的线条到复杂的形状,它还可以话对齐的文字和图像。通常它在自然坐标系中绘画,但是它也可以改变视角。QPainter可以操作任何继承于QPainterDevice类的对象。
通常QPainter用在一个控件的绘画事件里:构造并且定制绘画者。然后绘画。画完后清除绘画者对象。比如:
void SimpleExampleWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
}
绘画者的核心功能是绘画,但是这个类也提供了一些函数用来允许你定制绘画者的设置和它的渲染质量,还有使能剪切。另外你可以控制许多不同的形状合并到一起,通过指定绘画者的组成模式。
isActive()函数标明绘画者是不是活跃的。一个绘画者被激活通过begin()函数和带有QPaintDevice参数的构造函数。end()函数和析构函数停用这个绘画者。
与QPaintDevice类和QPaintEngine类一起,QPainter来自于Qt的绘画系统的基础。QPainter是用来执行绘画操作的类。QPaintDevice表现了一个可以用QPainter绘画的设备。QPaintEngine提供了绘画者在不同类型设备上绘画的接口。如果绘画者是活跃的,device()返回绘画者所在的绘画设备,paintEngine()返回当前绘画者操作的绘画引擎。更多的信息去参考帮助手册的Paint System。
有时候我们需要在一些非QPaintDevice上进行绘画,QPainter提供了一个静态函数setRedirected()来做这件事情。
警告:当绘画设备是一个控件的时候,QPaint仅能被用在paintEvent()内部,或者由paintEvent()函数调用的函数中。设置Qt::WA_PaintOutsidePaintEvent不起作用。在Mac或者Windows上,你只能在paintEvent()中绘画,不管这个属性如何设置。
这个也是个很复杂的功能,下面的具体介绍太长了,用到的时候再查吧。