| 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
- #include
- class WebKitPluginInterface
- {
- 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;
- };
- 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 plugins()const ; QObject *create(const QString &mimeType, const QUrl &url, const QStringList &argumentNames, const QStringList &argumentValues) const ;
- protected:
- };
复制代码
- #include "bluefish.h"
- #include
- #include
- #include
- bluefish::bluefish(): WebKitPluginInterface(){
- };
- bluefish::~bluefish(){};
- QList bluefish::plugins()const
- { 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()); //}
- 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
- #include
- #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 plugins () const;
- private: mutable QList pluginslist; // 插件列表 mutable QList interfaces; //插件接口,这个接口是我们自定义的插件的同意接口。
- };
复制代码
- qt_modules.cpp
- #include "qt_modules.h"
- #include
- #include
- #include
- #include
- #include
- #include
- #include "bluefish.h"
- WebkitPluginFactory::WebkitPluginFactory() : QWebPluginFactory()
- { qDebug()plugins()); pluginslist.append(interface->plugins()); interfaces.append(interface); if(plugins.isEmpty()){ qDebug()
复制代码 注释部分是动态载入. 但其实不是必须的. 如果有多个插件的话, 可以这样做
- //初始化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还是很方便的 |
|
|