I am new to kivy wanted to know how we can bind textinput box to suggestion so that user and touch and select the suggestions.
I have long list of buttons out of which i want to select on the basis of name.
i am using kivymd i dont real know how to do it in kivy but here are the codes for kivymd
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.screenmanager import Screen
from kivymd.icon_definitions import md_icons
from kivymd.app import MDApp
from kivymd.uix.list import OneLineIconListItem
Builder.load_string(
'''
#:import images_path kivymd.images_path
<CustomOneLineIconListItem>:
IconLeftWidget:
icon: root.icon
<PreviousMDIcons>:
BoxLayout:
orientation: 'vertical'
spacing: dp(10)
padding: dp(20)
BoxLayout:
size_hint_y: None
height: self.minimum_height
MDIconButton:
icon: 'magnify'
MDTextField:
id: search_field
hint_text: 'Search icon'
on_text: root.set_list_md_icons(self.text, True)
RecycleView:
id: rv
key_viewclass: 'viewclass'
key_size: 'height'
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
'''
)
class CustomOneLineIconListItem(OneLineIconListItem):
icon = StringProperty()
class PreviousMDIcons(Screen):
def set_list_md_icons(self, text="", search=False):
'''Builds a list of icons for the screen MDIcons.'''
def add_icon_item(name_icon):
self.ids.rv.data.append(
{
"viewclass": "CustomOneLineIconListItem",
"icon": name_icon,
"text": name_icon,
"callback": lambda x: x,
}
)
self.ids.rv.data = []
for name_icon in md_icons.keys():
if search:
if text in name_icon:
add_icon_item(name_icon)
else:
add_icon_item(name_icon)
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = PreviousMDIcons()
def build(self):
return self.screen
def on_start(self):
self.screen.set_list_md_icons()
MainApp().run()
some results
here is the screen shot result when your not searching for anything
here is a screen shot when i am trying to search
second
here is a result when the word am searching does not exist
Here is a simple example that will use the use text input to search in the option_list and will display the suggestion under the text input widget.
I used the kivymd widget to get a nice look design you replaced with the normal kivy widgets if you want
from kivy.properties import ListProperty
from kivymd.app import MDApp
from kivymd.uix.list import OneLineAvatarIconListItem
from kivymd.uix.textfield import MDTextField
kv = """
Screen:
BoxLayout:
orientation: 'vertical'
spacing: 1
BoxLayout:
size_hint_y: 1/5
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size[0], 2
MDIconButton:
icon: 'magnify'
size_hint_y: 1
SearchTextInput:
id: Search_TextInput_id
size_hint_y: .97
pos_hint:{ 'left':0 , 'top': 1}
hint_text: 'search'
hint_text_color: 1,1,1,1
icon_left: 'magnify'
mode: "fill"
helper_text_mode: "persistent"
helper_text: "Search"
line_color: [1,1,1,1]
color_normal: [1,1,1,1]
font_size: .35 * self.height
active_line: False
multiline: False
MDIconButton:
icon: 'close'
size_hint_y:1
text_color: 0,0,0,1
BoxLayout:
orientation: 'vertical'
padding: 4
RecycleView:
viewclass: 'Search_Select_Option'
data:app.rv_data
RecycleBoxLayout:
spacing: 15
padding : 10
default_size: None, None
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<Search_Select_Option>:
on_release: print(self.text)
IconRightWidget:
icon: "arrow-top-left"
"""
class Search_Select_Option(OneLineAvatarIconListItem):
pass
class SearchTextInput(MDTextField):
option_list = 'one1,two1,two2,three1,three2,three3,four1,four2,four3,four4,five1,five2,five3,five4,five5'.split(',')
def on_text(self, instance, value):
app = MDApp.get_running_app()
option_list = list(set(self.option_list + value[:value.rfind(' ')].split(' ')))
val = value[value.rfind(' ') + 1:]
if not val:
return
try:
app.option_data = []
for i in range(len(option_list)):
word = [word for word in option_list if word.startswith(val)][0][len(val):]
if not word:
return
if self.text + word in option_list:
if self.text + word not in app.option_data:
popped_suggest = option_list.pop(option_list.index(str(self.text + word)))
app.option_data.append(popped_suggest)
app.update_data(app.option_data)
except IndexError:
pass
class RVTestApp(MDApp):
rv_data = ListProperty()
def update_data(self, rv_data_list):
self.rv_data = [{'text': item} for item in rv_data_list]
print(self.rv_data, 'update')
def build(self):
return Builder.load_string(kv)
RVTestApp().run()
Incase anyone is wondering how can get the value of the selected item from the list, I have a little updated version here:
from kivymd.icon_definitions import md_icons
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.list import OneLineListItem
from kivy.uix.screenmanager import Screen
KV = '''
<ListSelect>
BoxLayout:
orientation: 'vertical'
spacing: dp(10)
padding: dp(20)
pos_hint:{'center_x': 0.5, 'y': 0.85}
BoxLayout:
size_hint_y: None
height: self.minimum_height
MDIconButton:
icon: 'magnify'
MDTextField:
id: search_field
hint_text: 'Search icon'
on_text:
root.set_list(self.text)
RecycleView:
pos_hint:{'center_x': 0.5, 'center_y': 0.4}
MDList:
id: container
'''
class ListSelect(Screen):
def pressed(self, value):
# value here is the OneLineListItem
self.ids.container.clear_widgets()
# set TextField text to selected list item
self.ids.search_field.text = value.text
print(value.text)
def set_list(self, text=" "):
# text defaults to blank space to not show any icons initally
# each OneLineListItem takes the pressed func on press
self.ids.container.clear_widgets() # refresh list
for icon in md_icons.keys():
# using casefold() to make the input case insensitve
if icon.startswith(text.casefold()):
self.ids.container.add_widget(
OneLineListItem(text=icon, on_press=self.pressed)
)
class Test(MDApp):
def build(self):
Builder.load_string(KV)
self.screen = ListSelect()
return self.screen
def on_start(self):
self.screen.set_list()
Test().run()
One catch: the search needs quite a long time to finish. If anyone has any hints on how to speed it up, you are very welcome!
Related
I´m learning kivy, and it has passed 3 weeks since i face this problem without encounter a solution, so i hope any of u guys could help me, i would appreciate it.
I have a main file:
from app import MyProgramApp
if __name__ == "__main__":
winapp = MyProgramApp()
winapp.run()
from where i start my app. Then i have a directory called "app", inside there is the following "init.py" file.
from kivy.app import App
from kivy.utils import QueryDict, rgba
from kivy.core.window import Window
from .view import MainWindow
Window.minimum_width = 500
Window.minimum_height = 650
Window.maximize()
class MyProgramApp(App):
colors = QueryDict()
colors.primary = rgba('#2D9CDB')
colors.secondary = rgba('#16213E')
colors.succes = rgba('#1FC98E')
colors.warning = rgba('#F2C94C')
colors.danger = rgba('#E85757')
colors.grey_dark = rgba('#C4C4C4')
colors.grey_light = rgba('#F5F5F5')
colors.black = rgba('#A1A1A1')
colors.white = rgba('#FFFFFF')
def build(self):
return MainWindow()
Same folder app, i have the following "view.py".
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager
class MainWindow(BoxLayout):
username = StringProperty("Usuario")
def __init__(self, **kw):
super().__init__(**kw)
def on_press_home(self):
self.ids.scrn_mngr.current = "scrn_home"
class ViewManager(ScreenManager):
def __init__(self, **kw):
super().__init__(**kw)
class NavTab(ToggleButtonBehavior, BoxLayout):
text = StringProperty('')
icon = StringProperty('')
def __init__(self, **kw):
super().__init__(**kw)
and finally for that folder i have "myprogram.kv"
#:kivy 2.1.0
#:import Home views.home.Home
<MainWindow>:
spacing: dp(8)
canvas.before:
Color:
rgba: app.colors.white
Rectangle:
pos: self.pos
size: self.size
# NAVIGATION BAR
BoxLayout:
id: nav_menu
size_hint_x: .2
orientation: "vertical"
size_hint_min_x: dp(100)
# LOGO
BoxLayout:
id: logo_nbox
size_hint_y: .1
size_hint_min_y: dp(70)
padding: dp(16)
AnchorLayout:
anchor_x: "right"
size_hint_x: None
width: dp(52)
halign: "left"
Label:
text: "COMPANY"
halign: "center"
BoxLayout:
orientation: "vertical"
Label:
text: "COMPANY"
halign: "center"
Label:
text: "Phrase"
halign: "center"
# OPTIONS
GridLayout:
id: tabs_box
cols: 1
spacing: dp(4)
size_hint_y: .5
canvas.before:
Color:
rgba: app.colors.grey_dark
Rectangle:
pos: self.pos
size: [self.size[0], dp(1)]
NavTab:
text: "Home"
state: "down"
on_press: root.on_press_home()
# BODY
BoxLayout:
size_hint_x: .8
spacing: dp(8)
orientation: "vertical"
padding: [dp(16), dp(8), dp(12), dp(8)]
canvas.before:
Color:
rgba: app.colors.grey_light
Rectangle:
pos: self.pos
size: self.size
# SCREENS
BoxLayout:
ViewManager:
id: scrn_mngr
<ViewManager>:
Screen:
name: "scrn_home"
Home:
id: home
<NavTab>:
background_normal: ""
background_down: ""
background_color: [0,0,0,0]
group: "tabs"
size_hint_y: None
height: dp(45)
spacing: dp(4)
canvas.before:
Color:
rgba: [0,0,0,0] if self.state == "normal" else rgba("#E1F1FF")
Rectangle:
pos: self.pos
size: self.size
Color:
rgba: [0,0,0,0] if self.state == "normal" else app.colors.primary
Rectangle:
pos: [self.pos[0]+self.size[0]-dp(1), self.pos[1]]
size: [dp(8), self.size[1]]
Label:
halign: "left"
valign: "middle"
text: root.text
color: app.colors.grey_dark if root.state == "normal" else app.colors.primary
Then i got another folder called "views" inside i have another folder called "home", inside home we encounter 3 files "init.py", "home.kv", "home.py". the first one "init.py" is the following.
from .home import Home
then we got "home.kv".
#:kivy 2.1.0
<Home>:
orientation: "vertical"
Label:
size_hint_y: .1
text: "Other"
Button:
size_hint_y: .1
text: "popup"
on_press: root.open_popup()
BoxLayout:
size_hint_y: .8
id: home_box
<SomePopup>:
title: "SOME TITLE"
title_align: "center"
title_color: app.colors.primary
size_hint: .25, .8
size_hint_min_x: dp(200)
pos_hint: {"x": .1, "top": .9}
BoxLayout:
orientation: "vertical"
padding: [dp(0), dp(12), dp(0), dp(12)]
Label:
text: "Some text"
Button:
text: "create buttons on box"
on_press: root.modify_home_box()
and finally and the problem that i´m facing is whit the following file "home.py"
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.app import App
Builder.load_file('views/home/home.kv')
class Home(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def open_popup(self):
Factory.SomePopup().open()
class SomePopup(Popup):
def __init__(self, **kw):
super().__init__(**kw)
def modify_home_box(self):
my_app = App.get_running_app().root
my_box = my_app.ids.home.ids.home_box
custom_button = Button(
text = "something"
)
my_box.add_widget(custom_button)
That i´m trying to do is actually modify "home_box" with is store on ids Home dictionary, i also try with ObjectProperty (but that gives and Attribute Error, since i only could read propertys but doesnt modify it), instance of a new Home class doesnt work... searching for current app in App appear doesnt work since Home is store i think on screenManager...
I need add a button or some widget to "home_box" from outside class "SomePopup". I also drop here a repository on github with the whole code. github_repo
I don't know how to solve my issue, and i try with the resources available here on stack as well other net places... any help would be appreciate.
Just a very complicated path to the widget of interest. In your modify_home_box() method, try replacing:
my_app = App.get_running_app().root
my_box = my_app.ids.home.ids.home_box
with:
my_app = App.get_running_app().root
my_box = my_app.ids.scrn_mngr.ids.home.ids.home_box
I have created code with 2 screens and need one of them to have an expansion panel.
Unfortunately I can't get the panel to show up with the content inside of it. Instead I'm stuck with chaos in my head and a side of migraine, so here is my code, an example of what I want it to look like and what I managed to create minus my full code.
Video example: https://www.kapwing.com/videos/62f4074bafd00100c829b84c
Video example of problem: https://www.kapwing.com/videos/62f41c828f6acd00521caae1
As shown in the video example:
1st code:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivymd.uix.expansionpanel import MDExpansionPanel
from kivymd.uix.expansionpanel import MDExpansionPanelOneLine
from kivymd.uix.boxlayout import MDBoxLayout
KV = '''
MDScreen:
MDNavigationLayout:
ScreenManager:
id: manager
MDScreen:
name: 'Home'
AnchorLayout:
anchor_x: "center"
anchor_y: "top"
MDToolbar:
md_bg_color: 0, 0, 0, 0.5
title: "Example"
elevation: 10
left_action_items: [["menu", lambda x: mud_list.set_state("open")]]
right_action_items: [["dots-vertical", lambda x:app.dropdown(x)]]
MDNavigationDrawer:
id: mud_list
BoxLayout:
orientation: 'vertical'
spacing: '5dp'
padding: '5dp'
ScrollView:
MDList:
OneLineIconListItem:
text: '[Settings]'
on_release:
manager.current = 'Settings'
root.ids.mud_list.set_state(new_state='toggle', animation=True)
divider: None
IconLeftWidget:
icon: 'cog'
on_release:
manager.current = 'Settings'
root.ids.mud_list.set_state(new_state='toggle', animation=True)
MDLabel:
text:' By Author'
size_hint_y: None
font_style: 'Button'
height: self.texture_size[1]
MDScreen:
name: 'Settings'
AnchorLayout:
anchor_x: "center"
anchor_y: "top"
MDToolbar:
id: mdt_color
md_bg_color: 1, 1, 1, 1
elevation: 10
MDIconButton:
icon: "keyboard-backspace"
pos_hint: {"center_x": 0.09, "center_y": 0.945}
on_release: manager.current = 'Home'
MDBoxLayout:
size_hint: 1, 0.89
orientation : 'vertical'
ScrollView:
MDBoxLayout:
orientation:'vertical'
adaptive_height: True
padding:[dp(15),dp(15),dp(15),dp(35)]
spacing:dp(15)
Content
adaptive_height: True
orientation: 'vertical'
OneLineIconListItem:
text: "Dark"
on_release:app.theme_changer2()
divider: None
IconLeftWidget:
icon: 'weather-night'
on_release:app.theme_changer2()
OneLineIconListItem:
text: "Light"
on_release:app.theme_changer()
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
on_release:app.theme_changer()
ScrollView:
MDGridLayout:
id: box
cols: 1
adaptive_height: True
'''
class Content(MDBoxLayout):
"""Custom content."""
def __draw_shadow__(self, origin, end, context=None):
pass
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.menu = None
self.menu_list = None
self.kvs = Builder.load_string(KV)
self.screen = Builder.load_string(KV)
def on_start(self):
self.root.ids.box.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
content=Content(),
panel_cls=MDExpansionPanelOneLine(
text="Theme",
)
)
)
def theme_changer(self):
self.theme_cls.theme_style = "Light"
self.root.ids.mdt_color.md_bg_color = [1, 1, 1, 1]
def theme_changer2(self):
self.theme_cls.theme_style = "Dark"
self.root.ids.mdt_color.md_bg_color = [0, 0, 0, 1]
def build(self):
self.theme_cls.theme_style = "Light"
screen = Screen()
screen.add_widget(self.kvs)
return self.screen
ma = MainApp()
ma.run()
2nd code: I got from the kivymd documentation here https://github.com/kivymd/KivyMD/wiki/Components-Expansion-Panel
3rd code is much the same as the 2nd but I made my own:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
KV = '''
<Content>
adaptive_height: True
orientation: 'vertical'
OneLineIconListItem:
text: "Dark"
divider: None
IconLeftWidget:
icon: 'weather-night'
OneLineIconListItem:
text: "Light"
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
ScrollView:
MDGridLayout:
id: box
cols: 1
adaptive_height: True
'''
class Content(MDBoxLayout):
"""Custom content."""
def __draw_shadow__(self, origin, end, context=None):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
self.root.ids.box.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
content=Content(),
panel_cls=MDExpansionPanelOneLine(
text="Theme",
)
)
)
Test().run()
My problem is, as seen in the example of problem video, that the expansion panel itself doesn't show up.
I am figuring this out as I go along, so among all the chaos I have tried, I have noticed that the position of the "Content" and all that's under it in relation the anchor layout of screen 'settings', causes the panel to show up but the content isn't inside.
Same effect with whether the "content" or "MDGridlayout" have the id: box.
In summary, I want to be able to create something like in the 2nd code but in the settings screen of my main app, or basically copy and paste the 3rd code into my main app.
Oh, and I may make this a question on it's own later, but if it's simple enough, how can I make it so when the theme is changed it becomes the default?
Took a while, but with help i finally got it. Here's an example with what i think are some key notes in the code #Comments#.
Hope this helps someone.
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
KV = '''
#THE CONTENT GOES ABOVE EVERYTHING ELSE EVEN THE 1ST SCREEN.#
#IT IS REFERENCED LATER IN WHICH EVER SCREEN IT IS TO APPEAR USING MDGridLayout AND A def LATER ON#
#It should also always have these angle brackets <>#
<Content>
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
OneLineIconListItem:
text: 'Dark theme'
on_release:app.theme_changer2()
divider: None
IconLeftWidget:
icon: 'weather-night'
on_release:app.theme_changer2()
OneLineIconListItem:
text: 'Light theme'
on_release:app.theme_changer()
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
on_release:app.theme_changer()
<Content2>
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
OneLineIconListItem:
text: 'I try to explain what i think are key points to note'
on_release:app.theme_changer2()
divider: None
IconLeftWidget:
icon: 'weather-night'
on_release:app.theme_changer2()
OneLineIconListItem:
text: 'Hope this helps someone else'
on_release:app.theme_changer()
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
on_release:app.theme_changer()
MDScreen:
MDNavigationLayout:
ScreenManager:
id: manager
MDScreen:
name: 'Home'
#The location of this MDGridLayout shows it appears in the 1st screen.#
MDGridLayout:
cols: 1
adaptive_height: True
#This id is used to reference what content goes to which expansion panel#
id: box2
MDRaisedButton:
pos_hint: {"center_x": 0.5, "center_y": 0.5}
text: "PRESS ME"
on_release: manager.current = 'Home2'
MDScreen:
name: 'Home2'
MDBoxLayout:
orientation: "vertical"
MDToolbar:
id: mdt_color
elevation: 10
ScrollView:
divider: 'None'
#The location of this MDGridLayout shows it appears in the 2nd screen.#
MDGridLayout:
cols: 1
adaptive_height: True
id: box
MDIconButton:
pos_hint: {"center_x": 0.05, "center_y": 0.945}
icon: "keyboard-backspace"
on_release: manager.current = 'Home'
'''
# Every expansion panel should have a class corresponding to it's name e.g.#
class Content(MDBoxLayout):
pass
# The class name is referenced in the contents= section below#
class Content2(MDBoxLayout):
pass
class MainApp(MDApp):
def theme_changer(self):
self.theme_cls.theme_style = "Light"
self.root.ids.mdt_color.md_bg_color = [1, 1, 1, 1]
def theme_changer2(self):
self.theme_cls.theme_style = "Dark"
self.root.ids.mdt_color.md_bg_color = [0, 0, 0, 1]
def build(self):
return Builder.load_string(KV)
def on_start(self):
# Here you see the id box used#
self.root.ids.box.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
# Here the content name Content is referenced in content=#
content=Content(),
panel_cls=MDExpansionPanelOneLine(
text="Theme",
)
)
)
# Here you see the id box2 used#
self.root.ids.box2.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
# Here the content name Content2 is referenced in content=#
content=Content2(),
panel_cls=MDExpansionPanelOneLine(
text="Read the code #comments# for details",
)
)
)
ma = MainApp()
ma.run()
I am trying to modify a KivyMD OneLineIconListItem example from https://kivymd.readthedocs.io/en/latest/themes/icon-definitions/ that does the autocomplete search that I've been trying to figure out for a current project.
TODO: Add on_release to item to print item and add to list
Bind on_release in Kivy List item to function is the closest that I've found to do what I want, but I'm having trouble adapting it.
Here is what I have so far:
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.screenmanager import Screen
from kivymd.app import MDApp
from kivymd.icon_definitions import md_icons
from kivymd.uix.list import OneLineIconListItem
Builder.load_string(
'''
#:import images_path kivymd.images_path
<CustomOneLineIconListItem>
on_release: root.print_item
IconLeftWidget:
icon: root.icon
<PreviousMDIcons>
MDBoxLayout:
orientation: 'vertical'
spacing: dp(10)
padding: dp(20)
MDBoxLayout:
adaptive_height: True
MDIconButton:
icon: 'magnify'
MDTextField:
id: search_field
hint_text: 'Search icon'
on_text: root.set_list_md_icons(self.text, True)
RecycleView:
id: rv
key_viewclass: 'viewclass'
key_size: 'height'
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
'''
)
class CustomOneLineIconListItem(OneLineIconListItem):
def print_item(self, instance):
print(instance, instance.text)
icon = StringProperty()
class PreviousMDIcons(Screen):
def set_list_md_icons(self, text="", search=False):
"""Builds a list of icons for the screen MDIcons."""
def add_icon_item(name_icon):
self.ids.rv.data.append(
{
"viewclass": "CustomOneLineIconListItem",
"icon": name_icon,
"text": name_icon,
"callback": lambda x: x,
}
)
self.ids.rv.data = []
for name_icon in md_icons.keys():
if search:
if text in name_icon:
add_icon_item(name_icon)
else:
add_icon_item(name_icon)
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = PreviousMDIcons()
def build(self):
return self.screen
def on_start(self):
self.screen.set_list_md_icons()
MainApp().run()
All you need to do is just replacing
<CustomOneLineIconListItem>
on_release: root.print_item
with,
<CustomOneLineIconListItem>
on_release: root.print_item(self) # Call the method by passing this instance.
I have the following code where I would like to be able to click on the navigation drawer, select Status under Users, input the name and then be sent to a given screen with the current_user now populated.
main.py
from kivy.uix.behaviors import CoverBehavior
from kivy import utils
from kivy.properties import ObjectProperty, ListProperty, NumericProperty, StringProperty, BooleanProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemableBehavior
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.list import MDList
#from models import ServerInfo
from navigation_drawer import navigation_helper
Window.fullscreen = 'auto'
class ItemWidget(BoxLayout):
sid = NumericProperty()
name = StringProperty()
work = NumericProperty()
is_disabled = BooleanProperty()
description = StringProperty()
has_issue = BooleanProperty()
class MainWidget(FloatLayout):
recycleView = ObjectProperty()
items = ListProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.items = [
#ServerInfo(1, "server1", 95, "desc", is_disabled=False, has_issue=False),
]
# Put the servers with issues on top, otherwise sort by name
self.items = sorted((s for s in self.items), key=lambda x: (not(x.has_issue),x.name))
# Load the server information when the recycleView is called
def on_recycleView(self, widget, parent):
self.recycleView.data = [
m.get_dictionary() for m in self.items
]
class Content(BoxLayout):
pass
class DemoApp(MDApp):
class ContentNavigationDrawer(BoxLayout):
pass
class DrawerList(ThemableBehavior, MDList):
pass
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.dialog = None
# set main interface
self.manager = ScreenManager()
self.current_user = ""
# return main interface
#return self.manager
def build(self):
screen = Builder.load_string(navigation_helper)
return screen
def on_start(self):
pass
def dialog_close(self, *args):
self.dialog.dismiss(force=True)
def set_current_user(self,user,sm):
self.current_user = user
# Send over to given screen
# ???? = sm
def show_user_input_dialog(self,user,screen):
#print(sm.current)
if not self.dialog:
self.dialog = MDDialog(
title="Input Username:",
type="custom",
content_cls=Content(),
buttons=[
MDFlatButton(
text="CANCEL",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
on_release=self.dialog_close
),
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
#on_release=self.set_current_user(user,screen)
on_release=self.dialog_close
),
],
)
# Send to user management page with current user now populated
# Fails as sm is a str instead of ScreenManager()
#sm.current = 'user_management'
self.dialog.open()
DemoApp().run()
navigation_drawer.py
navigation_helper = """
Screen:
MDNavigationLayout:
ScreenManager:
id: screenManager
Screen:
name: "monitor"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Administration'
left_action_items: [["menu", lambda x: nav_drawer.set_state('toggle')]]
elevation:5
MainWidget:
Screen:
name: "user_management"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Administration'
left_action_items: [["menu", lambda x: nav_drawer.set_state('toggle')]]
elevation:5
UserManagement:
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
orientation: 'vertical'
padding: "2dp"
spacing: "2dp"
ScrollView:
DrawerList:
id: md_list
MDList:
TwoLineIconListItem:
text: "Monitor"
secondary_text: "Monitor servers"
on_release: screenManager.current = 'monitor'
IconLeftWidget:
icon: "monitor"
MDLabel:
padding: dp(10), dp(0)
text: "Users"
bold: True
font_size: dp(22)
size_hint_y: None
OneLineIconListItem:
text: "Status"
on_release: app.show_user_input_dialog('abc999','user_management')
<Content>
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "60dp"
MDTextField:
id: username_input
hint_text: "Username"
<UserManagement#BoxLayout>:
Label:
text: "Users"
<MainWidget>:
id: mainWidgetId2
recycleView: recycleView
CoverImage:
source: 'images/monitor.png'
# Darken the photo
canvas:
Color:
rgba: 0, 0, 0, .6
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
padding: dp(20)
spacing: dp(10)
BoxLayout:
orientation: "horizontal"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
RecycleView:
id: recycleView
viewclass: 'ItemWidget'
RecycleGridLayout:
cols: 1
default_size: self.parent.width / 5, dp(60)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
width: self.minimum_width
spacing: dp(155), dp(0)
BoxLayout:
orientation: "vertical"
spacing: dp(10)
BoxLayout:
spacing: dp(10)
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "APPS"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "OTHER"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "MOUNTS"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "RECENT ACTIVITY"
<CoverImage#CoverBehavior+Image>:
reference_size: self.texture_size
<ItemWidget>:
BoxLayout:
#size_hint_max_x: dp(360)
size_hint_min_x: dp(150)
orientation: "horizontal"
BoxLayout:
orientation: "horizontal"
Button:
# Use the on_press to print troubleshooting info, otherwise comment out
on_press: print(self.background_color)
padding: dp(10), dp(10)
text_size: self.size
font_size: dp(22) if root.has_issue else dp(18)
background_color: .5,1,1,.6 #utils.get_random_color(alpha=.6)
halign: "left"
valign: "top"
size: 1,1
# Show last 4 of server name
text: str(root.name).upper()[-4:]
bold: True
color: (1,0,0) if root.has_issue else (1,1,1)
Button:
background_color: .5,1,1,.6
text_size: self.size
valign: "center"
halign: "center"
text: str(root.work)
padding: dp(8), dp(8)
bold: True if root.work > 90 else False
color: (1,0,0) if root.work > 90 else (1,1,1) # Red if greater than 90
"""
Questions:
How do I get the value from this so that I can pass the value instead of the hardcoded username of abc999
MDTextField:
id: username_input
How do I send to the given screen (user_management here) from
def set_current_user(self,user,sm):
self.current_user = user
# Send over to given screen
# ???? = sm
You can toggle commenting on the two on_release below to see the issue
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
#on_release=self.set_current_user(user,screen)
on_release=self.dialog_close
You can do what you want by modifying set_current_user() to:
def set_current_user(self, button):
self.current_user = self.dialog.content_cls.ids.username_input.text
self.dialog_close()
self.root.ids.users.add_widget(Label(text=self.current_user))
sm = self.root.ids.screenManager
sm.current = 'user_management'
The above code requires the addition of an id to the UserManagerment in the kv:
UserManagement:
id: users
This just adds a new Label with the name of the user.
good day
what im trying to accomplish is to input text on one screen then have it create a button on another screen within a recycleview where if I keep adding buttons the recycleview keeps getting populated. I would assume that the button1 function would update the rvs.rv.data and that the recycleview would use the it update itself. could you point me in the right direction please?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = """
<custombutton#BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Button:
text: root.value
<Root>:
RVScreen:
name: 'Rv'
InputScreen:
name: 'input_screen'
<InputScreen>:
name: "input_screen"
orientation: "vertical"
BoxLayout:
orientation: "vertical"
TextInput:
id: textinput
Button:
text: "hi"
on_press: print(app.rvs.rv.data)
on_press: app.rvs.button1('some_value')
Button:
text: 'rvscreen'
on_press: root.manager.current = 'Rv'
<RVScreen>:
name: 'Rv'
rv: rv
orientation: "vertical"
BoxLayout:
orientation: "vertical"
Button:
text: 'refresh'
on_press: root.rv.refresh_from_data()
on_press: print(app.rvs.rv.data)
Button:
text: "input page"
on_press: root.manager.current = 'input_screen'
RecycleView:
id: rv
viewclass: 'custombutton'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
"""
Builder.load_string(kv)
class Root(ScreenManager):
pass
class RVScreen(Screen):
def __init__(self, **kwargs):
super(RVScreen, self).__init__(**kwargs)
def button1(self, value):
self.rv.data.insert(0, {'value': value or 'default value'})
class InputScreen(Screen):
pass
class TestApp(App):
rvs = RVScreen()
def build(self):
return Root()
if __name__ == '__main__':
TestApp().run()
The problem is that you are populating the data of the app.rvs, but that is created by the line:
rvs = RVScreen()
which creates a new RVScreen instance that is entirely unrelated to the RVScreen that is displayed in your GUI. In order to update the RVScreen in your GUI, you need to get a reference to that RVScreen.
I like to that that through a method in the App, just because it makes the code simpler. So here is a modified version of your code that does it my way:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
kv = """
<custombutton#BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Button:
text: root.value
<Root>:
RVScreen:
name: 'Rv'
InputScreen:
name: 'input_screen'
<InputScreen>:
name: "input_screen"
orientation: "vertical"
BoxLayout:
orientation: "vertical"
TextInput:
id: textinput
Button:
text: "hi"
on_press: app.add_data(textinput.text)
Button:
text: 'rvscreen'
on_press: root.manager.current = 'Rv'
<RVScreen>:
name: 'Rv'
rv: rv
orientation: "vertical"
BoxLayout:
orientation: "vertical"
Button:
text: 'refresh'
on_press: rv.refresh_from_data()
Button:
text: "input page"
on_press: root.manager.current = 'input_screen'
RecycleView:
id: rv
viewclass: 'custombutton'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
"""
Builder.load_string(kv)
class Root(ScreenManager):
pass
class RVScreen(Screen):
pass
class InputScreen(Screen):
pass
class TestApp(App):
# rvs = RVScreen() # this code does nothing useful
def build(self):
return Root()
def add_data(self, value):
# get the `Screen` instance
rvs = self.root.get_screen('Rv')
# insert the new value into the data
rvs.ids.rv.data.insert(0, {'value': value or 'default value'})
if __name__ == '__main__':
TestApp().run()
I also made a small modification, so that the new value comes from the TextInput.