Qt写Activex插件 总结
最近写的插件功能基本完成,也遇到了一些坑,在这里记录一下。
我写的这个插件的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,可以直接运行,调试的时候方便不少,而且还能当桌面程序用,哈哈。