1. 程式人生 > >qt 完全實現qq翻轉登入介面

qt 完全實現qq翻轉登入介面

如何用qt實現qq登入介面翻轉效果,這個問題已經糾結我很久了。在一段時間後,再次重新回到這個問題,終於解決了                                                                                                          

QWidget是甚於2維座標的,所以要實現像qq那樣的翻轉效果,必須要能夠繞y軸在3維空間旋轉。看了一些網上教程參考部落格

用到的是QGraphics框架,所以也就用它做了。


注意看的話,上面好像有點bug;不要懷疑,那是因為GIF播放結束,重新播放時的重置效果。程式碼如下

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QTimer>
#include <QDebug>
#include <QTimeLine>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //新建登入窗體控制元件
    QWidget *wid1 = new QWidget;
    QLabel *user = new QLabel(QStringLiteral("使用者名稱:"));
    QLabel *password = new QLabel(QStringLiteral("密  碼:"));
    QLineEdit *userEdit = new QLineEdit;
    userEdit->setPlaceholderText(QStringLiteral("請輸入使用者名稱"));
    QLineEdit *passwordEdit = new QLineEdit;
    passwordEdit->setPlaceholderText(QStringLiteral("請輸入密碼"));
    //輸入窗體佈局
    QHBoxLayout *usrHBoxLay = new QHBoxLayout;
    usrHBoxLay->addWidget(user);
    usrHBoxLay->addWidget(userEdit);
    QHBoxLayout *passwordHBoxLay = new QHBoxLayout;
    passwordHBoxLay->addWidget(password);
    passwordHBoxLay->addWidget(passwordEdit);
    QVBoxLayout *vBoxLay1 = new QVBoxLayout;
    vBoxLay1->addLayout(usrHBoxLay);
    vBoxLay1->addLayout(passwordHBoxLay);
    wid1->setLayout(vBoxLay1);
    //新建設定窗體控制元件
    QWidget *wid2 = new QWidget;
    QLabel *ip = new QLabel(QStringLiteral("IP地址"));
    QLabel *port = new QLabel(QStringLiteral("埠號"));
    QLineEdit *ipEdit = new QLineEdit;
    ipEdit->setPlaceholderText(QStringLiteral("請輸入IP地址"));
    QLineEdit *portEdit = new QLineEdit;
    portEdit->setPlaceholderText(QStringLiteral("請輸入埠號"));
    //設定窗體佈局
    QHBoxLayout *ipHBoxLay = new QHBoxLayout;
    ipHBoxLay->addWidget(ip);
    ipHBoxLay->addWidget(ipEdit);
    QHBoxLayout *portHBoxLay = new QHBoxLayout;
    portHBoxLay->addWidget(port);
    portHBoxLay->addWidget(portEdit);
    QVBoxLayout *vBoxLay2 = new QVBoxLayout;
    vBoxLay2->addLayout(ipHBoxLay);
    vBoxLay2->addLayout(portHBoxLay);
    wid2->setLayout(vBoxLay2);
    //新建View和Scene
    QGraphicsView *view = new QGraphicsView;
    QGraphicsScene *scene = new QGraphicsScene;
    view->setScene(scene);
    //加入登入和設定窗體
    QGraphicsProxyWidget *graphicsWid1 = scene->addWidget(wid1);
    QGraphicsProxyWidget *graphicsWid2 = scene->addWidget(wid2);
    //設定窗體旋轉-180度,並隱藏
    graphicsWid2->setTransform(QTransform().translate(graphicsWid2->boundingRect().width()/2,graphicsWid2->boundingRect().height()/2)
                               .rotate(-180,Qt::YAxis)
                               .translate(-graphicsWid2->boundingRect().width()/2,-graphicsWid2->boundingRect().height()/2));
    graphicsWid2->setVisible(false);
    //新建線型值鬧鐘
    QTimeLine *timeLine = new QTimeLine(1500,scene);
    timeLine->setStartFrame(0);
    timeLine->setEndFrame(180);
    //每次值發生變化,旋轉一定角度
    QObject::connect(timeLine,&QTimeLine::frameChanged,[timeLine,graphicsWid1,graphicsWid2](const int frame){
        //對於登入窗體正常旋轉
        graphicsWid1->setTransform(QTransform().translate(graphicsWid1->boundingRect().width()/2,graphicsWid1->boundingRect().height()/2)
                                   .rotate(frame,Qt::YAxis)
                                   .translate(-graphicsWid1->boundingRect().width()/2,-graphicsWid1->boundingRect().height()/2));
        //對於設定窗體由於開始旋轉到了-180度,所以現在應該是-179,-176,-170....,0,這裡應該是frame-180;也就是加上起點為-180度啦
        graphicsWid2->setTransform(QTransform().translate(graphicsWid2->boundingRect().width()/2,graphicsWid2->boundingRect().height()/2)
                                   .rotate(frame - 180,Qt::YAxis)
                                   .translate(-graphicsWid2->boundingRect().width()/2,-graphicsWid2->boundingRect().height()/2));

        switch (timeLine->direction()) {
        case QTimeLine::Forward: //前面向後面轉,frame從0到180,當frame大於或等於90度時,顯示設定窗體,登入窗體隱藏
            if(frame > 90 || frame == 90){
                graphicsWid1->setVisible(false);
                graphicsWid2->setVisible(true);
            }
            break;
        case QTimeLine::Backward: //後面向前面轉,frame從180到0,當frame小於或等於90度時,顯示登入窗體,設定窗體隱藏
            if(frame < 90 || frame == 90){
                graphicsWid1->setVisible(true);
                graphicsWid2->setVisible(false);
            }
            break;
        }
    });
    //旋轉結束後,設定線形鬧鐘值的方向
    QObject::connect(timeLine,&QTimeLine::finished,[timeLine]{
    	timeLine->toggleDirection();
    });
    //啟用鬧鐘,每過一段時間,啟動一次線型鬧鐘
    QTimer *timer = new QTimer;
    timer->setInterval(3000);
    QObject::connect(timer,&QTimer::timeout,[timeLine]{timeLine->start();});

    view->show();
    timer->start();

    return a.exec();
}

QTimeLine 是一個線形值定時器

它可以附帶一個值的變化,對應的訊號是valueChanged和frameChanged。valueChanged是0-1之間,frameChanged是startFrame()和endFrame()之間,對應值
是從小到大方向,還是從大到小方向;這個取決於direction()屬性值。

setTransform是一個設定旋轉的函式,函式執行結束直接旋轉到指定位置

在QGraphicsScene中的圖元或者QWidget通過QGraphicsScene通過addWidget返回的QGraphicsProxyWidget都可以通過該函式旋轉,並且可以指定旋轉軸。
在開始窗體的角度為0度,初始化後背面的窗體變為-180度;在鬧鐘驅動旋轉時,此時背面窗體要與前面窗體一起旋轉,而且角度應該是-180度到0度,線形鬧鐘反向,然後又是0度到-180度這點很關鍵。而前面的窗體開始是0度,旋轉一次後變180,線型鬧鐘反向,再轉一次又回到0就不需要額外操作。

第一次做就是沒有注意到這裡,所以沒有解決,這次終於搞定。另外就是根據旋轉方向設定其中一個隱藏,一個顯示以90度為界限,這個大家都懂吧。

其它方法

也就是在今晚發麵這篇部落格的同時,我突然搜到了一個解決這個問題的另一種方案,為什麼在今晚寫部落格之前,從未找到過,真草蛋。
方法類似,主要還是我對類還不夠全面的熟悉呀。原來QPainter也有setTransform方法,這樣就可以直接在QWidget的paint函式中直接旋轉了就用不著QGraphics框架了
。同時也謝謝搜到的那篇博主。