实现思路
绘制滚动文本是个很简单的事,只需要用定时器累计一个文本坐标偏移量,然后 update ,在 paintEvent 里把文本画出来。
Qt 定时刷新可以用(可能还有其他方式):
- QObject的startTimer配合paintEvent;
- QBasicTimer配合paintEvent;(源码中很多都用的这个类,我也就用的这个类了)
- QTimer;
- QTimeLine;
- QAnimation;(如果除了滚动还有颜色等动画,建议用动画组实现)
paintEvent 里 QPainter 的设置默认是被样式表影响的,所以我在样式表里设置的颜色和字体。
获取文本高度的时候还有个坑,就是 Qt 文本的 height() 包含基线往上和往下两部分,但是不一定代表字体的真实信息:
我绘制的时候减去了基线底部距离 descent 才显示正常了,不然就没上下居中对齐(直接用 ascent 感觉有点偏),也可以用 Qt5.8 引入的 capHeight:
/*const int text_height = painter.fontMetrics().capHeight();
const int text_y = (height()+text_height) / 2;*/
const int text_height = painter.fontMetrics().height();
const int text_y = (height()+text_height) / 2-painter.fontMetrics().descent();
实现基本功能之后也可以进行扩展,如颜色变换,在某一位置暂留,多条文本滚动等。
最终实现
实现效果:
代码链接:
github链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/ScrollLabel
主要代码:
#ifndef SCROLLLABEL_H
#define SCROLLLABEL_H
#include <QLabel>
#include <QBasicTimer>
//滚动文字的label
class ScrollLabel : public QLabel
{
Q_OBJECT
public:
enum ScrollDirection{ //滚动方向
RightToLeft=1,
LeftToRight=2
};
public:
explicit ScrollLabel(QWidget *parent = nullptr);
//滚动方向
ScrollLabel::ScrollDirection getDirection() const;
void setDirection(ScrollLabel::ScrollDirection direction);
//刷新间隔
int getInterval() const;
void setInterval(int interval);
protected:
//basictimer定时器触发
void timerEvent(QTimerEvent *event) override;
//绘制
void paintEvent(QPaintEvent *event) override;
//大小变化时重新计算
void resizeEvent(QResizeEvent *event) override;
private:
//滚动定时器
//也可以使用QTimer QTimeLine QAnimation等实现
QBasicTimer scrollTimer;
int interval=20;
//偏移量
int offset=0;
int textWidth=0;
int labelWidth=0;
//默认右往左
ScrollDirection direction=RightToLeft;
};
#endif // SCROLLLABEL_H
#include "ScrollLabel.h"
#include <QTimerEvent>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QPainter>
#include <QDebug>
ScrollLabel::ScrollLabel(QWidget *parent)
: QLabel(parent)
{
//启动定时器,触发this的timerevent
scrollTimer.start(interval,this);
}
ScrollLabel::ScrollDirection ScrollLabel::getDirection() const
{
return direction;
}
void ScrollLabel::setDirection(ScrollLabel::ScrollDirection direction)
{
if(this->direction!=direction){
this->direction=direction;
offset=0;
}
}
int ScrollLabel::getInterval() const
{
return interval;
}
void ScrollLabel::setInterval(int interval)
{
if(this->interval!=interval){
this->interval=interval;
scrollTimer.start(interval,this);
}
}
void ScrollLabel::timerEvent(QTimerEvent *event)
{
//定时器timeout
if(event->timerId()==scrollTimer.timerId()){
event->accept();
++offset;
if(offset>textWidth+labelWidth){
offset=0;
}
update();
}else{
QLabel::timerEvent(event);
}
}
void ScrollLabel::paintEvent(QPaintEvent *event)
{
event->accept();
QPainter painter(this);
const int text_width = painter.fontMetrics().width(text());
//字体绘制坐标为左下角,y值就是 labelheight-(labelheight-textheight)/2
//因为取的字体高度还受基线影响,height=descent+ascent,这里去掉descent
//也可以用Qt5.8提供的capHeight
/*const int text_height = painter.fontMetrics().capHeight();
const int text_y = (height()+text_height) / 2;*/
const int text_height = painter.fontMetrics().height();
const int text_y = (height()+text_height) / 2-painter.fontMetrics().descent();
if (textWidth != text_width && text_width > 0) {
textWidth = text_width;
offset = 0;
}else {
if(direction==RightToLeft){
//从右往左
painter.drawText(labelWidth - offset, text_y, text());
}else{
//从左往右
painter.drawText(offset - textWidth, text_y, text());
}
}
}
void ScrollLabel::resizeEvent(QResizeEvent *event)
{
const int old_width = event->oldSize().width();
const int new_width = event->size().width();
if (new_width > 10) {
labelWidth = new_width;
//新宽度更小,就重置偏移
if (new_width < old_width) {
offset = 0;
}
}
QLabel::resizeEvent(event);
}