Running this example of DropDown works. However, after some use/time I get the error ReferenceError: weakly-referenced object no longer exists
This is likely do to an issue in on_release:dropdown.open(self)
Bonus points as to why on_parent: self.dismiss() also doesn't work with the way I have these widgets set up. Without this, I have the submenu items appearing when the app first runs and with this enabled, the submenu items flash (appear and quickly disappear).
#!/usr/bin/kivy
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty
from kivy.uix.dropdown import DropDown
from kivy.core.window import Window
Window.size = (400, 240)
sm = """
ScreenManager:
id:manager
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 0.5
Rectangle:
pos: 0,0
size: 800, 480
Notes:
id:Notes
name: 'Notes'
manager: manager
<Notes>:
name: "Notes"
orientation: "vertical"
FloatLayout:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 1, 0, 1
Button:
id: mainbutton
text: "Menu name"
font_size: 20
size_hint: None, None
size: 150, 50
pos: 20,400
on_release:dropdown.open(self)
CustomDropDown:
id: dropdown
#on_parent: self.dismiss()
on_select: mainbutton.text = '{}'.format(args[1])
Button:
id: button1
text: 'First Item'
size_hint_y: None
height: 40
font_size: 18
on_release: dropdown.select('First Item')
Button:
id: button2
text: 'Second Item'
size_hint_y: None
height: 40
font_size: 18
on_release: dropdown.select('Second Item')
Button:
id: button3
text: 'Third Item'
size_hint_y: None
height: 40
font_size: 18
on_release: dropdown.select('Third Item')
"""
class Notes(Screen):
pass
class CustomDropDown(DropDown):
pass
#dropdown = CustomDropDown()
class TestApp(App):
def build(self):
return Builder.load_string(sm)
if __name__ == '__main__':
TestApp().run()
In child widget, CustomDropDown
With on_parent: self.dismiss()
When you clicked on the main button, Menu name sometimes it gives an error, ReferenceError: weakly-referenced object no longer exists. If there is no ReferenceError, the drop-down list flash (appear and quickly disappear). The reason is that the DropDown was dismissed.
Without on_parent: self.dismiss()
It will display the CustomDropDown list at app startup. When the main button, Menu name is clicked, the drop-down list that appeared at startup disappeared but it displayed a drop-down list twice as long i.e. submenu items repeated twice.
Note
Drop-Down List is similar to Popup. They are special widget. Don't try to add it as a child to any other widget. If you do, they will be handled like an ordinary widget and won't be created hidden in the background.
Please refer to the example below illustrating how to create Drop-Down list.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.dropdown import DropDown
from kivy.core.window import Window
Window.size = (800, 480)
class CustomDropDown(DropDown):
pass
class Notes(Screen):
pass
class MyScreenManager(ScreenManager):
pass
class TestApp(App):
title = "Kivy Drop-Down List Demo"
def build(self):
return MyScreenManager()
if __name__ == '__main__':
TestApp().run()
test.kv
#:kivy 1.10.0
#:import Factory kivy.factory.Factory
<CustomDropDown>:
on_select: app.root.ids.Notes.ids.mainbutton.text = '{}'.format(args[1])
Button:
id: button1
text: 'First Item'
size_hint_y: None
height: 40
font_size: 18
on_release: root.select(self.text)
Button:
id: button2
text: 'Second Item'
size_hint_y: None
height: 40
font_size: 18
on_release: root.select(self.text)
Button:
id: button3
text: 'Third Item'
size_hint_y: None
height: 40
font_size: 18
on_release: root.select(self.text)
<MyScreenManager>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 0.5
Rectangle:
pos: 0,0
size: 800, 480
Notes:
id:Notes
name: 'Notes'
<Notes>:
orientation: "vertical"
FloatLayout:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 1, 0, 1
Button:
id: mainbutton
text: "Menu name"
font_size: 20
size_hint: None, None
size: 150, 50
pos: 20,400
on_release: Factory.CustomDropDown().open(self)
Output
from : $Yourkivydir/kivy-examples/demo/showcase/data/screens
ShowcaseScreen:
fullscreen: True
name: 'DropDown'
# trick to not lost the Dropdown instance
# Dropdown itself is not really made to be used in kv.
__safe_id: [dropdown.__self__]
Button:
id: btn
text: '-'
on_release: dropdown.open(self)
size_hint_y: None
height: '48dp'
Widget:
on_parent: dropdown.dismiss()
DropDown:
id: dropdown
on_select: btn.text = 'Selected value: {}'.format(args[1])
Button:
text: 'Value A'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('A')
Button:
text: 'Value B'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('B')
Button:
text: 'Value C'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('C')
Related
well I'm entirely new to kivy, it's a bit complicated but I'm trying to get on
so I'm stuck on something, I'm trying to get a text from a TextInput, but i can't, it just refuse to do it, can anyone crack it?
KV code:
<Signin>:
title:"Telegram Checker"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
BoxLayout:
width : 600
orientation: 'vertical'
size_hint: None, None
Label:
text:"Telegram Sign-In"
TextInput:
id : tel
input_type:'tel'
width:600
multiline: False
hint_text: '+XXXXXXXXXXXXX'
size_hint: None, None
height: 50
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
BoxLayout:
padding: 20
spacing: 100
width : 600
orientation: 'vertical'
size_hint: None, None
Button:
text: "Next ->"
#size_hint: 0.5,6
height : 100
pos:self.pos
on_release : app.send_code_request()
the function that's not getting the text :
class MyApp(App):
def build(self):
self.title="Telegram Checker"
return screen_manager
def send_code_request(self):
phone = self.root.ids.tel.text
print(phone)
The code at the bottom is runnable and it passes the argument you requested. It shows this being done two ways. I suggest not using the .ids property because you can link the items at the top of the .kv class and assign type hints to the object like this:
tel: TextInput
full code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
Builder.load_string('''
<Signin>:
# match the python object to the kivy id
# python on the left: kivy id on the right
tel: tel
title:"Telegram Checker"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
BoxLayout:
width : 600
orientation: 'vertical'
size_hint: None, None
Label:
text:"Telegram Sign-In"
TextInput:
id : tel
input_type:'tel'
width:600
multiline: False
hint_text: '+XXXXXXXXXXXXX'
size_hint: None, None
height: 50
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
BoxLayout:
padding: 20
spacing: 100
width : 600
orientation: 'vertical'
size_hint: None, None
Button:
text: "Next ->"
#size_hint: 0.5,6
height : 100
pos:self.pos
on_release : app.send_code_request(tel.text, self)
'''
)
class Signin(Screen):
# python objects mapped in the .kv file. this is a type hint which will help with auto-complete
tel: TextInput
def __init__(self, **kwargs):
super().__init__(**kwargs)
print(f" hint text is {self.tel.hint_text}")
class MyApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.my_top_widget = ScreenManager()
self.signin_screen = Signin(name="sign_in")
def send_code_request(self, phone: str, your_button, *args) -> None:
# this is one way
print(f" by id {self.signin_screen.ids.tel.text}")
print(f"vale passed={phone} \nyour button {your_button} other args {args}")
def build(self):
self.title = "Telegram Checker"
self.my_top_widget.add_widget(self.signin_screen, name="sign_in")
self.my_top_widget.current = "sign_in"
return self.my_top_widget
my_app = MyApp()
my_app.run()
I have a dropdown menu in which I would like to know which value the user has selected so I can use it later in my program. This is my .kv code:
BoxLayout:
orientation: 'horizontal'
size_hint_x: 1
Button:
pos_hint:{'center_x': .5, 'center_y': .5}
id: units_num_btn
text: '0'
size_hint_y: None
height: 44
on_parent: drop_player_numbers_units.dismiss()
on_release: drop_player_numbers_units.open(self)
DropDown:
id: drop_player_numbers_units
on_select: units_num_btn.text = '{}'.format(args[1])
on_select: app.return_player_numbers()
Button:
id: units_num_btn_1
text: '1'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('1')
Button:
id: units_num_btn_2
text: '2'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('2')
and so on.
My .py code is here:
class drop_content(DropDown):
pass
class PlayerScreen(Screen):
pass
class TheApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(PlayerScreen(name='player_setup'))
sm.current = 'player_setup'
return sm
def main():
Builder.load_file('menu.kv')
app = TheApp()
app.run()
if __name__ == '__main__':
main()
I have previously used a function such as this:
# .py example
def return_text(self):
text = self.root.get_screen('screen').ids.specific_id.text
print(text)
# .kv example
TextInput:
id: input
text: "2"
on_text: app.return_text()
which did return text using a Textinput type in my .kv file. I know it doesn't work for the dropdown menu since the text is not inputted in the same way. Do you know how I would do this?
Thanks in advance
from kivy.app import App
from kivy.uix.dropdown import DropDown
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.lang.builder import Builder
class TestScreen(Screen):
def return_player_numbers(self,play_number):
print('Test : ',play_number)
kv = '''
ScreenManager:
TestScreen:
<TestScreen>:
BoxLayout:
orientation: 'horizontal'
size_hint_x: 1
Button:
pos_hint:{'center_x': .5, 'center_y': .5}
id: units_num_btn
text: '0'
size_hint_y: None
height: 44
on_parent: drop_player_numbers_units.dismiss()
on_release: drop_player_numbers_units.open(self)
DropDown:
id: drop_player_numbers_units
on_select:
units_num_btn.text = '{}'.format(args[1])
app.root.current_screen.return_player_numbers(args[1])
app.return_player_numbers(args[1])
Button:
id: units_num_btn_1
text: '1'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('1')
Button:
id: units_num_btn_2
text: '2'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('2')
'''
class TheApp(App):
def return_player_numbers(self,play_number):
print('App : ',play_number)
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
TheApp().run()
I have found a way that I think is simpler than the current answer provided. This method uses that fact that the text on the button used to initiate the drop down changes as another button is selected. This is to show the user what they have selected. Since I want to know the text the user has selected from the drop down menu, and this is in fact the same thing that is updated on the initial button, I can just read the text from the button to obtatin my result like so:
(in .kv file)
<player_setup>
BoxLayout:
orientation: 'horizontal'
size_hint_x: 0.2
pos_hint: {'x':.4, 'y':0}
Button:
pos_hint:{'center_x': .5, 'center_y': .5}
id: tens_num_btn
text: '0'
size_hint_y: None
# size_hint_x: 0.1
height: 44
on_parent: drop_player_numbers_tens.dismiss()
on_release: drop_player_numbers_tens.open(self)
DropDown:
id: drop_player_numbers_tens
on_select:
#######
tens_num_btn.text = '{}'.format(args[1])
# this line here ^ is the one that re-formats the text of the button above to
# the selected text
app.return_player_numbers()
max_height: 120
(.py file)
def return_player_numbers(self):
player_number = self.root.get_screen('player_setup').ids.tens_num_btn.text
return player_number
This also allows me to concatenate multiple dropdown results using a single function, however it is menu specific. In my case, this works better for me
I am trying to type in text from one screen. Press a button and move to another screen and have that text be shown in a label. I've seen a few questions that are similar to mine, but have not been able to figure out how to use the posted solutions and have been stuck for hours (Link One, Link Two, Link Three). I believe that I need to use the __init__ method somewhere because this is an instance? I tried using the first link, but the label ends up blank (the code does run). Any Advice?
main.py
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
from kivy.lang.builder import Builder
class SecondWindow(Screen):
def get_unique_text(self):
x = self.manager.get_screen("first")
y = x.ids.unique.text
return str(y)
class FirstWindow(Screen):
pass
class MainWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv_main = Builder.load_file('main.kv')
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include First.kv
#:include Second.kv
WindowManager:
MainWindow:
FirstWindow:
SecondWindow:
<MainWindow>
name: "main"
BoxLayout:
Button:
text: "Press"
on_release:
app.root.current = "first"
First.kv
<FirstWindow#Screen>:
name: "first"
BoxLayout:
orientation: "vertical"
Label:
text: "Enter Unique Text for Saving"
font_size: 20
text_size: self.width, None
halign: 'center'
TextInput:
id: unique
hint_text: 'example: Stand25'
Button:
text: "Press"
on_release:
app.root.current = "second"
Second.kv
<SecondWindow#Screen>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Unique Text"
font_size: 20
text_size: self.width, None
halign: 'center'
Label:
text: root.get_unique_text()
font_size: 16
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
color: 0,0,0,1
Button:
text: "Go Back"
on_release:
app.root.current = "first"
Another approach is to use the on_enter() method of a Screen in order to fetch the text. This also requires an id for the unique Label:
<SecondWindow#Screen>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Unique Text"
font_size: 20
text_size: self.width, None
halign: 'center'
Label:
id: unique # added id
# text: root.get_unique_text()
font_size: 16
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
color: 0,0,0,1
Button:
text: "Go Back"
on_release:
app.root.current = "first"
Just add an on_enter() method to the SecondWindow class:
class SecondWindow(Screen):
def on_enter(self, *args):
self.ids.unique.text = self.get_unique_text()
def get_unique_text(self):
x = self.manager.get_screen("first")
y = x.ids.unique.text
return str(y)
In your Second.kv you can reference the text of the TextInput in the First.kv by making a couple changes to the kv files. First, in the main.kv, add an id for the FirstWindow (and eliminate the SecondWindow for now):
WindowManager:
MainWindow:
FirstWindow:
id: first # added id
# SecondWindow: # this gets added later
<MainWindow>
name: "main"
BoxLayout:
Button:
text: "Press"
on_release:
app.root.current = "first"
Then, in the Second.kv, set up the reference to the text of the TextInput:
<SecondWindow#Screen>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Unique Text"
font_size: 20
text_size: self.width, None
halign: 'center'
Label:
text: app.root.ids.first.ids.unique.text # reference to unique text
font_size: 16
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
color: 0,0,0,1
Button:
text: "Go Back"
on_release:
app.root.current = "first"
Since the kv for SecondWindow uses app.root, it will cause an error if SecondWindow is instantiated before the root widget of the App is assigned. To avoid that, add the SecondWindow after a slight delay:
class MyApp(App):
def build(self):
Clock.schedule_once(self.add_second_screen)
return kv_main
def add_second_screen(self, dt):
self.root.add_widget(SecondWindow())
I have this code where i want to change the position of the buttons, but if i change the position, the scroll is no longer working. How to manage to make it work? Below is the working version. If i change size_hint: 1, .1 to size_hint: 1, .7, the scroll no longer works...
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<Root>:
ScrollView:
size_hint: 1, .1
GridLayout:
size_hint_y: None
cols: 1
# minimum_height: self.height
Button
text: 'one'
Button:
text: 'two'
Button:
text: 'three'
Button:
text: 'four'
''')
class Root(FloatLayout):
pass
class DemoApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DemoApp().run()
You are doing it right. The ScrollView allows you to scroll to see parts of your GridLayout that don't fit in the ScrollView. When you set the size_hint to (1, .7), everything fits within the ScrollView, so it does not scroll.
You can force scrolling by adding Widgets to take up more space (like Labels with no text):
<Root>:
ScrollView:
size_hint: 1, .7
GridLayout:
size_hint_y: None
cols: 1
height: self.minimum_height
Label:
text: ''
size_hint_y: None
height: 300
Button
text: 'one'
size_hint: 1, None
height: self.texture_size[1]
Button:
text: 'two'
size_hint: 1, None
height: self.texture_size[1]
Button:
text: 'three'
size_hint: 1, None
height: self.texture_size[1]
Button:
text: 'four'
size_hint: 1, None
height: self.texture_size[1]
Label:
text: ''
size_hint_y: None
height: 300
Once theres a size_hint_y = None in the parent widget, then you have to manually declare the height of the child widget since size_hint_y deals with the height or Y axis.. so i modified your code to make things a lil clearer
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<Root>:
ScrollView:
size_hint_y:None
height:root.width / 2
GridLayout:
size_hint_y: None
cols: 1
height:self.minimum_height
spacing:dp(20)
Button
text: 'one'
size_hint_y:None
height:dp(50)
Button:
text: 'two'
size_hint_y:None
height:dp(50)
Button:
text: 'three'
size_hint_y:None
height:dp(50)
Button:
text: 'four'
size_hint_y:None
height:dp(50)
''')
class Root(FloatLayout):
pass
class DemoApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DemoApp().run()
I am trying to create a two screen Kivy touchscreen interface on a Raspberry Pi that acts like a selectable Menu interface to do a function depending on the Selection.
Screen 1:
Shows Eight Blocks - each block is made up by a BoxLayout that consists of Label, Image, Timer, Button.
Screen 2:
A pre defined list of blocks with names and pictures and a timer value.
Now lets say I want block 2 on screen one to have its values set to whatever I select on Screen2, I would like to press on the Button of Block2 on screen 1, it opens screen2 and I select one of the items. Now depending on what I selected on screen2, it should now load the values of that item and update block 2 on screen1 with Label, Image, Timer. The Timer should be a countdown timer.
I have the basic Layout that works showing screen1 and screen2 but am batteling with the value update concept and the multiple Blocks. The Code is still a bit of a mess but hope it gives an idea of what I am trying to do - will past it after posting the question. Screen Two still has no values..
main.py
# class for aqua.kv file
class MainScreen(Screen):
pass
# class for screen.kv file
class SelectScreen(Screen):
pass
class Tray1(Screen):
pass
# class for date.kv file
class Date(BoxLayout,EventDispatcher):
def __init__(self,**kwargs):
super(Date,self).__init__(**kwargs)
Builder.load_file('date.kv')
Builder.load_file('tray1.kv')
class MyApp(App):
time = StringProperty()
def update(self,*args):
self.time = str(time.asctime())
def build(self):
Clock.schedule_interval(self.update,1)
# Set up the layout:
return Builder.load_file('screen.kv')
if __name__ == '__main__':
MyApp().run()
screen.kv
ScreenManager:
id: screen_manager
MainScreen:
id: main_screen
name: 'MainScreen'
manager: 'screen_manager'
SelectScreen:
id: select_screen
name: 'SelectScreen'
manager: 'screen_manager'
# on_release: background_color = 1,0,0,1
<MainScreen>:
BoxLayout:
canvas.before:
Color:
rgba: .5,.6,.7,1
Rectangle:
pos: self.pos
size: self.size
orientation:'vertical'
BoxLayout:
size_hint:1,.2
Date:
BoxLayout:
orientation: 'horizontal'
# size_hint: .2,1
BoxLayout:
orientation: 'vertical'
Label:
id:btn_tray1
font_size: 30
Image:
source:"logo.png"
Tray1:
Button:
id:btn_tray1_1
font_size: 30
text:"Tray1"
on_release: app.root.current = "SelectScreen"
#on_release: app.root.current = "SelectScreen"
# on_release: btn_tray1.background_color = 1,0,0,1
Button:
id:btn_tray2
font_size: 30
text:"Tray2"
on_release: app.root.current = "SelectScreen"
Button:
id: btn_tray3
font_size: 30
text:"Tray3"
on_release: app.root.current = "SelectScreen"
<SelectScreen>:
BoxLayout:
canvas.before:
Color:
rgba: .5,.6,.7,1
Rectangle:
pos: self.pos
size: self.size
orientation:'vertical'
BoxLayout:
orientation: 'horizontal'
BoxLayout:
orientation: 'horizontal'
# size_hint: .2,1
Button:
size_hint:1,.2
id:btn_tray5
font_size: 30
text:"Tray5"
on_release: app.root.current = "MainScreen"
# on_release: btn_tray1.background_color = 1,0,0,1
Button:
size_hint:1,.2
id:btn_tray6
font_size: 30
text:"Tray6"
on_release: app.root.current = "MainScreen"
Button:
size_hint:1,.2
id: btn_tray7
font_size: 30
text:"Tray7"
on_release: app.root.current = "MainScreen"
# on_release: btn_tray3.background_color = 1,0,0,1
Button:
size_hint:1,.2
id: btn_tray8
font_size: 30
text:"Tray8"
on_release: app.root.current = "MainScreen"
# on_release: btn_tray4.background_color = 1,0,0,1