实现思路
绘制滚动文本是个很简单的事,只需要用定时器累计一个文本坐标偏移量,然后 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的timereventscrollTimer.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)
{//定时器timeoutif(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);
}