最近寫的插件功能基本完成,也遇到了一些坑,在這里記錄一下。
我寫的這個插件的js接口是仿造google earth的js接口,盡可能的達到與它的api一致。先從最簡單的說起:
1. 導出接口中的float參數
GE中的一些接口有些參數是float,比如下面這個:
float KmlMouseEvent::getLatitude()
你要是真按着這個函數來返回float,我估計你在頁面里調用這個方法的時候肯定會提示你“方法不存在,或不支持該方法”之類的。
明明寫了,為啥提示沒有這個方法呢?原因很簡單,因為這個接口沒有導出成功!沒有導出成功的方法,你咋調呢?
那么為什么沒有導出成功?這個就得問assisant了。
請翻到 Building ActiveX servers in Qt 這一頁,往下翻,會看到一個Qt data types與COM property的對應表。
仔細看就會發現,根本沒有float這一項!
我對COM不熟,不知道COM是不是本身就沒有float;還是Qt認為有double就夠用了,float不需要;還是因為javascript只有number類型,不區分float和double,如果能導出float,會影響精度。。。
所以解決辦法就出來了,接口參數都用double,不要用float!
2. 導出類的創建和回收
這個其實是我對ActiveQt框架如何管理對象的一個疑惑。因為在這個框架中,所以導出的類都只能以new這種方式創建出來,在js代碼中也是new xxxx;而且所有的導出函數也不能用對象作為參數,而必須用對象的指針才行。那我什么時候刪除呢?
其實這里我也沒有看到一些說明文檔,不過倒是在網上搜的時候看到
這篇博客,寫的還是挺不錯的,解釋的很清楚。
3. 枚舉類型的導出
在GE的api中是存在這樣的調用的:
ge.getLayerRoot().enableLayerById(ge.LAYER_TERRAIN, true);
(其中ge是
google.earth.createInstance得到的對象)
這里就涉及到枚舉值。那怎么在Qt里導出枚舉呢。
這個其實翻翻例子,應該就能找到這個宏:
Q_ENUMS
用法:
class MyClass : public QObject{Q_OBJECTQ_ENUMS(Priority)public:MyClass(QObject *parent = 0);~MyClass();enum Priority { High, Low, VeryHigh, VeryLow };void setPriority(Priority priority);Priority priority() const;}
看上去,似乎這樣就OK了。
實際上,枚舉也確實是這樣導出的。
但你對照着GE的API看看,就會發現,這個定義的枚舉值我怎么在其他的函數里調用!而且
ge.LAYER_TERRAIN 這樣調用,仔細看似乎並不是枚舉,因為ge是對象,用對象再點出來一個枚舉。。我反正是沒見過。
所以我的最后的解決方案是用:property。代碼如下:
class MyClass : public QWidget, public QAxBindable{Q_OBJECTQ_PROPERTY(int LAYER_TERRAIN READ enum_TERRAIN)Q_PROPERTY(int LAYER_BORDERS READ enum_BORDERS)
public:int enum_TERRAIN() {return 0;}int enum_BORDERS() {return 1;}}
看着很啰嗦是吧,暫時我也沒好的解決方案,如果哪位看官解決過,麻煩告訴一下:)
4. 動態生成函數,事件監聽
這里費了我很大的力氣,但最終的解決方案還是沒有與GE完全一致(哎,還得努力啊。。)。這里把我的解決方案記錄下來,供大家參考。
在GE的api里有個 addEventListener 函數,可以用於事件的綁定。函數的原型:
addEventListener(obj, 'signal', callbackfunc)
也就是當obj發出'signal'事件時,會回調callbackfunc這個函數。
可以直接用C++寫導出函數,但問題是怎么調用js的函數,我搜了很久也沒有找到。。。
所以就把這個方法寫成js的函數了。
因為ActiveQt中的事件寫成監聽函數要寫成:
function obj::signal(e) { }
這樣的方式才行,所以問題就變成了怎么用那三個參數生成一個監聽函數了。
//////////////////////////////////////////////////// 動態生成函數var X2={} //my namespace:)X2.Eval=function(code){if(!!(window.attachEvent && !window.opera)){//iealert(code);window.execScript(code);}else{//not iewindow.eval_r(code);}}MYOBJECT.property.addEventListener = function(obj, signal, func) {var src = "function "+obj+"::"+signal+"(event) {" +func+"(event);" +"}";X2.Eval(src);}
function mouseuphandler (e) { alert('ok'); }obj.addEventListener("mouseup", "mouseuphandler");
附上GE的調用方式:
google.earth.addEventListener(ge.getWindow(), 'click', function (event) { })
呵呵,差別還是挺大的吧,哎,還得改啊,有進展了再來分享。
PS:當用ActiveQt開發插件時,推薦用 out-of-process 方式,這樣寫出的插件是exe,可以直接運行,調試的時候方便不少,而且還能當桌面程序用,哈哈。