Qt 显示透明flash和编写QtWebkit插件

Qt 有两种方法可以显示flash.

1. 通过QAxWidget 调用com形式显示flash, 需要本机安装IE flash插件

2. 直接通过qwebview显示flash, 需要下载webkit 的flash插件 NPSWF32.dll

 

1. 通过IE显示flash 是通常做法. QAxWidget提供很方便的嵌入, 只是IE对页面的支持可能不是很好

但是QAxWidget有个最大的问题, 不支持透明的flash

如果直接设置frameless 和tranparent background的话, qaxwidget会整个窗口直接透明的. 

//flash->setWindowFlags(Qt::FramelessWindowHint); 

//flash->setAttribute(Qt::WA_TranslucentBackground);

 

摸索了很久, 最开始的解决方案为使用IWebBrowser2模拟出一个浏览器, 代码复杂的很, 继承一堆com 的东西. 估计有点像简洁版的mfc html控件

概要如下:

class CBrowserEventBridge :public IDispatch

{

public:

    CBrowserEventBridge();

    virtual ~CBrowserEventBridge();



public:

    STDMETHOD_(ULONG, AddRef)();

    STDMETHOD_(ULONG, Release)();

    STDMETHODIMP QueryInterface(REFIID riid, void** ppv);

    STDMETHODIMP GetTypeInfoCount(UINT* pCountTypeInfo);

    STDMETHODIMP GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo);

    STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);

    STDMETHODIMP Advise(IUnknown* pUnkCP, const IID& iid);

    STDMETHODIMP Unadvise(IUnknown* pUnkCP, const IID& iid);

    STDMETHODIMP Advise(CFlashPlayer *pPlayer, IWebBrowser2* pIE);

    STDMETHODIMP Unadvise();

    STDMETHODIMP Invoke(DISPID dispidMember,REFIID riid, LCID lcid, WORD wFlags,DISPPARAMS* dispParams,VARIANT* pvarResult,EXCEPINFO* pExcepInfo,UINT* puArgErr);

private:

    IWebBrowser2*    m_webBrowser;

    DWORD            m_dwCookie;

    CFlashPlayer    *m_pPlayer;

};



class CFlashPlayer 

    : public IOleClientSite

    , public IOleInPlaceSite

    , public IDocHostUIHandler

    , public IOleInPlaceFrame

{

public:

    class IFlashPlayerDelegate

    {

    public:

        //播放完毕回调

        virtual HRESULT OnFlashPlayEnd() = 0;

    };



    CFlashPlayer(IFlashPlayerDelegate *pDelegate);

    ~CFlashPlayer();



    //imp IUnknown

    ULONG STDMETHODCALLTYPE AddRef( void);

    ULONG STDMETHODCALLTYPE Release( void);

    HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid,__RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject);



    //imp IOleClientSite

    HRESULT STDMETHODCALLTYPE SaveObject( void);

    HRESULT STDMETHODCALLTYPE GetMoniker(  DWORD dwAssign,DWORD dwWhichMoniker,__RPC__deref_out_opt IMoniker **ppmk);

    HRESULT STDMETHODCALLTYPE GetContainer(__RPC__deref_out_opt IOleContainer **ppContainer);

    HRESULT STDMETHODCALLTYPE ShowObject( void);

    HRESULT STDMETHODCALLTYPE OnShowWindow( BOOL fShow);

    HRESULT STDMETHODCALLTYPE RequestNewObjectLayout( void);



    //imp IOleInPlaceSite

    HRESULT STDMETHODCALLTYPE CanInPlaceActivate( void);

    HRESULT STDMETHODCALLTYPE OnInPlaceActivate( void);

    HRESULT STDMETHODCALLTYPE OnUIActivate( void);

    HRESULT STDMETHODCALLTYPE GetWindowContext( __RPC__deref_out_opt IOleInPlaceFrame **ppFrame, __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc, __RPC__out LPRECT lprcPosRect, __RPC__out LPRECT lprcClipRect, __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo);

    HRESULT STDMETHODCALLTYPE Scroll( SIZE scrollExtant);

    HRESULT STDMETHODCALLTYPE OnUIDeactivate(  BOOL fUndoable);

    HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate( void);

    HRESULT STDMETHODCALLTYPE DiscardUndoState( void);

    HRESULT STDMETHODCALLTYPE DeactivateAndUndo( void);

    HRESULT STDMETHODCALLTYPE OnPosRectChange( __RPC__in LPCRECT lprcPosRect);



    //imp IOleInPlaceFrame

    HRESULT STDMETHODCALLTYPE InsertMenus(  __RPC__in HMENU hmenuShared, __RPC__inout LPOLEMENUGROUPWIDTHS lpMenuWidths);

    HRESULT STDMETHODCALLTYPE SetMenu( __RPC__in HMENU hmenuShared,__RPC__in HOLEMENU holemenu,__RPC__in HWND hwndActiveObject);

    HRESULT STDMETHODCALLTYPE RemoveMenus(  __RPC__in HMENU hmenuShared);

    HRESULT STDMETHODCALLTYPE SetStatusText(  __RPC__in_opt LPCOLESTR pszStatusText);

    HRESULT STDMETHODCALLTYPE TranslateAccelerator(  __RPC__in LPMSG lpmsg, WORD wID);



    //imp IOleInPlaceUIWindow

    HRESULT STDMETHODCALLTYPE GetBorder( __RPC__out LPRECT lprectBorder);

    HRESULT STDMETHODCALLTYPE RequestBorderSpace( __RPC__in_opt LPCBORDERWIDTHS pborderwidths);

    HRESULT STDMETHODCALLTYPE SetBorderSpace( __RPC__in_opt LPCBORDERWIDTHS pborderwidths);

    HRESULT STDMETHODCALLTYPE SetActiveObject(  __RPC__in_opt IOleInPlaceActiveObject *pActiveObject, __RPC__in_opt_string LPCOLESTR pszObjName);



    //imp IOleWindow

    HRESULT STDMETHODCALLTYPE GetWindow( __RPC__deref_out_opt HWND *phwnd);

    HRESULT STDMETHODCALLTYPE ContextSensitiveHelp( BOOL fEnterMode);



    //imp IDocHostUIHandler

    HRESULT STDMETHODCALLTYPE ShowContextMenu( DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved,IDispatch *pdispReserved);

    HRESULT STDMETHODCALLTYPE GetHostInfo( DOCHOSTUIINFO *pInfo);

    HRESULT STDMETHODCALLTYPE ShowUI( DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame,IOleInPlaceUIWindow *pDoc);

    HRESULT STDMETHODCALLTYPE HideUI( void);

    HRESULT STDMETHODCALLTYPE UpdateUI( void);

    HRESULT STDMETHODCALLTYPE EnableModeless( BOOL fEnable);

    HRESULT STDMETHODCALLTYPE OnDocWindowActivate( BOOL fActivate);

    HRESULT STDMETHODCALLTYPE OnFrameWindowActivate( BOOL fActivate);

    HRESULT STDMETHODCALLTYPE ResizeBorder( LPCRECT prcBorder,IOleInPlaceUIWindow *pUIWindow,BOOL fRameWindow);

    HRESULT STDMETHODCALLTYPE TranslateAccelerator( LPMSG lpMsg, const GUID *pguidCmdGroup,DWORD nCmdID);

    HRESULT STDMETHODCALLTYPE GetOptionKeyPath( __out  LPOLESTR *pchKey,DWORD dw);

    HRESULT STDMETHODCALLTYPE GetDropTarget( IDropTarget *pDropTarget, IDropTarget **ppDropTarget);

    HRESULT STDMETHODCALLTYPE GetExternal(  IDispatch **ppDispatch);

    HRESULT STDMETHODCALLTYPE TranslateUrl( DWORD dwTranslate,__in __nullterminated  OLECHAR *pchURLIn,__out  OLECHAR **ppchURLOut);

    HRESULT STDMETHODCALLTYPE FilterDataObject( IDataObject *pDO,IDataObject **ppDORet);



public:

void Resize(int width, int height);

    HRESULT BeforeNavigate2(std::wstring strUrl, DISPPARAMS* dispParams);

    static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



private:

    void _initialize(HINSTANCE hInstApp );

    bool _makeTransparent();

    bool _registWndClass(HINSTANCE hInstApp );

    void _createWindow(HINSTANCE hInstApp);

    bool _embedBrowser();

    void _unenbedBrowser();

    void _openWebPage(std::wstring strUrl);



private:

    HWND m_hWnd;

    IWebBrowser2* m_pWeb;

    IOleObject *m_pOleObj;

    IFlashPlayerDelegate *m_pDelegate;

    CBrowserEventBridge *m_pEvtBridge;



};

 

再后来, 又发现了一个比较好的解决方法: 

代码如下, 直接通过handle控制qaxwidget的窗口属性. 

 

	HWND hWnd = (HWND)flash->winId(); 

	LONG lStyle = ::GetWindowLong(hWnd, GWL_STYLE);

	lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);

	::SetWindowLong(hWnd, GWL_STYLE, lStyle); 

	LONG lExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);

	::SetWindowLong(hWnd, GWL_EXSTYLE, lExStyle|WS_EX_LAYERED|WS_EX_TOPMOST|WS_EX_TRANSPARENT); 

	typedef int (WINAPI* LPFUNC)(HWND, COLORREF , BYTE, DWORD);

	HINSTANCE hins = ::LoadLibraryW(L"User32.DLL");

	if(!hins)

		return ;

	LPFUNC func2 = (LPFUNC)GetProcAddress(hins,"SetLayeredWindowAttributes");

	if(!func2)

		return ;

	COLORREF clrMask = RGB(255,255,255);	

	func2(hWnd, clrMask, 0, LWA_COLORKEY);			

	FreeLibrary(hins);



	flash->setControl(QString::fromUtf8("{d27cdb6e-ae6d-11cf-96b8-444553540000}"));

	//connect(flash,SIGNAL(FSCommand(QString,QString)),this,SLOT(flashAction(QString,QString))); //用于处理FLASH传来的字符串

	flash->dynamicCall("LoadMovie(long,string)", 0, "d:/9023.swf"); //调用方法

	flash->dynamicCall("WMode", "transparent");

  

 

就这几行代码, 被搞了好久.  幸好, 完美解决透明问题

 

2. 通过webview直接显示flash

下载 NPSWF32_13_0_0_182.dll

放置在exe目录下的plugins, 否则可能无法加载flash 插件

目录结构: 

test.exe

plugins

      └────NPSWF32_13_0_0_182.dll

 

显示本地flash 需要加file:/// , 如 file:///d:/myswf.swf

npswf.dll 在本人写文章时, 最新版是13, 大小15M. 

估计大部分可能无法接受这个大小

所以, 如果你没有用到很高级的flash特性的话, 建议找版本老一点的npswf. 

比如本人用的就是9.x版的npswf.dll, 大小仅为2.6M

 

还有另外一种方法, 为QWebView编写插件, 手动解析flash

这个方法是有很严重的bug, 这里只是说下思路, 并且该方法也可以用于解析pdf等等

1). 写一个webkit的插件

WebKitPluginInterface.h



#pragma once



#include <QWebPluginFactory>  

#include <QtPlugin>

class WebKitPluginInterface  

{  

public:  

    WebKitPluginInterface(){};  

    virtual ~WebKitPluginInterface(){};  

    virtual QList<QWebPluginFactory::Plugin> plugins()const =0;  

    virtual QObject *create(const QString &mimeType,  

        const QUrl &url,  

        const QStringList &argumentNames,  

        const QStringList &argumentValues) const =0;  

};  

QT_BEGIN_NAMESPACE 

//声明WebKitPluginInterface为一个接口  

Q_DECLARE_INTERFACE(WebKitPluginInterface, "org.Qt-Plugin.WebkitPluginFlash")

QT_END_NAMESPACE
bluefish.h



#include "WebKitPluginInterface.h"

    

class  bluefish: public QObject, public WebKitPluginInterface  {  

    Q_OBJECT  

    //Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.WebkitPluginFlash" FILE "bluefish.json")  

    Q_INTERFACES(WebKitPluginInterface) //声明WebKitPluginInterface是一个接口  



public:  

    bluefish();  

    ~bluefish();  

    QList<QWebPluginFactory::Plugin> plugins()const ;  

    QObject *create(const QString &mimeType,  

        const QUrl &url,  

        const QStringList &argumentNames,  

        const QStringList &argumentValues) const ;  



protected:

};  
#include "bluefish.h"

#include <QUrl>  

#include <QDebug>  

#include <QAxWidget>



bluefish::bluefish(): WebKitPluginInterface(){



};  





bluefish::~bluefish(){};  



QList<QWebPluginFactory::Plugin> bluefish::plugins()const  

{  

    qDebug()<<"bluefish::plugins";  

    QWebPluginFactory::MimeType mimeType;  

    mimeType.name="application/x-shockwave-flash";  

    mimeType.description=QObject::tr("flash");  

    mimeType.fileExtensions.append(".flv");  

    mimeType.fileExtensions.append(".f4v");  

    mimeType.fileExtensions.append(".swf");  



    QList<QWebPluginFactory::MimeType> mimeTypes;  

    mimeTypes.append(mimeType);  



    QWebPluginFactory::Plugin plugin;  

    plugin.name = QObject::tr("ldh flash plugin");  

    plugin.description=QObject::tr("ldh flash plugin description");  

    plugin.mimeTypes=mimeTypes;  



    QList<QWebPluginFactory::Plugin> plugins ;  

    plugins.append(plugin);  

    return plugins;  

}  



QObject *bluefish::create(const QString &mimeType,  

    const QUrl &url,  

    const QStringList &argumentNames,  

    const QStringList &argumentValues) const  

{  

    qDebug()<<"bluefish::create";  

    QString strUrl = url.toString(); // 





    QAxWidget *flash = new QAxWidget(0, 0);

    flash->setControl(QString::fromUtf8("{d27cdb6e-ae6d-11cf-96b8-444553540000}"));

    //connect(flash,SIGNAL(FSCommand(QString,QString)),this,SLOT(flashAction(QString,QString))); //用于处理FLASH传来的字符串

    flash->dynamicCall("LoadMovie(long,string)", 0, strUrl.toLocal8Bit().data()); //调用方法



    //flash->setControl("{ca8a9780-280d-11cf-a24d-444553540000}");

    //flash->dynamicCall("LoadFile( const string& )", "d:/bcomps.1.pdf");



    //for(int i = 0; i < argumentNames.length(); i++)

    //{

    //    flash->dynamicCall(argumentNames[i].toLocal8Bit().data(), argumentValues[i].toLocal8Bit().data()); 

    //}



    Q_UNUSED(argumentNames);  

    Q_UNUSED(argumentValues);  

    

    return flash;  

}  

 

bluefish是可以作为dll 动态载入的. 这里我为了方便, 就去掉了导出接口的部分. 

怎么编写一个Qt插件, 可以参考Qt exsample中的样例代码

把QAxWidget对象返回, webkit会自动显示. 

同理, 也可以在webkit中嵌入QtextEdit等等widget

ps:

//flash->setControl("{ca8a9780-280d-11cf-a24d-444553540000}");

这行代码就是显示pdf.

 

然后继承QWebPluginFactory载入我们编写的插件

qt_modules.h



#include <QWebPluginFactory>  

#include <QUrl>  

#include "WebKitPluginInterface.h"



class WebkitPluginFactory : public QWebPluginFactory  

{  

    Q_OBJECT  

public:  

    WebkitPluginFactory();  

    QObject *create ( const QString & mimeType, const QUrl & url, const QStringList & argumentNames, const QStringList & argumentValues ) const;  

    QList<QWebPluginFactory::Plugin> plugins () const;  



private:  

    mutable QList<QList<QWebPluginFactory::Plugin> > pluginslist; // 插件列表  

    mutable QList<WebKitPluginInterface *> interfaces; //插件接口,这个接口是我们自定义的插件的同意接口。  

};  

 

qt_modules.cpp



#include "qt_modules.h"

#include <QtNetwork\QNetworkProxyFactory>

#include <Qdir>

#include <QPluginLoader>  

#include <QLibrary>

#include <QDebug>  

#include <QDir>  

#include "bluefish.h"



WebkitPluginFactory::WebkitPluginFactory() : QWebPluginFactory()  

{  

    qDebug()<<"=== ldh === WebkitPluginFactory::WebkitPluginFactory";  

}  



QList<QWebPluginFactory::Plugin> WebkitPluginFactory::plugins () const  

{  

    qDebug()<<"=== ldh === WebkitPluginFactory::create";  

    static bool isFirst=true;  



    static QList<QWebPluginFactory::Plugin> plugins;  

    if(!isFirst)  

    {  

        return plugins;  

    }  

    isFirst=false;  

    plugins.clear();  



    bluefish *obj = new bluefish; 



    WebKitPluginInterface * interface= qobject_cast<WebKitPluginInterface*> (obj);//载入自定义的接口,支持动态插件创建  

    plugins.append(interface->plugins());  

    pluginslist.append(interface->plugins());  

    interfaces.append(interface);  



    if(plugins.isEmpty()){  

        qDebug()<<"no plugins is loaded!";  

    }



    //QDir dir(QCoreApplication::applicationDirPath());  

    //QStringList filters;  

    //QString abspath=dir.absolutePath();  

    //qDebug()<<"the webkit plugin dir is:"<< abspath;  



    ////获取指定目录下的所有插件,linux下是插件库的后缀为so,windows下则是dll  

    //filters<<"*.dll";  

    //QStringList files=dir.entryList(filters);  

    //foreach(QString file,files)  

    //{  

    //    file=dir.filePath(file);//dir.filePath(file);  //file.absoluteFilePath();

    //    qDebug()<<"the webkit plugin path is: "<<file;         



    //    QPluginLoader loader(file);  

    //    QObject * obj= loader.instance();  

    //    if(!obj)

    //    {

    //        qDebug()<<"error: "<<loader.errorString();

    //        continue ; 

    //    }  

    //    WebKitPluginInterface * interface= qobject_cast<WebKitPluginInterface*> (obj);//载入自定义的接口,支持动态插件创建  

    //    qDebug() << "loading plugins is: " << interface->plugins().at(0).name;  

    //    plugins.append(interface->plugins());  

    //    pluginslist.append(interface->plugins());  

    //    interfaces.append(interface);  

    //}  

    //if(plugins.isEmpty()){  

    //    qDebug()<<"no plugins is loaded!";  

    //}  

    return plugins;  

}  



QObject * WebkitPluginFactory::create ( const QString & mimeType, const QUrl & url, const QStringList & argumentNames, const QStringList & argumentValues ) const  

{  

    qDebug()<<"=== ldh === WebkitPluginFactory::create";  

    for(int i=0;i<pluginslist.size();i++)  

    {  

        for( int j=0;j< pluginslist[i].size();j++)  

        {  

            foreach(QWebPluginFactory::MimeType mt, pluginslist[i][j].mimeTypes)  

            {  

                if(mt.name == mimeType) //根据MIME类型,创建相应的插件实例  

                {  

                    return interfaces[i]->create( mimeType, url, argumentNames, argumentValues);  

                }  

            }  

        }  

    }  

    return NULL; //如果没有,直接返回NULL,webkit会进行处理的  

}  

 

注释部分是动态载入.  但其实不是必须的. 如果有多个插件的话, 可以这样做

 

    //初始化flash插件

    QNetworkProxyFactory::setUseSystemConfiguration (true);

    pWebView->settings()->setAttribute(QWebSettings::PluginsEnabled, true);  

    pWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);

    pWebView->page()->setPluginFactory(new WebkitPluginFactory());  

最后对QWebView 设置下就可以了

 

这个方法有个很严重的bug, 不能动态增加axwidget. 而对于普通QWidget对象, 则没有问题

即: 如果html在载入时必须确定所有的object(flash, pdf等). 不能动态增加

比如在html中用js增加html代码

这会导致webkit崩溃. 本人无法解决该bug. 无力-_-!

猜测原因可能是resize时, 绘图对象被提前删了

然后对于QWidget来说, 可以随意添加. 定制webkit还是很方便的

 

 

 

你可能感兴趣的:(webkit)