How to access widgets in screens using ScreenManager?

1,820 views
Skip to first unread message

Nobody999

unread,
May 20, 2021, 2:59:24 PM5/20/21
to Kivy users support
Hello everybody.
I tried to dynamically add Buttons to a GridLayout at runtime. It worked well when I inserted Widgets from a widget class in the .kv file.
Now I use the ScreenManager to switch between screens. One of them has a GridLayout with the id: gl. I now want to access this GridLayout from within the python code to add a button. I tried to add gl=ObjectProperty() in the Screen and ScreenManager class but I constatntly got errors when I tried to add gl:gl in the .kv file.
I don't know where to add that gl:gl, where to define the gl=ObjectProperty and how to finally add a button (I guess something like ScreenMain.gl.add_widget(...))
Can anywone give me a hint to fix it?
Thanks for any help.

myapp.py: (Python 3.9)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty

class ScreenMenue(Screen):
    pass         # add gl=ObjectProperty() here?

class ScreenJob(Screen):
    pass

class ScreenMngr(ScreenManager):
    pass         # ... or here ?

class MyApp(App):
    pass

if __name__ == '__main__':
    app = MyApp()
    app.run()



myapp.kv
#: kivy 2.0.0

ScreenMngr:
    ScreenMenue:
    ScreenJob:
                    # Think I have to add gl:gl anywhere here
<ScreenMenue>:
    name: 'menue'
    BoxLayout
        orientation: "vertical"
        size: root.width, root.height
        LabelHead:
            text: "Head"
        GridLayout:
            id: gl                   # id to access
            cols: 3
            spacing: 10, 10
            padding: 10, 10
        Button:
            text: "Switch to Job window"
            font_size: 28
            size_hint: None, None
            size: 300, 100
            on_release: app.root.current = "job"

<ScreenJob>:
    name: 'job'
    BoxLayout
        orientation: "vertical"
        size: root.width, root.height
        LabelHead:
            text: "Job"
        Widget:
        Button:
            text: "Back to Menue window"
            font_size: 28
            size_hint: None, None
            size: 300, 100
            on_release: app.root.current = "menue"

Nobody999

unread,
May 20, 2021, 5:37:37 PM5/20/21
to Kivy users support
Meanwhile I found that the GridLayout with the id gl is well accessible from within the screen class/object (I used gl=gl in the .kv file and gl=ObjectProperty() in the ScreenMenue class). If i call a on_release methode (in the ScreenMenue class) from a butten on that screen, I can print out the gl object reference (it is: <Screen name='menue'>) and I can add another button to the GridLayout using self.gl.add_widget(Button()) from within that methode.
But how can I access that GridLayout from any other methode or function in python if I don't have a reference (self) to the instance of ScreenMain.
Is it possible to let kivy give that Screen an id when its instanciated in python like I would normally do when creating an object  (scr = ScreenMenue())? -> ScreenMenue(scr).gl.add_widget...
Or can I access it in a way like Screen(name = 'menue').gl.add_widget(button()) ?

Elliot Garbus

unread,
May 20, 2021, 11:27:17 PM5/20/21
to kivy-...@googlegroups.com

How you add the button depends on where you are…

For example:

 

class MyApp(App):

   
def on_start(self):
       
self.root.get_screen('menue').ids.gl.add_widget(Button(text='Test Button'))

 

In this example self is app, root is the root widget, a ScreenManger.  The ScreenManager has a method, get_screen() that returns the Screen instance of the same name.  ids access the ids dict on the selected screen.

 

In a different screen you could access the screen manager with the manager attribute

 

class AnotherScreenClass(Screen):

    def add_button(self):
       
self.manager.get_screen('menue').ids.gl.add_widget(Button(text='Test Button'))

--
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/b29778dd-3c87-48c4-aa39-e9be0e9a4799n%40googlegroups.com.

 

Nobody999

unread,
May 21, 2021, 10:44:05 AM5/21/21
to Kivy users support
Works great - thank you!

I was afraid that it would be easy. ;-) It's hard if you are not familiar with OOP. I was already close, but I still have great problems understanding the structure of the Kivy elements. I let print the Elements and their Types step by Step and found the following.

self.root.get_screen('menue'). ids.gl.add_widget(...) or app.root.get_screen('menue'). ids.gl.add_widget(...) means:
- app / self references the application's MyApp object  (<__main__.TazApp object at 0x...>
- This has a property "root", which references the ScreenMngr object (of which there can only be one?).  (<__main__.ScreenMngr object at 0x...>)
- The ScreenMngr Object has a get_screen() method that returns the ScreenMenue object I'm looking for.  (<Screen name = 'menue'>) .
- The screen object has an ids property, which is a list (class ObservableDict) of object (WeakProxy) references to the widgets.  ({'gl': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x...>>})
- gl is the object reference (class WeakProxy) to the widget (GridLayout) I am looking for.  (<kivy.uix.gridlayout.GridLayout object at 0x...>)

So it is in a sense a chain of properties and methods that leads from object (reference) to object (reference) to the widget object that I was looking for.

Using self.manager.get_screen('menue'). Ids.gl.add_widget (Button (text = 'Test Button')) from the Screen Class means:
- self is the <Screen name = 'menue'> object.
- It has a manager property that references the ScreenManager object (which was root above).
- From then on the procedure is the same.

What is this <screen name = 'menu'>? It looks and behaves differently from the other elements.
PyCharm's code completion shows me 'root' as a possible element (field) in app, get_screen() as a possible element (methode) in root.
But I am not offered ids as a possible element in get_screen('menue') although gl is shown as a possible element in ids. I guess the methode would have to be executed to know that it returns an ids list - which is only possible at runtime, not while coding.
The Kivy help says: Screen is an element intended to be used with a ScreenManager. Name is a StringProperty of Screen.
And Screen is a RelativeLayout which is a class. So Screen is an object of the RelativeLayout type. Then why isn't it called <__ main __. RelativeLayout object at 0xblabla>?

So what is the difference between <Screen name = 'menue'> and the other object references?

Elliot Garbus

unread,
May 21, 2021, 4:59:11 PM5/21/21
to kivy-...@googlegroups.com

The kivy widgets are arranged in a tree.  There can be only one root widget.  The app class has a property root, the root widget.  In your app that root widget is a Screenmanager.   A kivy program can have only one root widget, but it can have many ScreenManagers.

 

What is this <screen name = 'menu'>? It looks and behaves differently from the other elements.

The ScreenManager method get_screen(‘screen_name’) takes the screen name as a parameter, and returns the Screen widget associated with that name.  ScreenManagers are unique in that there are multiple screens that are at the same level of the widget tree, the name is used to select the screen. 

 

PyCharm’s code completion is not perfect. 

 

A Screen is a Screen, it inherits from a RelativeLayout.

 

 

From: Nobody999
Sent: Friday, May 21, 2021 7:44 AM
To: Kivy users support

--

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.

Nobody999

unread,
May 22, 2021, 3:15:56 AM5/22/21
to Kivy users support
I'm slowly starting to understand a little better how Kivy and Python work together. I am not a programmer, although I have always programmed a little for a long time. As a teenager I taught myself procedural programming (with Basic) - at that time object orientation was not yet widespread, there was no internet yet and I only had a finger-thick command reference, not even a programming textbook. ;-) I played around with cobol, C, C#, PHP and others later. I know the basic concepts of object orientation, but I have little experience with OO programming and even less with GUIs. But it's very exciting and Kivy is obviously very capable. Anyway, it's fun and I keep trying.

Thanks for your help.

By the way, which IDE would you recommend for python?

Elliot Garbus

unread,
May 22, 2021, 9:58:28 AM5/22/21
to kivy-...@googlegroups.com

I use Pycharm

 

Here are a few resource I hope you find helpful.

 

  • I like this article on Python OOP - https://realpython.com/python3-object-oriented-programming/
  • This book on kivy,  Creating Apps in Kivy by Dusty Phillips,  is helpful for learning more about kv and how it works with python.  One of the examples uses a deprecated widget ListView (It has been replaced with RecycleView) but there is still value in the book.   You can find free pdfs if you do a search.
  • https://checkio.org/  Checkio presents small programming problems in a game-ified framework.  It was very helpful in learning how to us the capabilities of python.  When problems are completed you can see the code of others – and the top voted results.

 

From: Nobody999
Sent: Saturday, May 22, 2021 12:16 AM
To: Kivy users support
Subject: Re: [kivy-users] How to access widgets in screens using ScreenManager?

 

I'm slowly starting to understand a little better how Kivy and Python work together. I am not a programmer, although I have always programmed a little for a long time. As a teenager I taught myself procedural programming (with Basic) - at that time object orientation was not yet widespread, there was no internet yet and I only had a finger-thick command reference, not even a programming textbook. ;-) I played around with cobol, C, C#, PHP and others later. I know the basic concepts of object orientation, but I have little experience with OO programming and even less with GUIs. But it's very exciting and Kivy is obviously very capable. Anyway, it's fun and I keep trying.

 

Thanks for your help.

 

By the way, which IDE would you recommend for python?

--

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.

Nobody999

unread,
May 24, 2021, 12:27:02 PM5/24/21
to Kivy users support
I found Dusty Phillips' book a few weeks ago. I even managed to replace the ListView with a RecycleView - although it took me some time.
The article didn't bring me much new information. I actually know the basics. The implementation in Python / Kivy gives me more problems in detail. But your help has improved my understanding a lot.
I'll have a look at Checkio.org in the next few days.

Thanks for the tips.

coutinh...@gmail.com

unread,
Apr 20, 2022, 9:54:03 AM4/20/22
to Kivy users support
I have something similar:

I have two ScreenManagers.
The first one is called id: screen_manager_login
It manages 3 screens:

ScreenManager:
    id: screen_manager_login:
   
    LoginScreen:
        name: 'screen_login'
   
    MainScreen:
        name: 'main_screen'

    ScreenSingUp:
        name: 'singup_screen'

The second ScreenManager is inside

<MainScreen>:
    name: 'main_screen'

and it's called screen_manager
It manages 4 screens:

        ScreenManager:
            id: screen_manager

            ScreenRecycleView

            ScreenOne:
                name: "scr 1"

            ScreenTwo:
                name: "scr 2"
           
            ScreenItem


my problem is:
With the ScreenRecycleView screen open, double-clicking on the image will save the image in the class below, specifically in selected_image:

class TestNavigationDrawer(MDApp):
    selected_image = StringProperty()

    def build(self):
        return Builder.load_string(KV)

Then I try to get this image from another screen, if I can't, I open a help request here to see how I get this image from another screen.

Elliot Garbus

unread,
Apr 20, 2022, 10:15:40 AM4/20/22
to kivy-...@googlegroups.com

I thought you resolved this issue.  If this is still a problem, post a minimal runnable example.

Elias Coutinho

unread,
Apr 20, 2022, 2:11:27 PM4/20/22
to kivy-...@googlegroups.com
Yes, I had solved it, but we entered the phase of refactoring the code and in this refactoring I separated the screens into files.
I had to replace the button with a click on the image and that's where I stopped, I can't call the new window by double clicking on the image.

After that I'll see if I can get the clicked image and play on the new screen.

I compressed this code as much as possible to send you, despite having 5 files it is more readable thanks to your screen separation tip.

The main problem is found on line 94 of the screen_lista.

It is attached here.
My_code.zip

Elliot Garbus

unread,
Apr 20, 2022, 7:31:52 PM4/20/22
to kivy-...@googlegroups.com

In the screen_lista.py file, as part of a Screen (ScreenRecycleView) you want to access another screen that is under the ScreenManager that is the root widget.

In ScreenRecycleVIew, the attribute self.manager refers the screen_manager.  This is because the ScreenRecycleView is instanced under that ScreenManager.

 

The ScreenManager you want to access is actually the root widget of the app.  Get the app, and the root widget (screen_manager_login), then access the screen.

 

def on_double_tap(self, *args):
   
print("<on_double_tap> event")
    app = MDApp.get_running_app()
   
print(app.root.get_screen('main_screen').ids)

 

 

 

 

From: Elias Coutinho
Sent: Wednesday, April 20, 2022 11:12 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] How to access widgets in screens using ScreenManager?

 

Yes, I had solved it, but we entered the phase of refactoring the code and in this refactoring I separated the screens into files.

--

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.

Elias Coutinho

unread,
Apr 20, 2022, 8:26:53 PM4/20/22
to kivy-...@googlegroups.com
I believe this time I sent you a file that is easier to read.
It was perfect what I wanted.
Now I'm going to run in search of sending the clicked image to app.selected_image.

I'm not finding an ImageCard source to send.

I'm even embarrassed with so much dependency but the two screen managers confused me a lot.



--
Elias Coutinho.
Aprender sobre alguns assuntos é fundamental.
Aprender sobre Deus é indiscutivelmente o melhor conteúdo.

Elias Coutinho

unread,
Apr 20, 2022, 9:04:30 PM4/20/22
to kivy-...@googlegroups.com
I just need to get the source found in screen_lista.py and send it to selected_image in the TestNavigationDrawer class of the main.py file

Elliot Garbus

unread,
Apr 21, 2022, 1:19:43 AM4/21/22
to kivy-...@googlegroups.com

You want to add the touch behavior to your ImageCard.  This is what you want the user to touch.

Inside ImageCard, self.source is the filename.  In the appropriate touch routine, add the code below:

app = MDApp.get_running_app()

app.selected_image = self.source

Elias Coutinho

unread,
Apr 21, 2022, 7:43:43 AM4/21/22
to kivy-...@googlegroups.com
It was my first choice.
It looks like it prints the event 6 times.
That's why I left there.
I attached a screenshot of the event print, double clicked and it printed six times.

Captura de tela de 2022-04-21 08-40-29.png

Elias Coutinho

unread,
Apr 21, 2022, 6:44:51 PM4/21/22
to kivy-...@googlegroups.com
I opened an issue on kivymd's github. I'll use the long press for now.
But my brother, this two screenmanager thing really bugged me.

In the file screen_tela_item.py n in the on_release event of the last MDFloatingActionButton I'm wanting to go back to the previous screen and it says it doesn't recognize transition!

AttributeError: 'str' object has no attribute 'transition'

I already tried like this:
                 app.root.get_screen('main_screen').ids.screen_manager.current = 'recycle_view' app.root.get_screen('main_screen').ids.screen_manager = 'left' #'right'
               
and so:
                 app.root.get_screen('main_screen').ids.screen_manager.transition.direction = 'right'
                 app.root.get_screen('main_screen').ids.screen_manager.current = 'recycle_view'

I'm leaning to make the decision to use only one screen manager and block the toolbar when logging in.

Elliot Garbus

unread,
Apr 21, 2022, 6:52:14 PM4/21/22
to kivy-...@googlegroups.com

There is something odd there with the TouchBehavior, the recycleview and the MDCard…

As a workaround, I added the touch behavior to the RecycleGridLayout and get the info from with widget on a double_tap.

 
from kivymd.app import MDApp
from kivy.properties import ListProperty
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen
from kivymd.uix.card import MDCard
from kivymd.uix.behaviors import TouchBehavior
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.lang import Builder
from random import sample, choice
from string import ascii_lowercase

sample_images = [
   
"https://cdn.neemo.com.br/uploads/settings_webdelivery/logo/5591/No-Image.jpg",
   
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/SL_Bundala_NP_asv2020-01_img08.jpg/640px-SL_Bundala_NP_asv2020-01_img08.jpg",
   
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Palaeobranchiostoma_hamatotergum.jpg/482px-Palaeobranchiostoma_hamatotergum.jpg",
   
"https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/No_image_available.svg/1024px-No_image_available.svg.png",
]

## A T EN C A O o click para chamar a tela de imagem será aqui!
Builder.load_string("""
<ImageCard>
    id: ec
   
    size_hint: ('180dp', None)
    ripple_behavior: True
    source:''
    text:''

    orientation:'vertical'

    FitImage:
        pos_hint: {'center_x': .5, 'center_y': .25}
        source: root.source
        size_hint: None, None
        width: dp(150)
        height: dp(150)
        #radius: [99, 99, 99, 99]

    MDBoxLayout:
        orientation:'horizontal'
        MDLabel:
            text:root.text
            halign:"center"
            #bold: True



<ScreenRecycleView>:
    name: 'screen_listagem'

    FloatLayout:

        MDBoxLayout:
           
            pos_hint: {'center_x': 0.5, 'center_y': 0.3}
            RecycleView:
                id: rv
                data: root.image_data
                viewclass: "ImageCard"
                TouchRecycleGridLayout:
                    screen: root
                    cols: 2
                    default_size: dp(155), dp(200)
    
                    padding: dp(5) # Determina o espaçamento entre um widget e outro pelos lados              
                    spacing: dp(5) # Determina o espaçamento entre um widget e outro de cima para baixo
                    #radius:dp(25)
                    # Tamanho do retangulo com a imagem e os textos
                    #default_size: dp(155), dp(75) # Determina o tamanho do widget (Largura, Altura)
                    default_size_hint: 1, None ##### Define a largura do widget sempre próximo as bordas
                    size_hint_y: None ##### Associado a default_size_hint
                    height: self.minimum_height
                    #ripple_behavior: True"""
)   


class TouchRecycleGridLayout(TouchBehavior, RecycleGridLayout):
    screen = ObjectProperty()

   
def on_double_tap(self, touch, *args):
        i =
self.get_view_index_at(touch.pos)
        w =
self.parent.view_adapter.views[i]
        app = MDApp.get_running_app()
        app.selected_image = w.source
       
self.screen.manager.current = 'tela_item'



class ImageCard(MDCard):
    dialog =
None
   
text = StringProperty()
    source = StringProperty()


class ScreenRecycleView(Screen):
    image_data = ListProperty()

   
def on_kv_post(self, base_widget):
       
self.ids.rv.data = [
            {
               
'source': choice(sample_images),
               
'text': ''.join(sample(ascii_lowercase, 6))

            }
           
for x in range(50)
        ]

Elliot Garbus

unread,
Apr 21, 2022, 7:03:15 PM4/21/22
to kivy-...@googlegroups.com

Here you go:

 


MDFloatingActionButton:
    pos_hint: {'center_x': .9, 'center_y': .9}
    icon: "arrow-left-bold"
    elevation_normal: 12 
    on_release:
        print('The problem button')
        # root is the TelaItem screen instance.  Each screen has a manager...
        # this is the screen manager with the id screen_manager.
        root.manager.direction = 'right'
        root.manager.current = 'screen_listagem'

Elias Coutinho

unread,
Apr 21, 2022, 8:21:16 PM4/21/22
to kivy-...@googlegroups.com
Elliot Garbus,

All right, working!
I'm going to open a documentation to leave these tips saved.

Reply all
Reply to author
Forward
0 new messages