Search code examples
pythonkivypython-asynciokivy-language

Kivy with asyncio. Error when changing screen in .kv file


I have cut down my code to focus on only the problem I have. The asyncio part of my code is working -I have left stubs for these procs. When I incorporated my user interface kivy code and my asyncio kivy code, I get the above error on a screen change. I think I need to get an "App" setup with Builder.load_file() inside it ??, but my attempts to do so have failed. Hope someone can help ? On clicking the "Post a Photo!" button I get this error...

   File "C:\TEMP\kivy1\kivy_venv\src\screens.kv", line 31, in <module>
     on_press: app.switch_screen='TakePhotoScreen'
   File "C:\TEMP\kivy1\kivy_venv\lib\site-packages\kivy\lang\parser.py", line 83, in __setattr__
     object.__getattribute__(self, '_ensure_app')()
   File "C:\TEMP\kivy1\kivy_venv\lib\site-packages\kivy\lang\parser.py", line 70, in _ensure_app
     app.bind(on_stop=lambda instance:
 AttributeError: 'NoneType' object has no attribute 'bind'

Code: screen.kv

Manager:
    TestMyFeedScreen3:
    TakePhotoScreen:

#:import ScrollEffect kivy.effects.scroll.ScrollEffect
#:import Window kivy.core.window.Window

<TestMyFeedScreen3>:
    name:'TestMyFeedScreen3'
    id:TestMyFeedScreen3
    orientation: 'vertical'

    GridLayout:
        cols:1
        rows:2
        height: 50
        Label:
            text: 'My Story'
            size_hint_y: None
            height: 50
        GridLayout:
            cols:2
            rows:1
            height: 50
            Button:
                text: 'Post a Photo!'
                size_hint_y: None
                height: '48dp'
                # ERROR HERE >>>>>>>>>>>>>>>>>>>>>>>>>>>>
                #on_press: app.root.current='TakePhotoScreen'
                on_press: app.switch_screen='TakePhotoScreen'  
            Button:
                text: 'Back'
                size_hint_y: None
                height: '48dp'
                on_release:
                    app.root.current='TestMyFeedScreen3'
                    root.manager.transition.direction = "left"

    ScrollView:
        effect_cls: ScrollEffect
        size_hint: 1, None
        size: Window.width, (Window.height -100)
        bar_width: 5
        bar_color: 1, 0, 0, 1   # red
        bar_inactive_color: 0, 0, 1, 1   # blue
        scroll_type: ['bars','content']     # move scrool by bar or photos

        GridLayout:
            id:picsx
            do_scroll_x: False
            height: self.minimum_height
            size_hint_y: None
            spacing: 3, 3
            cols: 3
            Button:
                #text:'Pic'
                background_normal: "Image1.PNG"
                background_down: 'Click.PNG'
                size_hint_y:None
                height:100


<TakePhotoScreen>:
    name:'TakePhotoScreen'
    orientation: 'vertical'
    GridLayout:
        cols:1
        rows:3
        Button:
            text: 'Take Photo and Post It !'
            size_hint_y: None
            height: '48dp'

        Button:
            text: 'Back'
            size_hint_y: None
            height: '48dp'

        Image:
            id: imageView
            source: 'mob_photo.jpg'
            allow_stretch: True

Code: mainx.py

# This code has been cut down to focus on the error
from __future__ import print_function

import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.lang import Builder
from kivy.config import Config
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout


import asyncio
from kivy.app import async_runTouchApp
from kivy.cache import Cache
import aioconsole

from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty


images = 1
#client_number = sys.argv[1]
client_number = "0"
print("This is client " + client_number)

Config.set('graphics', 'width', '380')
Config.set('graphics', 'height', '700')

async def send_messages(writer, stdin):
    print("In send_messages stub")

async def receive_messages(reader, stdout):
    print("In receive_messages stub")


async def run_client():
    print("In run_client")

Code

# This code has been cut down to focus on the error
from __future__ import print_function

import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.lang import Builder
from kivy.config import Config
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout


import asyncio
from kivy.app import async_runTouchApp
from kivy.cache import Cache
import aioconsole

from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty


images = 1
#client_number = sys.argv[1]
client_number = "0"
print("This is client " + client_number)

Config.set('graphics', 'width', '380')
Config.set('graphics', 'height', '700')

async def send_messages(writer, stdin):
    print("In send_messages stub")

async def receive_messages(reader, stdout):
    print("In receive_messages stub")


async def run_client():
    print("In run_client")


# Screen code

class TestMyFeedScreen3(Screen):
    pass

class TakePhotoScreen(Screen):
    pass

class Manager(ScreenManager):
    pass

kv=Builder.load_file('screens.kv')
root = kv

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()


if __name__ == '__main__':
    def root_func():

        while True:
            print("In __main__")
            other_task = asyncio.ensure_future(run_client())
            return asyncio.gather(run_app_happily(kv, other_task), other_task)

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


Solution

  • Since you don't have an actual App in your code, you cannot reference it in the line:

    on_press: app.switch_screen='TakePhotoScreen'
    

    but you can reference the ScreenManager and change the current Screen like this:

            Button:
                text: 'Post a Photo!'
                size_hint_y: None
                height: '48dp'
                # ERROR HERE >>>>>>>>>>>>>>>>>>>>>>>>>>>>
                on_press: root.manager.current='TakePhotoScreen'
    

    You have a similar situation elsewhere in your code.