I am making a simple weather app. The user enters a location and gets results in plain text. I have made a loading screen that has a spinning animation. When the program is loading these weather results, it changes to the loading screen and the animation spins well.
I have the animation set for a duration of 20 seconds for now. This allows the first instance of my load screen display more than enough time to yield results and then change to the results screen (normally about 5 seconds). That seemingly works well.
My issue is: Once the animation has completed its 20 second run (in the background) it doesn't re start when called for again. It seems the app will allow it to be called once only. For it to animate again, the app must be closed completely and restarted.
Ideally I'd like the animation to stop spinning and be reset as soon as the load screen has been exited (so once the results have been delivered). The user can get weather multiple times, so I need it to start again every time the load screen is displayed.
I have tried the undesired solution of having it constantly spinning in the background so that when the load screen is shown, its spinning.
But, even then I am having trouble getting it to repeat... (anim.repeat = True
doesn't work for me)
Here is a working example that has been stripped back as much as possible to still behave the same as my real app.
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.animation import Animation
import time
from threading import Thread
class HomeScreen(Screen):
pass
class Results1Screen(Screen):
pass
class LoadScreen(Screen):
pass
GUI = Builder.load_file("main.kv")
class TestApp(App):
def build(self):
return GUI
def change_screen(self, screen_name):
screen_manager = self.root.ids['screen_manager']
screen_manager.current = screen_name
def spin_widget(self):
widget = self.root.ids["load_screen"].ids["spinning_widget"]
anim = Animation(angle=-2000, duration=20)
anim.repeat = True # this doesn't seem to work?
anim.start(widget)
def thread1(self): # this starts the actual "work" of the app by calling get_briefing() below
p2 = Thread(target=lambda: self.get_briefing())
p2.start()
def get_briefing(self):
time.sleep(5)# I have added a 5sec sleep to emulate work being done
self.change_screen("results_1_screen") # screen change from loading screen to results screen after work complete
TestApp().run()
main.kv (just for screen managing)
#:import utils kivy.utils
#:include kv/homescreen.kv
#:include kv/results1screen.kv
#:include kv/loadscreen.kv
GridLayout:
cols: 1
FloatLayout:
canvas:
Rectangle:
size: self.size
pos: self.pos
pos_hint: {"top": 1, "left": 1}
size_hint: 1, 0
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
Results1Screen:
name: "results_1_screen"
id: results_1_screen
LoadScreen:
name: "load_screen"
id: load_screen
homescreen.kv
<HomeScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#000523")
Rectangle:
size: self.size
pos: self.pos
Button:
text: "GO!"
pos_hint: {"top": .2, "center_x": .5}
size_hint: .5, .08
padding: 20, 20
on_release:
app.spin_widget()
app.change_screen("load_screen") # widget spinning and going to load screen
app.thread1() # start the app working in background
results1screen.kv (in real app, this has desired results displayed)
<Results1Screen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#000523")
Rectangle:
size: self.size
pos: self.pos
Label:
text: "RESULTS"
pos_hint: {"top": .9, "center_x": .5}
size_hint: 1, .1
Button:
text: "back"
size_hint: .1, .1
on_release:
app.change_screen("home_screen")
loadscreen.kv
<LoadScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#000523")
Rectangle:
size: self.size
pos: self.pos
Label:
text: "loading"
SpinningWidget:
id: spinning_widget
angle: 0
size_hint: .1, .1
pos_hint: {"center_x": .5, "center_y": .6}
<SpinningWidget@Image>
angle: 0
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: root.center
canvas.after:
PopMatrix
I have tried a couple of ways of exiting or resetting the spin widget function after the get_briefing
function has finished but to no avail. I have tried anim.cancel()
and things like exit(self.spin_widget())
in an attempt to reset it, ready for its next call up.
Can someone please help me out, I can't seem to find an answer in Kivy docs or anywhere else online for that matter. Thank you.
Try resetting the angle
to 0
before each Animation
:
def spin_widget(self):
widget = self.root.ids["load_screen"].ids["spinning_widget"]
widget.angle = 0
anim = Animation(angle=-2000, duration=20)
anim.repeat = True # this doesn't seem to work?
anim.start(widget)
The problem is that after the first Animation
, the angle
value is -2000
, so any Animation
after that will not actually change the angle. Same problem with repeat
.