For my very first Kivy project I want to create a basic app that will store my passwords. If you happen to get the access code wrong, a popup will appear that says "Incorrect Password". Whenever I put the password in correctly first try the transitions work with no issues, but if the password is ever wrong and the popup appears, any subsequent attempts will result in the two pages overlapping.
main.py:
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
class PassWindow(Screen):
pass
class MainWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class PassPopup(FloatLayout):
pass
def openPopup():
show = PassPopup()
global popupWin
popupWin = Popup(title="Error", content=show, size_hint=(None, None), size=(400, 400))
popupWin.open()
def closePopup():
popupWin.dismiss()
kv = Builder.load_file("my.kv")
class MyApp(App):
def build(self):
return kv
if __name__ == "__main__":
MyApp().run()
my.kv:
#:import openPopup main.openPopup
#:import closePopup main.closePopup
WindowManager:
PassWindow:
MainWindow:
<Button>:
font_size: 30
color: 0.33, 0, 0.51, 1
<Label>:
color: 1, 1, 1, 1
<PassWindow>:
name: "passWin"
id: passWin
Label:
text: "Password Storage"
pos_hint: {"top": 1}
font_size: 50
size_hint: 1, 0.2
Label:
text: "Please Input Code to Access"
pos_hint: {"top": .8}
font_size: 50
size_hint: 1, 0.2
TextInput:
id: codeInput
multiline: False
password: True
font_size: 55
size_hint: 0.6, 0.2
pos_hint: {"x": .2, "top": .6}
Button:
text: "Submit"
size_hint: 0.5, 0.2
pos_hint: {"x": .25, "top": .35}
on_release:
app.root.current = "mainWin" if codeInput.text == "1234" else openPopup()
app.root.transition.direction = "left"
codeInput.text = ""
<PassPopup>:
Label:
text: "Incorrect Password"
size_hint: 0.6, 0.2
pos_hint: {"x": 0.2, "top": 0.8}
font_size: 30
Button:
text: "Close"
size_hint: 0.8, 0.2
pos_hint: {"x": 0.1, "top": 0.3}
on_release:
closePopup()
<MainWindow>:
name: "mainWin"
Button:
text: "Exit"
size_hint: 1, 0.2
on_release:
app.root.current = "passWin"
root.manager.transition.direction = "right"
The logic in your on_release: of the Submit Button is incorrect. The line:
app.root.current = "mainWin" if codeInput.text == "1234" else openPopup()
sets the current screen to "mainWin" if the password provided is "1234", otherwise, it sets the current screen to the return value of openPopup() (which is None). I suspect that is the cause of the strange behavior. To fix that, use the following for the on_release:
on_release:
if codeInput.text == "1234": app.root.current = "mainWin"
else: openPopup()
app.root.transition.direction = "left"
codeInput.text = ""
See the documentation.
Related
I want to use "pos_hint" to position the buttons, but for some reason they just stay at their default position, unless I use "pos", which I don't want to. For me it looks like both buttons loads, but the second one overlaps the first one, since they dont move to the position I have set for them.
main.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_file("test.kv")
class Widgets(Widget):
def yourProducts(self):
print("your products")
def newProduct(self):
print("Add new product")
class MainApp(App):
def build(self):
return Widgets()
if __name__ == "__main__":
MainApp().run()
text.kv
<Widgets>:
Button:
text: "New product"
size_hint: .6, .2
pos_hint: {"x": 0.5, "top": 1}
on_release: root.newProduct()
Button:
text: "See your product"
size_hint: .6, .2
pos_hint: {"x": 0.5, "top": 0.3}
on_release: root.yourProducts()
This is how it looks like when I run the program.
After trying some things I found out that I need to add "FloatLayout" under "Widgets:" like this:
<Widgets>:
Button:
text: "New product"
size_hint: .6, .2
pos_hint: {"x": 0.5, "top": 1}
on_release: root.newProduct()
Button:
text: "See your product"
size_hint: .6, .2
pos_hint: {"x": 0.5, "top": 0.3}
on_release: root.yourProducts()
I am trying to get TextInput's text from another screen. In the code below I can get the text but my project has multiple functions. So I thought if I can make textinput a class variable it would be eaiser to coding.
Example Code:
.py
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.screenmanager import NoTransition
import requests
class MainPage(Screen):
pass
class ExercisePage(Screen):
def test(self):
link = self.manager.get_screen('mainpage')
a = requests.get(f'{link.ids.http.text}/gpio12On')
def testt(self):
link = self.manager.get_screen('mainpage')
a = requests.get(f'{link.ids.http.text}/gpio12Off')
class MyApp(App):
def build(self):
global sm
sm = ScreenManager(transition=NoTransition())
sm.add_widget(MainPage(name='mainpage'))
sm.add_widget(ExercisePage(name='exercisepage'))
return sm
if __name__ == '__main__':
MyApp().run()
Here is the .kv file
<MainPage>
FloatLayout:
TextInput:
id: http
text: ""
multiline: False
size_hint: 0.5,0.05
pos_hint: {"x": 0.3, "y": 0.8}
Button:
text:"Enter"
size_hint: 0.1,0.06
pos_hint: {"x": 0.82, "y": 0.795}
on_release:
app.root.current = "exercisepage"
<ExercisePage>
FloatLayout:
Button:
text:"test"
size_hint: 0.5,0.075
pos_hint: {"x": 0.2, "top": 0.4}
on_release:
root.test()
Button:
text:"test2"
size_hint: 0.5,0.075
pos_hint: {"x": 0.2, "top": 0.5}
on_release:
root.testt()
What I am trying to do is like this:
class ExercisePage(Screen):
link = self.manager.get_screen('mainpage')
def test(self):
a = requests.get(f'{self.link.ids.http.text}/gpio12On')
def testt(self):
a = requests.get(f'{self.link.ids.http.text}/gpio12Off')
If you define your application root in the kv file, then the ids are available everywhere in that root rule. And then you can define an ObjectProperty in the ExercisePage that refers to the TextInput text. Here is what such a kv file could look like:
ScreenManager:
MainPage:
name: 'mainpage'
FloatLayout:
TextInput:
id: http
text: ""
multiline: False
size_hint: 0.5,0.05
pos_hint: {"x": 0.3, "y": 0.8}
Button:
text:"Enter"
size_hint: 0.1,0.06
pos_hint: {"x": 0.82, "y": 0.795}
on_release:
app.root.current = "exercisepage" # this could also be just root.current = "exercisepage"
ExercisePage:
id: escr
link: http.text # link to the TextInput text
name: 'exercisepage'
FloatLayout:
Button:
text:"test"
size_hint: 0.5,0.075
pos_hint: {"x": 0.2, "top": 0.4}
on_release:
escr.test() # changed to use id
Button:
text:"test2"
size_hint: 0.5,0.075
pos_hint: {"x": 0.2, "top": 0.5}
on_release:
escr.testt() # changed to use id
Then you define the ObjectProperty in the ExercisePage, and use that in the methods:
class ExercisePage(Screen):
link = ObjectProperty(None)
def test(self):
# link = self.manager.get_screen('mainpage')
print(self.link)
# a = requests.get(f'{link.ids.http.text}/gpio12On')
def testt(self):
# link = self.manager.get_screen('mainpage')
print(self.link)
# a = requests.get(f'{link.ids.http.text}/gpio12Off')
And since you define the root widget in the kv file, you can completely eliminate the build() method from your MyApp class.
So, I've been working on a very simple kivy app and am having trouble with the backend. How would you go about getting the values from text_input and changing label values? I've watched a few tutorials but their methods vary and I get error messages.
Here is my python code:
import kivy
from kivy.app import App
kivy.require('1.9.1')
class MyWindowApp(App):
pass
window = MyWindowApp()
window.run()
And here is the .kv file:
Screen:
side: side
FloatLayout:
Label:
text: "Side:"
pos_hint: {"x": 0.1, "y": 0.7}
text_size: self.size
Label:
text: "Volume:"
pos_hint: {"x": 0.1, "y": 0.65}
text_size: self.size
Label:
text: "Surface Area:"
pos_hint: {"x": 0.1, "y": 0.6}
text_size: self.size
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x": 0.24, "y": 0.7}
id: side
Label:
text: "0cm"
id: volume
pos_hint: {"x": 0.27, "y": 0.65}
text_size: self.size
Label:
text: "0cm"
id: surface_area
pos_hint: {"x": 0.355, "y": 0.6}
text_size: self.size
As mentioned in the other answer, place Builder.load_file or Builder.load_string in your build method.
To handle changes e.g. if the user hits enter in the side input use on_text_validate: app.on_side_change(self) or place a button to trigger the calculation and use on_press method.
The on_side_change method in your class MyApp will handle the change. It will be called once the user hits enter. See the example code below, for a basic calculation.
For getting/setting values from the labels/inputs you can use ObjectProperty or StringProperty from kivy.properties. In the below code I'm using StringProperty.
Important you need the StringProperty in your app class and use it in the kv file.
For the mode of your calculation I've added a property, so you can use it in your calculation method.
The mode is also used in the kv file so it's displaying the current mode.
The switcher dictionary is the Python way to do a switch/case statement.
Example code
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
kv = """
Screen:
side: side
GridLayout:
rows: 1
cols:2
spacing:0
GridLayout:
rows: 5
cols:1
Button:
text: "Cube"
# on_press: app.mode = self.text
on_press: app.setMode(self)
Button:
text: "Cuboid"
on_press: app.setMode(self)
Button:
text: "Cylinder"
on_press: app.setMode(self)
Button:
text: "Cone"
on_press: app.setMode(self)
Button:
text: "Sphere"
on_press: app.setMode(self)
FloatLayout:
Label:
text: "The Volume and surface area of a {}:".format(app.mode)
pos_hint: {"x":0.1, "y":0.8}
text_size: self.size
Label:
text:"Side:"
pos_hint: {"x":0.1, "y":0.7}
text_size: self.size
Label:
text:"Volume:"
pos_hint: {"x":0.1, "y":0.65}
text_size: self.size
Label:
text:"Surface Area:"
pos_hint: {"x":0.1, "y":0.6}
text_size: self.size
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":0.24, "y":0.7}
id: side
text: app.sideText
on_text_validate: app.on_side_change(self)
Label:
text: app.volume
pos_hint: {"x":0.27, "y":0.65}
text_size: self.size
Label:
text: app.area
pos_hint: {"x":0.355, "y":0.6}
text_size: self.size
"""
class MyApp(App):
sideText = StringProperty("")
area = StringProperty("0 cm²")
volume = StringProperty("0 cm³")
mode = StringProperty("Cube")
def build(self):
return Builder.load_string(kv)
def setMode(self, btn):
self.mode = btn.text
def on_side_change(self, instance):
print(instance.text)
result = 0
try:
value = float(instance.text)
except:
# failed to convert
return
def cubeCalc(val):
return {
"volume": val * val * val,
"area": val * val
}
switcher = {
"Cube": cubeCalc
}
method = switcher.get(self.mode, "Unknown mode")
if method is not "Unknown mode":
result = method(value) # result is a dictionary with volume & area
#print(result)
print(self.volume)
self.volume = "{:.2f} cm³".format(result["volume"])
self.area = "{:.2f} cm²".format(result["area"])
if __name__ == "__main__":
MyApp().run()
why you don't have a build method in your App class?
try this:
import kivy
from kivy.app import App
from kivy.lang.builder import Builder
kivy.require('1.9.1')
class MyWindowApp(App):
def build(self):
self.root = Builder.load_file("YOUR_KV_FILE.kv")
return self.root
window = MyWindowApp()
window.run()
Don't forget to change YOUR_KV_FILE.kv to your own kv filename
since you are not using Builder.load_file method to load your kv file you your kv file name must be something like mywindow.kv but im recommended to use Builder.load_file because you can save the whole window in a variable and access it later easy and it also make you free to change your kv design filename
I want the last button in the pop up to add a label to the previous screen with the text of whatever in entered into the text input in the pop up but I cant find a way to do so, is it possible?
I want the button with id add to add a label to screen the List every time it is clicked, and the text of said label should be whatever value put into text inputs with the ids lab, club, and blub
And if it is possible, how can I do it, any help would be greatly appreciated.
Python :
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.uix.popup import Popup
import time
from kivy.properties import StringProperty
class Enter_Name(Screen):
input_1 = StringProperty()
def line(self):
App.get_running_app().root.get_screen('list').lab_text = self.airline.text
pass
class Pop(Screen):
air_craft = StringProperty()
def lad(self):
plane = App.get_running_app().root.get_screen('pop').lab_text = self.airplane.text
self.plane = plane
class List(Screen):
Enter_Name.line
def add(self):
show_popup()
def show_popup():
show = Pop()
pop_up_window = Popup(title="Add Route", content=show, size_hint=(None, None), size=(400, 400))
pop_up_window.open()
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("pot.kv")
class am4(App):
def build(self):
return kv
if __name__ == "__main__":
am4().run()
Kivy :
#:kivy 1.0
WindowManager:
Enter_Name
List
Pop
#_______________________________________________________________________________________________________
#LOGIN
#_______________________________________________________________________________________________________
<Enter_Name>
airline: input_1
name: 'enter_name'
id: enter_nom
FloatLayout:
cols: 3
size: root.size
Label:
text: "Name of Airline?"
size_hint: 1, 0.3
pos_hint: {"x": 0, "top":1}
TextInput:
multiline: False
name: 'input_one'
id: input_1
size_hint: 0.6, 0.06
pos_hint: {"x": 0.20, "top":0.6}
Button:
size_hint: 0.2, 0.1
pos_hint: {"x": 0.4, "top":0.4}
text: "Enter"
on_release:
app.root.current = 'list'
root.line()
#_______________________________________________________________________________________________________
#MAIN
#_______________________________________________________________________________________________________
<List>
lab_text: ''
name: 'list'
FloatLayout:
Label:
text: root.lab_text
size_hint: 1, 0.3
pos_hint: {"x": -0.38, "top":1.1}
font_size: 50
Label:
text: '--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------'
size_hint: 1, 0.3
pos_hint: {"x": -0.38, "top":1}
font_size: 50
Button:
text: "Add Route"
size_hint: 0.2, 0.1
pos_hint: {"x":0.79, "top":0.99}
on_release: root.add()
<Pop>
name: "pop"
airplane: air_craft
FloatLayout:
Label:
id: lab
text: "Aircraft"
pos_hint: {"x": -0.38, "top":1.45}
Label:
id: club
text: "Departure"
pos_hint: {"x": 0, "top":1.45}
Label:
id: blub
text: "Arrival"
pos_hint: {"x": 0.38, "top":1.45}
TextInput:
multiline: False
name: 'aircraft'
id: air_craft
size_hint: 0.23, 0.06
pos_hint: {"x": 0, "top":0.9}
TextInput:
multiline: False
name: 'departure'
id: leaving
size_hint: 0.23, 0.06
pos_hint: {"x": 0.38, "top":0.9}
TextInput:
multiline: False
name: 'arrival'
id: arriving
size_hint: 0.23, 0.06
pos_hint: {"x": 0.76, "top":0.9}
Button:
size_hint: 0.2, 0.1
pos_hint: {"x": 0.4, "top":0.5}
id: add
text: "Add"
on_release:
root.lad()
Here is a modified version of your code that does what I think you want:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
class Enter_Name(Screen):
input_1 = StringProperty()
def line(self):
App.get_running_app().root.get_screen('list').lab_text = self.airline.text
class ListRow(BoxLayout):
ac = StringProperty('')
arr = StringProperty('')
lv = StringProperty('')
class Pop(RelativeLayout):
def lad(self, ac, arr, lv):
list_scr = App.get_running_app().root.get_screen('list')
if ac != '' or arr != '' or lv != '':
box_layout = list_scr.ids.box_layout
box_layout.add_widget(ListRow(ac=ac, arr=arr, lv=lv))
list_scr.pop.dismiss()
class List(Screen):
Enter_Name.line
pop = ObjectProperty(None)
def add(self):
self.pop = show_popup()
def show_popup():
show = Pop()
pop_up_window = Popup(title="Add Route", content=show, size_hint=(None, None), size=(400, 400))
pop_up_window.open()
return pop_up_window
class WindowManager(ScreenManager):
pass
# kv = Builder.load_file("pot.kv")
kv = Builder.load_string('''
#:kivy 1.0
WindowManager:
Enter_Name
List
#_______________________________________________________________________________________________________
#LOGIN
#_______________________________________________________________________________________________________
<Enter_Name>
airline: input_1
name: 'enter_name'
id: enter_nom
FloatLayout:
cols: 3
size: root.size
Label:
text: "Name of Airline?"
size_hint: 1, 0.3
pos_hint: {"x": 0, "top":1}
TextInput:
multiline: False
name: 'input_one'
id: input_1
size_hint: 0.6, 0.06
pos_hint: {"x": 0.20, "top":0.6}
Button:
size_hint: 0.2, 0.1
pos_hint: {"x": 0.4, "top":0.4}
text: "Enter"
on_release:
app.root.current = 'list'
root.line()
#_______________________________________________________________________________________________________
#MAIN
#_______________________________________________________________________________________________________
<List>
lab_text: ''
name: 'list'
BoxLayout:
id: box_layout
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
pos_hint: {'top':1}
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: 50
BoxLayout:
orientation: 'horizontal'
Label:
id: lab1
text: root.lab_text
size_hint_x: 1
#pos_hint: {"x": -0.38, "top":1}
font_size: 50
Button:
text: "Add Route"
size_hint_x: 0.5
#pos_hint: {"x":0.79, "top":1}
on_release: root.add()
Label:
text: '--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------'
size_hint: 1, 0.3
#pos_hint: {"x": -0.38, "top":0.99}
font_size: 50
<ListRow>:
orientation: 'horizontal'
size_hint: (1, None)
height: self.minimum_height
Label:
text: 'Aircraft: ' + root.ac
halign: 'left'
size_hint: 1, None
height: self.texture_size[1]
Label:
text: 'Leave: ' + root.lv
halign: 'left'
size_hint: 1, None
height: self.texture_size[1]
Label:
text: 'Arrive: ' + root.arr
halign: 'left'
size_hint: 1, None
height: self.texture_size[1]
<Pop>
airplane: air_craft
FloatLayout:
Label:
id: lab
text: "Aircraft"
pos_hint: {"x": -0.38, "top":1.45}
Label:
id: club
text: "Departure"
pos_hint: {"x": 0, "top":1.45}
Label:
id: blub
text: "Arrival"
pos_hint: {"x": 0.38, "top":1.45}
TextInput:
multiline: False
name: 'aircraft'
id: air_craft
size_hint: 0.23, 0.06
pos_hint: {"x": 0, "top":0.9}
TextInput:
multiline: False
name: 'departure'
id: leaving
size_hint: 0.23, 0.06
pos_hint: {"x": 0.38, "top":0.9}
TextInput:
multiline: False
name: 'arrival'
id: arriving
size_hint: 0.23, 0.06
pos_hint: {"x": 0.76, "top":0.9}
Button:
size_hint: 0.2, 0.1
pos_hint: {"x": 0.4, "top":0.5}
id: add
text: "Add"
on_release:
root.lad(air_craft.text, arriving.text, leaving.text)
''')
class am4(App):
def build(self):
return kv
if __name__ == "__main__":
am4().run()
Some of the significant changes that I made:
Redefined the Pop class as extending RelativeLayout instead of Screen and removed Pop from the children of WindowManager.
Defined a ListRow class that is added to the List Screen when the lad() method is called.
Added arguments to the lad() method for information to be added, and added a dismiss() call to close the Popup.
The show_pop() method now returns the created Popup instance which is saved in the List Screen for use of dismiss().
Redesigned the List Screen using a vertical BoxLayout to make adding ListRows simpler.
The kv is loaded using Builder.load_string() in the above code, but that is only for my own convenience.
My app wants to make a list from the strings typed in the TextInput field and show them in the gridlayout in the middle of the window after pressing the button "Buscar". I'm sharing variables and functions between classes but when i try to add a new button with the TextInput.text inside the gridlayout shows the error message:
"AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'add_widget'"
Thank you
The interface looks like this
This is my .py file
from kivy.app import App
from kivy.uix.button import Button
from kivy.properties import ObjectProperty, NumericProperty, StringProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
class Lista(ScrollView):
lista_repuestos = ObjectProperty()
class CustomWidget(FloatLayout):
campo_de_busqueda_text_input = ObjectProperty()
repuesto = StringProperty('')
def submit_repuesto(self):
self.repuesto = self.campo_de_busqueda_text_input.text
Lista.lista_repuestos.add_widget(Button(text=self.repuesto))
class CustomWidgetApp(App):
def build(self):
return CustomWidget()
if __name__ == "__main__":
CustomWidgetApp().run()
this is my .kv file
CustomWidget:
<CustomWidget>:
campo_de_busqueda_text_input: campodebusqueda
TextInput:
id: campodebusqueda
size_hint: .7, .1
pos_hint: {"x": .15, "y": .85}
Button:
on_release: root.submit_repuesto()
size_hint: .1, .1
pos_hint: {"x": .85, "y": .85}
text: "Buscar"
Label:
size_hint: .15, .05
pos_hint: {"x": .05, "y": .15}
text: "Descripción"
text_size: self.size
halign: "left"
Label:
size_hint: .15, .05
pos_hint: {"x": .05, "y": .10}
text: "Referencia"
text_size: self.size
halign: "left"
Label:
size_hint: .15, .05
pos_hint: {"x": .05, "y": .05}
text: "Cantidad"
text_size: self.size
halign: "left"
<Lista>:
lista_repuestos: listarepuestos
GridLayout:
id: listarepuestos
size_hint: .7, .6
pos_hint: {"x": .15, "y": .25}
cols: 1
row_default_height: 50
row_force_default: True
padding: 5
height: self.minimum_height
size_hint_y: None
Your line in the CustomWidget:
Lista.lista_repuestos.add_widget(Button(text=self.repuesto))
should reference an instance of Lista, not the class itself.