qstyle 一步一个jio印(4)—— 整理框架 阅读理解

13 篇文章 0 订阅
9 篇文章 0 订阅

最后了!

是这样,大佬们查看了框架,然后自己简化了一套出来,融合进了代码。

而我,跟随这这套框架继续拧螺丝,开始压根不用管框架做了什么,直接调用即可。

现在慢慢理解框架,看看框架处理了什么东西吧。

当然,在运行完整的demo和运行自己随便添加效果的demo相比,差距还是很大的。

那么,其实可以理解成有框架和没框架的差距,就能加深理解框架究竟在干嘛。

暴露问题

从效果上看,没了框架,动画资源的管理很不方便。就在上一节中,我们只有一个动画资源,绑定的只有一个target即一个button,那时设置是每次点击都把上一个资源给释放掉,重新分配给当前点击的控件。这样问题就很大了呀:

  1. 当点完一个按钮立刻点另一个按钮,上一个按钮的动画还没结束就delete掉了?
  2. 当连续点击一个按钮,资源也是释放得太快甚至都没开始?
  3. 绘制元素函数是不区分button的统一绘制入口,分不清楚当前播放动画的究竟是哪一个button?

在这里插入图片描述

从图中看出问题所在,先直面效果中出现的问题,在代码中解释如下:

  1. 点击一个按钮后,移动鼠标到另一个按钮2,显然2虽然没点击但动画资源有数据,由于数据没有绑定按钮2,所以按钮2只有一刻的圆;
  2. 点击按钮1速度再点按钮2,按钮1的波纹立刻没了;
  3. 连续点击按钮1,效果很差懒得描述;

问题解决

框架很复杂,但主要包含两个方面:

  1. 让控件和动画资源绑定,建立起联系,让每个控件都有独立的动画资源;
  2. 允许每个控件拥有多个动画资源,同时需要管理多个动画资源;

这样,当我们连续点击同一个按钮,产生了多个按钮1的动画资源,动画1在跑同时动画2也在跑,互不影响。或者连续切换按钮点,按钮1的动画和按钮2的动画独立的跑也互不影响了。

开始吧阅读理解

代码实在太多太杂。只能按照步骤一个一个来,去除一些不太关键的资源释放的步骤吧。

引擎管理

这边我理解是数据的触发,就像一个引擎:

Animations::Animations( QObject* parent ): QObject( parent )
{
    registerEngine( _widgetStateEngine = new WidgetStateEngine( this ) );
}
Animations::~Animations(){

}
WidgetStateEngine& Animations::widgetStateEngine( void ) const{
    return *_widgetStateEngine;
}

void Animations::registerWidget( QWidget* widget ) const
{
    if( !widget ) return;

    if( qobject_cast<QPushButton *>(widget) ) {
        _widgetStateEngine->registerWidget( widget);
    }

    return;
}

这里就是新建了一个引擎,让引擎注册了button控件,接下来就是引擎在干嘛?

绑定控件
bool WidgetStateEngine::registerWidget(QWidget* widget)
{
    if( !widget ) return false;
    if(!m_data.contains( widget ) ) { m_data.insert( widget, new WidgetStateData( this, widget)); }
    connect( widget, SIGNAL(destroyed(QObject*)), this, SLOT(unregisterWidget(QObject*)), Qt::UniqueConnection );
    return true;
}

m_data是一个封装了一些函数的qmap数据结构,以输入的widget为键,数据类为键值。

那么,这个数据类WidgetStateData又在干嘛呢?

设置触发条件,设置动画参数
WidgetStateData::WidgetStateData(QObject* parent, QWidget* target):
    GenericData(parent, target){
    target->installEventFilter(this);
}
WidgetStateData::~WidgetStateData(){

}
bool WidgetStateData::eventFilter(QObject *object, QEvent *event){
    if(target() != object)
        return GenericData::eventFilter( object, event );

    if (object->inherits("QPushButton")){
        switch( event->type() )
        {
        case QEvent::MouseButtonPress:
            startRipple(static_cast<QMouseEvent *>(event), target());
            break;

        default: break;
        }
        return GenericData::eventFilter( object, event );
    }

}

void WidgetStateData::startRipple(QMouseEvent *event, QWidget *target){
    QPoint pos = target->rect().center();
    qreal radiusEndValue = static_cast<qreal>(target->width());

    MaterialRipple *ripple = new MaterialRipple(pos, target);

    ripple->setOpacityStartValue(0.35);
    ripple->setOpacityEndValue(0);

    ripple->setRadiusStartValue(0);
    ripple->setRadiusEndValue(radiusEndValue);

    ripple->radiusAnimation()->setDuration(1600);
    ripple->opacityAnimation()->setDuration(1300);

    addRipple(ripple);
}

这里为注册的控件重载了事件过滤,在这里设置触发,至于这个动画类MaterialRipple上一节已经理解过了。

而这个数据类的父类GenericData就是曾经提到的管理,它的作用主要是管理多个动画资源:

void GenericData::addRipple(MaterialRipple *ripple){

    m_animations.push_back(ripple);
    ripple->start();

    connect(ripple, SIGNAL(finished()), this, SLOT(removeRipple()));
    connect(this, SIGNAL(destroyed(QObject*)), ripple, SLOT(stop()));
    connect(this, SIGNAL(destroyed(QObject*)), ripple, SLOT(deleteLater()));
}

void GenericData::removeRipple(){
    MaterialRipple *ripple = qobject_cast<MaterialRipple *>(sender());
    if(m_animations.removeOne(ripple)){
        ripple->deleteLater();
    }
}

void GenericData::removeAllRipple(){
    foreach(MaterialRipple *ripple, m_animations){
        m_animations.removeOne(ripple);
        ripple->deleteLater();
    }
}

QWidget * GenericData::target() const{
    return m_target;
}

QList<RippleData> GenericData::ripples(){
    QList<RippleData> result;
    qCritical() << m_animations.count();
    foreach(MaterialRipple *ripple, m_animations){
        RippleData data;
        data.radius = ripple->radius();
        data.opacity = ripple->opacity();
        result.append(data);
    }
    return result;
}

m_animations保存着多个动画资源,同时也可控制删除,数据可通过ripples访问;

调用与回顾

在mystyle中新建animation类,在polish(QWidget *widget)中注册widget:_animation->registerWidget(widget);

在绘制button元素的最后加上数据即可:

QList<RippleData> rippleDatas = _animation->widgetStateEngine().ripples(widget);
if (enabled){
    foreach (RippleData data, rippleDatas) {
        QPointF center = rect.center();
        painter->setOpacity(data.opacity);
        QBrush brush;
        brush.setColor(palette.color(QPalette::Highlight));
        brush.setStyle(Qt::SolidPattern);
        painter->setBrush(brush);
        painter->drawEllipse(center, data.radius, data.radius);
    }

}

回顾一下,在这个注册的时候,所有按钮该绑定的已经绑定好了,触发条件该设置的已经设置好了。

在调用_animation->widgetStateEngine().ripples(widget)的时候,就是根据widget为键在qmap数据结构中寻找键值,键值是数据类,它的ripple()即是GenericData类保存的数据了。

这时抛开代码,简化过程:

绑定

建立引擎 -> 引擎建立qmap数据结构,键为widget键值为WidgetStateData类 -> WidgetStateData类设置鼠标点击事件要产生动画类MaterialRipple -> MaterialRipple类生成一堆数据 -> MaterialRipple类的数据以及自己在GenericData管理。

触发

点击按钮 -> 去引擎查找当前widget为键的键值 -> 访问到对应WidgetStateData类 -> 访问到WidgetStateData类的父类GenericData类保存的数据

落幕

最终的效果,显示出来的就应该是丝滑的感觉。

在这里插入图片描述

Okay,以下是一个基本的QT框架中视频播放器的示例代码,你可以根据你的需求进行修改。 #include <QApplication> #include <QMediaPlayer> #include <QVideoWidget> #include <QMediaPlaylist> #include <QPushButton> #include <QSlider> #include <QHBoxLayout> #include <QVBoxLayout> #include <QStyle> #include <QFile> #include <QFileDialog> int main(int argc, char *argv[]) { QApplication app(argc, argv); //创建悬浮窗口 QVideoWidget *videoWidget = new QVideoWidget(); QMediaPlayer *player = new QMediaPlayer(); //设置窗口为播放器的输出 player->setVideoOutput(videoWidget); //创建媒体播放列表 QMediaPlaylist *playlist = new QMediaPlaylist(); playlist->addMedia(QUrl::fromLocalFile("video1.mp4")); playlist->addMedia(QUrl::fromLocalFile("video2.mp4")); playlist->addMedia(QUrl::fromLocalFile("video3.mp4")); playlist->setCurrentIndex(0); //设置自动播放模式 player->setPlaylist(playlist); player->play(); //创建控制台 QPushButton *playButton = new QPushButton(); playButton->setIcon(app.style()->standardIcon(QStyle::SP_MediaPlay)); QHBoxLayout *playLayout = new QHBoxLayout; playLayout->addWidget(playButton); QSlider *slider = new QSlider(); slider->setOrientation(Qt::Horizontal); QHBoxLayout *sliderLayout = new QHBoxLayout; sliderLayout->addWidget(slider); QPushButton *fullscreenButton = new QPushButton(); fullscreenButton->setIcon(app.style()->standardIcon(QStyle::SP_ArrowFullScreen)); QHBoxLayout *fullscreenLayout = new QHBoxLayout; fullscreenLayout->addWidget(fullscreenButton); QVBoxLayout *controlLayout = new QVBoxLayout; controlLayout->addLayout(playLayout); controlLayout->addLayout(sliderLayout); controlLayout->addLayout(fullscreenLayout); //进度条更新 QObject::connect(player, &QMediaPlayer::durationChanged, slider, &QSlider::setMaximum); QObject::connect(player, &QMediaPlayer::positionChanged, slider, &QSlider::setValue); //播放按钮事件 QObject::connect(playButton, &QPushButton::clicked, [player, playButton]() { if (player->state() == QMediaPlayer::PlayingState) { player->pause(); playButton->setIcon( qApp->style()->standardIcon(QStyle::SP_MediaPlay)); } else { player->play(); playButton->setIcon( qApp->style()->standardIcon(QStyle::SP_MediaPause)); } }); //全屏按钮事件 videoWidget->setFullScreen(false); QObject::connect(fullscreenButton, &QPushButton::clicked, [videoWidget]() { if (!videoWidget->isFullScreen()) { videoWidget->setFullScreen(true); } else { videoWidget->setFullScreen(false); } }); //打开本地视频 QPushButton *openButton = new QPushButton(); openButton->setText("打开本地视频"); QHBoxLayout *openLayout = new QHBoxLayout; openLayout->addWidget(openButton); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(videoWidget); mainLayout->addLayout(controlLayout); mainLayout->addLayout(openLayout); QWidget *widget = new QWidget(); widget->setLayout(mainLayout); widget->show(); QObject::connect(openButton, &QPushButton::clicked, [player, videoWidget]() { QString fileName = QFileDialog::getOpenFileName(nullptr, QStringLiteral("选择文件"), "", QStringLiteral("所有文件(*)")); if (fileName.isEmpty()) { return; } QFile videoFile(fileName); if (videoFile.exists()) { player->setMedia(QUrl::fromLocalFile(fileName)); videoWidget->show(); } }); return app.exec(); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值