".cancel()" function of Kivy.Clock doesn't work? - python

I have currently a problem regarding the "Clock" library of the framework
"Kivy". I am creating a Clock-Thread and want to terminate/cancel it but it does not seem to work.
I already read the documentation of Kivy.Clock function and did the same like them.
The link to the Kivy.Clock function
My Code:
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
class MainWindow(BoxLayout):
def __init__(self):
super(MainWindow, self).__init__()
Window.size = (1280, 720)
ev1 = Clock.schedule_interval(self.print_func, 1)
ev1.cancel()
def print_func(self, dt = None):
print ("Test")
Imagine that a .kv file exist and that the MainWindow class is getting called. Everything works fine, except of the canceling of the Clock interval.
Expected result should be that the Clock-Thread should be terminated or rather set to inactive.
Actual result is that it has no effect and the Clock-Thread is still running.
EDIT: I got this function of Clock to work..
I found out that the .cancel() and .unschedule() functions for Clock.schedule_interval() don‘t work. I created then a trigger: Clock.create_trigger() and tried it with .cancel() and .unschedule(). These both functions worked perfectly.

Related

How do I fix 'ValueError: callback must be a callable, got 18:19:46'

So I'm trying to get a good grasp on kivy and I want to create a basic clock app and I keep running into a problem when I go to update the clock label. I've looked at other threads on here and still cant find the right solution. I believe I am doing something wrong at Clock.schedule_interval but again, I'm trying to get a grasp on kivy and I'm not sure. Any help would be greatly appriciated
Python code
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.clock import Clock
import time
Builder.load_file('myApp.kv')
class ClockScreen(Widget):
def updateClock(self):
hour = time.strftime("%H")
minute = time.strftime("%M")
second = time.strftime("%S")
Clocktime = self.root.ids.clock.text
print(Clocktime)
newTime = self.root.ids.clock.text = f'{hour}:{minute}:{second}'
return newTime
class FirstApp(App):
def build(self):
return ClockScreen()
def on_start(self):
Clock.schedule_interval(ClockScreen.updateClock(self), 1)
FirstApp().run()
```
KV File
:
FloatLayout:
size: root.width, root.height
Label:
id: clock
text: "12:00"
Try
def on_start(self):
Clock.schedule_interval(ClockScreen.updateClock, 1)
Reason:
ClockScreen.updateClock is a callable (a function handle). This is also explained in the docs.
ClockScreen.updateClock(self) is actually calling the function that returns the time, and you cannot schedule a string (18:19:46, as per your error).
However, this doesn't say which instance of the ClockScreen gets updated (you are referencing the function via a class rather than a specific instance), so this may be more accurate for what you want
class FirstApp(App):
def build(self):
return ClockScreen()
def on_start(self):
cs = self.build()
Clock.schedule_interval(cs.updateClock, 1)

Image not showing when created from a thread kivy/kivymd

I'm working on an app, and I need the images to display independently at a specific timing. I have set up a thread using python's stock threading module, it runs and works normally instead of the image it displays a black square. Does anyone know how to fix it?
Here is my code to reproduce the issue:
import threading
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
class TestApp(App):
def build(self):
self.fl = FloatLayout()
self.fl.add_widget(Button(text="show image", on_press=self.start_thread))
return self.fl
def insertfunc(self):
self.fl.add_widget(Image(source="HeartIcon.png"))
def start_thread(self, instance):
threading.Thread(target=self.insertfunc).start()
TestApp().run()
Any help will be appreciated!
The add_widget() must be done on the main thread. I assume that you are using threading because you have additional things to do on the Thread aside from just the add_widget(). Based on that assumption, here is a modified version of your code that does what I think you want:
import threading
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
class TestApp(App):
def build(self):
self.fl = FloatLayout()
self.fl.add_widget(Button(text="show image", on_press=self.start_thread))
return self.fl
def insert_image(self, dt):
self.fl.add_widget(Image(source="HeartIcon.png"))
def insertfunc(self):
# do some calculations here
Clock.schedule_once(self.insert_image)
def start_thread(self, instance):
threading.Thread(target=self.insertfunc).start()
TestApp().run()
If you are not doing anything else in the new thread, then you don't actually need another thread. The start_thread() method can just do the:
self.fl.add_widget(Image(source="HeartIcon.png"))

Kivy TabbedPanel switch_to work inconsistently

I am writing a code which starts the frontend, runs the backend and then loads the frontend. The frontend consists of TabbedPanel, and the currently displayed tab may be change by backend.
Here's the MRE:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
def button(instance):
instance.parent.parent.switch_to(instance.parent.parent.tab_2) # accessing TabbedPanel without messing with sending
# a variable
def backend(frontend):
# this class represents main backend function. In the result of its execution there might be a need to switch to
# another tab
frontend.switch_to(frontend.tab_2)
class MyTabbedPanel(TabbedPanel):
def __init__(self, **kwargs):
super().__init__()
self.tab_1 = TabbedPanelHeader()
self.tab_2 = TabbedPanelHeader()
self.tab_1.content = Button(text='Tab 1')
self.tab_1.content.bind(on_release=button)
self.tab_2.content = Label(text='Tab 2')
self.add_widget(self.tab_1)
self.add_widget(self.tab_2)
class Application(App):
def build(self):
frontend = MyTabbedPanel()
backend(frontend)
return frontend
Application().run()
The button, which I have added to compare, to switch from tab 1 to tab 2 works just fine, however, the auto swith when starting the app does not work.
What is the problem? Thank you in advance.
At the time that you're calling backend, there is no root widget returned by the build method, let alone a tab to switch to.
One way to solve this, is to schedule the call to the backend for after the build ends, using the Clock module.
def build(self):
frontend = MyTabbedPanel()
# backend(frontend)
from functools import partial
from kivy.clock import Clock
Clock.schedule_once(partial(backend, frontend))
return frontend
You also have to add an args argument to the backend method, because Clock sends a dt value:
def backend(frontend, *args):

How to prevent memory leaks when dismissing Kivy ModalView instance?

In my app, I create instances of ModalView that contain child widgets with callbacks that are bound to widget properties or scheduled with Clock. Here is an example code to demonstrate this. I find that dismiss() method of the ModalView instance leaves intact the callback bindings and the Clock scheduled callbacks of its child widgets. I have to take care of unbinding and unscheduling these myself. This can get messy when I bind to callbacks that take args (I then have to use fbind and funbind_uid methods whilst keeping track of uids). Similarly, Clock scheduled callbacks that take args are tricky to unschedule since they are anonymous then, having been scheduled either using a lambda or a partial.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.clock import Clock
import datetime
Builder.load_string('''
#: kivy 1.9.2
<MainWidget>:
Button:
text: 'Push the button (pu-push the button)'
on_press:
root.showtime()
''')
class MyWidget(FloatLayout):
text=StringProperty() # this can be bound to 'text' property of a child widget for observation
timenow=ObjectProperty()
def __init__(self, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
self.bind(timenow=self.update_text)
Clock.schedule_interval(self.poll_datetime, .5)
def poll_datetime(self, dt):
self.timenow = datetime.datetime.now()
print "polling datetime"
def update_text(self, *args):
self.text=self.timenow.strftime("%Y-%m-%d %H:%M:%S")
print "updating text"
def cleanup(self, *args):
self.unbind(timenow=self.update_text)
Clock.unschedule(self.poll_datetime)
print "cleaning up"
class MainWidget(FloatLayout):
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
def showtime(self):
overlay = ModalView()
container=MyWidget()
timelabel=Label()
container.bind(text=timelabel.setter('text'))
container.bind(pos=timelabel.setter('pos'))
container.add_widget(timelabel)
cancelbutton=Button(text='Cancel', size_hint=(None, None))
cancelbutton.bind(on_press=container.cleanup)
cancelbutton.bind(on_press=overlay.dismiss)
container.add_widget(cancelbutton)
overlay.add_widget(container)
overlay.open()
class MyApp(App):
def build(self):
mw=MainWidget()
return mw
if __name__ == '__main__':
MyApp().run()
Am I doing this right? Does ModalView's dismiss() method leave other objects behind that I am not even aware of? What is a good way to detect such objects being left behind? Is there a way to ensure complete destruction of child widgets of ModalView instance upon dismiss() is called?
I think you are doing it right. When the ModalView is dismissed, its reference in the parent window is removed, and if no other references to it are held anywhere, it will be garbage collected, and any references that it holds will also be garbage collected. However, the Clock.schedule_interval() is holding a reference to the ModalView, so it does not get garbage collected. This is the correct behavior, as your call to schedule events means that you want those scheduled events to continue until they are cancelled.
An easier way to cancel the scheduled events is to use:
self.sched = Clock.schedule_interval(self.poll_datetime, .5)
in the __init__() method of MyWidget. Then in the cleanup() method use:
self.sched.cancel()
You don't need to unbind the timenow binding as that will disappear with the garbage collection that will happen after the cancel.

Running functions in kivy extremely frequently (Kivy Clock/FreeClock)

I am working on an app that takes in data from a Bluetooth device (that I handle simply through the serial module). That I know how to do. But giving it a new life in Kivy - displaying it - creates a problem of everything being synced with FPS. I want to run the function all the time in the background, hundreds of times a second. It's fair to say that out of 10000 incoming packets 10 might be useful. So if I would go by clock scheduling it would have to be (well) under 20 ms per cycle.
Simply put: how do I run one of the functions separately from the FPS? Is there a clean way of using the Free version of the clock for only one function and how is that achieved?
I want a simple solution, I can reinvent the wheel, but I don't want to.
Any help is appreciated, thank you.
You could use threading for this.
Here is a little example of using threading with kivy:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.properties import NumericProperty
import threading
import time
Builder.load_string('''
<MyLayout>:
Label:
text: str(root.data)
''')
class MyLayout(BoxLayout):
data = NumericProperty(0)
count = 0
running = True
def __init__(self,**kwargs):
super(MyLayout,self).__init__(**kwargs)
Clock.schedule_once(self.after_init)
def after_init(self, dt):
threading.Thread(target=self.func).start()
def func(self):
while self.running:
self.data += 1
time.sleep(0.1)
class MyApp(App):
def build(self):
self.root = MyLayout()
return self.root
def on_stop(self):
self.root.running = False
MyApp().run()

Categories