Hey guys trying to create an app with a dropdown button but it seems to not work (the full app has GridLayout with 7 rows and this FloatLayout in the example is one of the rows) I've tried with GridLayout, BoxLayout, and FloatLayout and still doesn't appear on the app. Any ideas what's wrong here?
.py file
class WindowManager(ScreenManager, Screen):
TestMe = ObjectProperty(None)
text_lists = ['hi', 'nice one', 'another one']
class TestMe(Screen, FloatLayout):
global text_lists
main_button = ObjectProperty(None)
selected_list = 'SELECTED'
top_layout = ObjectProperty(None)
top_layout = GridLayout(cols=4)
def __init__(self, **kwargs):
super(TestMe, self).__init__(**kwargs)
self.dropdown = DropDown()
self.create_drop_down()
self.create_go_button()
def create_drop_down(self):
for list_name in text_lists:
# When adding widgets, we need to specify the height manually
# (disabling the size_hint_y) so the dropdown can calculate
# the area it needs.
btn = Button(text= list_name, size_hint_y=None, height=88, width=400, background_color=(41/255, 21/255, 228/255, 1))
# for each button, attach a callback that will call the select() method
# on the dropdown. We'll pass the text of the button as the data of the
# selection.
btn.bind(on_release=lambda btn: self.dropdown.select(btn.text),
on_press=lambda btn: self.select_list(btn.text))
# then add the button inside the dropdown
self.dropdown.add_widget(btn)
# create a big main button
self.main_button = Button(text='Choose A List', size_hint=(None, None), height=88, width=400, background_color=(41/255, 21/255, 228/255, 1))
# show the dropdown menu when the main button is released
# note: all the bind() calls pass the instance of the caller (here, the
# mainbutton instance) as the first argument of the callback (here,
# dropdown.open.).
self.main_button.bind(on_release=self.dropdown.open)
# one last thing, listen for the selection in the dropdown list and
# assign the data to the button text.
self.dropdown.bind(on_select=lambda instance, x: setattr(self.main_button, 'text', x))
self.top_layout.add_widget(self.main_button)
def create_go_button(self):
go_btn = Button(text="Go!", size_hint=(None, None), height=88, width=400, background_color=(41/255, 21/255, 228/255, 1))
self.top_layout.add_widget(go_btn)
def select_list(self, selected):
self.selected_list = selected
class MyTest(App):
def build(self):
return kv
if __name__ == '__main__':
kv = Builder.load_file('test_kv.kv')
MyTest().run()
test_kv.kv file
WindowManager:
TestMe:
<TestMe>:
name: "testy"
id: testy
top_layout: top_layout
FloatLayout:
Label:
text: 'Test Screen'
font: 'Aharoni'
font_size: 24
pos_hint: {"left": 0.45, "y": 0.45}
GridLayout:
pos_hint: {"top": 0.9}
size_hint: 1, 0.8
rows: 2
spacing: 10
padding: 10
GridLayout:
id: top_layout
cols: 4
Button:
text: "Fun!"
Label:
text: "This is a test"
Button:
text: "Run!"
The problem is that you are calling create_drop_down()
and create_go_button()
in the __init__()
method of the TestMe
class. Since you also define a <TestMe>:
rule in your kv
, there is a conflict. According to the somewhat unclear documentation, the kv
rule is applied after the __init__()
is run. That means that any Widgets
added to TestMe
in its __init__()
will be overwritten by the Widegets
specified in the kv
rule. The possible solutions are to add all the children of TestMe
in either the __init__()
method or in the kv
, or to move the adding of Widgets
out of the __init__()
method. Here is a modified version of your code that does the latter approach:
class WindowManager(ScreenManager, Screen):
TestMe = ObjectProperty(None)
text_lists = ['hi', 'nice one', 'another one']
class TestMe(Screen, FloatLayout):
global text_lists
main_button = ObjectProperty(None)
selected_list = 'SELECTED'
top_layout = ObjectProperty(None)
#top_layout = GridLayout(cols=4)
def __init__(self, **kwargs):
super(TestMe, self).__init__(**kwargs)
self.dropdown = DropDown()
Clock.schedule_once(self.create_drop_down)
Clock.schedule_once(self.create_go_button)
# self.create_drop_down()
# self.create_go_button()
def create_drop_down(self, *args):
for list_name in text_lists:
# When adding widgets, we need to specify the height manually
# (disabling the size_hint_y) so the dropdown can calculate
# the area it needs.
btn = Button(text= list_name, size_hint_y=None, height=88, width=400, background_color=(41/255, 21/255, 228/255, 1))
# for each button, attach a callback that will call the select() method
# on the dropdown. We'll pass the text of the button as the data of the
# selection.
btn.bind(on_release=lambda btn: self.dropdown.select(btn.text),
on_press=lambda btn: self.select_list(btn.text))
# then add the button inside the dropdown
self.dropdown.add_widget(btn)
# create a big main button
# self.main_button = Button(text='Choose A List', size_hint=(None, None), height=88, width=400, background_color=(41/255, 21/255, 228/255, 1))
self.main_button = Button(text='Choose A List', background_color=(41/255, 21/255, 228/255, 1))
# show the dropdown menu when the main button is released
# note: all the bind() calls pass the instance of the caller (here, the
# mainbutton instance) as the first argument of the callback (here,
# dropdown.open.).
self.main_button.bind(on_release=self.dropdown.open)
# one last thing, listen for the selection in the dropdown list and
# assign the data to the button text.
self.dropdown.bind(on_select=lambda instance, x: setattr(self.main_button, 'text', x))
self.top_layout.add_widget(self.main_button)
def create_go_button(self, *args):
# go_btn = Button(text="Go!", size_hint=(None, None), height=88, width=400, background_color=(41/255, 21/255, 228/255, 1))
go_btn = Button(text="Go!", background_color=(41/255, 21/255, 228/255, 1))
self.top_layout.add_widget(go_btn)
def select_list(self, selected):
self.selected_list = selected
class MyTest(App):
def build(self):
return kv
I have modified the code to call the create
methods using Clock.schedule_once()
, so that it happens after the kv
rule is applied.
I have also removed the size_hint
and size
arguments to the Buttons
created to allow the GridLayout
to size them. I have also commented out some unnecessary code.