模拟时钟
“模拟时钟”示例演示了如何绘制自定义控件的内容。

“模拟时钟”示例的屏幕截图
本示例还演示了如何利用QPainter 的变换和缩放功能,从而更轻松地绘制自定义小部件。
AnalogClock 类定义
AnalogClock 类提供了一个带有时针、分针和秒针的时钟控件,该控件每秒自动更新一次。我们继承QWidget 类,并重写标准的paintEvent()函数来绘制表盘:
class AnalogClock : public QWidget
{
Q_OBJECT
public:
AnalogClock(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
};AnalogClock 类的实现
在构建控件时,我们会设置一个一秒计时器来跟踪当前时间,并将其连接到标准的update()槽,以便当计时器发出timeout()信号时,时钟表盘随之更新。最后,我们会调整控件的大小,使其以合理尺寸显示。
AnalogClock::AnalogClock(QWidget *parent)
: QWidget(parent)
{
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);
setWindowTitle(tr("Analog Clock"));
resize(200, 200);
}每当小部件的内容需要更新时,都会调用paintEvent() 函数。这会在小部件首次显示时发生,也会在它被遮盖后再次显露时发生,但当调用小部件的update()槽时,该函数也会被执行。由于我们将计时器的timeout()信号连接到了该槽上,因此该函数每秒至少会被调用一次。
在设置绘图器并绘制时钟之前,我们首先定义三个QPoint列表和三个QColor,它们将分别用于时针、分针和秒针。 我们使用palette()函数获取与窗口其余部分协调的合适颜色,无论是在浅色模式还是深色模式下。时针和分针使用前景色绘制,秒针则使用强调色绘制。
我们还会确定控件最短边的长度,以便将钟面适配到控件内。在开始绘制之前确定当前时间也很有帮助。
void AnalogClock::paintEvent(QPaintEvent *)
{
static const QPoint hourHand[4] = {
QPoint(5, 14),
QPoint(-5, 14),
QPoint(-4, -71),
QPoint(4, -71)
};
static const QPoint minuteHand[4] = {
QPoint(4, 14),
QPoint(-4, 14),
QPoint(-3, -89),
QPoint(3, -89)
};
static const QPoint secondsHand[4] = {
QPoint(1, 14),
QPoint(-1, 14),
QPoint(-1, -89),
QPoint(1, -89)
};
const QColor hourColor(palette().color(QPalette::Text));
const QColor minuteColor(palette().color(QPalette::Text));
const QColor secondsColor(palette().color(QPalette::Accent));
int side = qMin(width(), height());自定义控件的内容使用 `QPainter` 进行绘制。绘图器(Painter)可用于在任何 `QPaintDevice` 上绘图,但通常与控件配合使用,因此我们将控件实例传递给绘图器的构造函数。
我们通过传入 `QPainter::Antialiasing ` 调用 `QPainter::setRenderHint()` 来启用抗锯齿功能。这使得对角线的绘制效果更加平滑。
painter.setRenderHint(QPainter::Antialiasing);平移操作将原点移至控件中心,缩放操作则确保后续绘制操作经过缩放后能完全容纳在控件内。我们采用的缩放因子使 x 和 y 坐标范围限定在 -100 到 100 之间,并确保这些坐标位于控件最短边的长度范围内。
painter.translate(width() / 2, height() / 2);
painter.scale(side / 200.0, side / 200.0);为了简化代码,我们将绘制一个固定大小的时钟表盘,并通过定位和缩放使其位于控件的中心。
绘制器负责处理绘制事件期间的所有变换,并确保所有内容都正确绘制出来。让绘制器处理变换通常比为了绘制自定义小部件的内容而进行手动计算要简单得多。

我们将画笔设置为Qt::NoPen ,因为我们不需要任何轮廓,并使用一种颜色适合显示小时的实心画笔。画笔用于填充多边形和其他几何形状。
painter.setPen(Qt::NoPen);
painter.setBrush(hourColor);我们首先绘制时针,使用一个公式将坐标系逆时针旋转一定角度,该角度由当前的时和分决定。这意味着时针将以所需的角度顺时针旋转显示。 我们通过实例化QPainterStateGuard 来保存并恢复旋转前后的变换矩阵,因为我们希望在放置分针时无需考虑之前的任何旋转。
{
QPainterStateGuard guard(&painter);
painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
painter.drawConvexPolygon(hourHand, 4);
}我们为每个小时在时钟边缘绘制标记,其颜色与时针相同。绘制每个标记后,我们会旋转坐标系,以便绘图器能为下一个标记做好准备。
for (int i = 0; i < 12; ++i) {
painter.drawRect(73, -3, 16, 6);
painter.rotate(30.0);
}分针的旋转和绘制方式与时针类似。
painter.setBrush(minuteColor);
{
QPainterStateGuard guard(&painter);
painter.rotate(6.0 * time.minute());
painter.drawConvexPolygon(minuteHand, 4);
}对于秒针,我们采用相同的方法,并添加两个圆圈作为视觉亮点。
painter.setBrush(secondsColor);
{
QPainterStateGuard guard(&painter);
painter.rotate(6.0 * time.second());
painter.drawConvexPolygon(secondsHand, 4);
painter.drawEllipse(-3, -3, 6, 6);
painter.drawEllipse(-5, -68, 10, 10);
}最后,我们在时钟边缘绘制标记,以指示分钟和秒数。这次我们将它们绘制为线条,因此将画笔颜色设置为相应的颜色。
painter.setPen(minuteColor);
for (int j = 0; j < 60; ++j) {
painter.drawLine(92, 0, 96, 0);
painter.rotate(6.0);
}© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.