Menu Home

使用QGraphicsProxyWidget实现字幕滚动和翻转动画

OS : ubuntu 12.04
QT : 4.8

multiTxtWidget.h

#ifndef MULTITEX_WIDGET_H
#define MULTITEX_WIDGET_H

#include <QGraphicsItem>
#include <QPainter>
#include <QPropertyAnimation>
#include <QGraphicsWidget>
#include <QGraphicsProxyWidget>
#include <QWidget>
#include <qcoreevent.h>

class MulitTexWidget : public QWidget {
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE setText)

public:
    explicit MulitTexWidget(QWidget *parent = 0);
    virtual ~MulitTexWidget();

    void setText(const QString &newText);
    QString text();
    QSize sizeHint() const;

protected:
    void paintEvent(QPaintEvent *event);
    void timerEvent(QTimerEvent *event);
    void showEvent(QShowEvent *event);
    void hideEvent(QHideEvent *event);

private:
    QString myText;
    int offset;
    int myTimerId;
};

class GraphicsWidget : public QGraphicsProxyWidget
{
public:
    GraphicsWidget() : QGraphicsProxyWidget(0) {}
protected:
    virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    virtual void resizeEvent(QGraphicsSceneResizeEvent *event);
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

class TwoSidedGraphicsWidget : public QObject
{
    Q_OBJECT
public:
    TwoSidedGraphicsWidget(QGraphicsScene *scene);
    void setWidget(int index, QWidget *widget);
    QWidget *widget(int index);
public slots:
    void flip();
protected slots:
    void animateFlip();
private:
    GraphicsWidget *m_proxyWidgets[2];
    int m_current;
    int m_angle;
    int m_delta;
};

#endif // MULTITEX_WIDGET_H

multiTxtWidget.cpp

#include "multiTex_widget.h"
#include <QGraphicsOpacityEffect>
#include <QGraphicsScene>
#include <QTimer>

MulitTexWidget::MulitTexWidget(QWidget *parent) :
    QWidget(parent)
{
    offset = 0;
    myTimerId = 0;
    setWindowOpacity(0.5);
}

MulitTexWidget::~MulitTexWidget() {

}

QString MulitTexWidget::text() {
    return myText;
}

void MulitTexWidget::setText(const QString &newText) {
    myText = newText;
    update();
    updateGeometry();
}

QSize MulitTexWidget::sizeHint() const {
    return fontMetrics().size(0, text());
}

void MulitTexWidget::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
 // painter.setFont(QFont("Helvetica", 20));
    painter.setPen(QPen(Qt::blue, 1));

    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;
    }
}

void MulitTexWidget::showEvent(QShowEvent *event) {
    myTimerId = startTimer(50);
}

void MulitTexWidget::timerEvent(QTimerEvent *event) {
    if(event->timerId() == myTimerId) {
        offset++;
        if(offset >= fontMetrics().width(text())) {
            offset = 0;
        }
        scroll(-1, 0);
    }  else {
        QWidget::timerEvent(event);
    }
}

void MulitTexWidget::hideEvent(QHideEvent *event) {
    killTimer(myTimerId);
    myTimerId = 0;
}

QVariant GraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemPositionChange && scene()) {
        QRectF rect = boundingRect();
        QPointF pos = value.toPointF();
        QRectF sceneRect = scene()->sceneRect();
        if (pos.x() + rect.left() < sceneRect.left())
            pos.setX(sceneRect.left() - rect.left());
        else if (pos.x() + rect.right() >= sceneRect.right())
            pos.setX(sceneRect.right() - rect.right());
        if (pos.y() + rect.top() < sceneRect.top())
            pos.setY(sceneRect.top() - rect.top());
        else if (pos.y() + rect.bottom() >= sceneRect.bottom())
            pos.setY(sceneRect.bottom() - rect.bottom());
        return pos;
    }
    return QGraphicsProxyWidget::itemChange(change, value);
}

void GraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    setCacheMode(QGraphicsItem::NoCache);
    setCacheMode(QGraphicsItem::ItemCoordinateCache);
    QGraphicsProxyWidget::resizeEvent(event);
}

void GraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setRenderHint(QPainter::Antialiasing, false);
    QGraphicsProxyWidget::paint(painter, option, widget);
}

TwoSidedGraphicsWidget::TwoSidedGraphicsWidget(QGraphicsScene *scene)
    : QObject(scene)
    , m_current(0)
    , m_angle(0)
    , m_delta(0)
{
    for (int i = 0; i < 2; ++i)
        m_proxyWidgets[i] = 0;
}

void TwoSidedGraphicsWidget::setWidget(int index, QWidget *widget)
{
    if (index < 0 || index >= 2)
    {
        qWarning("TwoSidedGraphicsWidget::setWidget: Index out of bounds, index == %d", index);
        return;
    }

    GraphicsWidget *proxy = new GraphicsWidget;
    proxy->setWidget(widget);

    if (m_proxyWidgets[index])
        delete m_proxyWidgets[index];
    m_proxyWidgets[index] = proxy;

    proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache);
    proxy->setZValue(1e30);

    if (index != m_current)
        proxy->setVisible(false);

    qobject_cast<QGraphicsScene *>(parent())->addItem(proxy);
}

QWidget *TwoSidedGraphicsWidget::widget(int index)
{
    if (index < 0 || index >= 2)
    {
        qWarning("TwoSidedGraphicsWidget::widget: Index out of bounds, index == %d", index);
        return 0;
    }
    return m_proxyWidgets[index]->widget();
}

void TwoSidedGraphicsWidget::flip()
{
    m_delta = (m_current == 0 ? 9 : -9);
    animateFlip();
}

void TwoSidedGraphicsWidget::animateFlip()
{
    m_angle += m_delta;
    if (m_angle == 90) {
        int old = m_current;
        m_current ^= 1;
        m_proxyWidgets[old]->setVisible(false);
        m_proxyWidgets[m_current]->setVisible(true);
        m_proxyWidgets[m_current]->setGeometry(m_proxyWidgets[old]->geometry());
    }

    QRectF r = m_proxyWidgets[m_current]->boundingRect();
    m_proxyWidgets[m_current]->setTransform(QTransform()
        .translate(r.width() / 2, r.height() / 2)
        .rotate(m_angle - 180 * m_current, Qt::XAxis)
        .translate(-r.width() / 2, -r.height() / 2));

    if ((m_current == 0 && m_angle > 0) || (m_current == 1 && m_angle < 180))
        QTimer::singleShot(25, this, SLOT(animateFlip()));
}

main.cpp

#include "multiTex_widget.h"
#include <QApplication>
#include <QTextCodec>
#include <QGLWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    QTextCodec::setCodecForTr(codec);

    QGraphicsScene scene;

    MulitTexWidget *mtw1 = new MulitTexWidget;
    mtw1->setText("       I am your friend      ");
    mtw1->setWindowFlags(Qt::FramelessWindowHint);
    mtw1->setGeometry(0, 0, 800, 40);

    MulitTexWidget *mtw2 = new MulitTexWidget;
    mtw2->setText("    yes, you are my friend    ");
    mtw2->setWindowFlags(Qt::FramelessWindowHint);
    mtw2->setGeometry(0, 0, 800, 40);

    TwoSidedGraphicsWidget *twoSided = new TwoSidedGraphicsWidget(&scene);
    twoSided->setWidget(0, mtw1);
    twoSided->setWidget(1, mtw2);

    QTimer *tm = new QTimer;
    tm->setInterval(2000);

    QObject::connect(tm, SIGNAL(timeout()), twoSided, SLOT(flip()));

    QtGraphicsView view(&scene);   
    view.setFrameShape(QFrame::NoFrame);

    view.show();
    tm->start();

    return a.exec();
}

PS:此方法效果不错,但是效率较低,cpu占用高,主要瓶颈在滚动字幕的重绘上。

Categories: C/C++ QT

Tagged as:

lnmcc