"AttributeError: 'NoneType' object has no attribute 'current" error when using: "self.manager.current" in classes that are screens

437 views
Skip to first unread message

Gary Kuipers

unread,
Feb 28, 2023, 11:05:30 AM2/28/23
to Kivy users support
I am getting a "AttributeError: 'NoneType' object has no attribute 'current" error when using: "self.manager.current" in classes that are screens

I read that if the screens are instantiated within the ScreenManager then they will automatically have access to "self.manager.current". My code has an error someplace because they do not.

Here is the relevant code. I appreciate guidance. Thank you


main.py contains:

MyApp().run()  (instantiates and runs MyApp)

class MyApp(App):
    def build(self):
        self.enable_swipe = False
        self.sm = ScreenManager()
        self.screens = [HomeScreen0(filemanager, name='0'),   # Home Screen
                        PhotoScreen1(name='1'),  # Camera Screen (Portrait)
                        PhotoScreen2(name='2')]
        for s in self.screens:
            self.sm.add_widget(s)
        return self.sm
       
  class SwipeScreen(Screen, CommonGestures):

    def cgb_horizontal_page(self, touch, right):
        App.get_running_app().swipe_screen(right)
       
       
class HomeScreen0(SwipeScreen):    (this is also a Screen because the SwipeScreen is a Screen)
    def __init__(self, filemanager, **args):
        super().__init__(**args)
    ...
        self.review_ct = Button(text=str(ct_review), font_size=150, size_hint=(1, 0.15))
        self.review_ct.bind(on_press=self.switch_screens('review'))
    ...
   
    def switch_screens(self, switch_to, *args):
        ...
        if switch_to == 'review':
            print(f"HomeScreen0: switch_screens({switch_to})")
            self.manager.current = '2'
           
Gives the following error:

File "/home/gary/PycharmProjects/YSI_Read/c4k_photo_example/.buildozer/android/app/applayout/homescreen0.py", line ##, in switch_screens
 AttributeError: 'NoneType' object has no attribute 'current'

Gary Kuipers

unread,
Feb 28, 2023, 12:04:38 PM2/28/23
to Kivy users support
I think I need to pass the screen manager to the classes that are being instantiated!

Tomek CEDRO

unread,
Feb 28, 2023, 12:36:45 PM2/28/23
to kivy-...@googlegroups.com
On Tue, Feb 28, 2023 at 6:04 PM Gary Kuipers
<gary.k...@casinfosystems.com> wrote:
> I think I need to pass the screen manager to the classes that are being instantiated!

Content warning: I am still learning Kivy ;-)

In my application I keep all sorts of common resources as objects in
the app object, then these are directly available in kv files with for
example app.db sqlite3 handler, or I pass whole app to the class that
will use its methods, this way app is a kind of context.

If you cannot pass the app in your python code use this (usually in
the object constructor) to get it:

from kivy.app import App

self.app = App.get_running_app()
if self.app.root is None:
return False
self.screen = self.app.root.get_screen('screen_blah')

Notes:

1. You can extend standard Kivy objects with your own python class and
custom handlers. You can even subclass several different Widgets into
one custom object (i.e. button + focus behavior that allows you
keyboard control of your application on a desktop). You can then
create custom kv file that will visually represent the object on the
screen. This way you have a separate code handlers in py files and
visual representation in kv files.

2. Know the difference between app.root (global screen manager) and kv
root (top level object in the kv file and/or the custom python object
that you have created) these are two different things. If you want to
make sure what screen object you are working on then use
self.app.root.get_screen('screen_blah') and work on what is returned.

3. When application objects are constructed for the first time when
you have py + kv pair the code may be executed with app.root being
None, then you should eject from the code and only proceed when screen
manager is operational and ready for use (not None). First call of the
constructor / method may not be triggered by user but the application
builder.

4. App.get_running_app() will slow down the app a bit, so you can call
it once in the constructor and remember it for later use.

4. Some objects may have two way dependencies, so both objects need to
already exist, for instance I have database that is used by datamodel
that creates database tables. In that case in my app constructor I am
creating database handler first, then datamodel handler, then as the
third step I need to call datamodel operations that require both
database and datamodel. In most cases you have no such situation :-)

I am not sure if passing whole app to the sub-objects is effective,
but it works like a charm and sticks to my context based programming
habits from C applications. From what I searched only a reference it
passed, so no memory doubling takes place when you "remember" app in
several objects, but I did not profile memory use yet (can someone
confirm please?). This is very convenient way of keeping common
methods and objects in one place, where app itself acts also as the
program context. I also tried to be more conservative and pass only
selected methods of that app context but I did not notice any
performance impact and it turned out then I needed another method etc
so right now I just pass whole app.

Hope that helps :-)
Tomek

--
CeDeROM, SQ7MHZ, http://www.tomek.cedro.info

Tomek CEDRO

unread,
Feb 28, 2023, 12:47:59 PM2/28/23
to kivy-...@googlegroups.com
Ah, Gary, I also saw you use jsonstore. I am using standard sqlite3
module for local data storage. Its blazingly fast! I then convert
to/from dict objects.

Note that sqlite3 works on a local filesystem. Opening and closing
file is time consuming (do not put that in a loop). But when you put a
loop inside between open and close with whatever complex sql syntax
even with commits in the middle on each iteration it works as fast as
you had worked with standard objects.

What are your feelings on jsonstore? I use REST API for data exchange
and that works on JSON. I have decided to use sqlite3 because of its
efficiency. Maybe jsonstore would have been a better solution? Do you
also have to convert incoming/outgoing json stuff to/from dicts?

berk berk

unread,
Feb 28, 2023, 4:50:12 PM2/28/23
to Kivy users support
Here are runnable example:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.lang.builder import Builder

kiv=Builder.load_string("""

<MainScreen>:
    BoxLayout:
        orientation:'vertical'
        Label:
            text:'screen-1'
        Button:
            text:'Next Screen'
            on_press:app.on_start()
           

<OtherScreen>:
    BoxLayout:
        orientation:'vertical'
        Label:
            text:'screen-2'
        Button:
            id:buton
            text:'Back Screen'
            on_press:root.click_back()
""")

class MainScreen(Screen):
    pass

class OtherScreen(Screen):
    def click_back(self):
        self.manager.current='main'  #in Screen class
       

class Example(App):

    def on_start(self):
        self.root.get_screen('main').manager.current='other'
#in app class
 
    def build(self):
        scr=ScreenManager()
        scr.add_widget(MainScreen(name='main'))
        scr.add_widget(OtherScreen(name='other'))
        return scr

Example().run()


focus the marked and colored sections. These are different call to screen.current on different class.

If you run these code piece; happens these therefore Kivy app life cycle:
.run() --> build() ---> on_start() ---> app functions()
So you'll see when app start screen change to other screen cause of on_start(), manager.current=other

28 Şubat 2023 Salı tarihinde saat 20:47:59 UTC+3 itibarıyla to...@cedro.info şunları yazdı:

Elliot Garbus

unread,
Feb 28, 2023, 6:50:14 PM2/28/23
to kivy-...@googlegroups.com

Here are a few things to consider:

        self.review_ct.bind(on_press=self.switch_screens('review'))
The bind statement above is not correct.  You want to set on_press to a function name, not call the function.  Given you want to pass a parameter you could create a lambda to call the function, or use functools.partial to create a callable object that includes the parameter.

 

The other thing to keep in mind is that when the app starts, the on_enter for the first screen under the ScreenManager fires.  This can sometimes cause issues depending on the order of initialization.

 

In the example code below, the name of the screen is printed to the console when the screen is entered.  Note it fires at app startup, and the name is not yet set.  There are a number of ways to workaround this is this is a problem, the simplest would be to add a dummy screen as the first screen under the ScreenManager.

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import
Screen

kv =
"""
<ButtonScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: f'This screen is named {root.name}'
        Button:
            size_hint_y: None
            height: dp(48)
            text: 'next_screen'
            on_release: root.manager.current = root.manager.next()
   
ScreenManager:
    ButtonScreen:
        name: 'screen_1'
    ButtonScreen:
        name: 'screen_2'
    ButtonScreen:
        name: 'screen_3'
"""


class ButtonScreen(Screen):
   
def on_enter(self, *args):
       
print(f'Entered Screen {self.manager.current}')
       
# note the screen name does not print out the first time...


class ScreenExampleApp(App):
   
def build(self):
       
return Builder.load_string(kv)


ScreenExampleApp().run()

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/e02bd18a-ca3a-42b0-b4f6-45ae0ac2d6d5n%40googlegroups.com.

 

Reply all
Reply to author
Forward
0 new messages