zoukankan      html  css  js  c++  java
  • 组合使用QT的资源管理高级功能简化开发过程

    使用 QT 进行团队开发的时候,常常碰到一个问题,就是如何共同管理资源?甚至一个人进行开发的时候如何简化资源的维护,避免无谓的消耗?

    如果可以做到在开发的时候,大家把美工做的图片(往往是程序员先自己随便做一个然后等美工来替换)放到一个目录中,在程序中直接进行引用,等到发布的时候把这些图片(或者XML、声音等其他资源文件)进行打包,只发布一个二进制资源文件,并且程序中引用资源的地方不需要进行任何修改能有多好?

    为了完成这个目标,首先想到的是仿照 Orge 实现一个 ZIP 包文件引擎,经过查找 QT 的源代码,发现可以使用QT 内部的 File Engine 系统,子类化 QAbstractFileEngine,并实现一个 QAbstractFileEngineHandler  的继承类,自动将自定义的ZipFileEngine 注册到 QT 的文件系统中。

    经过一番资料的搜素与尝试,发现这个任务不是能够轻松完成的,遂开始思考把资源外部化的其他方法,看到了下列的两篇参考文章:

    http://qt-project.org/doc/qt-5/resources.html

    http://qt-project.org/doc/qt-5/qdir.html#setSearchPaths

    在第一篇文章中提到资源外部化的方法,就是使用 rcc 工具对 qrc 资源描述文件进行二进制的编译,然后在程序中调用 QResource::registerResource 静态函数注册资源,请注意该函数的第二个参数:该资源可以挂载到资源树的特定节点路径上。

    而在第二篇文章中提到可以使用 QDir::setSearchPaths 设定特定前缀的搜索路径,这样QFile 打开使用了特定前缀的文件时,首先根据设定的搜索路径列表确定文件位置,然后再打开,更妙的是:它的搜索路径可以指定文件系统路径也可以指定资源路径,设想一下启动画面使用 image:splash.png 这个文件,在开发是把image 的搜索路径设置为文件系统目录,那么只要设定的文件系统目录中存在 splash.png ,就可以进行调试了,资源会正确的加载,在发布时,把 image 的搜索路径设定为资源的 prefix,那么一切就简单了。

    如果把上述的信息填写到配置文件中,程序启动时根据配置文件自动完成上述设定搜索路径的过程,那么程序就可以做到对资源存储路径的无关性;

    在程序中加载资源的时候只需要这样写:

    QPixmap pixmap("image:splash.png");

    QIcon icon( "image:usergroup.png");

    QFile file("data:database-init.sql");

    至于最后 image 中的资源采用文件还是打包在资源中,和程序代码完全无关,一切尽在配置中。

    好了,开发配合(程序员之间以及和美工之间)的问题基本上解决了,还有最后一个问题,开发的时候是存放在目录中,发布时如何打包这些资源呢?

    最朴素的思想就是在发布时手工编辑一个 qrc 文件,使用 rcc –binary 命令编译这个资源文件,但是一个项目,很多开发人员放置资源,最终这些资源会很多,编写这个文件也需要耗时,而且发布的过程是迭代的,这个工程耗时并且容易出错,继续寻找解决办法…

    如果可以自动扫描资源目录生成一个 qrc 文件就好了,经过多次尝试改进后发现rcc 工具提供了这个功能,rcc --project 参数可以扫描当前目录,生成一个 qrc 文件,该文件会包含当前目录以及子目录中的所有文件,但是该文件中没有资源前缀,没有关系,我们可以将不包含前缀的qrc 编译为二进制资源文件,在调用 registerResource函数注册资源的时候补充前缀就可以了,当前这个也可以使用配置的方式,例如使用 image=res:images.rcc 的配置项,那么就把 images.rcc 文件注册到 /image 资源路径上;

    当然还有一个方法,就是在使用 rcc 编译资源的时候自动增加一个前缀,这使用 --root 参数就可以了,下面是我整理的批处理脚本以供参考

    @ cd images

    @ rcc --project -o allres.qrc

    @ if exist ..images.resx del /F ..images.resx

    @ rcc --binary -o ..images.resx --root "/image" allres.qrc

    @ del /f allres.qrc

    下面附上 debug 版和 release 版的配置文件样例

    Debug 版

    [Application]

    LocaleCodec=GB18030

    Language=zh_CN

    Theme=Dark

     

    [Resource]

     

     

    [Plugins]

    Path=$(AppDir)/QT5

     

    [Search]

    skin=$(AppDir)/res

    lang=$(AppDir)/res

    res=$(AppDir)/res

    image=$(AppDir)/res/images

     

    [Splash]

    Image=image:splash.png

     

    Release 版

    [Application]

    LocaleCodec=GB18030

    Language=zh_CN

    Theme=Dark

     

    [Resource]

    @=res:SLM.resx

     

    [Plugins]

    Path=$(AppDir)/QT5

     

    [Search]

    skin=$(AppDir)/res

    lang=$(AppDir)/res

    res=$(AppDir)/res

    image=:/image

     

    [Splash]

    Image=image:splash.png

     

    下面是加载应用配置文件的程序

    AppHelper.h

    #ifndef APPHELPER_H

    #define APPHELPER_H

     

    void initializeApplication( void );

     

    QStringList & dirExpand( QStringList & pathList );

    QString & dirExpand( QString & path );

    QString dirExpand( const char * path );

     

    void regResource( const QString & Path , const QString & Root );

     

    void addTranslator( const QString & Name );

    void setSkinStyles( const QString & Name );

     

    // http://qt-project.org/doc/qt-5/richtext-html-subset.html

    void splashShow( const QString & text , const QColor & color = Qt::black );

    void splashFini( QWidget & mainWidget );

    void splashInit( void );

    void splashHide( void );

     

    #endif // APPHELPER_H

    AppHelper.cpp

    #include "AppHelper.h"

     

    // http://qt-project.org/doc/qt-5/qdir.html#setSearchPaths

    // http://qt-project.org/doc/qt-5/resources.html

     

    struct QSettingGroup

    {

    QSettingGroup( QSettings & config , const QString & group ) : settings( config )

    {

    settings.beginGroup( group );

    }

     

    template< typename T > T Value( const QString & Key , const QVariant & Def = QVariant( ) )

    {

    return settings.value( Key , Def ).value< T >( );

    }

     

    ~QSettingGroup( ) { settings.endGroup( ); }

     

    QSettings & settings;

    };

     

    struct QSplashArgs

    {

    explicit QSplashArgs( ) : splashScreen( NULL ){ }

     

    QSplashScreen * splashScreen ;

    QString splashImageFile ;

    };

     

    static QSplashArgs & splashArgs( )

    {

    static QSplashArgs splashArgs;

    return splashArgs ;

    }

     

    QString & dirExpand( QString & path )

    {

    static QString appDir , binary , config ;

    if( appDir.isEmpty( ) || appDir.isNull( ) )

    {

    binary = QCoreApplication::applicationDirPath( );

    appDir = QDir( binary + "/.." ).canonicalPath( );

    config = appDir + "/etc" ;

    }

     

    path.replace( "$(AppDir)" , appDir );

    path.replace( "$(Binary)" , binary );

    path.replace( "$(Config)" , config );

     

    return path;

    }

     

    QString dirExpand( const char * path ){ return dirExpand( QString( path ) ); }

     

    QStringList & dirExpand( QStringList & pathList )

    {

    QStringList::iterator itr = pathList.begin( );

    for( ; itr != pathList.end( ) ; itr ++ )

    {

    dirExpand( * itr );

    }

     

    return pathList ;

    }

     

    static void initResourceSearch( QSettings & settings )

    {

    QSettingGroup config( settings , "Search" );

     

    QStringList resDirs = settings.childKeys( );

    foreach( const QString & resDir , resDirs )

    {

    QStringList resPaths = config.Value< QStringList >( resDir );

    QDir::setSearchPaths( resDir , dirExpand( resPaths ) );

    }

    }

     

    static void initResourceFiles( QSettings & settings )

    {

    QSettingGroup config( settings , "Resource" );

     

    QStringList resPaths = settings.childKeys( );

    foreach( const QString & resRoot , resPaths )

    {

    QString resFile = config.Value< QString >( resRoot );

    regResource( resFile , resRoot );

    }

    }

     

    static void initPluginsPaths( QSettings & settings )

    {

    QSettingGroup config( settings , "Plugins" );

     

    QStringList dirLibraries = config.Value< QStringList >( "Path" );

    qApp->setLibraryPaths( dirExpand( dirLibraries ) + qApp->libraryPaths( ) );

    }

     

    static void initAppSettings( QSettings & settings )

    {

    QSettingGroup config( settings , "Application" );

     

    // initialize resource file

    QString resFile = QCoreApplication::applicationFilePath( );

    resFile.replace( ".exe" , ".rcc" , Qt::CaseInsensitive );

    QResource::registerResource( resFile );

     

    // initialize Locale Codec

    QByteArray codec = config.Value< QByteArray >( "LocaleCodec" , "GB18030" );

    QTextCodec::setCodecForLocale( QTextCodec::codecForName( codec ) );

     

    // install language translator

    addTranslator( config.Value< QString >( "Language" , "zh_CN" ) );

     

    // initialize skin styles

    setSkinStyles( config.Value< QString >( "Theme" , "Default" ) );

    }

     

    static QString getString( QStringList & list , int index , const QString & def = QString( ) )

    {

    return list.size( ) > index ? list[ index ] : def ;

    }

     

    static void initSplashArgs( QSettings & settings )

    {

    QSettingGroup config( settings , "Splash" );

    QSplashArgs & Args = splashArgs( );

     

    Args.splashImageFile = config.Value< QString >( "Image" , "Splash.png" );

    }

     

    void initializeApplication( void )

    {

    QString appFile = QCoreApplication::applicationFilePath( );

    appFile.replace( ".exe" , ".ini" , Qt::CaseInsensitive );

    QDir::setCurrent( dirExpand( QString( "$(AppDir)" ) ) );

    QSettings config( appFile , QSettings::IniFormat );

    QDir::setCurrent( dirExpand( "$(AppDir)" ) );

     

    initResourceSearch( config );

    initResourceFiles( config );

    initPluginsPaths( config );

    initAppSettings( config );

    initSplashArgs( config );

    }

     

    void addTranslator( const QString & Name )

    {

    QTranslator * translator = new QTranslator( 0 );

    translator->load( QString( "lang:" ) + Name + ".lang" );

    qApp->installTranslator( translator );

    }

     

    void setSkinStyles( const QString & Name )

    {

    QFile skinFile( QString( "skin:" ) + Name + ".skin" );

    skinFile.open( QFile::ReadOnly | QFile::Unbuffered );

    qApp->setStyleSheet( skinFile.readAll( ) );

    skinFile.close( );

    }

     

    void regResource( const QString & Name , const QString & Root )

    {

    QString name( Name ), root( Root );

    if( root[ 0 ] == ':' ){ root[ 0 ] = '/' ; }

    if( root[ 0 ] == '$' ){ root[ 0 ] = '/' ; }

    if( root[ 0 ] == '@' ){ root[ 0 ] = '/' ; }

    if( root[ 0 ] != '/' ){ root.insert( 0 , '/' ); }

    QResource::registerResource( dirExpand( name ) , root );

    }

     

    struct QSplash : public QSplashScreen

    {

    void closeEvent( QCloseEvent * event );

     

    explicit QSplash( );

    };

     

    void QSplash::closeEvent( QCloseEvent * event )

    {

    QSplashScreen::closeEvent( event );

    splashArgs( ).splashScreen = NULL;

    }

     

    QSplash::QSplash( ) : QSplashScreen( QPixmap( splashArgs( ).splashImageFile ) )

    {

     

    }

     

    void splashInit( void )

    {

    if( splashArgs( ).splashScreen != NULL ) return;

     

    if( QSplashScreen * splash = new QSplash( ) )

    {

    splashArgs( ).splashScreen = splash;

    splash->show( );

    }

    }

     

    void splashShow( const QString & text , const QColor & color )

    {

    if( QSplashScreen * splash = splashArgs( ).splashScreen )

    {

    splash->showMessage( text , Qt::AlignCenter , color );

    qApp->processEvents( );

    }

    }

     

    void splashFini( QWidget & mainWidget )

    {

    if( QSplashScreen * splash = splashArgs( ).splashScreen )

    {

    splash->finish( & mainWidget );

    }

    }

     

    void splashHide( void )

    {

    if( QSplashScreen * splash = splashArgs( ).splashScreen )

    {

    splashArgs( ).splashScreen = NULL ;

    splash->deleteLater( );

    splash->close( );

    }

    }

    Main.cpp 中的样例代码

    #include "AppFrame.h"

    #include "AppHelper.h"

     

    int main(int argc, char *argv[])

    {

    QApplication app(argc, argv);

    initializeApplication( );

     

    splashInit( );

     

    QAppFrame appFrame;

    appFrame.show( );

     

    splashFini( appFrame );

     

    return app.exec( );

    }

  • 相关阅读:
    block 相关清单
    在Objective-C 中使用字符生成NSArray、NSDictionary、NSNumber
    NSURLSession 相关清单
    iOS 相关博客清单
    sqlite 一条记录判断一个字段是否like另一个字段
    iphone程序适配ipad可以用下面的宏进行尺寸改写
    NSURLSession使用说明及后台工作流程分析
    iOS 6 新的快捷初始化写法
    ios 应用发布渠道大全
    iOS-获取当前时间的年、月、日、时、分、秒
  • 原文地址:https://www.cnblogs.com/WonKerr/p/3622770.html
Copyright © 2011-2022 走看看