分享

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

 QomoIT 2014-07-07



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控件
概要如下:
  1. class CBrowserEventBridge :public IDispatch
  2. {
  3. public:        CBrowserEventBridge();        virtual ~CBrowserEventBridge();
  4. 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);
  5. private:        IWebBrowser2*        m_webBrowser;        DWORD        m_dwCookie;        CFlashPlayer        *m_pPlayer;
  6. };
  7. class CFlashPlayer        : public IOleClientSite        , public IOleInPlaceSite        , public IDocHostUIHandler        , public IOleInPlaceFrame
  8. {
  9. 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
  10.         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
  11.         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);
  12. public:
  13. 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);
  14. private:        void _initialize(HINSTANCE hInstApp );        bool _makeTransparent();        bool _registWndClass(HINSTANCE hInstApp );        void _createWindow(HINSTANCE hInstApp);        bool _embedBrowser();        void _unenbedBrowser();        void _openWebPage(std::wstring strUrl);
  15. private:        HWND m_hWnd;        IWebBrowser2* m_pWeb;        IOleObject *m_pOleObj;        IFlashPlayerDelegate *m_pDelegate;        CBrowserEventBridge *m_pEvtBridge;
  16. };
复制代码
再后来, 又发现了一个比较好的解决方法:
代码如下, 直接通过handle控制qaxwidget的窗口属性.
  1. 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的插件
  1. WebKitPluginInterface.h
  2. #pragma once
  3. #include
  4. #include
  5. class WebKitPluginInterface
  6. {
  7. public:        WebKitPluginInterface(){};        virtual ~WebKitPluginInterface(){};        virtual QList plugins()const =0;        virtual QObject *create(const QString &mimeType,        const QUrl &url,        const QStringList &argumentNames,        const QStringList &argumentValues) const =0;
  8. };
  9. QT_BEGIN_NAMESPACE
  10. //声明WebKitPluginInterface为一个接口
  11. Q_DECLARE_INTERFACE(WebKitPluginInterface, "org.Qt-Plugin.WebkitPluginFlash")
  12. QT_END_NAMESPACE
复制代码
  1. bluefish.h
  2. #include "WebKitPluginInterface.h"
  3. 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是一个接口
  4. public:        bluefish();        ~bluefish();        QList plugins()const ;        QObject *create(const QString &mimeType,        const QUrl &url,        const QStringList &argumentNames,        const QStringList &argumentValues) const ;
  5. protected:
  6. };
复制代码
  1. #include "bluefish.h"
  2. #include
  3. #include
  4. #include
  5. bluefish::bluefish(): WebKitPluginInterface(){
  6. };
  7. bluefish::~bluefish(){};
  8. QList bluefish::plugins()const
  9. {        qDebug()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());        //}
  10.         Q_UNUSED(argumentNames);        Q_UNUSED(argumentValues);        return flash;
  11. }
复制代码
bluefish是可以作为dll 动态载入的. 这里我为了方便, 就去掉了导出接口的部分.
怎么编写一个Qt插件, 可以参考Qt exsample中的样例代码
把QAxWidget对象返回, webkit会自动显示.
同理, 也可以在webkit中嵌入QtextEdit等等widget
ps:
//flash->setControl("{ca8a9780-280d-11cf-a24d-444553540000}");
这行代码就是显示pdf.
然后继承QWebPluginFactory载入我们编写的插件
  1. qt_modules.h
  2. #include
  3. #include
  4. #include "WebKitPluginInterface.h"
  5. class WebkitPluginFactory : public QWebPluginFactory
  6. {        Q_OBJECT
  7. public:        WebkitPluginFactory();        QObject *create ( const QString & mimeType, const QUrl & url, const QStringList & argumentNames, const QStringList & argumentValues ) const;        QList plugins () const;
  8. private:        mutable QList pluginslist; // 插件列表         mutable QList interfaces; //插件接口,这个接口是我们自定义的插件的同意接口。
  9. };
复制代码
  1. qt_modules.cpp
  2. #include "qt_modules.h"
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include "bluefish.h"
  10. WebkitPluginFactory::WebkitPluginFactory() : QWebPluginFactory()
  11. {        qDebug()plugins());        pluginslist.append(interface->plugins());        interfaces.append(interface);        if(plugins.isEmpty()){        qDebug()
复制代码
注释部分是动态载入.  但其实不是必须的. 如果有多个插件的话, 可以这样做
  1. //初始化flash插件 QNetworkProxyFactory::setUseSystemConfiguration (true); pWebView->settings()->setAttribute(QWebSettings::PluginsEnabled, true); pWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
  2. pWebView->page()->setPluginFactory(new WebkitPluginFactory());
复制代码
最后对QWebView 设置下就可以了
这个方法有个很严重的bug, 不能动态增加axwidget. 而对于普通QWidget对象, 则没有问题
即: 如果html在载入时必须确定所有的object(flash, pdf等). 不能动态增加
比如在html中用js增加html代码
这会导致webkit崩溃. 本人无法解决该bug. 无力-_-!
猜测原因可能是resize时, 绘图对象被提前删了
然后对于QWidget来说, 可以随意添加. 定制webkit还是很方便的

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多