在Python中找不到DOMEventTarget.add_event_listener

时间:2013-06-26 14:25:57

标签: python events webkit webkitgtk

我将WebKitGTK +项目从C ++转换为Python(使用PyGI)。我需要在页面上的<input>元素获得或失去焦点的任何时候进行检测。在C ++项目中,我使用此代码(基于this example):

WebKitDOMDocument* document = webkit_web_view_get_dom_document(view);
WebKitDOMNodeList* nodes = webkit_dom_document_get_elements_by_tag_name(document, "*");
int len = webkit_dom_node_list_get_length(nodes);
for(int i = 0; i < len; i++) {
    WebKitDOMNode* node = webkit_dom_node_list_item(nodes, i);
    if(WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(node)) {
        webkit_dom_event_target_add_event_listener(WEBKIT_DOM_EVENT_TARGET(node), "focus", G_CALLBACK(inputFocus), false);
        webkit_dom_event_target_add_event_listener(WEBKIT_DOM_EVENT_TARGET(node), "blur", G_CALLBACK(inputBlur), false);
    }
}

我尝试在Python中做同样的事情并提出:

document = view.get_dom_document()
nodes = document.get_elements_by_tag_name('*')
for i in range(nodes.get_length()):
    node = nodes.item(i)
    if isinstance(node, WebKit.DOMHTMLInputElement):
        node.add_event_listener('focus', inputFocus, False)

问题是,DOMHTMLInputElement没有add_event_listener方法。基于C绑定,它应该是DOMEventTargetDOMHTMLInputElement扩展)的一部分,但它与remove_event_listener一起丢失,尽管dispatch_event以某种方式使它成为in。可能是由于this WebKit bug;我不确定

有没有办法在Python中执行此操作?它不必使用我在C ++中使用的相同方案,我只需要一些方法来注册每次焦点在页面上的某些元素上发生变化时调用的回调

2 个答案:

答案 0 :(得分:0)

我的理解是这些函数不会被内省暴露,因此在python中不可用。我也遇到了同样的问题,找到了解决这个问题的黑客方法。它对我有用。

  1. 使用javascript来收听您感兴趣的活动。在javascript事件处理函数函数调用警报函数中,例如

    alert("FOCUS:"+ node.getId()); //Or name or whatever that can help python side to identify

  2. 在python端使用警报处理程序获取消息并解析消息以确定哪个节点

  3. 如果您无法通过name / id或xpath识别唯一节点,则无效。但是如果您正在控制html,则可以轻松解决此问题。

    我可以在https://github.com/nhrdl/notesMD/blob/master/notesmd.py找到我的示例代码。看一下文件中的警报功能。 HTML文件只调用alert和python端解析消息并对其进行操作。

答案 1 :(得分:0)

我最后通过欺骗一点并放入C来添加一个回调到Python的事件监听器来解决这个问题。我写了这个简短的C文件并将其编译成共享对象:

typedef void (*callback)(WebKitDOMNode* node);

static bool handler(WebKitDOMNode* node, WebKitDOMEvent* event, callback cb) {
    cb(node);
    return true;
}

void add_event_listener(WebKitDOMEventTarget* target, callback focused, callback blurred) {
    webkit_dom_event_target_add_event_listener(target, "focus", G_CALLBACK(handler), false, (void*)focused);
    webkit_dom_event_target_add_event_listener(target, "blur", G_CALLBACK(handler), false, (void*)blurred);
}

我从this answer窃取了一些代码,以便在Python对象和C指针之间进行转换,并对其进行了一些修改:

class _PyGObject_Functions(ctypes.Structure):
    _fields_ = [
        ('register_class', ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.py_object, ctypes.py_object)),
        ('register_wrapper', ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
        ('lookup_class', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
        ('newgobj', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
    ]

class PyGObjectCPAI(object):
    def __init__(self):
        PyCObject_AsVoidPtr = ctypes.pythonapi.PyCObject_AsVoidPtr
        PyCObject_AsVoidPtr.restype = ctypes.c_void_p
        PyCObject_AsVoidPtr.argtypes = [ctypes.py_object]
        addr = PyCObject_AsVoidPtr(ctypes.py_object(gi._gobject._PyGObject_API))
        self._api = _PyGObject_Functions.from_address(addr)

    def pygobject_new(self, addr):
        return self._api.newgobj(addr)

def cToPython(ptr, capi = PyGObjectCPAI()):
    return capi.pygobject_new(ptr)

def pythonToC(obj):
    return hash(obj)

我写了一个python事件监听器:

def focusChanged(node, focused):
    ....

制作C-callable函数(它们需要是全局函数,因此它们不会被垃圾收集):

native = ctypes.cdll.LoadLibrary('native.so')
native.add_event_listener.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]

def focused(nodeAddr): focusChanged(cToPython(nodeAddr), True)
def blurred(nodeAddr): focusChanged(cToPython(nodeAddr), False)

CALLBACK = ctypes.CFUNCTYPE(None, ctypes.c_void_p)
focused = CALLBACK(focused)
blurred = CALLBACK(blurred)

并将事件监听器添加到每个节点:

document = view.get_dom_document()
nodes = document.get_elements_by_tag_name('*')
for i in range(nodes.get_length()):
    node = nodes.item(i)
    if isinstance(node, WebKit.DOMHTMLInputElement) or isinstance(node, WebKit.DOMHTMLTextAreaElement):
        native.add_event_listener(pythonToC(node), focused, blurred)