1、效果展示
我们经常能够在外面看到那种滚动字幕,那么就拿qt来做一个吧。
2、实现思路
实现一个窗口部件,这个窗口部件显示了一串文本标语,它会每t毫秒向左移动一个像素。如果窗口部件比文本宽,那么文本将会被多次重复,直到能够填满整个窗口部件的宽度为止。
3、滚动窗口部件
创建一个滚动窗口类,将其命名为ticker。
3.1、成员变量
我们需要提供几个成员变量。
-
myText用来表示要显示的文本内容。
-
offset表示当前偏移量。
-
myTimerId表示定时器的ID编号。
QString myText;
int offset;
int myTimerId;
3.2、事件重写
需要重新实现了Ticker中的4个事件处理器,分别为paintEvent()、timerEvent()、showEvent()和 hideEvent();关于每个事件的职责后面再说。
virtual void paintEvent(QPaintEvent* event) override; // 绘制事件
virtual void timerEvent(QTimerEvent* event) override; // 定时器事件
virtual void showEvent(QShowEvent* event) override; // 显示事件
virtual void hideEvent(QHideEvent* event) override; // 隐藏事件
3.3、成员方法
还需要提供几个成员方法。关于每个方法的职责后面再说。
void setText(const QString& newText);
QString text() const { return myText; }
QSize sizeHint() const;
3.4、方法实现
1.构造函数
构造函数把 offset变量初始化为0。用来绘制文本的x坐标值就取自于这个offset 值。
定时器的ID通常是非零的,所以可以使用0来表示定时器还没有启动。
Ticker::Ticker(QWidget *parent)
: QWidget{parent}
{
offset = 0;
myTimerId = 0;
}
2.setText函数
setText()函数用来设置要显示的文本。它调用update()强制执行一个重绘操作,并且调用updateGeometry()通知对Ticker窗口部件负责的布局管理器,提示该窗口部件的大小发生了变化。
void Ticker::setText(const QString &newText)
{
myText = newText;
update();
updateGeometry();
}
3.sizeHint函数
sizeHint()函数返回文本所需的空间大小,并以此作为窗口部件的理想尺寸。QWidget::fontMetrics()函数返回一个QFontMetrics对象;可以用这个对象查询并获得与这个窗口部件字体相关的信息。
QSize Ticker::sizeHint() const
{
return fontMetrics().size(0, text());
}
4.paintEvent事件
paintEvent()函数使用QPainter::drawText()绘制文本。它使用fontMetrics()确定文本在水平方向上所需要的空间,并且在考虑offset值的同时,多次绘制文本,直到能够填充整个窗口部件的宽度为止。
void Ticker::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
int textWidth = fontMetrics().width(text());
if(textWidth < 1)
{
return;
}
int x = -offset;
while(x < width())
{
painter.drawText(x, 0, textWidth, height(), Qt::AlignLeft | Qt::AlignVCenter, text());
x += textWidth;
}
}
5.timerEvent定时器事件
系统每隔一定时间,都会调用一次timerEvent()函数。
通过在offset上加1来模拟移动,从而形成文本宽度的连续滚动。然后,它使用QWidget::scroll()把窗口部件的内容向左滚动一个像素。
如果这个定时器事件不是我们所关注的那个定时器,就可以把它传递给基类。
这里也可以调用update()代替scrol(),但使用scroll()会更有效率,因为它只是简单地移动屏幕上已经存在的像素并且只对这个窗口部件的新显示区域(此时,只是一个1像素乘以宽度的像素条)产生一个绘制事件。
void Ticker::timerEvent(QTimerEvent *event)
{
if(event->timerId() == myTimerId)
{
++offset;
if(offset >= fontMetrics().width(text()))
{
offset = 0;
}
scroll(-1, 0);
}
else
{
QWidget::timerEvent(event);
}
}
6.showEvent显示事件
showEvent()函数用来启动个定时器。QObject::startTimer()调用会返回一个ID数字,用这个数字识别该定时器。QObject支持多个独立的定时器,每一个都可以有自己的时间间隔。
在startTimer()调用之后,大约每30毫秒Qt都会产生一个定时器事件。至于具体的时间精度,则取决于所在的操作系统。
我们也可以在Ticker的构造函数中完成startTimer()的调用,但是只有在窗口部件实际可见的时候,才有必要保存由Qt产生的定时器事件的那些资源。让资源合理利用。
void Ticker::showEvent(QShowEvent *event)
{
Q_UNUSED(event);
myTimerId = startTimer(30);
}
7.hideEvent隐藏事件
hideEvent()函数调用QObject::killTimer()来停止该定时器。
void Ticker::hideEvent(QHideEvent *event)
{
killTimer(myTimerId);
myTimerId = 0;
}
定时器事件是一种低级事件,而且如果需要多个定时器时,保持对所有定时器ID的跟踪将会变得很麻烦。
在这种情况下,通常更为简单的方式是为每一个定时器分别创建一个QTimer对象。QTimer会在每个时间间隔发射timeout()信号。当然QTimer也提供了一个非常方便的接口,可用于单触发定时器(只触发一次的定时器)QTimer::singleShot(t, this, &Ticker::onTimer)。