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):
...
Related
I just wanted to ask, is it possible to set transitions for attributes changes in kivy? Let's say my widget with id toolbar has opacity 0:
self.root.ids.toolbar.opacity = 0
Is it possible to set a transition if I plan to change it into opacity 1?
self.root.ids.toolbar.opacity = 1
Answers, suggestions, and corrections are appreciated. Thanks!
I have used kivy for some time and ran into similar problem, but I'm not an expert! So there might be better ways to do this.
That being said, here's a small example introducing fading effect. I believe, you should be able to implement a version for your own problem based on this.
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<Screen1>:
test_widget : ID1
BoxLayout:
padding : (50, 50)
Button:
text: 'Trigger'
on_release:
root.trigger()
Label:
id : ID1
text: "Your Fading Text"
font_size : 30
bold : True
""")
class Screen1(Screen):
# Loading object properties based on the id
test_widget = ObjectProperty()
# Variables for the fading effect
event = None
trans_type = "fade_out"
# The trigger function schedules a task using Clock
def trigger(self):
if self.event != None:
# See below
self.event.cancel()
# The clock calls the given function every X seconds
self.event = Clock.schedule_interval(lambda _:self.transition(self.test_widget), 0.03)
def transition(self, widget):
# Changing the opacity of the widget, pretty self explanatory I think
if self.trans_type == "fade_out":
if (widget.opacity > 0.5):
widget.opacity -= 0.05
else:
self.trans_type = "fade_in"
# We need to make sure to cancel events, otherwise they would not stop
self.event.cancel()
elif self.trans_type == "fade_in":
if (widget.opacity < 1):
widget.opacity += 0.05
else:
self.trans_type = "fade_out"
self.event.cancel()
class TestApp(App):
def build(self):
# Creating a simple screen using ScreenManager and Screen
sm = ScreenManager()
sm.add_widget(Screen1(name='screen1'))
return sm
if __name__ == '__main__':
# Starting the app
TestApp().run()
Also, you might be interested in:
https://kivy.org/doc/stable/api-kivy.animation.html
This might simplify your solution, however I'm not familiar with it, so you have to figure that out for yourself :)
I have the below code in test.py and test.kv.
The MDProgressBar UI does not move until end of the program. I've read similar questions in stackoverflow but the solutions posted were only snippets.
Assuming self.process_some_data() is a function that takes a long while to complete, the MDProgressBar UI instance self.progress_bar is supposed to reflect current progress. Based on solutions from other forum posts, I've
created a thread for self.process_some_data() function.
used Clock.schedule_once() for self.update_progress_bar() function which updates self.progress_bar.value in main thread at different stages of completion.
included decorator #mainthread for self.update_progress_bar() function which does not seem to make any difference.
Feel free to edit the code and repost the full solution. Thanks.
main.py
from kivymd.app import MDApp
from kivymd.uix.progressbar import MDProgressBar
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivymd.uix.dialog import MDDialog
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
import time
import threading
from kivy.clock import mainthread
from functools import partial
class Screen1(Screen):
def show_popup(self):
self.progress_bar = MDProgressBar()
self.popup = Popup(
title ='Progress',
content = self.progress_bar,
auto_dismiss = False, # dialog does NOT close if click outside it
size_hint = (None, None),
size = (400, 400)
)
self.popup.bind( on_open = lambda x: self.run_thread() )
# self.progress_bar.max = 100
self.progress_bar.value = 10
self.popup.open()
print(self.progress_bar.value)
#mainthread
def update_progress_bar(self, val, _):
self.progress_bar.value = val
print(self.progress_bar.value)
if val >= 100:
# self.popup.dismiss()
dialog = MDDialog(title="Status", text="Completed")
dialog.open()
def run_thread(self):
t1 = threading.Thread(target=self.process_some_data())
t1.start()
# t1.join()
def process_some_data(self):
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 25), 0)
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 50), 0)
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 75), 0)
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 100), 0)
# Create the App class
class MyApp(MDApp):
def build(self):
return Builder.load_file("test.kv")
# run the App
if __name__ in ("__main__"):
MyApp().run()
test.kv
Screen1:
Button:
text: "Run program"
on_release: root.show_popup()
size_hint: 0.4, 0.1
pos_hint: {"center_x": 0.5, "center_y": 0.5}
In your run_thread() method, the line:
t1 = threading.Thread(target=self.process_some_data())
runs the process_some_data() method, then sets the target of the created thread to the return of that method. I think you just need to remove the () from that method, so that the target is set to the process_some_data method and not its return:
t1 = threading.Thread(target=self.process_some_data)
Im writing an app in Kivy which automaticaly adds Buttons and gives them a unique id using a for loop. This id is then used as a key in the dictionary for a link. So the dictionary works fine and after printing it, it outputs {'button0': 'somewebsite', 'button1': 'other website', 'button2': 'andanotherwebsite'} which is exactly what I want but the button callback function always prints out button2 instead of its own id. Am I assigning the ids wrong? The example below demonstrates my problem.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivymd.utils import asynckivy
from kivy.clock import Clock
class TestButton(Button):
def callback(self):
print(self.id)
class RootWidget(BoxLayout):
def __init__(self):
super().__init__()
self.links = ["somewebsite", "other website", "andanotherwebsite"]
self.dic_btn_to_lnk = {}
self.size_hint = (None, None)
self.size = ("600dp", "50dp")
Clock.schedule_once(self.add_widgets, 0)
def add_widgets(self, *args):
async def update():
number = 0
for link in self.links:
button = TestButton()
button.text = link
button.size = ("200dp", "50dp")
button.pos_hint = {"center_x": .5}
btn_id = "button" + str(number)
button.id = btn_id
button.bind(on_release=lambda x: button.callback())
number += 1
self.dic_btn_to_lnk[btn_id] = link
self.add_widget(button)
print(self.dic_btn_to_lnk)
asynckivy.start(update())
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
The problem is that your on_release binding is calling button.callback(), and button will be the last Button added by the time the on_release is triggered. The solution is to use partial, which freezes its arguments to their values when partial is executed, so the on_release calls the correct button.callback. Like this:
button.bind(on_release=partial(button.callback))
And to simplify the above, the definition of callback is changed to:
class TestButton(Button):
def callback(self, instance):
print(self.id)
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"])
I'm making a label that should update every second or so. I tried making it with the clock schedule but it doesn't seem to work. The weird is if I use a button to call the same function it works fine.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
class FirstLayout(BoxLayout):
r = 0
def __init__(self, **kwargs):
super(FirstLayout, self).__init__(**kwargs)
self.change = self.ids.temp_label
def my_callback(self, *args):
self.r += 1
print self.r
t = str(self.r)
self.change.text = t
class TryApp(App):
def build(self):
Clock.schedule_interval(FirstLayout().my_callback, 1)
return FirstLayout()
app = TryApp()
app.run()
the .kv file:
<FirstLayout>:
orientation: 'vertical'
Label:
id: temp_label
text: 'something'
Button:
on_press: root.my_callback()
When I run the code I get the prints showing that the function is running but the label doesn't update. Anything wrong with my logic there?
Thank you in advance.
PS: I know there are several questions about this here, sadly those I found were replied with answers about using the Clock which I already do
The problem is that the callback is for an instance that you don't use:
def build(self):
Clock.schedule_interval(FirstLayout().my_callback, 1) #<--- FirstLayout created and never used
return FirstLayout() #this one will be used :(
Instead, You need to call the method of the FirstLayout that you are using
def build(self):
first_layout = FirstLayout() # "There should be one ..." :)
Clock.schedule_interval(first_layout.my_callback, 1)
return first_layout