Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when using bleak with kivy #176

Closed
dgatf opened this issue Apr 9, 2020 · 7 comments
Closed

Error when using bleak with kivy #176

dgatf opened this issue Apr 9, 2020 · 7 comments
Assignees
Labels
asyncio Problems related to asyncio and multiple clients Backend: BlueZ Issues and PRs relating to the BlueZ backend Backend: pythonnet Issues or PRs relating to the .NET/pythonnet backend

Comments

@dgatf
Copy link

dgatf commented Apr 9, 2020

  • bleak version: 0.6.1
  • Python version: 3.7.7
  • Operating System: Windows 10

Description

I'm trying to use bleak with kivy

What I Did

I've develop an app in kivy which subscribes to a ble service. This is working under Ubuntu 18.10 and Python 3.7

Under windows 10 I can't even import the bleak module. The kivy app is not created

The following code throws an error:

from kivy.app import App
from kivy.uix.button import Button
from bleak import BleakClient

class AsyncApp(App):
    def build(self):
        return Button(text='Hello!')

AsyncApp().run()

The error:

[INFO   ] [Logger      ] Record log in C:\Users\danie\.kivy\logs\kivy_20-04-08_67.txt
[INFO   ] [deps        ] Successfully imported "kivy_deps.glew" 0.2.0
[INFO   ] [deps        ] Successfully imported "kivy_deps.sdl2" 0.2.0
[INFO   ] [Kivy        ] v1.11.1
[INFO   ] [Kivy        ] Installed at "C:\Users\danie\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\__init__.py"
[INFO   ] [Python      ] v3.7.7 (tags/v3.7.7:d7c567b08f, Mar 10 2020, 10:41:24) [MSC v.1900 64 bit (AMD64)]
[INFO   ] [Python      ] Interpreter at "C:\Users\danie\AppData\Local\Programs\Python\Python37\python.exe"
[INFO   ] [Factory     ] 184 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_gif (img_pil, img_ffpyplayer ignored)
[INFO   ] [Text        ] Provider: sdl2
[CRITICAL] [Window      ] Unable to find any valuable Window provider. Please enable debug logging (e.g. add -d if running from the command line, or change the log level in the config) and re-run your app to identify potential causes
sdl2 - TypeError: __import__() takes at least 1 argument (0 given)
  File "C:\Users\danie\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\core\__init__.py", line 63, in core_select_lib
    fromlist=[modulename], level=0)

[CRITICAL] [App         ] Unable to get a Window, abort.

Other python modules:

appdirs==1.4.3
bleak==0.6.1
certifi==2020.4.5.1
chardet==3.0.4
distlib==0.3.0
docutils==0.16
filelock==3.0.12
idna==2.9
importlib-metadata==1.6.0
Kivy==1.11.1
kivy-deps.glew==0.2.0
kivy-deps.sdl2==0.2.0
Kivy-Garden==0.1.4
plyer==1.4.3
PyBluez==0.23
Pygments==2.6.1
pypiwin32==223
pythonnet==2.4.0
pywin32==227
requests==2.23.0
six==1.14.0
urllib3==1.25.8
virtualenv==20.0.16
zipp==3.1.0
@hbldh
Copy link
Owner

hbldh commented Apr 9, 2020

This does not seem to be a bleak error, given that the error seems to originate from sdl2. Does the app start if you comment out the bleak import?

I recommend writing your bleak app first and then integrating it with your kivy app once you know both are working.

@dgatf
Copy link
Author

dgatf commented Apr 9, 2020

Yes, it works if I comment the bleak import

@hbldh
Copy link
Owner

hbldh commented Apr 9, 2020

Then write the bleak app you want without kivy and I can advise you on that and then we can see if the problem is bleak or in integrating with kivy.

@dgatf
Copy link
Author

dgatf commented Apr 9, 2020

Ok, thanks

The app below subscribes to a gatt notification, wait 5sec and then finish the ble coroutine. It is done in a thread to run in parallel with kivy app. This code is working in windows 10 and ubuntu

If I add the kivy app it works in ubuntu, but not windows. Under windows 10 anytime I import at the same time kivy and bleak it throws the previous error

import asyncio
import threading
from bleak import BleakClient

address = "C8:FD:19:0E:F0:A6"
uuid = "0000ffe1-0000-1000-8000-00805f9b34fb"


def notification_handler(sender, data):
    print("{}:{}".format(sender, data))

async def ble(address, loop):
    print('start ble coroutine')
    async with BleakClient(address, loop=loop) as client:
        await client.start_notify(uuid, notification_handler)
        global event
        await event.wait()
        await client.stop_notify(uuid)
        print('finish ble coroutine')

async def finish(delay, loop):
    print('start finish coroutine')
    global event
    await asyncio.sleep(delay, loop)
    event.set()
    print('end finish coroutine')

def do_ble(loop):
    print('start thread ble')
    loop.create_task(finish(5, loop))
    loop.run_until_complete(ble(address, loop))
    loop.close()
    print('finish thread ble')

print('start app')
loop = asyncio.get_event_loop()
event = asyncio.Event()
thread_ble = threading.Thread(name='thread_ble', target=do_ble, args=(loop,), daemon=True)
thread_ble.start()
while True:
    pass
print('finish app')

@hbldh
Copy link
Owner

hbldh commented Apr 21, 2020

This uses threading and asyncio with global events. It would be better to stick with either asyncio or threading. I still do not think that this is an error in bleak...

Try something like

import asyncio
import threading
from bleak import BleakClient

address = "C8:FD:19:0E:F0:A6"
uuid = "0000ffe1-0000-1000-8000-00805f9b34fb"

def notification_handler(sender, data):
    print("{}:{}".format(sender, data))


async def ble(address, event, loop):
    print("start ble coroutine")
    async with BleakClient(address, loop=loop) as client:
        print("connected")
        loop.create_task(finish(5, event, loop))
        await client.start_notify(uuid, notification_handler)
        await event.wait()
        await client.stop_notify(uuid)
        print("finish ble coroutine")


async def finish(delay, event, loop):
    print("start finish coroutine")
    await asyncio.sleep(delay, loop)
    event.set()
    print("end finish coroutine")


if __name__ == "__main__":
    print("start app")
    loop = asyncio.get_event_loop()

    print("start thread ble")
    event = asyncio.Event()
    loop.run_until_complete(ble(address, event, loop))
    loop.close()
    print("finish thread ble")

instead. Your while True: locks the entire program at the end regardless of what happens. This also waits until the context manager has finished connecting and fetching services from the device (async with BleakClient(address, loop=loop) as client:) before putting the finish coroutine on the loop. Adding that before letting the device connect will disconnect it as soon as the connection has been made.

@hbldh
Copy link
Owner

hbldh commented Jun 2, 2020

Closing this since it does not seem to be a direct bug or issue wih bleak.

@hbldh hbldh closed this as completed Jun 2, 2020
@hbldh hbldh self-assigned this Jun 2, 2020
@hbldh hbldh added Backend: pythonnet Issues or PRs relating to the .NET/pythonnet backend asyncio Problems related to asyncio and multiple clients Backend: BlueZ Issues and PRs relating to the BlueZ backend labels Jun 2, 2020
@BioMycoBit
Copy link

Henrik:

I rediscovered this error when attempting to combine bleak and kivy.

Example Code
https://raw.githubusercontent.com/kivy/kivy/master/examples/async/asyncio_basic.py

The below code works fine

import asyncio

from kivy.app import async_runTouchApp
from kivy.lang.builder import Builder

kv = '''
BoxLayout:
    orientation: 'vertical'
    Button:
        id: btn
        text: 'Press me'
    BoxLayout:
        Label:
            id: label
            text: 'Button is "{}"'.format(btn.state)
'''


async def run_app_happily(root, other_task):
    '''This method, which runs Kivy, is run by the asyncio loop as one of the
    coroutines.
    '''
    # we don't actually need to set asyncio as the lib because it is the
    # default, but it doesn't hurt to be explicit
    await async_runTouchApp(root, async_lib='asyncio')  # run Kivy
    print('App done')
    # now cancel all the other tasks that may be running
    other_task.cancel()


async def waste_time_freely():
    '''This method is also run by the asyncio loop and periodically prints
    something.
    '''
    try:
        while True:
            print('Sitting on the beach')
            await asyncio.sleep(2)
    except asyncio.CancelledError as e:
        print('Wasting time was canceled', e)
    finally:
        # when canceled, print that it finished
        print('Done wasting time')

if __name__ == '__main__':
    def root_func():
        '''This will run both methods asynchronously and then block until they
        are finished
        '''
        root = Builder.load_string(kv)  # root widget
        other_task = asyncio.ensure_future(waste_time_freely())
        return asyncio.gather(run_app_happily(root, other_task), other_task)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(root_func())
    loop.close()

TypeError: import() takes at least 1 argument (0 given) is thrown when bleak is imported

import asyncio
import kivy

from kivy.app import async_runTouchApp
from kivy.lang.builder import Builder

from bleak import BleakClient, BleakError

kivy.require("1.10.1")

kv = '''
BoxLayout:
    orientation: 'vertical'
    Button:
        id: btn
        text: 'Press me'
    BoxLayout:
        Label:
            id: label
            text: 'Button is "{}"'.format(btn.state)
'''


async def run_app_happily(root, other_task):
    '''This method, which runs Kivy, is run by the asyncio loop as one of the
    coroutines.
    '''
    # we don't actually need to set asyncio as the lib because it is the
    # default, but it doesn't hurt to be explicit
    await async_runTouchApp(root, async_lib='asyncio')  # run Kivy
    print('App done')
    # now cancel all the other tasks that may be running
    other_task.cancel()


async def waste_time_freely():
    '''This method is also run by the asyncio loop and periodically prints
    something.
    '''
    try:
        while True:
            print('Sitting on the beach')
            await asyncio.sleep(2)
    except asyncio.CancelledError as e:
        print('Wasting time was canceled', e)
    finally:
        # when canceled, print that it finished
        print('Done wasting time')


if __name__ == '__main__':
    def root_func():
        '''This will run both methods asynchronously and then block until they
        are finished
        '''
        root = Builder.load_string(kv)  # root widget
        other_task = asyncio.ensure_future(waste_time_freely())
        return asyncio.gather(run_app_happily(root, other_task), other_task)


    loop = asyncio.get_event_loop()
    loop.run_until_complete(root_func())
    loop.close()
C:\repos\BioController\venv\Scripts\python.exe C:/repos/BioController/ex_kivy_async.py
[INFO   ] [Logger      ] Record log in C:\Users\Brandon\.kivy\logs\kivy_21-03-06_27.txt
[INFO   ] [deps        ] Successfully imported "kivy_deps.angle" 0.3.0
[INFO   ] [deps        ] Successfully imported "kivy_deps.glew" 0.3.0
[INFO   ] [deps        ] Successfully imported "kivy_deps.sdl2" 0.3.1
[INFO   ] [Kivy        ] v2.0.0
[INFO   ] [Kivy        ] Installed at "C:\repos\BioController\venv\lib\site-packages\kivy\__init__.py"
[INFO   ] [Python      ] v3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
[INFO   ] [Python      ] Interpreter at "C:\repos\BioController\venv\Scripts\python.exe"
[INFO   ] [Factory     ] 186 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
 Traceback (most recent call last):
   File "C:/repos/BioController/ex_kivy_async.py", line 67, in <module>
     loop.run_until_complete(root_func())
   File "C:/repos/BioController/ex_kivy_async.py", line 61, in root_func
     root = Builder.load_string(kv)  # root widget
   File "C:\repos\BioController\venv\lib\site-packages\kivy\lang\builder.py", line 404, in load_string
     widget = Factory.get(parser.root.name)(__no_builder=True)
   File "C:\repos\BioController\venv\lib\site-packages\kivy\factory.py", line 153, in __getattr__
     module = __import__(
 TypeError: __import__() takes at least 1 argument (0 given)

Process finished with exit code 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
asyncio Problems related to asyncio and multiple clients Backend: BlueZ Issues and PRs relating to the BlueZ backend Backend: pythonnet Issues or PRs relating to the .NET/pythonnet backend
Projects
None yet
Development

No branches or pull requests

3 participants