Kivy ActionDropDown does not open - kivy

In the kivy gui below, when I press the button 'Pen', an ActionDropDown is supposed to come. I do not see it opening. But the 'Pen' button behaves as if a DropDown was opened with it (e.g. you can not press on it on the first try). What is wrong?
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.actionbar import ActionDropDown
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
Builder.load_string("""
<NoteScreen>:
BoxLayout:
orientation: 'vertical'
NoteScreenMenu:
Label:
text: 'i am a label'
<NoteScreenMenu>:
orientation: 'horizontal'
size_hint_y: None
height: 48
ActionBar:
pos_hint: dict(top=1)
ActionView:
use_separator: True
ActionPrevious:
title: 'Drawing Screen'
with_previous: False
ActionOverflow:
ActionButton:
id: pen_button
text: 'Pen'
on_release: root.pen_drop_down.open(self)
ActionButton:
text: 'Eraser'
<PenDropDown>:
Button:
text: 'Close this'
Button:
text: 'btn1'
""")
class NoteScreen(Screen):
pass
class NoteScreenMenu(BoxLayout):
def __init__(self, **kwargs):
super(NoteScreenMenu, self).__init__(**kwargs)
self.pen_drop_down = PenDropDown()
class PenDropDown(ActionDropDown):
pass
class TheApp(App):
def build(self):
return NoteScreen(name='note')
if __name__ == '__main__':
TheApp().run()

The problem was here:
<PenDropDown>:
Button:
text: 'Close this'
Button:
text: 'btn1'
I needed to add height information to buttons in the dropdown:
<PenDropDown>:
Button:
text: 'Close this'
height: 48
size_hint_y : None
Button:
text: 'btn1'
height: 48
size_hint_y : None

Related

How to disable a widget when a button from a different class is pressed?

Im making a Button which disables a TopAppBar menu form my user interface, the problem is that the Button is on one class an the TopAppBar in another, because one belongs to the screen and the other is shared between multiple screens.
code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivymd.uix.screen import MDScreen
Window.size = (300, 500)
navigation_helper = """
<UserScreen>
name: 'user_screen'
MDBoxLayout:
orientation: "vertical"
pos_hint: {"top": 1}
adaptive_height: True
padding: (0, "80dp" , 0, 0)
FloatLayout:
MDRaisedButton:
id: save_btn_user
text: "Guardar"
md_bg_color: "white"
text_color: "black"
font_size: 16.2
pos_hint: {"left":1, "bottom": 1}
size_hint: (.2,.2)
disabled: True
on_press: root.save_btn_press()
MDRaisedButton:
id: edit_btn_user
text: "Editar"
md_bg_color: "white"
text_color: "black"
font_size: 16.2
pos_hint: {"right":1, "bottom": 1}
size_hint: (.2,.2)
disabled: False
on_press:
root.disable_top_bar()
<MainScreen>:
name: "main_screen"
BoxLayout:
orientation: 'vertical'
MDTopAppBar:
id: title_bar
title: 'Dietas'
md_bg_color: 0.08,0.07,0.45
specific_text_color: 1,1,1
left_action_items: [["menu", lambda x: nav_drawer.set_state('toggle')]]
Widget:
MDNavigationLayout:
ScreenManager:
id: scr
UserScreen:
MDNavigationDrawer:
id: nav_drawer
BoxLayout:
orientation: 'vertical'
spacing: '8dp'
ScrollView:
MDList:
OneLineIconListItem:
text: 'Usuario'
on_press:
scr.current= 'user_screen'
title_bar.title = "Usuario"
nav_drawer.set_state('close')
IconLeftWidgetWithoutTouch:
icon: 'account'
on_press:
scr.current= 'user_screen'
title_bar.title = "Usuario"
nav_drawer.set_state('close')
MainScreen:
"""
class UserScreen(MDScreen):
def disable_top_bar(self):
self.a=MainScreen()
self.a.disable_top_barr()
class MainScreen(MDScreen):
def disable_top_barr(self):
self.ids.title_bar.disabled = True
print("testmsg")
class DemoApp(MDApp):
def build(self):
self.theme_cls.theme_style = ("Dark")
screen = Builder.load_string(navigation_helper)
return screen
DemoApp().run()
As you may have seen, I tried creating two method, one in the TopAppBar class disabling it and printing a test message(that its shown) and the other in the Button class calling that function.
With the following approach
def disable_top_bar(self):
self.a=MainScreen()
self.a.disable_top_barr()
you will see no change in your GUI because each time you call that method, it will create a new instance of MainScreen (which is not related to your GUI) and call the asked method on it.
In order to access that specific instance you can use the method get_running_app of MDApp as follows,
def disable_top_bar(self):
# First access the running app instance.
app = MDApp.get_running_app()
# Access the target screen that you set as the root widget.
main_screen = app.root
# Call the method.
main_screen.disable_top_barr()
As a note avoid using predefined names like a, b, g etc. when working with KivyMD widgets.

When creating a widget with a button, how to specify an ID?

So I already make an expanding list when pressing a button, the problem is that I can´t find I way to add an icon in the right part(as the sample one) of the ListItem
Code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivymd.uix.screen import MDScreen
from kivymd.uix.list import OneLineRightIconListItem
from kivymd.uix.list.list import IconRightWidget
Window.size = (300, 500)
navigation_helper = """
<DietScreen>:
Screen:
MDBoxLayout:
orientation: 'vertical'
padding: ("5dp", "65dp" , "5dp", "5dp")
pos_hint: {"top": 1}
MDScrollView:
MDList:
id: box
OneLineRightIconListItem:
id: hola
text: 'testitem'
on_press:
IconRightWidget:
icon: "dots-vertical"
MDRaisedButton:
text: "Nueva Dieta"
md_bg_color: "white"
text_color: "black"
font_size: 16.2
pos_hint: {"right": 1, "bottom": 1}
on_press: root.buttonClicked()
Screen:
MDNavigationLayout:
ScreenManager:
id: scr
DietScreen:
"""
class DietScreen(MDScreen):
def buttonClicked(self):
newButt = OneLineRightIconListItem(text='Button')
self.ids.box.add_widget(newButt)
class DemoApp(MDApp):
def build(self):
screen = Builder.load_string(navigation_helper)
return screen
DemoApp().run()
I tried to specify an id to the ListItem created, and in the same method after addind the ListItem, add an IconRightWidget with an icon determined, but it results in an error.
Replace your:
newButt = OneLineRightIconListItem(text='Button')
With:
newButt = OneLineRightIconListItem(IconRightWidget(icon='dots-vertical'), text="Button")

Kivy >> On Press method triggered twice

For some reason the On Press event for the instance of Word_button is being triggered twice. The code below demonstrates this.
To repeat the problem.
run the code below
click on the "CREATE LIST OF WORD" button. This create a list of buttons. Each button has Boolean property if the word is correct or not.
then click on the word buttons. When clicking the button, the print statements print the correct boolean variable and the text of the button.
Problem:
The print commands are run twice.
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import (NumericProperty, BooleanProperty)
from kivymd.app import MDApp
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.boxlayout import MDBoxLayout
kv = '''
<Word_button#MDRaisedButton>:
pos_hint: {'center_x': .5}
size_hint: 1, 1
font_size: "16sp"
on_press: self.check_word()
<Words_Box#MDBoxLayout>:
pos_hint: {'center_x': .5}
size_hint: 1, 1
Screen:
id: spelling_screen
name: "spelling_screen"
MDBoxLayout:
orientation: 'vertical'
padding: dp(15)
spacing: dp(10)
MDLabel:
text: 'Words'
Words_Box:
id: words_box
orientation: 'vertical'
padding: dp(15)
spacing: dp(10)
MDRaisedButton:
text: 'CREATE LIST OF WORDS'
on_release: root.ids.words_box.add_word_buttons()
'''
class Word_button(MDRaisedButton):
correct = BooleanProperty()
def check_word(self):
print('Answer is ', self.correct)
print('Button Text is ', self.text)
class Words_Box(MDBoxLayout):
def add_word_buttons(self):
app = MDApp.get_running_app()
words = ['$WORD 1', 'WORD 2', 'WORD 3']
for word in words:
correct = False
if '$' in word:
correct = True
word = word[1:]
btn = Word_button(text=word, correct=correct)
self.add_widget(btn)
class RootScreen(Screen):
def __init__(self, **kwargs):
super(RootScreen, self).__init__(**kwargs)
class Main(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_string(kv)
def build(self):
return self.screen
Main().run()
This was resolved by not naming the kv file main.kv.

Kivy how to create dropdownlist within a GridLayout

I have 2 files one of the py and the other kv, well as the title says, I do not know how to add a dropdown.
---> main.py
from kivy.app import App
from kivy.uix.dropdown import DropDown
from kivy.uix.gridlayout import GridLayout
class MyGridLayout(GridLayout):
pass
class LayoutsApp(App):
def build(self):
return MyGridLayout()
if __name__ == '__main__':
LayoutsApp().run()
---> layouts.kv
<MyGridLayout>:
rows: 2
id: main_win
Dropdown:
id: dropdown
Button:
id: btn1
text: 'option 1'
Button:
id: btn2
text: 'option 2'
BoxLayout:
BoxLayout:
when compiling, it generates error for that part. How is the correct way to make the call for dropdown-list?
Question 3
call the Button id: btn2, how would I do it? - I mean:class
MyGridLayout(GridLayout):
Solution 3
Python script
Add import statements: from kivy.uix.dropdown import DropDown; from kivy.properties import ObjectProperty
Implement class CustomDropDown(DropDown):
In class MyGridLayout, declare an ObjectProperty at class level (e.g. dropdown = ObjectProperty(None); implement a method dropdown_open(self):; instantiate the class CustomDropDown and assigned it to self.dropdown, etc. Please refer to snippets for detail.
kv file
Remove self.dismiss()
Replace dynamic class, <CustomDropdown#DropDown>: with class rule, <CustomDropDown>:
Replace rows: 2 with rows: 3
Replace Factory.CustomDropdown().open(self) with root.dropdown_open(self)
Example 3
main.py - Snippets
from kivy.uix.dropdown import DropDown
from kivy.properties import ObjectProperty
class CustomDropDown(DropDown):
pass
class MyGridLayout(GridLayout):
dropdown = ObjectProperty(None)
def dropdown_open(self, instance):
self.dropdown = CustomDropDown()
self.dropdown.open(instance)
print(self.dropdown.ids.btn2.text)
layouts.kv - Snippets
<CustomDropDown>:
id: dropdown
on_select:
app.root.ids.btn.text = '{}'.format(args[1])
...
<MyGridLayout>:
rows: 3
id: main_win
Button:
id: btn
text: 'Press'
size_hint_y: None
height: '48dp'
on_release:
root.dropdown_open(self)
Output 3
Question 2
when I added these commands, it does not work: BoxLayout: orientation:
'vertical' size_hint: (.9,.9) BoxLayout: orientation: 'vertical'
size_hint_y: 0.5
Note
You might need to increase the rows from 2 to 3.
Example #2
Added the config for the two BoxLayout
For illustration, added colour to the canvas for both BoxLayout
layouts.kv
#:kivy 1.10.1
#:import Factory kivy.factory.Factory
<CustomDropdown#DropDown>:
id: dropdown
on_select:
app.root.ids.btn.text = '{}'.format(args[1])
self.dismiss()
Button:
id: btn1
text: 'option 1'
size_hint_y: None
height: '48dp'
on_release:
dropdown.select(btn1.text)
Button:
id: btn2
text: 'option 2'
size_hint_y: None
height: '48dp'
on_release:
dropdown.select(btn2.text)
<MyGridLayout>:
rows: 2
id: main_win
Button:
id: btn
text: 'Press'
on_release: Factory.CustomDropdown().open(self)
size_hint_y: None
height: '48dp'
BoxLayout:
orientation: 'vertical'
size_hint: (.9,.9)
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
size_hint_y: 0.5
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
pos: self.pos
size: self.size
Output #2
Solution #1
DropDown widget is like Popup widget i.e. they are special widget.
Popup / DropDown is a special widget. Don’t try to add it as a child to any other
widget. If you do, Popup / DropDown will be handled like an ordinary widget and
won’t be created hidden in the background.
Therefore, create a dynamic class with inheritance of DropDown widget, and use Factory to instantiate the class.
Example
main.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
class MyGridLayout(GridLayout):
pass
class LayoutsApp(App):
def build(self):
return MyGridLayout()
if __name__ == '__main__':
LayoutsApp().run()
layouts.kv
#:kivy 1.10.1
#:import Factory kivy.factory.Factory
<CustomDropdown#DropDown>:
id: dropdown
on_select:
app.root.ids.btn.text = '{}'.format(args[1])
self.dismiss()
Button:
id: btn1
text: 'option 1'
size_hint_y: None
height: '48dp'
on_release:
dropdown.select(btn1.text)
Button:
id: btn2
text: 'option 2'
size_hint_y: None
height: '48dp'
on_release:
dropdown.select(btn2.text)
<MyGridLayout>:
rows: 2
id: main_win
Button:
id: btn
text: 'Press'
on_release: Factory.CustomDropdown().open(self)
size_hint_y: None
height: '48dp'
BoxLayout:
BoxLayout:
Output

How to display ContextMenuTextItem over other components in Kivy

now i'm creating a GUI for application by kivy, and i have a problem as following:
When i click onto Title "Job", there is a menu, the problem is the menu is overlapped by other components (textinput...). How could i display this menu over textinput ? Please see picture for more details.
Problem
#: import main todo
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import ListItemButton kivy.uix.listview.ListItemButton
Root:
task_input: task_input_view
task_list: tasks_list_view
text_input: text_input
BoxLayout:
orientation: 'vertical'
id: layout
AppMenu:
id: app_menu
top: root.height
cancel_handler_widget: layout
AppMenuTextItem:
text: "Job"
ContextMenu:
ContextMenuTextItem:
text: "Open"
on_release: root.show_load()
ContextMenuTextItem:
text: "Save"
on_release: root.show_save()
ContextMenuTextItem:
text: "SaveAs"
ContextMenuDivider:
ContextMenuTextItem:
text: "Exec"
BoxLayout:
size_hint_y: None
height: "40dp"
TextInput:
id: task_input_view
size_hint_x: 70
Button:
text: "Add"
size_hint_x: 15
on_press: root.add_task()
Button:
text: "Del"
size_hint_x: 15
on_press: root.del_task()
ListView:
id: tasks_list_view
adapter:
ListAdapter(data=[], cls=main.TaskButton)
TextInput:
id: text_input
text: ''
How to make this type of menu has more than one solution, but basically you need something that will be displayed on the top of each widget (or the widgets you want in background). This could be achieved in BoxLayout, but it's not efficient. Rather than Box, make it FloatLayout and add the widget that handles your menu(that panel) as the last widget - this way it'll be on top of every widget added before it and menu should behave like desired.

Resources