Trying to understand "Dropdown" and "functools.partial"

30 views
Skip to first unread message

sergen

unread,
Oct 25, 2019, 4:34:13 PM10/25/19
to Kivy users support
Made a small dropdown test script.
There are two text fields. Entering text in the upper field, the number of characters entered is displayed in the lower field.
When you click on the bottom field, the menu must appears.
Something is not clear in the operation of the script.

KV file :
#:kivy 1.11.0
# -*- coding: utf-8 -*-
#:import partial functools.partial



<Root>:

GridLayout:
id: gl
cols: 1
rows:1
size: root.size
spacing: 10

WearItem:
id: wearitem
size_hint_y: .5



<WearItem>:

some_text:some_text
some_callback:some_callback


BoxLayout:

id: gll_1
orientation: 'vertical'
spacing: 200
padding: 3

TextInput:
id: some_text
hint_text: "first area"
text: ""
on_text:  root.name_inspector( self)


TextInput:
id: some_callback
text: ''
hint_text: "second area"
on_touch_up: root.add_dropdown(self)



<CustomDropDown>:
container: grid
GridLayout:
id: grid
cols:1
rows:2

Button:
text: 'Choose ONE'
size_hint_y: None
height: 60
on_release: root.select(self.text); root.dismiss()
Button:
text: 'Choose TWO'
size_hint_y: None
height: 60
on_release: root.select(self.text); root.dismiss()



main.py
!/usr/bin/env python3
# -*- coding: utf-8 -*-


from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
from kivy.properties import StringProperty
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.dropdown import DropDown
from functools import partial



class CustomDropDown(DropDown):
pass

class WearItem(BoxLayout):
group_count = StringProperty()
rut = ObjectProperty()

# lower textinput callback, return dropdown menu
def add_dropdown(self, inst):

dropdown = CustomDropDown() 
dropdown.open(inst)
dropdown.bind(on_select=lambda instance, x: setattr(inst, 'text', x))

# upper textinput callback
def name_inspector(self, inst):
if len(inst.text) != 0  :
print ('name_inspector WORKING ', inst.text)
self.ids.some_callback.text = str(len(inst.text))
else:
print ('name_inspector WORKING ZERO')
self.ids.some_callback.text = 'ZERO'


class Root(Widget):

def __init__(self):
super(Root, self).__init__()


class MyApp(App):
def build(self):
Window.size = (200, 400)
return Root()



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


1) for some reason, the dropdown list drops out when you click on any place in the window .
2) if you click on the lower text field, then the buttons of the drop-down menu can be pressed twice. If you click on any other place in the window, then you can click on the menu buttons only once. What is the reason for this behavior?
3) how can I rewrite the expression (  dropdown.bind (on_select = lambda instance, x: setattr (inst, 'text', x))  ) without using "lambda", but using "def"  ?
4) how can I access the return result (or instance) of the dropdown menu in the main file, inside the "add_dropdown" function?
def add_dropdown(self, inst):
dropdown = CustomDropDown() 
dropdown.open(inst)
dropdown.bind(on_select= <HOW TO GET A BUTTON INSTANCE > )



5) The "name_inspector function" is executed at boot time. I need it to be executed only when I enter text in the upper text field. I know that it is necessary to use "functools.partial", but I don’t quite understand what part of the code it should be in. I tried to insert it into the .kv file, but then this function stops executing:
TextInput:
 id
: some_text
 hint_text
: "first area"
 text
: ""
 on_text
:  partial(root.name_inspector, self)

I ask for help in resolving these issues. 

Elliot Garbus

unread,
Oct 26, 2019, 1:17:08 PM10/26/19
to kivy-...@googlegroups.com
  1. for some reason, the dropdown list drops out when you click on any place in the window .

 

You are using on_touch_up: on the text input field.  This is not the typical way to use on_touch_up.  The touch events happen at the window level and get propagated to all the widgets.

Read: https://kivy.org/doc/stable/guide/events.html

You need to test if the touch was within the widget using collide_point()

You could use on_touch_up and on_touch_down to create on_release and on_press events, much like buttons have – The TextInput does not have these.

 

  1. if you click on the lower text field, then the buttons of the drop-down menu can be pressed twice. If you click on any other place in the window, then you can click on the menu buttons only once. What is the reason for this behavior?

 

This has to do with the way touch events are propogated.  I suspect the on_touch_up event is happening twice when you click on the lower box, you have 2 overlapping sets of dropdowns.

 

 

  1. how can I rewrite the expression (  dropdown.bind (on_select = lambda instance, x: setattr (inst, 'text', x))  ) without using "lambda", but using "def"  ?

 

def my_callback_function():

    print(‘you called?’)

 

dropdown.bind (on_select=my_callback_function)

 

4) how can I access the return result (or instance) of the dropdown menu in the main file, inside the "add_dropdown" function?

def add_dropdown(self, inst):

   dropdown = CustomDropDown() 

   dropdown.open(inst)

   dropdown.bind(on_select= <HOW TO GET A BUTTON INSTANCE > )

 

I’m not sure I understand the question.  I you give the button an id in kv, you can use ids in python to access the button.  Read about ids:
https://kivy.org/doc/stable/api-kivy.lang.html?highlight=language#

 

5) The "name_inspector function" is executed at boot time. I need it to be executed only when I enter text in the upper text field. I know that it is necessary to use "functools.partial", but I don’t quite understand what part of the code it should be in. I tried to insert it into the .kv file, but then this function stops executing:

TextInput:
 id
: some_text
 hint_text
: "first area"
 text
: ""
 on_text
:  partial(root.name_inspector, self)

In your code:

on_textroot.name_inspector(self)

 

name_inspector is called when the text changes.  This seems to be working properly.

--
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/a68a70a0-ab69-48e5-a892-4288259535fd%40googlegroups.com.

 

sergen

unread,
Oct 26, 2019, 5:32:38 PM10/26/19
to Kivy users support

 

  1. how can I rewrite the expression (  dropdown.bind (on_select = lambda instance, x: setattr (inst, 'text', x))  ) without using "lambda", but using "def"  ?

 

def my_callback_function():

    print(‘you called?’)

 

dropdown.bind (on_select=my_callback_function)


Your answer is clear. But I had in mind something else. This question intersects with question number 4.
The DropDown menu should return the instance of the pressed button. 
This code has all the elements for this:  dropdown.bind (on_select = lambda instance, x: setattr (inst, 'text', x))
Lambda receives as an argument the pressed button instance and the x variable. And fulfills the function setattr.
I don’t understand where the lambda got access to the instance? Therefore, I ask you to translate this lambda code to another form with "def". In what form do I assume the code: 

        def callback(instance):
               pass   
        
    dropdown.bind(on_select= callback(dropdown.<GET_THE_DROPD_INSTANCE>) ) #something like this

 

5) The "name_inspector function" is executed at boot time. I need it to be executed only when I enter text in the upper text field. I know that it is necessary to use "functools.partial", but I don’t quite understand what part of the code it should be in. I tried to insert it into the .kv file, but then this function stops executing:

TextInput:
 id
: some_text
 hint_text
: "first area"
 text
: ""
 on_text
:  partial(root.name_inspector, self)

In your code:

on_textroot.name_inspector(self)

 

name_inspector is called when the text changes.  This seems to be working properly.

you are right, the given code works, but "name_inspector" is executed immediately when the program starts. And then also when entering text. To prevent code execution when the program starts, the partial function is used. Typically, in examples, it is used in the main.py file. (example from the site http://inclem.net/2014/03/11/kivy/kivy_bind_method/)
some_screenmanager.bind(current=partial(a_function,arg1, arg2))

But I'm trying to do this in my.kv file. In this form, the function stops working. It just doesn't execute. Trying to understand wgy ?
TextInput:
    on_text
:  partial(root.name_inspector, self)

Of course, there is an option to make a bind in the main file.
ti = self.ids.some_text
ti.bind(on_text = partial(self.name_inspector(ti.text) ) #something like this

But I wonder if there is an opportunity to do this in a KV lan ?.
 
P.S. Thank you for your time !

 

Elliot Garbus

unread,
Oct 26, 2019, 6:09:02 PM10/26/19
to kivy-...@googlegroups.com
5)  on_text:  if self.text: root.name_inspector(self)
This way name inspector will not be called when the string is empty.

 

3) Here is how you use funtools partial…

 

Import functools

 

def my_callback(i)

    print(f’you called with instance {i}’)

 

cb = functools.partial(my_callback, instance)

dropdown.bind (on_select =cb)

 

There is a great resource that provides examples of the standard lib functions: The python3 module of the week.

https://pymotw.com/3/index.html

 

 

From: 'sergen' via Kivy users support
Sent: Saturday, October 26, 2019 2:32 PM
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.

Reply all
Reply to author
Forward
0 new messages