Dropdown list display problem

93 views
Skip to first unread message

Shoumik Das

unread,
May 30, 2020, 7:50:05 AM5/30/20
to Kivy users support
Hi, I am trying to test a drop-down implementation but the drop-down list is not getting displayed. It shows the list for a fleeting second and then gets dismissed.

Here is the code:

Python:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.properties import NumericProperty, StringProperty
from kivy.uix.dropdown import DropDown

class CustomTextInput(TextInput):
   
# Numeric property defined for use in both Python and Kivy (binding)
    max_characters
= NumericProperty(0)
   
# Override the default behaviour of the insert_text method
   
def insert_text(self, substring, from_undo=False):
       
if len(self.text)==self.max_characters and self.max_characters>0:
            substring
=""
       
TextInput.insert_text(self, substring, from_undo)


class ImageLabel(BoxLayout):
    source
= StringProperty('atlas://data/images/defaulttheme/audio-volume-high')
    text
= StringProperty('default text')


class ImageLabelButton(ButtonBehavior, ImageLabel):
   
pass


class DropDownScreen(Screen):
   
pass


class DropApp(App):
   
def build(self):
       
# Initialize root widget
       
# DropDown Screen Instance
        dds
= DropDownScreen()
       
return dds


if __name__ == '__main__':
   
# Run application
   
DropApp().run()

Kivy

<DropDownScreen>:
    name: 'dropdown_screen'
    canvas.before:
        Color:
            rgba: 255/255, 255/255, 255/255, 1
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            orientation: 'horizontal'
            Label:
                text: 'No. of drop-down buttons:'
                markup: True
                color: 0, 0, 0, 1
            CustomTextInput:
                max_characters: 2
                multiline: False
                input_filter: 'int'
        BoxLayout:
            ImageLabelButton:
                id: parent_button
                source: 'expand-arrow-48.png'
                text: 'Expand Drop-Down'
                on_release: dropdown.open(self)
                on_parent: dropdown.dismiss()
                size_hint_y: None
                height: '48dp'
            DropDown:
                id: dropdown
                on_select:
                    parent_button.text = str(args[1])
                    print("Invoked inside dropdown")
                    print(args, root, self, "\n") # root - Screen, self - DropDown object
                ImageLabelButton:
                ImageLabelButton:
        BoxLayout:
            Label:
                text: 'Test dynamic drop-down'
                markup: True
                color: 0, 0, 0, 1


<ImageLabel>:
    orientation: 'horizontal'
    size_hint_y: None
    height: '48dp'
    spacing: 1
    Image:
        keep_ratio: True
        source: root.source  # root - ImageLabel
    Label:
        markup: True
        text: root.text  # root - ImageLabel
        color: 0, 0, 0, 1


<ImageLabelButton>:
    on_release:
        app.root.ids.dropdown.select(self.text)  # app.root - Screen
        print("Invoked inside ImageLabelButton rule")
        print(app.root, root, self, "\n") # app.root - Screen, root - ImageLabelButton object, self - ImageLabelButton object

<CustomTextInput>:
    use_bubble: True
    use_handles: True

I think the on_parent: dropdown.dismiss() statement is closing the list very fast. If I comment out this line, the list initially gets displayed but then closed once the button is clicked.

Any suggestion on how this may be fixed?

Thanks

Robert Flatt

unread,
May 30, 2020, 12:13:45 PM5/30/20
to Kivy users support
I think the on_parent: dropdown.dismiss() statement is closing the list very fast.
Its doing exactly what you are telling it to do. If that is not what you want, don't do it ;)

If I comment out this line, the list initially gets displayed but then closed once the button is clicked.

Elliot Garbus

unread,
May 30, 2020, 12:34:34 PM5/30/20
to kivy-...@googlegroups.com

There are a few issues here fixed below:

  1. The on_release defined in the instance and the rule,  are both firing.  I created a new button for the top, called ImageLabelButtonTop, defined in the python file simply as:
 
class ImageLabelButtonTop(ButtonBehavior, ImageLabel):
   
pass

 

This removes the conflict.

 

  1. You were not properly addressing the dropdown in the <ImageLabelButton> rule.  You have the drop down in a screen.  I moved the root widget to the kv file, and added a screenmanager and the screen.  Then addressed the select method for the dropdown.

 

 

 

 

<DropDownScreen>:
    name:
'dropdown_screen'
   
canvas.before:
       
Color:
           
rgba: 255/255, 255/255, 255/255, 1
       
Rectangle:
           
pos: self.pos
            size
: self.size
   
BoxLayout:
       
orientation: 'vertical'
       
BoxLayout:
           
orientation: 'horizontal'
           
Label:
               
text: 'No. of drop-down buttons:'
               
markup: True
               
color: 0, 0, 0, 1
           
CustomTextInput:
                max_characters:
2
               
multiline: False
                input_filter:
'int'
       
BoxLayout
:
            ImageLabelButtonTop:    
# conflct in the on_release defined for ImageLabelButton and this instance
               
id: parent_button
                
source: 'expand-arrow-48.png'
               
text: 'Expand Drop-Down'
               
on_release: dropdown.open(self)
               
on_parent: dropdown.dismiss()
               
size_hint_y: None
               
height: '48dp'
           
DropDown:
               
id: dropdown
               
on_select:
                    parent_button.
text = str(args[1])
                    print(
"Invoked inside dropdown")
                    print(args,
root, self, "\n") # root - Screen, self - DropDown object
               
ImageLabelButton:
                   
text: 'ImageLabelButton 1'
               
ImageLabelButton:
                   
text: 'ImageLabelButton 2'
           
Label:
               
text: 'Space in the BoxLayout'
       
BoxLayout:
            
Label:
               
text: 'Test dynamic drop-down'
               
markup: True
               
color: 0, 0, 0, 1


<ImageLabel>:
   
orientation: 'horizontal'
   
size_hint_y: None
   
height: '48dp'
   
spacing: 1
   
Image:
        keep_ratio: True
       
source: root.source  # root - ImageLabel
   
Label:
       
markup: True
       
text: root.text  # root - ImageLabel
       
color: 0, 0, 0, 1


<ImageLabelButton>:
   
on_release
:
       
#app.root.ids.dropdown.select(self.text)  # app.root - Screen
       
app.root.ids.sm.get_screen('dropdown_screen').ids.dropdown.select(self.text)

        print(
"Invoked inside ImageLabelButton rule")
        print(
app.root, root, self, "\n") # app.root - Screen, root - ImageLabelButton object, self - ImageLabelButton object

<CustomTextInput>:
    use_bubble: True
    use_handles: True

BoxLayout:
   
ScreenManager:
       
id: sm
        DropDownScreen:

--
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/d2ea3e7b-06c9-4be6-9bf3-5e24b30c939c%40googlegroups.com.

 

Shoumik Das

unread,
May 30, 2020, 1:49:11 PM5/30/20
to Kivy users support
Thanks, Eliott. That worked. Thank you for your patience while I learn the tricks of Kivy. I am planning to integrate this in an app where the screen manager is the root widget. So, I tweaked your code slightly:

Python:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.properties import NumericProperty, StringProperty
from kivy.uix.dropdown import DropDown

class CustomTextInput(TextInput):
   
# Numeric property defined for use in both Python and Kivy (binding)
    max_characters
= NumericProperty(0)
   
# Override the default behaviour of the insert_text method
   
def insert_text(self, substring, from_undo=False):
       
if len(self.text)==self.max_characters and self.max_characters>0:
            substring
=""
       
TextInput.insert_text(self, substring, from_undo)


class ImageLabel(BoxLayout):
    source
= StringProperty('atlas://data/images/defaulttheme/audio-volume-high')
    text
= StringProperty('default text')


class ImageLabelButton(ButtonBehavior, ImageLabel):
   
pass


class ImageLabelButtonTop(ButtonBehavior, ImageLabel):
   
pass

class DropDownScreen(Screen):
   
pass


class MyScreenManager(ScreenManager):

   
pass


class DropApp(App):
   
def build(self):
       
# Initialize root widget

       
# Screen Manager instance
        sm
= MyScreenManager()

       
# DropDown Screen Instance
        dds
= DropDownScreen()

        sm
.add_widget(dds)
       
return sm



Kivy:

<DropDownScreen>:
            ImageLabelButtonTop: # conflct in the on_release defined for ImageLabelButton and this instance
                id: parent_button
                source: 'expand-arrow-48.png'
                text: 'Expand Drop-Down'
                on_release: dropdown.open(self)
                on_parent: dropdown.dismiss()
                size_hint_y: None
                height: '48dp'
            DropDown:
                id: dropdown
                on_select:
                    parent_button.text = str(args[1])
                    print("Invoked inside dropdown")
                    print(args, root, self, "\n") # root - Screen, self - DropDown object
                ImageLabelButton:
                    source: 'twitter-48.png'
                    text: 'Twitter'
                ImageLabelButton:
                    source: 'linkedin-2-48.png'
                    text: 'LinkedIn'

        BoxLayout:
            Label:
                text: 'Test dynamic drop-down'
                markup: True
                color: 0, 0, 0, 1


<ImageLabel>:
    orientation: 'horizontal'
    size_hint_y: None
    height: '48dp'
    spacing: 1
    Image:
        keep_ratio: True
        source: root.source  # root - ImageLabel
    Label:
        markup: True
        text: root.text  # root - ImageLabel
        color: 0, 0, 0, 1


<ImageLabelButton>
:
    on_release:
        app.root.get_screen('dropdown_screen').ids.dropdown.select(self.text)

        print("Invoked inside ImageLabelButton rule")
        print(app.root, root, self, "\n") # app.root - Screen, root - ImageLabelButton object, self - ImageLabelButton object

<CustomTextInput>:
    use_bubble: True
    use_handles: True


The drop-down buttons are now working as expected. Thanks for your help. Whenever I select a button, this is what gets printed in the console:

Invoked inside dropdown
(<kivy.uix.dropdown.DropDown object at 0x7f1a955c7c10>, 'LinkedIn') <Screen name='dropdown_screen'> <kivy.uix.dropdown.DropDown object at 0x7f1a955c7c10>

Invoked inside ImageLabelButton rule
<__main__.MyScreenManager object at 0x7f1a9ab6e118> <__main__.ImageLabelButton object at 0x7f1a95417048> <__main__.ImageLabelButton object at 0x7f1a95417048>

I am trying to understand the flow of Kivy and Python. I guess this is what is happening. Please correct me if I am wrong:

1) When an ImageLabelButton object is clicked inside the drop-down, the app.root.get_screen('dropdown_screen').ids.dropdown.select(self.text) statement triggers and sets the return value for args. In this case, 'Twitter' or 'LinkedIn'.
2) The on_select: parent_button.text = str(args[1]) sets the label of the parent button equal to the selected ImageLabelButton text. If I omit this step, the value for the correct button will still be set but the display of the parent button shall not change.

Is my understanding correct?

Thanks for the detailed explanation.

Shoumik Das

unread,
May 30, 2020, 2:00:57 PM5/30/20
to Kivy users support
Sorry to bother you but I have one last question for this particular thread:

on_select: parent_button.text = str(args[1]) changes the text label of the parent button. How can I change the image of the parent button and set it to the same image of the button which was clicked? I guess args can handle only two values: object and text/image.

Thanks in advance.

Elliot Garbus

unread,
May 30, 2020, 10:36:56 PM5/30/20
to kivy-...@googlegroups.com

On the on_release of the button selected set the image source in the top button.

 

app.root.get_screen('dropdown_screen').ids.parent_button.source = self.source

 

From: Shoumik Das
Sent: Saturday, May 30, 2020 11:01 AM
To: Kivy users support
Subject: [kivy-users] Re: Dropdown list display problem

 

Sorry to bother you but I have one last question for this particular thread:

--

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.

Elliot Garbus

unread,
May 30, 2020, 10:49:39 PM5/30/20
to kivy-...@googlegroups.com

Another option is to pass a tuple of the text and the image in the args:

 

on_release:
  
    app.root.ids.sm.get_screen('dropdown_screen').ids.dropdown.select((self.text,self.source))

 

and then unpack them in on_select

 

on_select:
    parent_button.
text = args[1][0]
    parent_button.source = args[1][1]

Shoumik Das

unread,
May 31, 2020, 12:57:22 AM5/31/20
to Kivy users support
Thanks a ton. I implemented the tuple argument technique. Works perfectly now!!

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-...@googlegroups.com.

--
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-...@googlegroups.com.

Reply all
Reply to author
Forward
0 new messages