I'm trying to create a Fahrenheit to Celsius program with Kivy. I keep getting
an error that compute is not defined. I am not sure why this is happening since it is being defined under . Any help would be greatly appreciated. Thanks for any help.
from kivy.app import App
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
runTouchApp(Builder.load_string('''
<Convert>:
id: compute
cols:2
padding: 10
spacing: 10
GridLayout:
cols:2
rows:3
Label:
text: 'Fahrenheit'
TextInput:
id: entry
multiline: False
Label:
text: 'Celsius'
TextInput:
id: result
multiline: False
Button:
text: 'Convert'
on_press: compute.celsius(result.text)
'''))
class Convert(GridLayout):
def Temp(self,celsius):
celsius = (entry-32) * 0.556
if celsius:
try:
self.result.text = str(eval(celsius))
except Exception:
self.result.txt = 'Error'
class ConverApp(App):
def build(self):
return Convert()
if __name__=='__main__':
ConverApp().run()
Your code has the following errors:
When you use compute.celsius() you are assuming that you have a celsius() method in your Convert class but you do not have it.
You must use the Temp() method without passing any arguments to it.
When using self.result you are assuming that result is a member of the Convert class but it is not. If you want to access an element within the hierarchy tree of the .kv you must do it through the id.
from kivy.app import App
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
runTouchApp(Builder.load_string('''
<Convert>:
id: compute
cols:2
padding: 10
spacing: 10
GridLayout:
cols:2
rows:3
Label:
text: 'Fahrenheit'
TextInput:
id: entry
multiline: False
Label:
text: 'Celsius'
TextInput:
id: result
multiline: False
Button:
text: 'Convert'
on_press: root.temp()
'''))
class Convert(GridLayout):
def temp(self):
try:
fahrenheit = self.ids.entry.text
celsius = (int(fahrenheit)-32) * 0.556
self.ids.result.text = str(celsius)
except ValueError:
self.ids.result.text = 'Error'
class ConverApp(App):
def build(self):
return Convert()
if __name__=='__main__':
ConverApp().run()
Related
Hello I m very new to kivy python programming. I ve been banging my head pass 3 days with this problem. I would appreciate if someone could help me with this.
Basically I have variable called 'SCORE'. On first screen I have 2 buttons. One button called 'Update' which increase value of SCORE by 5 and second button called 'Next' which switch my application to next screen.
My Problem: After switching to second screen there is one label which I would like to display current value of variable SCORE right after I transition to this screen. I m unable to do that only thing I managed to do is SCORE updates after I hit update button but I want to display it correctly before hitting update button.
what should I put in my python code in my class Screen2 for parameter score = '' instead of empty string to correctly load value from previous screen?
I tried global variables, objectProperties nothing seems to solve my problem.
Thank you for any answer or help.
python code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('design.kv')
class Screen1(Screen):
app = App.get_running_app()
def update_score(self):
app = App.get_running_app()
app.SCORE += 5
self.ids.score.text = 'Score: ' + str(app.SCORE)
def next(self):
self.manager.current = 'screen2'
class Screen2(Screen):
score = ''
def update_score(self):
app = App.get_running_app()
app.SCORE += 5
self.ids.score.text = 'Score: ' + str(app.SCORE)
def next(self):
self.manager.current = 'screen2'
class RootWidget(ScreenManager):
pass
class MyApp(App):
SCORE = 0
def build(self):
return RootWidget()
if __name__ == '__main__':
MyApp().run()
kivy file:
<Screen1>:
GridLayout:
cols:1
width: root.width
height: root.height
padding: 20
spacing: 10
GridLayout:
cols:1
Label:
id: score
text: 'Score: 0'
GridLayout:
cols:2
Button:
text: 'Update'
on_release: root.update_score()
Button:
text: 'Next'
on_release: root.next()
<Screen2>:
GridLayout:
cols:1
width: root.width
height: root.height
padding: 20
spacing: 10
GridLayout:
cols:1
Label:
id:score
text: f'Level: {root.score}'
GridLayout:
cols:2
Button:
text: 'Update'
on_release: root.update_score()
Button:
text: 'Next'
on_release: root.next()
<RootWidget>:
Screen1:
id: screen1
name: 'screen1'
Screen2:
id: screen2
name: 'screen2'
You can accomplish this by defining SCORE as a NumericProperty of the App, and just referencing that property in your kv.
In your MyApp, define SCORE as:
class MyApp(App):
SCORE = NumericProperty(0)
and in your kv you can reference it in both Screens as:
text: 'Score: ' + str(app.SCORE)
and:
text: 'Level: ' + str(app.SCORE)
With the automatic update, you no longer need to update the Label text yourself, so the update_score() method (in both Screens) only needs to update the SCORE itself:
def update_score(self):
app = App.get_running_app()
app.SCORE += 5
# self.ids.score.text = 'Score: ' + str(app.SCORE)
In fact, since those update_score() methods are identical, you can just use one update_score() method in the App instead of in the Screens.
Note that kivy will set up automatic updates to those two Labels as long as it can recognize a simple dependency on the SCORE property. The use of the
text: f'Level: {root.score}'
hides that dependency from kivy, so the automatic update does not happen.
Here is a modified version of your code using the above suggestions:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('design.kv')
class Screen1(Screen):
def next(self):
self.manager.current = 'screen2'
class Screen2(Screen):
def next(self):
self.manager.current = 'screen2'
class RootWidget(ScreenManager):
pass
class MyApp(App):
SCORE = NumericProperty(0)
def build(self):
return RootWidget()
def update_score(self):
self.SCORE += 5
if __name__ == '__main__':
MyApp().run()
And the design.kv file:
<Screen1>:
GridLayout:
cols:1
width: root.width
height: root.height
padding: 20
spacing: 10
GridLayout:
cols:1
Label:
id: score
text: 'Score: ' + str(app.SCORE)
GridLayout:
cols:2
Button:
text: 'Update'
on_release: app.update_score()
Button:
text: 'Next'
on_release: root.next()
<Screen2>:
GridLayout:
cols:1
width: root.width
height: root.height
padding: 20
spacing: 10
GridLayout:
cols:1
Label:
id:score
text: 'Level: ' + str(app.SCORE)
GridLayout:
cols:2
Button:
text: 'Update'
on_release: app.update_score()
Button:
text: 'Next'
on_release: root.next()
<RootWidget>:
Screen1:
id: screen1
name: 'screen1'
Screen2:
id: screen2
name: 'screen2'
I am working on the same issue from my previous post. Now I added screen manager, and I can switch to different pages.
why is my kivy program not calling the function from another class?
I am still confused about OOP with GUIs, however I tried the following, and none of them worked.
I tried to add an instance of Screen 1() in the main app(), and it did nothing. Then, I tried to add the contractor method init into Screen_1() and it says I don't have a build method. And few other ideas that didn't work at all.
Then I realized that there is no link between Screen_Manager() and Screen_1() because all of my methods are in Screen_1(), but the build method is returning Screen_Manager(). On my kv file, there is this code:
<Screen_Manager>:
Screen_1:
Screen_2:
isn't this is where the program "links" between the Screen_Manager() class to other classes?
if someone can help me understand what I am not understanding and help me correct my problem, it would help me to learn about kivy. I understand, <> is like apply to rules, which has 2 widgets screen 1 and screen 2, and also have their own rules.
here is my main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.lang.builder import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.modules import keybinding
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
class Screen_Manager(ScreenManager):
pass
class Screen_1(Screen):
main_display = ObjectProperty()
text_input = ObjectProperty()
def plus_1(self):
self.value = int(self.main_display.text)
self.main_display.text = str(self.value + 1)
def minus_1(self):
self.value = int(self.main_display.text)
self.main_display.text = str(self.value - 1)
def up(self):
self.main_display.text = self.text_input.text
self.text_input.text = ''
class Keyboard(Widget):
def __init__(self, instance):
super().__init__()
self.a = instance
self.keyboard = Window.request_keyboard(None, self)
self.keyboard.bind(on_key_down=self.on_keyboard_down)
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'enter':
self.a.up()
return True
class Screen_2(Screen):
pass
class MainApp(App):
def build(self):
self.title = "Number Control App"
self.sm = Screen_Manager()
key = Keyboard(self.sm)
return self.sm
if __name__=="__main__":
MainApp().run()
my kv file
<Screen_Manager>:
Screen_1:
Screen_2:
<Screen_1>:
name: "first"
main_display: display_1
text_input: text_input_1
#------inherenting from BoxLayout
BoxLayout:
orientation: "vertical"
#------begining of conent--
BoxLayout:
size_hint_y: 1
#-nexted content------
Label:
id: display_1
text: "0"
font_size: "150sp"
background_color: 0.1, 0.5, 0.6,
#--------
BoxLayout:
size_hint_y: .35
orientation: "horizontal"
#-nexted content------
Button:
text: "-"
font_size : "60sp"
on_press: root.minus_1()
#-nexted content------
Button:
text: "+"
font_size : "35sp"
background_color: (0, 1, 0, 1)
on_press: root.plus_1()
#--------
BoxLayout:
size_hint_y: .15
orientation: "horizontal"
#-nexted content-------
Button:
text: "Config"
size_hint_x: .25
on_release:
root.manager.current = "second"
root.manager.transition.direction = "left"
#-nexted content-------
TextInput:
id: text_input_1
size_hint_x: 1
hint_text: "Enter your initial # here"
multiline: False
#-nexted content-------
Button:
text: "Up"
size_hint_x: .25
on_press: root.up()
<Screen_2>:
name: "second"
Button:
text: "Go Back"
on_press:
app.root.current = "first"
root.manager.transition.direction = "right"enter code here
thanks again for taking the time to help.
Not sure I understand your question, but pressing Enter in your TextInput executed your code:
self.a.up()
but a in your KeyBoard is the Screen_Manager as set at the line:
self.sm = Screen_Manager()
key = Keyboard(self.sm)
and Screen_Manager has no up method. You can fix that by changing the code in your KeyBoard code to:
self.a.current_screen.up()
Note that this will only work when the current screen is Screen_1.
I have a error in my code, I want to get or to print the product text but when i run it it gave me an error.
here is my code:
import kivy
from kivy.properties import ObjectProperty
from selenium import webdriver
import requests
from selectorlib import Extractor
from selenium.webdriver.common.keys import Keys
from kivymd.app import MDApp
from kivy.app import App
from kivy.lang import Builder
KV = '''
BoxLayout:
product: product
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(100)
orientation: "vertical"
MDTextFieldRound:
id: product
hint_text: 'Enter a product'
icon_left: 'magnify'
on_text_validate: app.System()
'''
class Main(MDApp):
def build(self):
return Builder.load_string(KV)
product = ObjectProperty(None)
def System(self):
print(self.product.text)
if __name__ == "__main__":
Main().run()
it always gave me the next error
File "C:/Users/Yesnia/Desktop/PYTHON/Apps development/App/App_Checking_Store.py", line 34, in
System
print(self.product.text)
AttributeError: 'NoneType' object has no attribute 'text'
help me please
So you build your kv string but have no way to reference it. There are many ways to mend the situation, heres an example.
KV = '''
BoxLayout:
###product: product ### this does not achieve anything, you're just giving
#some generic box layout a product attribute. this does not assign the App attribute.
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(100)
orientation: "vertical"
MDTextFieldRound:
id: product
hint_text: 'Enter a product'
icon_left: 'magnify'
on_text_validate: app.System()
'''
class Main(MDApp):
product = ObjectProperty(None)
my_kv = None
def build(self):
self.my_kv = Builder.load_string(KV)
self.product = self.my_kv.ids.product
return self.my_kv
def System(self):
print(self.product.text)
notice the comment i've left in your kv string, the addition of the new my_kv attribute to your App class, and the changes I've made to your build function.
Hope this helps!
So, I'm new to kivy and can't get this code to work. I'm trying to update a button text with a popup text input. The popup shows when the button is pressed, and when it's dismissed it should update the text on the button with whatever text was typed in it.
I've tried many variations of this code, but no one have worked. Either nothing happens or I get this error:
AttributeError: 'super' object has no attribute '__getattr__'
Here it is:
main.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.popup import Popup
class MainWidget(GridLayout):
pass
class PopText(Popup):
def textChange(self):
MyButton().change()
def getText(self):
text = self.display
return text
class MyButton(AnchorLayout):
def change(self):
self.ids.equip_bt.text = PopText().getText()
print(self.ids.my_bt.text)
class MyApp(App):
def build(self):
return MainWidget()
if __name__ == "__main__":
MyApp().run()
my.kv
#:kivy 1.10.0
#:import F kivy.factory.Factory
<PopText>:
size_hint: .7, .3
title: "Input Text"
on_dismiss: self.textChange()
display: pop_text.text
TextInput:
id: pop_text
focus: True
multiline: False
on_text_validate: root.dismiss()
<MyButton>:
anchor_y: "top"
anchor_x: "right"
Button:
id: my_bt
text: "Input Text"
on_release: F.PopText().open()
<MainWidget>:
cols: 1
rows: 2
MyButton:
MyButton:
Any ideas on how to solve this?
Here is a minimum example of what you are trying to achieve. The hard part is connection the button from the Popup to the Button which opened it. I am going through the app class to achieve that. I got to admit it is not a pretty solution.
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.button import Button
class PopupBttn(Button):
def openPopup(self):
Pop = TextPopup().open(self.text)
class TextPopup(Popup):
def open(self, text, **kwargs):
super(TextPopup, self).open(**kwargs)
self.ids.txtipt.text = text
class MyApp(App):
pass
if __name__ == "__main__":
MyApp().run()
kv file:
BoxLayout:
PopupBttn:
id: bttn
text: 'open Popup'
on_press: self.openPopup()
<TextPopup>:
BoxLayout:
orientation: "vertical"
TextInput:
id: txtipt
Button:
text: "OK"
on_release: app.root.ids.bttn.text=root.ids.txtipt.text
on_release: root.dismiss()
Here is an updated version to use multiple buttons. Unfortunately, you will need to set ids and name to the string of id per Button.
python file
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.properties import StringProperty
class PopupBttn(Button):
def openPopup(self):
print(self.name)
Pop = TextPopup().open(self.text, self.name)
class TextPopup(Popup):
bttnid = StringProperty()
text = StringProperty()
def open(self, text, id, **kwargs):
super(TextPopup, self).open(**kwargs)
self.ids.txtipt.text = text
self.bttnid = id
def setText(self):
App.get_running_app().root.ids[self.bttnid].text = self.text
class MyApp(App):
pass
if __name__ == "__main__":
MyApp().run()
kv file
BoxLayout:
orientation: 'vertical'
PopupBttn:
name: 'one'
id: one
text: 'I am the first Button'
PopupBttn:
name: 'two'
id: two
PopupBttn:
name: 'three'
id: three
PopupBttn:
name: 'four'
id: four
text: 'I am the fourth button'
<PopupBttn>:
text: 'open Popup'
on_press: self.openPopup()
<TextPopup>:
text: txtipt.text
BoxLayout:
orientation: "vertical"
TextInput:
id: txtipt
Button:
text: "OK"
on_release: root.setText()
on_release: root.dismiss()
In order to update both buttons, you need to assign unique id to each of them. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
class MainWidget(GridLayout):
btn_top = ObjectProperty()
btn_bottom = ObjectProperty()
class PopText(Popup):
pass
class MyButton(AnchorLayout):
my_btn = ObjectProperty()
class TestApp(App):
title = "Changing button text with popup text input Kivy"
def build(self):
return MainWidget()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import F kivy.factory.Factory
<PopText>:
size_hint: .7, .3
title: "Input Text"
TextInput:
focus: True
multiline: False
on_text_validate:
app.root.btn_top.my_btn.text = self.text
app.root.btn_bottom.my_btn.text = self.text
root.dismiss()
<MyButton>:
my_btn: my_bt
anchor_y: "top"
anchor_x: "right"
Button:
id: my_bt
text: "Input Text"
on_release: F.PopText().open()
<MainWidget>:
btn_top: btn_top
btn_bottom: btn_bottom
cols: 1
rows: 2
MyButton:
id: btn_top
MyButton:
id: btn_bottom
Output
Hy, the problem is that with the current code, which is at this point preety much nothing but a text editor, when ever I try to make a scrollable label in the kv language and call it on the main screen at the push of a button, I get no error, theres just nothing there. I should mention that the text is taken from a stored file, and the only version that works is with a regular label. This is the code, I know its a bit long but its preety easy to understand so stay with me. Any sort of input is greatly apreciated and I thank you for taking the time.
#kivy.require("1.8.0")
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import Line
from kivy.uix.gridlayout import GridLayout
kv = '''
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManager:
transition: FadeTransition()
MainScreen:
AddScreen:
AppendScreen:
<ScatterTextWidget>:
orientation: 'vertical'
TextInput:
id: main_input
font_size: 14
size_hint_y: None
height: root.height - botones_layout.height
font_color: [0.1,0.3,0.9,1]
focus: True
foreground_color: [0.2,0.5,0.9,1]
cursor_color: [0,0,1,1]
BoxLayout:
id: botones_layout
orientation: 'horizontal'
height: 30
Button:
id: home_button
text: "Back Home"
Button:
id: save_button
text: "Save to file"
on_press: root.saveToFile("Archive.txt", main_input.text)
<AppendTextWidget>:
orientation: 'vertical'
TextInput:
text: root.text
id: main_input
font_size: 14
size_hint_y: None
height: root.height - botones_layout.height
font_color: [0.1,0.3,0.9,1]
focus: True
foreground_color: [0.2,0.5,0.9,1]
cursor_color: [0,0,1,1]
BoxLayout:
id: botones_layout
orientation: 'horizontal'
height: 30
Button:
id: home_button
text: "Back Home"
on_release: app.root.current = "main"
Button:
id: save_button
text: "Save"
on_press: root.append(main_input.text)
#This does not work <--- <--- <---
<ScrollableLabel>:
Label:
text: root.text
font_size: 15
text_size: self.width, None
color: [0,255,0,1]
padding_x: 20
size_hint_y: None
pos_hint: {"left":1, "top":1}
height: self.texture_size[1]
<MainScreen>:
name: "main"
FloatLayout:
# This does work
Label:
text: root.text
font_size: 15
text_size: self.width, None
color: [0,255,0,1]
padding_x: 20
size_hint_y: None
pos_hint: {"left":1, "top":1}
height: self.texture_size[1]
ActionBar:
pos_hint: {'top':1}
ActionView:
use_separator: True
ActionPrevious:
title: "Text"
with_previous: False
ActionOverflow:
ActionButton:
text: "New"
on_release: app.root.current = "add"
ActionButton:
text: "Update"
on_press: root.clicked()
ActionButton:
text: "Add"
on_release: app.root.current = "append"
<AddScreen>:
name: "add"
FloatLayout:
ScatterTextWidget
<AppendScreen>:
name: "append"
FloatLayout:
AppendTextWidget
'''
class ScatterTextWidget(BoxLayout):
def saveToFile(self,name,text):
f = open(name, "w")
f.write("\n\n\n" + " " + ">>>" + text + "test"*500)
f.close()
class AppendTextWidget(BoxLayout):
text = StringProperty("")
def append(self,text):
with open("Archive.txt", "a") as f:
f.write("\n" + " " + ">>>" + text)
f.close()
class ScrollableLabel(ScrollView):
text = StringProperty('')
pass
class MainScreen(Screen):
text = StringProperty("")
def clicked(self):
with open("Archive.txt", "r") as f:
contents = f.read()
self.text = contents
pass
class AddScreen(Screen):
pass
class AppendScreen(Screen):
pass
class MyApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
MyApp().run()
Why it works:
Your text in MainScreen is updated from file, then passed to Label and the text is loaded. ScrollableLabel.text stays unchanged.
Why it doesn't work as you expect:
There's no communication between your classes, therefore only text changed is an actual self.text = MainScreen.text
How to make it work:
Either use something on a global range, i.e. variable in your App class, then a = App.get_running_app() and access variable through a.<variable> or use __init__ to initialize your ScrollableLabel inside the MainScreen and pass it the text
So it's basically a duplicate of this and that one is a duplicate of an older unsimplified question.