#ifndef RUNNINGTEXTWIDGET_H
#define RUNNINGTEXTWIDGET_H#include <QWidget>enum Direction
{North = 0, //上South, //下West, //左East //右
};class RunningTextWidget : public QWidget
{Q_OBJECT
public:explicit RunningTextWidget(QWidget *parent = nullptr);void setText(const QString & newText);void setDirection(Direction newDirection);void setSpeed(uint newSpeed);protected:void paintEvent(QPaintEvent *event) override final;private:void onTime();std::shared_ptr<struct RunningTextWidgetPrivate> d_ptr;
};#endif // RUNNINGTEXTWIDGET_H
#include "runningtextwidget.h"
#include <QPainter>
#include <QTimer>struct RunningTextWidgetPrivate
{QString text{"飞流直下三千尺,疑是银河落九天"};QString drawText;QColor textColor{Qt::red};int offset{0};uint changeOffset{6};//调节变化速度RunningTextWidget * p_ptr;QSize verticalTextSize;Direction direction{Direction::South};RunningTextWidget * q_ptr;QTimer timer;void updateText();void updateVerticalTextBoundingSize();void move();
};void RunningTextWidgetPrivate::updateText()
{if(direction == Direction::West || direction == Direction::East){if(direction == Direction::East){drawText.clear();;for (auto it = text.rbegin(); it != text.rend(); ++it){drawText.append(*it);}}else{drawText = text;}}else{if(direction == Direction::South){auto temp = text.split("", Qt::SkipEmptyParts).join("\n");drawText.clear();for (auto it = temp.rbegin(); it != temp.rend(); ++it){drawText.append(*it);}}else{drawText = text.split("", Qt::SkipEmptyParts).join("\n");}updateVerticalTextBoundingSize();}
}void RunningTextWidgetPrivate::updateVerticalTextBoundingSize()
{auto lines = drawText.split("\n");int maxWidth = 0;int totalHeight = 0;auto fontMetrics = q_ptr->fontMetrics();for (const auto &line : lines){int width = fontMetrics.horizontalAdvance(line);if (width > maxWidth){maxWidth = width;}totalHeight += fontMetrics.height();totalHeight += fontMetrics.leading();}totalHeight -= fontMetrics.descent();verticalTextSize = QSize(maxWidth, totalHeight);
}void RunningTextWidgetPrivate::move()
{if(direction == Direction::West){offset -= changeOffset;if(offset < 0 && abs(offset) >= q_ptr->fontMetrics().horizontalAdvance(drawText)){offset = q_ptr->width();}}else if(direction == Direction::East){offset += changeOffset;if(offset > q_ptr->width()){offset = -q_ptr->fontMetrics().horizontalAdvance(drawText);}}else if(direction == Direction::North){offset -= changeOffset;if(offset < 0 && abs(offset) >= verticalTextSize.height()){offset = q_ptr->height();}}else if(direction == Direction::South){offset += changeOffset;if(offset > q_ptr->height()){offset = -verticalTextSize.height();}}
}RunningTextWidget::RunningTextWidget(QWidget *parent): QWidget{parent}
{d_ptr = std::make_shared<RunningTextWidgetPrivate>();d_ptr->q_ptr = this;connect(&d_ptr->timer,&QTimer::timeout,this,&RunningTextWidget::onTime);d_ptr->timer.start(40);auto font = this->font();font.setPixelSize(36);font.setBold(true);setFont(font);d_ptr->updateText();
}void RunningTextWidget::setText(const QString &newText)
{d_ptr->text = newText;d_ptr->offset = 0;d_ptr->updateText();update();
}void RunningTextWidget::setDirection(Direction newDirection)
{d_ptr->direction = newDirection;d_ptr->updateText();
}void RunningTextWidget::setSpeed(uint newSpeed)
{d_ptr->changeOffset = newSpeed;
}void RunningTextWidget::paintEvent(QPaintEvent *event)
{QPainter painter(this);painter.setFont(this->font());painter.setPen(d_ptr->textColor);auto bRect = painter.boundingRect(rect(),Qt::AlignCenter,d_ptr->drawText);if(d_ptr->direction == Direction::West || d_ptr->direction == Direction::East){painter.drawText(QRect(d_ptr->offset,bRect.y(),bRect.width(),bRect.height()),d_ptr->drawText);}else{painter.drawText(QRect(bRect.x(),d_ptr->offset,bRect.width(),bRect.height()),Qt::AlignCenter,d_ptr->drawText);}
}void RunningTextWidget::onTime()
{d_ptr->move();update();
}
#include "runningtextwidget.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QRandomGenerator>int main(int argc, char *argv[])
{QApplication a(argc, argv);QWidget widget;auto hb = new QHBoxLayout(&widget);for(int i = 0;i < 4;++i){auto w = new RunningTextWidget;w->setDirection(static_cast<Direction>(i));w->setSpeed(QRandomGenerator::global()->bounded(8, 50));hb->addWidget(w);}widget.show();return a.exec();
}