Qt 顯示透明flash和編寫QtWebkit插件

Qt 有兩種方法能夠顯示flash.html

1. 經過QAxWidget 調用com形式顯示flash, 須要本機安裝IE flash插件linux

2. 直接經過qwebview顯示flash, 須要下載webkit 的flash插件 NPSWF32.dllweb

 

1. 經過IE顯示flash 是一般作法. QAxWidget提供很方便的嵌入, 只是IE對頁面的支持可能不是很好json

可是QAxWidget有個最大的問題, 不支持透明的flashwindows

若是直接設置frameless 和tranparent background的話, qaxwidget會整個窗口直接透明的. 瀏覽器

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

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

 

摸索了好久, 最開始的解決方案爲使用IWebBrowser2模擬出一個瀏覽器, 代碼複雜的很, 繼承一堆com 的東西. 估計有點像簡潔版的mfc html控件ide

概要以下:ui

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仍是很方便的

相關文章
相關標籤/搜索