zoukankan      html  css  js  c++  java
  • 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 <QtNetworkQNetworkProxyFactory>
    #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还是很方便的

  • 相关阅读:
    转: adroid音视延迟 10ms的原因与解答
    去应聘软件工程师记得这样介绍自己
    U盘中了磁碟机病毒怎么办
    Heartbeats
    视频格式研究
    开源镜像站汇总
    Linux各目录缩写含义
    虚拟中没有eth0
    使用#锚点时,jsp中不能有basePath
    android systemUI--Notification 整理
  • 原文地址:https://www.cnblogs.com/lingdhox/p/3666072.html
Copyright © 2011-2022 走看看