I have schedule_interval calling a function that fetches weather data from the web and then parses it into a dict. I have my kv file reading that dict and displaying values in a floatlayout. I know the function is being called because I am having it print to the console also, but it is not updating in the floatlayout window. I thought the values would automatically update from what I have read.
GUI.py
class weather(FloatLayout):
def w(self):
a = parse()
print(a)
return a
class weatherApp(App):
def build(self):
d = weather()
Clock.schedule_interval(d.w, 1)
return d
weather.kv
<Layout>:
DragLabel:
font_size: 600
size_hint: 0.1, 0.1
pos: 415,455
text: str(root.w()['temp0'])
This is just one of the labels. I am very new to Kivy so
if this looks atrocious to you experienced kivy
people, I apologize.
The print(a) part of def w(self): works every second, but the window does not display the new variables.
test.py
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
a = {}
a['num'] = 0
class test(FloatLayout):
def w(self):
a['num'] += 1
print(a['num'])
return a
class testApp(App):
def build(self):
d = test()
Clock.schedule_interval(test.w, 1)
return d
if __name__ == '__main__':
p = testApp()
p.run()
test.kv
#:kivy 1.10.1
<Layout>:
Label:
font_size: 200
size_hint: 0.1, 0.1
pos: 415,455
text: str(root.w()['num'])
It seems that you have several misconceptions:
If you invoke a function in python it does not imply that the .kv will be called. So if you call the w method with Clock.schedule_interval() it does not imply that the calculated value updates the value of the Label text.
When you call a function with Clock.schedule_interval you must use the object not a class. in your case, test is class and instead, d is the object.
When you call a function with Clock.schedule_interval you must use the object not a class. in your case, test is class and instead, d is the object.
*.py
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import DictProperty
class test(FloatLayout):
a = DictProperty({"num": 0})
def w(self, dt):
self.a["num"] += 1
print(self.a["num"])
class testApp(App):
def build(self):
d = test()
Clock.schedule_interval(d.w, 1)
return d
if __name__ == "__main__":
p = testApp()
p.run()
*.kv
#:kivy 1.10.1
<Layout>:
Label:
font_size: 200
size_hint: 0.1, 0.1
pos: 415,455
text: str(root.a["num"])
Related
I am trying to use Kivy to create an interface. But when I try to show the text of a variable (result) it doesn't show it. Here the code:
from kivymd.app import MDApp
from kivy.lang import Builder
from covid import Covid
covid = Covid(source='worldometers')
data = covid.get_status_by_country_name('Italy')
for k in data:
result = k+str(data[k])
KV = '''
Screen:
Label:
text: result
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
'''
class MainApp(MDApp):
def build(self):
self.title = "Covid"
self.theme_cls.theme_style = "Dark" # Light
self.theme_cls.primary_palette = "Red"
return Builder.load_string(KV)
MainApp().run()
I'm sorry for my english but I'm Italian then I'm not very good.
Good day. Kivy is an App framework. You need to create an entire App class and build your logic into the App.
I recommend that after you create the App class, instantiate it, run it, then run your logic code in your App.
kv script:
Screen:
MDRectangleButton:
on_release: app.calculate_result_callback
In Python:
import .......
data = covid.get_status_by_country_name('Italy')
covid = Covid(source='worldometers')
def calculate_result():
result = ""
for k in data:
result += k+str(data[k])
return result
class MyApp(App):
def calculate_result_callback(self, instance, *a):
result = calculate_result()
instance.text = result
I am trying to have a function run continuously and spit out a distance that the label uses that I'll eventually tie to a sonar module, but the label remains blank and I am at a loss as to what I am doing wrong. If I just add a print statement for that distance variable it prints and updates just fine, just can't get the label to use it.
Part II of my question is how do I reference my same function in the second window and also have a label that updates from that same function?
Thanks for the help in advance, I am very very new to kivy and just started learning python a few months ago as well.
Python code:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen # for multiple screens
from kivy.properties import StringProperty
class MySonar(Screen):
global i
i = 1
distance = StringProperty("")
#Generic function that just adds itself up, just using to try and get the label to change before I throw in my real function
def sonar(self):
global i
if i < 250:
distance = (10 + .1 * i)
i += 1
else:
i = 1
distance = 10
self.root.distance=str(distance)
class DropWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("help.kv")
class HelpMe(App):
def build(self):
#running interval update to keep running code above
Clock.schedule_interval(lambda dt: MySonar.sonar(self), 0.1)
return kv
if __name__ == "__main__":
HelpMe().run()
Kivy:
WindowManager:
MySonar:
DropWindow:
<MySonar>:
name:"Main"
GridLayout:
cols:1
##Need this to update
Label:
text:root.distance
Button:
text:"Next Window"
on_release:
app.root.current="Drop"
root.manager.transition.direction="left"
<DropWindow>:
name:"Drop"
GridLayout:
cols:1
##Need this to update, commented out the text so the program will run and you can see the blank label for part one of my question
Label:
##text:root.distance
Button:
text:"Cancel"
on_release:
app.root.current="Main"
root.manager.transition.direction="right"
To simplify the access to distance, you can put that StringProperty in the HelpMe App:
class MySonar(Screen):
global i
i = 1
#Generic function that just adds itself up, just using to try and get the label to change before I throw in my real function
def sonar(self):
global i
if i < 250:
distance = (10 + .1 * i)
i += 1
else:
i = 1
distance = 10
# set the value of distance in the StringProperty of the App
App.get_running_app().distance=str(distance)
print(distance)
class DropWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
# kv = Builder.load_file("help.kv")
class HelpMe(App):
distance = StringProperty('')
def build(self):
kv = Builder.load_file("help.kv")
#running interval update to keep running code above
sonar_instance = kv.get_screen('Main')
Clock.schedule_interval(lambda dt: sonar_instance.sonar(), 0.1)
return kv
if __name__ == "__main__":
HelpMe().run()
Note that I have also moved the Builder.load_file() inside the App. This is good practice when you reference app in the kv file (as I have done). Also, calling the sonar() method using MySonar.sonar(self) will not work. You need to use a reference to the instance of MySonar that is in your GUI.
Now the kv file becomes:
WindowManager:
MySonar:
DropWindow:
<MySonar>:
name:"Main"
GridLayout:
cols:1
##Need this to update
Label:
text: app.distance
Button:
text:"Next Window"
on_release:
app.root.current="Drop"
root.manager.transition.direction="left"
<DropWindow>:
name:"Drop"
GridLayout:
cols:1
##Need this to update, commented out the text so the program will run and you can see the blank label for part one of my question
Label:
text: app.distance
Button:
text:"Cancel"
on_release:
app.root.current="Main"
root.manager.transition.direction="right"
The change is that the text attribute of both Labels is now just app.distance.
I am trying to make a program that reads the dictionary after the user inputs their name and assigns a random selection based on weighted values in the dictionary. As of now the logic for selecting a random value from the dictionary is working but I have it printing to the console. I would like it to appear in a popup window (which i have but cannot get the output variable to show up there)
four.kv
WindowManager:
MainWindow:
<MainWindow>:
name:'main'
player_python:player_kv
GridLayout:
cols:1
GridLayout:
cols:2
Label:
text:'Player:'
TextInput:
id: player_kv
multiline: False
Button:
text: 'Random'
on_press: root.btn()
<P>:
output:output
FloatLayout:
Label:
id: output
main4.py
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
import random
#from Dict import *
#### example dictionary
character = {
'John':
{'Water': 2, #50%
'Fire': 1, #25%
'Earth': 1,}, #25%
'Bill':
{'Water': 1, #25%
'Fire': 2, #50%
'Earth': 1,}} #25%
####
class MainWindow(Screen):
player_python = ObjectProperty(None)
output = StringProperty('')
def btn(self):
show_popup()
player = self.player_python.text
weighted_list = []
for c in character[player]:
for w in range(character[player][c]):
weighted_list.append(c)
self.output= random.choice(weighted_list)
print(self.output) ###### instead of this printing to console I want it to display in popup window
self.player_python.text = ''
class P(FloatLayout):
pass
def show_popup():
show = P()
popupWindow = Popup(title='random character', content=show, size_hint=(None,None),size=(400,400) )
popupWindow.open()
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('four.kv')
class FourApp(App):
def build(self):
return kv
if __name__ == '__main__':
FourApp().run()
https://gist.github.com/PatrickToole/00cc72cdd7ff5146976e5d92baad8e02
Thanks in advance
-P
I haven't tested this code, but try passing self.output to your show_popup() method. This would mean changing your btn() method to something like:
def btn(self):
player = self.player_python.text
weighted_list = []
for c in character[player]:
for w in range(character[player][c]):
weighted_list.append(c)
self.output= random.choice(weighted_list)
print(self.output) ###### instead of this printing to console I want it to display in popup window
self.player_python.text = ''
show_popup(self.output)
And in the show_popup() method:
def show_popup(output):
show = P()
show.output.text = output
popupWindow = Popup(title='random character', content=show, size_hint=(None,None),size=(400,400) )
popupWindow.open()
As I mentioned, I haven't tested this code, but something like this should work.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanger import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
BoxLayout:
FloatLayout:
Image:
id: homebackg
pos: 0, 0
source: "dark.png"
<SettingsScreen>:
BoxLayout:
FloatLayout:
Image:
id: settingsbackg
pos: 0, 0
source: "dark.png"
Button:
text: ""
color: 1.0, 1.0, 1.0, 0.0
on_press:
root.buttonclick()
""")
class MenuScreen(Screen):
def darkmode(self):
self.ids.homebackg.source = "dark.png"
def litemode(self):
self.ids.homebackg.source = "lite.png"
class SettingsScreen(Screen):
def darkmode(self):
self.ids.settingsbackg.source = "dark.png"
def litemode(self):
self.ids.settingsbackg.source = "lite.png"
def buttonclick(self):
file = open("stat.txt", "r")
themestat = file.read()
file.close()
if themestat == "lite":
SettingsScreen.darkmode(self)
MenuScreen.darkmode(self)
file = open("stat.txt", "w")
file.write("dark")
file.close()
elif themestat == "dark":
SettingsScreen.litemode(self)
MenuScreen.litemode(self)
file = open("stat.txt", "w")
file.write("lite")
file.close()
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()
Assume I have done the screen transitions properly not adding to make code less bulky, I have issue with changing background of menuscreen from the settingsscreen. It is for darkmode you see, thanks in advance.
Self is not defined when executing MenuScreen.darkmode(self) from SettingsScreen.
You problem is that you are adding objects of screens to screen manager (and you will see only these objects) and then you are creating new objects and make changes there. So since you are using global variables the simplest solution would be:
sm = ScreenManager()
menuscreen = MenuScreen(name='menu')
settingsscreen = SettingsScreen(name='settings')
sm.add_widget(menuscreen)
sm.add_widget(settingsscreen)
And inside the classes you can just make changes to them like:
menuscreen.darkmode()
P.S. 'self' means object of the class, in Python self is used to have access to class variables and functions from functions defined in that class. And here you don't need to send 'self' to another class because you're sending object of SettingsScreen class to MenuScreen darkmode function and do nothing with it. If you want to send object there should be:
# you send object of SettingsScreen
menuscreen.darkmode(self)
# and receive it as obj for example
def darkmode (self, obj):
...
I know this might be a very basic question, but after spending hours wrapping my head around it I still can't figure it out.
I basically just want to bind the text of a label to a variable in the python code. lets call it value. however it should get updated everytime I run a loop Clock.schedule_interval(RootWidget.update, 1.0/1.0)
here is the python, simplified so its basically just the time, which is also printed just to see if it is actually working.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.core.window import Window
import time
class RootWidget(FloatLayout):
def update(self, *args):
value = time.time()
print value
self.ids.value_label.text = str(value)
class MainApp(App):
def build(self):
Window.size = (800, 480)
r = RootWidget()
Clock.schedule_interval(r.update, 1)
print 'build running'
return r
def on_pause(self):
return True
if __name__ == '__main__':
MainApp().run()
the kv file looks as such:
<RootWidget>:
Label:
id: value_label
text:
y: 20.0
x: 0.0
width: 100.0
italic: False
height: 50.0
Clock.schedule_interval(RootWidget.update, 1.0/1.0)
You need to schedule the update method of an instance, not the class itself.
For instance:
r = RootWidget()
Clock.schedule_interval(r.update, 1)
return r
The clock will pass some arguments by default, so you should also declare the update method to accept these. If you don't want to use them then you can just do:
def update(self, *args):
...