I am trying to create a dropdown list that is called when a button is submitted. After the drop down list is called i want the value of the button to be set to the button in the dropdown list that has been selected. I then want to retrieve this value back in my code to carry out some logic. I found this question that has been previously asked and it outlines exactly what i would like to achieve Python, Kivy. Get text from dynamically created buttons with a dropdown. However, i tried incorporating the answer in my code but for some reason the drop down list does not appear. I would really appreciate if someone could help me out and tell me what is it i am doing in correctly.
Scrap.py
from kivy.app import App
from kivy.uix.checkbox import CheckBox
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.uix.dropdown import DropDown
import datetime as dt
Window.size = (800,600)
class CustomDropDown(DropDown):
def __init__(self, **kwargs):
super(CustomDropDown, self).__init__(**kwargs)
self.add_buttons()
def add_buttons(self):
for index in range(10):
btn = Button(text='Value %d' % index, size_hint_y=None, height=44)
btn.bind(on_release=lambda btn: self.select(btn.text))
self.add_widget(btn)
class MainWindow(Screen):
check_solid = ObjectProperty(False)
check_water = ObjectProperty(False)
check_boiling = ObjectProperty(False)
s_id = str(dt.datetime.now().strftime("%y%m%d%H%M"))
def btn(self):
print(self.check_solid.state)
print(self.check_water.state)
print(self.check_boiling.state)
class MyScreenManager(ScreenManager):
def Run_Draws_Test(self, value):
print(value)
class Checkbox(CheckBox):
pass
class ScrapApp(App):
title = "Chemistry Sample Requests"
def build(self):
return MyScreenManager()
if __name__ == '__main__':
ScrapApp().run()
scrap.kv
#:import Factory kivy.factory.Factory
<Button>:
size_hint: 0.1,0.1
<Label>:
size_hint: 0.1,0.1
<Checkbox>:
size_hint: 0.1,0.1
<TextInput>:
size_hint: 0.2,0.1
multiline: False
<CustomDropDown>:
on_select:
app.root.ids.MainWindow.ids.mainbutton.text = '{}'.format(args[1])
app.root.Run_Draws_Test(args[1])
<MainWindow>:
orientation: "vertical"
check_solid: solid_check
check_boiling: boiling_check
check_water: water_check
FloatLayout:
Label:
text: "Sample ID: "
pos_hint: {"x":0.05, "top":0.95}
Label:
text: root.s_id
pos_hint: {"x":0.2, "top": 0.95}
Label:
text: "Plant ID: "
pos_hint: {"x":0.05, "top": 0.8}
Button:
id: mainbutton
text: "Choose"
pos: 400,400
size_hint: None,None
size: 150, 50
on_release: Factory.CustomDropDown().open(self)
Label:
text: "Formulation: "
pos_hint: {"x":0.05, "top": 0.65}
TextInput:
id: id_formulation
pos_hint: {"x":0.2, "top": 0.65}
Label:
text: "Solids Test: "
pos_hint: {"x":0.05, "top": 0.5}
Checkbox:
id: solid_check
pos_hint: {"x":0.25, "top": 0.5}
Label:
text: "Water Content Test: "
pos_hint: {"x":0.05, "top": 0.35}
Checkbox:
id: water_check
pos_hint: {"x":0.25, "top": 0.35}
Label:
text: "Boiling Point: "
pos_hint: {"x":0.05, "top": 0.2}
Checkbox:
id: boiling_check
pos_hint: {"x":0.25, "top": 0.2}
Button:
text: "Submit"
pos_hint: {"x": 0.7, "top": 0.5}
on_release: root.btn()
<MyScreenManager>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 0.5
Rectangle:
pos: 0,0
size: 800, 600
MainWindow:
id: MainWindow
name: 'MainWindow'
I've run into bugs like this before. I don't know if Kivy is working as intended with this or how to fix it. Anyone with more knowledge than me, I'd be glad to hear the reasoning to this.
class CustomDropDown(DropDown):
def __init__(self, **kwargs):
super(CustomDropDown, self).__init__(**kwargs)
self.add_buttons()
def add_buttons(self):
for index in range(10):
#btn = Button(text='Value %d' % index, size_hint_y=None, height=44)
btn = Button(text='Value %d' % index)
btn.size_hint_y = None
btn.height = 44
btn.bind(on_release=lambda btn: self.select(btn.text))
self.add_widget(btn)
These kind of things cause me nothing but frustration.
Related
I want to be able to click on MDFlatButton that says Štart and i want it to let's say for an example to call get_user_input(self) which would print whatever is inside text field. And I have been struggling with this for 2 whole days and i have no idea what to do, I am just a beginner and I have no clue what am I doing so sorry if it's messy. Ty for help, those are my files:
main.py file:
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.menu import MDDropdownMenu
from kivymd.uix.textfield import MDTextField
from kivy.properties import StringProperty
class testy(Screen):
novy_test = ObjectProperty()
class Test(MDApp):
Window.size = (1170 / 3, 2532 / 3)
# input_number = ObjectProperty()
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = 'Gray'
return testy()
def get_user_input(self):
print(self.root.ids.my_textfield_1.ids.user.text)
def callback(self, button):
pass
class CustomOverFlowMenu(MDDropdownMenu):
# In this class you can set custom properties for the overflow menu.
pass
Test().run()
test.kv file:
#:import Factory kivy.factory.Factory
#:import CustomOverFlowMenu __main__.CustomOverFlowMenu
<testy>:
name:'testy'
id: testy
MDBoxLayout:
orientation: "vertical"
MDTopAppBar:
title: "MDTopAppBar"
use_overflow: True
overflow_cls: CustomOverFlowMenu()
specific_text_color: .51,.51,.51,1
md_bg_color: 0, 0, 0, 1
left_action_items: [["car", lambda x: Factory.novy_test().open(), '',"Nový test"]]
MDBottomNavigation:
panel_color: "black"
selected_color_background: "white"
text_color_active: "lightgray"
selected_color_background: 1, 1, 1, .4
MDBottomNavigationItem:
name: 'screen 1'
text: 'Testy'
icon: 'road-variant'
MDLabel:
text: 'Test'
halign: 'center'
MDBottomNavigationItem:
name: 'screen 2'
text: 'chyby'
icon: 'alert-circle'
MDLabel:
text: 'Chyby'
halign: 'center'
MDBottomNavigationItem:
name: 'screen 3'
text: 'Settings'
icon: 'cog'
MDLabel:
text: 'LinkedIN'
halign: 'center'
<novy_test#Popup>:
id:my_textfield_1
size_hint: .8, .45
title: 'Nový test'
separator_color: 'black'
title_align: 'center'
BoxLayout:
id: layout
spacing: 10
orientation:'vertical'
MDTextField:
id: user
hint_text: "Číslo testu"
mode: "round"
pos_hint: {"top": 1}
MDFlatButton:
text: 'Štart'
pos_hint: {'center_x': .5}
on_press: app.get_user_input()
MDFlatButton:
pos_hint: {'center_x': .5}
text:'test z nesprávnych'
MDFlatButton:
text:'test z neurobených'
pos_hint: {'center_x': .5}
MDFlatButton:
text:'test z neurobených'
pos_hint: {'center_x': .5}
MDFlatButton:
text:'test z neurobených'
pos_hint: {'center_x': .5}
I didn't perfectly understand all that you wanted to do, but in this example, the text a person types into the box will print. I moved the .kv language into a string just for convenience of creating a one-file runnable project. I tested it.
The main point is this code, inside the .kv file/kivy language you can use the id property to get a reference to the objects, and then the .text property of that object is sent as an argument to the function.
on_press: app.get_user_input(user.text)
runnable:
from kivy.core.window import Window
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.menu import MDDropdownMenu
from kivy.uix.popup import Popup
from kivymd.uix.textfield import MDTextField
from kivy.properties import StringProperty
Builder.load_string('''
#:import Factory kivy.factory.Factory
<testy>:
name:'testy'
id: testy
MDBoxLayout:
orientation: "vertical"
MDTopAppBar:
title: "MDTopAppBar"
use_overflow: True
# overflow_cls: CustomOverFlowMenu()
specific_text_color: .51,.51,.51,1
md_bg_color: 0, 0, 0, 1
left_action_items: [["car", lambda x: Factory.NovyTest().open(), '',"Nový test"]]
MDBottomNavigation:
panel_color: "black"
selected_color_background: "white"
text_color_active: "lightgray"
selected_color_background: 1, 1, 1, .4
MDBottomNavigationItem:
name: 'screen 1'
text: 'Testy'
icon: 'road-variant'
MDLabel:
text: 'Test'
halign: 'center'
MDBottomNavigationItem:
name: 'screen 2'
text: 'chyby'
icon: 'alert-circle'
MDLabel:
text: 'Chyby'
halign: 'center'
MDBottomNavigationItem:
name: 'screen 3'
text: 'Settings'
icon: 'cog'
MDLabel:
text: 'LinkedIN'
halign: 'center'
<NovyTest#Popup>:
id:my_textfield_1
size_hint: .8, .45
title: 'Nový test'
separator_color: 'black'
title_align: 'center'
BoxLayout:
id: layout
spacing: 10
orientation:'vertical'
MDTextField:
id: user
hint_text: "Číslo testu"
mode: "round"
pos_hint: {"top": 1}
MDFlatButton:
text: 'Štart'
pos_hint: {'center_x': .5}
# inside .kv/kivy language you can use the id property
on_press: app.get_user_input(user.text)
MDFlatButton:
pos_hint: {'center_x': .5}
text:'test z nesprávnych'
MDFlatButton:
text:'test z neurobených'
pos_hint: {'center_x': .5}
MDFlatButton:
text:'test z neurobených'
pos_hint: {'center_x': .5}
MDFlatButton:
text:'test z neurobených'
pos_hint: {'center_x': .5}
'''
)
class NovyTest(Popup):
# just an example, not used in this code
def __init__(self, **kw):
super().__init__(**kw)
class testy(Screen):
# can list objects defined in .kv file and give them a type hint corresponding to object type
novy_test: NovyTest
def __init__(self, **kw):
super().__init__(**kw)
print("creating screen testy")
# novy_test = ObjectProperty()
class Test(MDApp):
Window.size = (1170 / 3, 2532 / 3)
# input_number = ObjectProperty()
def build(self) -> testy:
self.theme_cls.material_style = "M3"
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = 'Gray'
return testy()
def get_user_input(self, input_text, *args):
print(f"{self} user input: {input_text}")
# print(self.root.ids.my_textfield_1.ids.user.text)
def callback(self, button):
pass
class CustomOverFlowMenu(MDDropdownMenu):
# In this class you can set custom properties for the overflow menu.
pass
Test().run()
end
I have in my app some images , each image belongs to a screen and when I press a button I try to get another screen but a random one. I think I am pretty close to do what I want to do but for 2 days I am stuck at this stage.. when i run my code in the console i have this error
" AttributeError: 'ScreenManager' object has no attribute 'random_screen' "
And I really don't know where to put that attribute ... please heeeelppp ! :((
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from random import choice
class MyScreens(Screen):
screens = ["Q1", "Q2", "Q3"] # ........
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.random_screen()
def random_screen(self, *_):
self.source = choice(self.screens)
class Q1(Screen):
pass
class Q2(Screen):
pass
class Q3(Screen):
pass
class TestApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(Q1(name='Q1'))
sm.add_widget(Q2(name='Q2'))
sm.add_widget(Q2(name='Q3'))
return sm
if __name__ == '__main__':
TestApp().run()
<Q1>:
Image:
source: 'Q1.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: app.root.random_screen()
<Q2>:
Image:
source: 'Q2.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_press: app.root.random_screen()
<Q3>:
Image:
source: 'Q3.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_press: app.root.random_screen()
You got a lot of bugs in there...
Myscreen class isn't connected to your other classes or kv file at all, so that function will not be found at all.
Your 3rd add.widget call adds the 'Q2' screen instead of Q3
Screen does not have a self.source parameter. You need to use the screenmanager to change screens. root.manager.current = 'Q#' would be the correct way.
This worked for me...
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
import random
class Q1(Screen):
def random_screen(self):
screens = ['Q1', 'Q2', 'Q3']
return random.choice(screens)
class Q2(Screen):
def random_screen(self):
screens = ['Q1', 'Q2', 'Q3']
return random.choice(screens)
class Q3(Screen):
def random_screen(self):
screens = ['Q1', 'Q2', 'Q3']
return random.choice(screens)
class TestApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(Q1(name='Q1'))
sm.add_widget(Q2(name='Q2'))
sm.add_widget(Q3(name='Q3'))
return sm
if __name__ == '__main__':
TestApp().run()
KV file
<Q1>:
Image:
source: 'Q1.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: root.manager.current = root.random_screen()
<Q2>:
Image:
source: 'Q2.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: root.manager.current = root.random_screen()
<Q3>:
Image:
source: 'Q3.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: root.manager.current = root.random_screen()
You could also put the method in the app class and in the kv file call it by using
app.random_screen()
Just means you don't have to repeat the same method in each screen class.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
import random
class Q1(Screen):
Pass
class Q2(Screen):
Pass
class Q3(Screen):
Pass
class TestApp(App):
def random_screen(self):
screens = ['Q1', 'Q2', 'Q3']
return random.choice(screens)
def build(self):
sm = ScreenManager()
sm.add_widget(Q1(name='Q1'))
sm.add_widget(Q2(name='Q2'))
sm.add_widget(Q3(name='Q3'))
return sm
if __name__ == '__main__':
TestApp().run()
kV:
<Q1>:
Image:
source: 'Q1.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: root.manager.current = app.random_screen()
<Q2>:
Image:
source: 'Q2.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: root.manager.current = app.random_screen()
<Q3>:
Image:
source: 'Q3.png'
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: root.manager.current = app.random_screen()
Another thing to consider, and I am not sure how this would work as I haven't used images like the way you are doing, but you could have the button change the image rather then change the screen... In the app class you could have a string property that is changed in the same way the button changes your screen. In the kv file, have the image source reference this string property, app.Pic for example below...
from kivy.app import App
from kivy.uix.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager, Screen
import random
class Q1(Screen):
Pass
class TestApp(App):
Pic = StringProperty('Q1.png')
def random_pic(self):
Pics = ['Q1.png', 'Q2.png', 'Q3.png']
self.Pic = random.choice(Pics)
def build(self):
sm = ScreenManager()
sm.add_widget(Q1(name='Q1'))
return sm
if __name__ == '__main__':
TestApp().run()
kV:
<Q1>:
Image:
source: app.Pic
FloatLayout:
size: root.width, root.height/2
Button:
size_hint: 0.3, 0.25
pos_hint: {"x":0.09, "top":1.16}
background_color: 1, 1, 1, 0.2
on_release: app.random_pic()
I know this isnt another answer to your original question but as per the above comment here is a really paired back quiz app, to give you an idea of how you can change the question and the answers and use the same buttons and screen...
I put everything into the app class, it makes it easier to access from the kv file. I tried to add in a few comments to try and expalin the code
from kivy.app import App
from kivy.properties import StringProperty, BooleanProperty,ObjectProperty, NumericProperty
from kivy.uix.screenmanager import ScreenManager, Screen
TestBank = {1 : {"Current Question": "Press 3", "Correct Answer": "3", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]},
2 : {"Current Question": "Press 4", "Correct Answer": "4", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]},
3 : {"Current Question": "Press 1", "Correct Answer": "1", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]},
4 : {"Current Question": "Press 2", "Correct Answer": "2", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]}}
def CreateTest(A_Test = TestBank):
#do something to randomise your test, maybe select a certain number of questions or merge several test banks etc...
return A_Test
class MenuScreen(Screen):
pass
class ResultScreen(Screen):
pass
class TestScreen(Screen):
pass
class QuizApp(App):
TestCurrentQuestion = StringProperty()
'This is updated when next question is selcted'
ChosenAnswerText = StringProperty()
'Every time an answer is selected this property is updated with the selected answer, then the label is automatically updated'
CurrentQuestion = NumericProperty(1)
'The Next and Previous buttons change this property up or down'
TotalQuestions = 4
AnswerButtonA = StringProperty()
AnswerButtonB = StringProperty()
AnswerButtonC = StringProperty()
AnswerButtonD = StringProperty()
'These are the four answer buttons, the Next and Previous buttons update these properties with the answer options, the button texts are then autmatically updated'
Score = NumericProperty()
def StartTest(self):
self.Test = CreateTest() #The test is created
self.UpdateQuestionAndAnswerButtons() #This method updates the text properties that updates all the buttons
def NextQuestion(self):
self.CurrentQuestion +=1 #changes the current question property +1
self.UpdateQuestionAndAnswerButtons() #This method updates the text properties that updates all the buttons
def PreviousQuestion(self):
self.CurrentQuestion -=1 #changes the current question property -1
self.UpdateQuestionAndAnswerButtons() #This method updates the text properties that updates all the buttons
def UpdateChosenAnswer(self, button):
self.Test[self.CurrentQuestion]["Chosen Answer"] = button.text #so that a score can be calculated, the chosen answer is stored in the dictionary TestBank
self.ChosenAnswerText = self.Test[self.CurrentQuestion]["Chosen Answer"] #ChosenAnswerProperty is updated, this then updates the label so the chosen answer appears on screen
def UpdateQuestionAndAnswerButtons(self):
self.TestCurrentQuestion = self.Test[self.CurrentQuestion]["Current Question"]
#TestCurrentQuestion is the property, it is updated to ["Current Question"] for the Test Dic
self.AnswerButtonA, self.AnswerButtonB, self.AnswerButtonC, self.AnswerButtonD = self.Test[self.CurrentQuestion]["Multiple Chocies"]
#["Multiple Chocies"] is a list containing 4 items, so the four properties are updated in one line
self.ChosenAnswerText = self.Test[self.CurrentQuestion]["Chosen Answer"]
#The chosen answer is updated, this is needed here incase the previous button is selcted, the previous selected answer will be updated to avoid confusion for the user
def CalculateScore(self):
score = 0
for Question in self.Test.keys():
if self.Test[Question]["Chosen Answer"]== self.Test[Question]["Correct Answer"]:
score+=1
self.Score = score
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(ResultScreen(name='result'))
sm.add_widget(TestScreen(name='test'))
return sm
QuizApp().run()
And the KV file
<ResultScreen>
BoxLayout:
orientation: "vertical"
padding: "20px"
spacing: "20px"
BoxLayout:
Label:
text: f"Your Score was {app.Score} out of {app.TotalQuestions}\n {round(app.Score/app.TotalQuestions*100)}%"
Label:
text: "PASS" if app.Score/app.TotalQuestions*100 >= 70 else "FAIL" #pass mark is 70%
Button:
text: "Good Bye"
on_press: app.get_running_app().stop()
<MenuScreen>
BoxLayout:
orientation: "vertical"
padding: "20px"
spacing: "20px"
Label:
text:"Wecome to the Test App"
Button:
text: "New Test"
on_press: app.StartTest(); root.manager.current = "test"
<TestScreen>
BoxLayout:
orientation: "vertical"
BoxLayout:
padding: "20px"
orientation: "vertical"
Label:
text: app.TestCurrentQuestion
halign: "center"
Label:
text: app.ChosenAnswerText
GridLayout:
padding: "20px"
spacing: "20px"
cols: 2
rows: 2
Button:
text: app.AnswerButtonA
on_press: app.UpdateChosenAnswer(self)
Button:
text: app.AnswerButtonB
on_press: app.UpdateChosenAnswer(self)
Button:
text: app.AnswerButtonC
on_press: app.UpdateChosenAnswer(self)
Button:
text: app.AnswerButtonD
on_press: app.UpdateChosenAnswer(self)
BoxLayout:
size_hint: (1,None)
orientation: "horizontal"
padding: "20px"
spacing: "20px"
Button:
text: "Previous"
size_hint: (0.3,1)
on_press: app.PreviousQuestion()
disabled: True if app.CurrentQuestion == 1 else False
Button:
text: "Submit"
size_hint: (0.4,1)
on_press: app.CalculateScore(); root.manager.current = "result"
Button:
text: "Next"
on_press: app.NextQuestion();
size_hint: (0.3,1)
disabled: True if app.CurrentQuestion == app.TotalQuestions else False
I'm working on a project using Kivy with ScreenManager (and KivyMD).
The reason for using this is my need of making an application with multiple screens. My goal is to get User Input from an existing MDTextField by (a string) ID and then Print it out.
However, I keep getting this Error: File "<string>", line 37, in <module> File "c:/Users/admin/Desktop/myApp/Main.py", line 75, in loginFunction username = mainScreenInstance.ids["input_username"].text KeyError: 'input_username'.
I have searched for answers all over the internet and StackOverflow, but no solution seems to work for me. Maybe there's someone who can help me with my problem and make my code work.
Anyway, here's my short & simple Code:
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
import sys
screen_helper = """
ScreenManager:
MenuScreen:
name: 'menu'
SecondScreen:
name: 'second'
<MenuScreen>:
MDToolbar:
title: "Menu Screen"
pos_hint: {"top": 1}
anchor_title: "center"
md_bg_color: (0/255, 0/255, 0/255, 1)
Image:
source: "Logo_h_black.png"
pos_hint: {"center_x": 0.5, "center_y": 0.75}
size_hint_x: (0.25)
size_hint_y: (0.25)
MDTextField:
id: input_username
hint_text: "Username"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.55}
font_size: 20
mode: "rectangle"
MDTextField:
id: input_password
hint_text: "Password"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.45}
font_size: 20
mode: "rectangle"
MDFillRoundFlatButton:
text: "LOG IN"
font_size: 17
pos_hint: {"center_x": 0.5, "center_y": 0.25}
on_press: app.loginFunction()
<SecondScreen>:
MDLabel:
text: 'Profile'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class SecondScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SecondScreen(name='second'))
class myApp(MDApp):
def loginFunction(self, args=None):
if args is None:
args = sys.argv
mainScreenInstance = sm.get_screen('menu')
username = mainScreenInstance.ids["input_username"].text
password = mainScreenInstance.ids["input_password"].text
print (username)
print (password)
def build(self):
screen = Builder.load_string(screen_helper)
return screen
myApp().run()
The problem is that your loginFunction is trying to get the mainScreenInstance from sm, but sm is created before the screen_helper is loaded, so it doesn't know about the ids you have defined in your screen_helper. Also, that sm is not actually used as part of you GUI, it is just ignored (except for your reference to it in the loginFunction).
So, I recommend that you eliminate the following lines completely:
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SecondScreen(name='second'))
And change loginFunction to not use sm by replacing:
mainScreenInstance = sm.get_screen('menu')
with:
mainScreenInstance = self.root.get_screen('menu')
You're duplicating the widget definitions and then not using them in code. Finally, you're overwriting the Screen manager from the screen_helper string with a new instance. Tbh I did some of that when I started learning Kivy. The kv language is confusing at first but once you get it is beautiful. This version should work (I haven't tested it since I haven't found an easy way to install kivymd on my phone's dev env):
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen
import sys
screen_helper = """
ScreenManager:
MenuScreen:
name: 'menu'
SecondScreen:
name: 'second'
<MenuScreen>:
MDToolbar:
title: "Menu Screen"
pos_hint: {"top": 1}
anchor_title: "center"
md_bg_color: (0, 0, 0, 1)
Image:
source: "Logo_h_black.png"
pos_hint: {"center_x": 0.5, "center_y": 0.75}
size_hint_x: (0.25)
size_hint_y: (0.25)
MDTextField:
id: input_username
hint_text: "Username"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.55}
font_size: 20
mode: "rectangle"
MDTextField:
id: input_password
hint_text: "Password"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.45}
font_size: 20
mode: "rectangle"
MDFillRoundFlatButton:
text: "LOG IN"
font_size: 17
pos_hint: {"center_x": 0.5, "center_y": 0.25}
on_press: app.loginFunction()
<SecondScreen>:
MDLabel:
text: 'Profile'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class SecondScreen(Screen):
pass
class myApp(MDApp):
sm = None
def on_start():
self.sm = self.root
def loginFunction(self, args=None):
if args is None:
args = sys.argv
mainScreenInstance = self.sm.get_screen('menu')
username = mainScreenInstance.ids["input_username"].text
password = mainScreenInstance.ids["input_password"].text
print (username)
print (password)
# if self.is_login_correct(username, password):
# self.sm.current('second')
def build(self):
return Builder.load_string(screen_helper)
myApp().run()
Personally, I would move all login-related functions to the MenuScreen class, for consistency.
What i want is that:
Press button A >> Open choose folder dialog >> Select folder A >> Show path A in label A;
Press button B >> Open choose folder dialog >> Select folder B >> Show path B in label B.
If you run my code, it works very good. But if you read the code carefully, you will find a issue.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<Root>:
loadFolderA: loadFolderA
loadFolderB: loadFolderB
cols: 2
rows: 2
Button:
id: selectFolderA
text: 'select folder A'
pos_hint: {'center_x': 0.21, 'center_y': .5}
on_release: root.show_select_folder_dialogA()
Button:
id: selectFolderB
text: 'select folder B'
pos_hint: {'center_x': 0.785, 'center_y': .5}
on_release: root.show_select_folder_dialogB()
Label:
id: loadFolderA
text: 'path A'
font_size: 30
pos_hint: {'center_x': 0.21, 'center_y': .5}
Label:
id: loadFolderB
text: 'path B'
font_size: 30
pos_hint: {'center_x': 0.785, 'center_y': .5}
<SelectFolder>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Choose"
on_release: root.choose(filechooser.path, filechooser.selection)
''')
class Root(GridLayout):
posixPathA = StringProperty('')
posixPathB = StringProperty('')
def dismiss_popup(self):
self._popup.dismiss()
def show_select_folder_dialogA(self):
content = SelectFolder(choose=self.chooseA, cancel=self.dismiss_popup)
self._popup = Popup(title="Select Folder", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def show_select_folder_dialogB(self):
content = SelectFolder(choose=self.chooseB, cancel=self.dismiss_popup)
self._popup = Popup(title="Select Folder", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def chooseA(self, path, filename):
self.posixPathA = path
self.loadFolderA.text = self.posixPathA
self.dismiss_popup()
def chooseB(self, path, filename):
self.posixPathB = path
self.loadFolderB.text = self.posixPathB
self.dismiss_popup()
class SelectFolder(FloatLayout):
choose = ObjectProperty(None)
cancel = ObjectProperty(None)
class DropApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DropApp().run()
The function 'show_select_folder_dialog' I write in two times, one for binding with button A, one for button B. And same to the function 'choose'. It really makes me unhappy. So I want to know if I can make the Code know which button is pressed, then pass the folder path to the right label. Hope someone can help.
#
#
It takes all night, now i come to this place:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<Root>:
loadFolderA: loadFolderA
loadFolderB: loadFolderB
cols: 2
rows: 2
NewButton:
id: selectFolderA
text: 'select folder A'
pos_hint: {'center_x': 0.21, 'center_y': .5}
on_release: self.callback()
NewButton:
id: selectFolderB
text: 'select folder B'
pos_hint: {'center_x': 0.785, 'center_y': .5}
on_release: self.callback()
Label:
id: loadFolderA
text: 'path A'
font_size: 30
pos_hint: {'center_x': 0.21, 'center_y': .5}
Label:
id: loadFolderB
text: 'path B'
font_size: 30
pos_hint: {'center_x': 0.785, 'center_y': .5}
<SelectFolder>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Choose"
on_release: root.choose(filechooser.path, filechooser.selection)
''')
class NewButton(Button):
def __init__(self, **kwargs):
super(Button, self).__init__(**kwargs)
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
Root.show_select_folder_dialog(Root)
class Root(GridLayout):
posixPathA = StringProperty('')
posixPathB = StringProperty('')
def dismiss_popup(self):
self._popup.dismiss()
def show_select_folder_dialog(self):
content = SelectFolder(choose=self.choose, cancel=self.dismiss_popup)
self._popup = Popup(title="Select Folder", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def choose(self, path, filename):
self.posixPathA = path
self.loadFolderA.text = self.posixPathA
self.dismiss_popup()
class SelectFolder(FloatLayout):
choose = ObjectProperty(None)
cancel = ObjectProperty(None)
class DropApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DropApp().run()
My plan is that use Callback(instance) to know which button was calling the function. Then call the Function to open choose folder dialog. It's strange but this line works here:
Root.show_select_folder_dialog(Root)
But I cannot go further. Now the function report error:'TypeError: choose() missing 1 required positional argument: 'filename''
Hope someone can help me!
You can remove both the duplicate A/B methods by sending an idx with the button press:
Builder.load_string('''
#:kivy 1.11.0
<Root>:
loadFolderA: loadFolderA
loadFolderB: loadFolderB
cols: 2
rows: 2
Button:
id: selectFolderA
text: 'select folder A'
pos_hint: {'center_x': 0.21, 'center_y': .5}
on_release: root.show_select_folder_dialog("A") # send an idx here
Button:
id: selectFolderB
text: 'select folder B'
pos_hint: {'center_x': 0.785, 'center_y': .5}
on_release: root.show_select_folder_dialog("B") # send an idx here
Label:
id: loadFolderA
text: 'path A'
font_size: 30
pos_hint: {'center_x': 0.21, 'center_y': .5}
Label:
id: loadFolderB
text: 'path B'
font_size: 30
pos_hint: {'center_x': 0.785, 'center_y': .5}
<SelectFolder>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Choose"
on_release: root.choose(filechooser.path, filechooser.selection)
''')
from functools import partial
class Root(GridLayout):
posixPathA = StringProperty('')
posixPathB = StringProperty('')
def dismiss_popup(self):
self._popup.dismiss()
def show_select_folder_dialog(self, idx):
# pass the idx to the choose method
content = SelectFolder(choose=partial(self.choose, idx),
cancel=self.dismiss_popup)
self._popup = Popup(title="Select Folder", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def choose(self, idx, path, filename):
if idx == "A": # The "A" button was selected
self.posixPathA = path
self.loadFolderA.text = self.posixPathA
else: # You can use more elifs if you like
self.posixPathB = path
self.loadFolderB.text = self.posixPathB
self.dismiss_popup()
class SelectFolder(FloatLayout):
choose = ObjectProperty(None)
cancel = ObjectProperty(None)
class DropApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DropApp().run()
Hey Friend i have got a simple solution for your code which makes it easier to recognise which button you have chosen for that you can use easygui module:
choice = easygui.buttonbox(title="Choose button",msg="Choose a button",choices=["Button 1","Button 2",])
which opens a box like:
And now you can use your if condition to and do the rest of the process.
i am trying to make an app, a game actually that will display random screens when the "NEXT" button is touched, so i made the screens and the labels to be displayed on each screen but i cant get it to display a random screen when the "NEXT" button is touched, it just follows a pattern, would anyone help me here? heres the code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import Image kivy.uix.image.Image
#: import SoundLoader kivy.core.audio.SoundLoader
ScreenManagement:
transition: FadeTransition()
MainScreen:
GameScreen:
GameScreen2:
GameScreen3:
GameScreen4:
<Button>:
font_size: 12
size_hint: 0.2, 0.1
<MainScreen>:
name: "main"
FloatLayout:
Button:
text: "START GAME"
color: 1,0,1,1
pos_hint: {"x": 0, "y":0}
on_release: app.root.current = "game"
Button:
text: "QUIT"
color: 1,0,0,1
pos_hint: {"x": .8, "y": 0}
on_release: quit()
Button:
text: "SOUND"
color: 0,1,0,1
pos_hint: {"x":.2 , "y": .4}
on_press: app.play_sound1()
<GameScreen>:
name: "game"
FloatLayout:
Label:
text: "Python\nSnowden\nMr.Robot"
font_size: 40
color: 0,1,0,1
pos_hint: {"x":0, "y": 0}
Button:
text: "Home"
on_release: app.root.current = "main"
color: 1,0,0,1
pos_hint: {"right":1, "top":1}
Button:
text: "Next"
on_release: app.root.current = "game2"
color: 0,1,0,1
pos_hint: {"x":0, "y":0}
<GameScreen2>:
name: "game2"
FloatLayout:
Label:
text: "Banana\n\nOrange\n\nTea\n\nSleep"
font_size: 40
color: 0,1,0,1
pos_hint: {"x":0, "y": 0}
Button:
text: "Home"
on_release: app.root.current = "main"
color: 1,0,0,1
pos_hint: {"right":1, "top":1}
Button:
text: "Next"
on_release: app.root.current = "game3"
color: 0,1,0,1
pos_hint: {"x": 0, "y": 0}
<GameScreen3>:
name: "game3"
FloatLayout:
Label:
text: "Assembly\n\nRuby\n\nC"
font_size: 40
color: 0,1,0,1
pos_hint: {"x":0, "y":0}
Button:
text: "Home"
on_release: app.root.current = "main"
color: 1,0,0,1
pos_hint: {"right":1, "top":1}
Button:
text: "Next"
on_release: app.root.current = "game4"
color: 0,1,0,1
pos_hint: {"x": 0, "y": 0}
<GameScreen4>:
name: "game4"
FloatLayout:
Label:
text: "Prolog\n\nPygame\n\nC++"
font_size: 40
color: 0,1,0,1
pos_hint: {"x":0, "y":0}
Button:
text: "Home"
on_release: app.root.current = "main"
color: 1,0,0,1
pos_hint: {"right":1, "top":1}
the above is the kv code, sorry for the mess im an amateur when it comes to kivy
and heres the python side code:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class MainScreen(Screen):
pass
class GameScreen(Screen):
pass
class GameScreen2(Screen):
pass
class GameScreen3(Screen):
pass
class GameScreen4(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("quora.kv")
class MainApp(App):
def build(self):
self.load_sounds()
return presentation
def load_sounds(self):
self.sounds = {}
for i in range(10):
fname = 'sound' + str(i+1) + '.wav'
self.sounds[i] = SoundLoader.load(fname)
def play_sound1(self):
sound = self.sounds.get(0)
if sound is not None:
sound.volume = 0.5
sound.play()
def play_sound2(self):
sound = self.sounds.get(1)
if sound is not None:
sound.volume = 0.5
sound.play()
if __name__ == "__main__":
MainApp().run()
this is the sample i made for you because the original one is way bigger than this i dont know how to use somewhat of a LOOP to generate screen so i made more that 20 screens on the original code so, and if you could help me figure out how can i STOP THE MENU SONG when i hit the "START" BUTTON, so that i can hit the "SONG" BUTTON on SCREEN1 to PLAY its SONG i would be gratefull. thanks you for the patience.
Display random screens when the "NEXT" button is touched
Use screen_names
screen_names
List of the names of all the Screen widgets added. The list is
read only.
screens_names is an AliasProperty and is read-only. It is updated
if the screen list changes or the name of a screen changes.
Snippets
#:import choice random.choice
...
Button:
text: "Next"
on_release:
root.manager.current = choice(root.manager.screen_names[1:])
color: 0,1,0,1
pos_hint: {"x":0, "y":0}
Stop / Play Music
You might want to use a ToggleButton instead of Button for playing music. Please refer to the example below.
Example
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.togglebutton import ToggleButton
from kivy.core.audio import SoundLoader
from kivy.properties import ObjectProperty
class MusicScreen(Screen):
enter = ObjectProperty(None)
text_input = ObjectProperty(None)
stop = ObjectProperty(None)
musicbutton = ToggleButton()
class ScreenManagement(ScreenManager):
pass
class MainApp(App):
sound = ObjectProperty(None, allownone=True)
def build(self):
return ScreenManagement()
def on_state(self, state, filename):
print("ONSTATE!!!")
print("\tstate=", state)
if self.sound is None:
self.sound = SoundLoader.load(filename)
# stop the sound if it's currently playing
if self.sound.status != 'stop':
self.sound.stop()
if state == "down":
self.sound.volume = .5
self.sound.play()
else: # if state == "normal":
if self.sound:
self.sound.stop()
# self.sound.unload()
self.sound = None
if __name__ == "__main__":
MainApp().run()
main.kv
#:kivy 1.11.0
<ScreenManagement>:
MusicScreen:
name: 'music'
<MusicScreen>:
text_input: text_input
id: "music"
name: "music"
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
rootpath: "/home/iam/Music/"
on_selection: text_input.text = self.selection and self.selection[0] or ''
TextInput:
id: text_input
size_hint_y: None
height: 50
multiline: False
ToggleButton:
size_hint: 1, .2
text: "Play/Stop By File"
on_state: app.on_state(self.state, text_input.text)
ToggleButton:
id: musicbutton
size_hint: 1, .2
text: "Play/Stop By Title"
on_state: app.on_state(self.state, text_input.text)