如何在不放弃主线程的情况下将CoreBluetooth用于Python [英] How can I use CoreBluetooth for Python without giving up the main thread

查看:91
本文介绍了如何在不放弃主线程的情况下将CoreBluetooth用于Python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现将在OS / X上运行并与BLE外围设备对话的通用BLE接口。外围设备非常复杂:它可以被查询,发送数百个不同的命令,提供通知等。我需要能够连接到该设备,发送命令,读取响应,获取更新等。

I am trying to implement a generic BLE interface that will run on OS/X and talk to a BLE peripheral device. The peripheral is very complex: It can be queried, sent hundreds of different commands, offers notifications, etc. I need to be able to connect to it, send it commands, read responses, get updates, etc.

我拥有所需的所有代码,但由于一件事而感到沮丧:从可以在网上找到的有限信息中,看来使CoreBluetooth的委托回调被调用的唯一方法是通过运行:

I have all of the code I need but am being frustrated by one thing: From the limited information I can find online, it looks like the only way to make CoreBluetooth's delegate callbacks get called is by running:

from PyObjCTools import AppHelper

# [functional CoreBluetooth code that scans for peripherals]
AppHelper.runConsoleEventLoop()

问题是 AppHelper.runConsoleEventLoop 阻止主线程继续执行,因此我无法执行代码与外围设备进行交互。

The problem is that AppHelper.runConsoleEventLoop blocks the main thread from continuing, so I cannot execute code to interact with the peripheral.

我尝试运行事件循环:


  • 从不同的线程--->未调用的委托回调

  • 从子进程--->委托的回调不

  • 从一个派生的孩子---> Python崩溃并显示错误消息:该过程已派生,您不能安全地使用此CoreFoundation功能。您必须exec()。

  • 来自 multiprocessing.Pool(1).apply_async(f)- -> Python崩溃并显示错误消息:该进程已分叉,您不能安全地使用此CoreFoundation功能。您必须是exec()。

  • From a different thread ---> Delegate callbacks not called
  • From a subprocess ---> Delegate callbacks not called
  • From a forked child ---> Python crashes with error message: The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().
  • From multiprocessing.Pool(1).apply_async(f) ---> Python crashes with error message: The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().

都没有成功。

我不了解 AppHelper.runConsoleEventLoop 的性质。为什么需要运行它才能调用CoreBluetooth委托回调?是否有一些其他版本可以不必在主线程上运行?我在网上阅读了一些与GUI相关的内容,因此必须在主线程上运行,但是我的python应用程序没有任何GUI元素。有没有我可以使用的与GUI无关的标志或API?

I do not understand the nature of AppHelper.runConsoleEventLoop. Why does it need to be run in order for the CoreBluetooth delegate callbacks to be called? Is there some other version that can be called that doesn't have to be run on the main thread? I read something on the web about it being GUI related and therefore had to be run on the main thread but my python application does not have any GUI elements. Is there a flag or API that is less concerned with GUI that I could use?

任何帮助将不胜感激。谢谢您的宝贵时间!

Any help would be enormously appreciated. Thanks for your time!

更新:

我与iOS / CoreBluetooth专家在工作,发现Dispatch Queues可能是解决方案。我进一步研究发现,PyObjC软件包的作者最近发布了v4.1,该版本增加了对以前缺少的调度队列的支持。

I spoke with an iOS/CoreBluetooth expert at work and found out that Dispatch Queues are probably the solution. I dug further and found that the author of the PyObjC package recently released a v4.1 that adds support for dispatch queues that was heretofore missing.

我一直在阅读Apple开发人员文档已经有几个小时了,我了解可以创建监视某些系统事件(例如我感兴趣的BLE外围事件)的Dispatch Source对象,并且配置它们涉及创建和分配Di​​spatch Queue,这是该类调用我的CBCentralManager委托回调方法。我仍然缺少的难题之一是如何将Dispatch Source / Queue内容连接到 AppHelper.runConsoleEventLoop ,后者称为 Foundation。 NSRunLoop.currentRunLoop()。如果我在单独的线程上调用 AppHelper ,如何告诉它使用哪个Dispatch Source / Queue以获取事件信息?

I've been reading Apple developer documentation for hours now and I understand that it's possible to create Dispatch Source objects that monitor certain system events (such as BLE peripheral events that I am interested in) and that configuring them involves creating and assigning a Dispatch Queue, which is the class that calls my CBCentralManager delegate callback methods. The one piece of the puzzle that I am still missing is how to connect the Dispatch Source/Queue stuff to the AppHelper.runConsoleEventLoop, which calls Foundation.NSRunLoop.currentRunLoop(). If I put the call to AppHelper on a separate thread, how do I tell it which Dispatch Source/Queue to work with in order to get event info?

推荐答案

所以我终于弄清楚了。如果要在单独的线程上运行事件循环,以免失去对主线程的控制,则必须创建一个新的调度队列并使用它初始化CBCentralManager。

So I finally figured it out. If you want to run an event loop on a separate thread so that you don't lose control of the main thread, you must create a new dispatch queue and initialize your CBCentralManager with it.

import CoreBluetooth
import libdispatch


class CentralManager(object):
    def __init__(self):
        central_manager = CoreBluetooth.CBCentralManager.alloc()
        dispatch_queue = libdispatch.dispatch_queue_create('<queue name>', None)
        central_manager.initWithDelegate_queue_options_(delegate, dispatch_queue, None)


    def do_the_things(args):
        # scan, connect, send messages, w/e


class EventLoopThread(threading.Thread):
    def __init__(self):
        super(EventLoopThread, self).__init__()
        self.setDaemon(True)
        self.should_stop = False


    def run(self):
        logging.info('Starting event loop on background thread')
        AppHelper.runConsoleEventLoop(installInterrupt=True)


    def stop(self):
        logging.info('Stop the event loop')
        AppHelper.stopEventLoop()


event_loop_thread = EventLoopThread()
event_loop_thread.start()
central_device = BLECentralDevice(service_uuid_list)
central_device.do_the_things('woo hoo')
event_loop_thread.stop()

这篇关于如何在不放弃主线程的情况下将CoreBluetooth用于Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆