2013-01-11 226 views
6

我正在尝试使用Python库来使用Objective-C中的Ubutu One API。这里是我的源代码: https://github.com/JoseExposito/U1-Finder-Plugin/blob/master/U1FinderLib/U1FinderLib.py在一个线程中执行Twisted reactor.run()?

我需要对API进行多次调用,为此我需要让反应器运行一次而不是运行它并停止它,就像在Ubuntu One文档的示例中一样: https://one.ubuntu.com/developer/files/store_files/syncdaemontool

因为无法运行两次反应堆......而且我需要在线程中执行reactor.run(),因为我无法阻止使用该库的应用程序!

有可能这样做吗?我无法在一个线程中运行反应堆并调用Ubuntu One API同步。

编辑:

我使用这个简单的源代码,以验证这一想法:

#!/usr/bin/env python 
import objc 
import thread 
import os 
import time 
from twisted.internet import reactor, defer 
from ubuntuone.platform.tools import (SyncDaemonTool, is_already_running) 
from threading import Thread 
NSObject = objc.lookUpClass('NSObject') 

## 
# Variable to get the result of the calls to the Sync Daemon. 
# The result is a JSON string stored in returned_value[0]. 
returned_value = [''] 

## 
# Objective-C facade to the methods of the U1FinderLib. 
class U1FinderLib(NSObject): 

    def init(self): 
     self = super(U1FinderLib, self).init() 
     self.sync_daemon_tool = SyncDaemonTool(None) 
     Thread(target=reactor.run, args=(False,)).start() 
     return self 

    @objc.typedSelector('@@:') 
    def volumeList(self): 
     print "Begin volumeList" 
     reactor.callLater(0, run_command, "volume_list", [], self.sync_daemon_tool) 
     print "End volumeList" 
     return returned_value[0] 

## 
# Auxiliar functions to call to the sync daemon. 
@defer.inlineCallbacks 
def run_command(action, params, sync_daemon_tool): 
    print "run_command" 
    running = yield is_already_running() 
    print "After is_already_running" 
    try: 
     if not running: 
      returned_value[0] = '{ type:"error" reason:"Sync Daemon is not running" }' 
     else: 
      print "Before run_action" 
      yield run_action(action, params, sync_daemon_tool) 
      print "After run_action" 
    except Exception, e: 
     returned_value[0] = '{ type:"error" reason:"Exception: %s" }' % e 

@defer.inlineCallbacks 
def run_action(action, params, sync_daemon_tool): 
    if action == "volume_list": 
     d = sync_daemon_tool.get_folders() 
     returned_value[0] = yield d.addCallback(lambda r: volume_list(r)) 

# Volume List 
def volume_list(folders): 
    volumes_json = '{ type:"volume_list" volumes: { \n\t{ volume:"' + os.path.expanduser('~/Ubuntu One') + '" subscribed:"YES" }' 
    for folder in folders: 
     volumes_json += ',\n\t{ volume:"' + folder['path'] + '" subscribed:"' + ('YES' if bool(folder['subscribed']) else 'NO') + '" }' 
    volumes_json += '\n} }' 
    return volumes_json 

if __name__ == '__main__': 
    py = U1FinderLib.alloc().init() 
    print py.volumeList() 
    print "EXIT" 

这是该程序的输出:

Begin volumeList 
End volumeList 

EXIT 

的问题是, “run_command”函数永远不会被调用

+0

你可以随时注册你的功能作为deferands,通过反应堆发射他们# –

+0

你能解释一下吗?我是一个Python完全noob(实际上这是我的第一个Python代码) – user1204395

+0

首次在Python中编程并直接跳到Twisted?祝你好运...... :) –

回答

15

不是在一个线程中运行反应器,而是应该使用与您想要使用它的应用程序集成在一起的反应器。例如,也许您想使用CoreFoundation reactor(因为您的应用程序使用Obj-C并且在名称中具有“Finder”)。如果你真的不能这么做(例如,如果Ubuntu One需要不同的反应器 - 我不知道是否是这种情况),那么你可能可以在一个线程中运行该反应器。大多数反应堆都支持这一点,尽管如果Ubuntu One需要特定的反应堆,该反应堆可能不支持线程化使用。

你实际上没有解释当你尝试在一个线程中运行reactor时你有什么问题,所以我不能帮你理解为什么它不起作用。但是,这应该很容易。刚:

from twisted.internet import reactor 
from threading import Thread 

Thread(target=reactor.run, args=(False,)).start() 

请记住,一旦你选择在一个线程中运行的反应堆,你可能只使用扭曲的API在线程。

如果这不起作用,请提供有关在问题中不起作用的更多详细信息。

+0

非常感谢您的帮助。 我在我的问题中添加了示例源代码。 如果我尝试使用CFReactor,我有一个“No module named CFNetwork”错误。我必须说我的应用程序是我在Finder进程中注入的一个包,以改变它们的行为,并且我不能使用许多功能。 – user1204395

+0

你的包中有CFNetwork吗? –

+0

解决了,核心基础反应堆是正确的解决方案。非常感谢你!! – user1204395

相关问题