一、使用QPainter绘图
http://blog.csdn.net/hellozex/article/details/78361311
二、坐标系统变换
在QPainter的默认坐标系中,点(0,0)位于坐标绘制设备的左上角,x坐标向右,y坐标向下,类似图像处理中的图像像素排列,默认每个像素占用1*1大小的区域。
理论上,像素的中心取决于版像素坐标哦。例如,窗口部件的左上角像素覆盖了点(0,0)到点(1,1)的区域,它的中心在(0.5,0.5)位置。如果告诉QPainter绘制一个像素,例如(100,100),它会相应地在两个方向做+0.5的偏移,使得像素点的中心位置在(100.5,100.5)。这一差别初看起来理论性很强但是在实践中却是非常重要。首先,只有当反走样无效时才偏移+0.5;如果反走样有效,并且我们试图在(100,100)绘制一个黑色像素,实际上QPainter会在(99.5,99.5)、(99.5,100.5)、(100.5,99.5)、(100.5,100.5)四个像素着浅灰色,给人的印象就是正好位于四个点的交点处,如果不需要这种效果,可以通过指定半像素坐标或者通过便宜QPainter(+0.5,+0.5)来避免这种效果的出现。
这种窗口-视口机制对于编写独立于绘制设备大小和分辨率的绘制代码很有用的。例如,如果想让逻辑坐标从(-50,-50)到(+50,+50),并且(0,0)在中间,可以这样设置窗口:
painter.setWindow(-50, -50, 100, 100);
前面参数指定原点,后面是宽和高;这就意味着逻辑坐标(-50,-50)对应物理坐标(0,0),而逻辑坐标(+50,+50)对应物理坐标(320,200),如图:
世界变换实在窗口-视口转换之外使用的变换矩阵。它允许使用移动、缩放、旋转或者拉伸绘制的项。例如,想要45°角绘制文本,他可以使用这段代码:
QTransform tramsform;
transform.rotate(+45.0);
painter.setWorldTransform(transform);
painter.drawText(pos, tr("Sales"));
坐标变换的更为简单的方式是使用QPainter的translate()\scale()\rotate()和shear()这些简便函数。
下面来做一个微波炉旋钮:
头文件:
#ifndef OVENTIMER_H
#define OVENTIMER_H
#include <QDateTime>
#include <QWidget>
#include <QTimer>
class OvenTimer : public QWidget
{
Q_OBJECT
public:
OvenTimer(QWidget *parent = 0);
void setDuration(int secs);
int duration() const;
void draw(QPainter *painter);
signals:
void timeout();
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
private:
QDateTime finishTime;
QTimer *updateTimer;
QTimer *finishTimer;
};
#endif
重新实现QWidget的
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
并且设置几个参数,控制烤箱定时器外观:
const double DegreesPerMinute = 7.0;
const double DegreesPerSecond = DegreesPerMinute / 60;
const int MaxMinutes = 45;
const int MaxSeconds = MaxMinutes * 60;
const int UpdateInterval = 5;
oventimer.cpp
#include <QtGui>
#include <cmath>
#ifndef M_PI
#define M_PI 3.14159265359
#endif
#include "oventimer.h"
const double DegreesPerMinute = 7.0;
const double DegreesPerSecond = DegreesPerMinute / 60;
const int MaxMinutes = 45;
const int MaxSeconds = MaxMinutes * 60;
const int UpdateInterval = 5;
OvenTimer::OvenTimer(QWidget *parent)
: QWidget(parent)
{
finishTime = QDateTime::currentDateTime();
updateTimer = new QTimer(this);
connect(updateTimer, SIGNAL(timeout()), this, SLOT(update()));
finishTimer = new QTimer(this);
finishTimer->setSingleShot(true);
connect(finishTimer, SIGNAL(timeout()), this, SIGNAL(timeout()));
connect(finishTimer, SIGNAL(timeout()), updateTimer, SLOT(stop()));
QFont font;
font.setPointSize(8);
setFont(font);
}
void OvenTimer::setDuration(int secs)
{
secs = qBound(0, secs, MaxSeconds);
finishTime = QDateTime::currentDateTime().addSecs(secs);
if (secs > 0) {
updateTimer->start(UpdateInterval * 1000);
finishTimer->start(secs * 1000);
} else {
updateTimer->stop();
finishTimer->stop();
}
update();
}
int OvenTimer::duration() const
{
int secs = QDateTime::currentDateTime().secsTo(finishTime);
if (secs < 0)
secs = 0;
return secs;
}
void OvenTimer::mousePressEvent(QMouseEvent *event)
{
QPointF point = event->pos() - rect().center();
double theta = std::atan2(-point.x(), -point.y()) * 180.0 / M_PI;
setDuration(duration() + int(theta / DegreesPerSecond));
update();
}
void OvenTimer::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
int side = qMin(width(), height());
painter.setViewport((width() - side) / 2, (height() - side) / 2,
side, side);
painter.setWindow(-50, -50, 100, 100);
draw(&painter);
}
void OvenTimer::draw(QPainter *painter)
{
static const int triangle[3][2] = {
{ -2, -49 }, { +2, -49 }, { 0, -47 }
};
QPen thickPen(palette().foreground(), 1.5);
QPen thinPen(palette().foreground(), 0.5);
QColor niceBlue(150, 150, 200);
painter->setPen(thinPen);
painter->setBrush(palette().foreground());
painter->drawPolygon(QPolygon(3, &triangle[0][0]));
QConicalGradient coneGradient(0, 0, -90.0);
coneGradient.setColorAt(0.0, Qt::darkGray);
coneGradient.setColorAt(0.2, niceBlue);
coneGradient.setColorAt(0.5, Qt::white);
coneGradient.setColorAt(1.0, Qt::darkGray);
painter->setBrush(coneGradient);
painter->drawEllipse(-46, -46, 92, 92);
QRadialGradient haloGradient(0, 0, 20, 0, 0);
haloGradient.setColorAt(0.0, Qt::lightGray);
haloGradient.setColorAt(0.8, Qt::darkGray);
haloGradient.setColorAt(0.9, Qt::white);
haloGradient.setColorAt(1.0, Qt::black);
painter->setPen(Qt::NoPen);
painter->setBrush(haloGradient);
painter->drawEllipse(-20, -20, 40, 40);
QLinearGradient knobGradient(-7, -25, 7, -25);
knobGradient.setColorAt(0.0, Qt::black);
knobGradient.setColorAt(0.2, niceBlue);
knobGradient.setColorAt(0.3, Qt::lightGray);
knobGradient.setColorAt(0.8, Qt::white);
knobGradient.setColorAt(1.0, Qt::black);
painter->rotate(duration() * DegreesPerSecond);
painter->setBrush(knobGradient);
painter->setPen(thinPen);
painter->drawRoundRect(-7, -25, 14, 50, 99, 49);
for (int i = 0; i <= MaxMinutes; ++i) {
if (i % 5 == 0) {
painter->setPen(thickPen);
painter->drawLine(0, -41, 0, -44);
painter->drawText(-15, -41, 30, 30,
Qt::AlignHCenter | Qt::AlignTop,
QString::number(i));
} else {
painter->setPen(thinPen);
painter->drawLine(0, -42, 0, -44);
}
painter->rotate(-DegreesPerMinute);
}
}
在构造函数中,创建两个QTime对象:updateTimer用来每隔5秒刷新窗口部件的外观,finishTimer在定时器达到0发射timeout()信号。因为只执行一次所以调用setSingleShot(true)。默认情况下,定时器会重复触发直到停止或者销毁。最后一个connect()调用是一个优化,目的是当定时器停止运行时,停止更新窗口部件。
最后也有设置字体大小,以保证显示大小字体一致。
void OvenTimer::setDuration(int secs)
设置定时器持续的时间为给定的秒数。
int OvenTimer::duration() const
返回定时器完成前剩余的秒数,如果定时器未激活,返回0.