Running kivy and infinite loop at the same time

Viewed 302

I need to make a countdown and then use it on mobile phone. I heard that kivy is good graphic for mobile phones, so I tried to use this. Im trying to run infinite while loop that will count down the time and the kivy app that will create window and display remaining time, both at the same time. I am also using pygame clocks to time it. But when I run this code the while loop is counting the time, but it only creates white blank window and after some time another window. What am I doing wrong ?

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.core.window import Window
from multiprocessing import Process
import pygame


class MyFloat(FloatLayout):
    def __init__(self, **kwargs):
        super(MyFloat, self).__init__(**kwargs)

        self.background = Image(source="textures/background.jpg", allow_stretch=True, keep_ratio=False)
        self.add_widget(self.background)


class MyApp(App):
    def build(self):
        return MyFloat()


def check_time():
    global days
    global hours
    global minutes
    global seconds
    global miliSeconds

    miliSeconds -= 1
    if miliSeconds < 0:
        miliSeconds += 100
        seconds -= 1
    if seconds < 0:
        seconds += 60
        minutes -= 1
    if minutes < 0:
        minutes += 60
        hours -= 1
    if hours < 0:
        hours += 24
        days -= 1
    if days < 0:
        global active
        active = False
        print("End")

    print("days: ", days)
    print("hours: ", hours)
    print("minutes: ", minutes)
    print("seconds: ", seconds)
    print("miliseconds: ", miliSeconds)


def loop():
    while active:
        check_time()

        clock.tick(100)


clock = pygame.time.Clock()

days = 0
hours = 0
minutes = 0
seconds = 30
miliSeconds = 0
active = True


if __name__ == "__main__":
    p1 = Process(target=loop)
    p2 = Process(target=MyApp().run)

    p1.start()
    p2.start()
2 Answers

Just change the last part of your code to:

if __name__ == "__main__":
    p1 = Process(target=loop)

    p1.start()
    MyApp().run()

You don't need pygame nor while True because kivy has class Clock with functions

# call my_callback every 0.5 seconds
Clock.schedule_interval(my_callback, 0.5)

# call my_callback in 5 seconds
Clock.schedule_once(my_callback, 5)

# call my_callback as soon as possible (usually next frame.)
Clock.schedule_once(my_callback)

Start task:

task = Clock.schedule_interval(check_time, 0.1)

Stop task:

task.cancel()

And your function will get delta_time between executions so you can check if it was executed exactly after 0.1 second or not. And you can use this to display correct time.


from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.core.window import Window

from kivy.clock import Clock

class MyFloat(FloatLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.background = Image(source="textures/background.jpg", allow_stretch=True, keep_ratio=False)
        self.add_widget(self.background)

class MyApp(App):
    def build(self):
        return MyFloat()


def check_time(dt):
    global days
    global hours
    global minutes
    global seconds
    global miliSeconds
    global active

    miliSeconds -= 1
    if miliSeconds < 0:
        miliSeconds += 100
        seconds -= 1
    if seconds < 0:
        seconds += 60
        minutes -= 1
    if minutes < 0:
        minutes += 60
        hours -= 1
    if hours < 0:
        hours += 24
        days -= 1
    if days < 0:
        active = False
        print("End")
        task.cancel()  # <-- stoping task

    print("days: ", days)
    print("hours: ", hours)
    print("minutes: ", minutes)
    print("seconds: ", seconds)
    print("miliseconds: ", miliSeconds)

    
days = 0
hours = 0
minutes = 0
seconds = 1
miliSeconds = 0
active = True


if __name__ == "__main__":
    task = Clock.schedule_interval(check_time, 0.1)  #  <-- starting task
    MyApp().run()

EDIT:

Example which uses Clock to display current time in Label

from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.clock import Clock
import datetime

class MyFloat(FloatLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.label = Label()
        self.add_widget(self.label)
        self.task = Clock.schedule_interval(self.update_label, 0.1)  

    def update_label(self, dt):
        now = datetime.datetime.now()
        self.label.text = now.strftime("%Y.%m.%d  %H:%M.%S")
        
class MyApp(App):
    def build(self):
        return MyFloat()

if __name__ == "__main__":
    MyApp().run()

BTW:

You may need multiprocessing/threading when you have to run long-running code. But it may need also Queue to communicate with main process. Usually GUIs can't modify widgets in separated processes/threads.

Related