How to run a Method on the exit of a kivy app - python

I would like to run a Method when the user tries to exit the app , kind of like a "are you sure you want to exit" or "Do you want to save the file" type of message whenever the user tries to exit by clicking the Exit button on top of the window
Some thing like
on_quit: app.root.saveSession()

If you want your application to simply run things after the GUI has closed, the easiest and smallest approach would be to place any exit code after TestApp().run(). run() creates a endless loop which also clears any event-data from within kivy so it doesn't hang. That endless loop breaks as soon as the window/gui instance dies. So there for, any code after will execute only after the GUI dies too.
If you want to create a graceful shutdown of the GUI with for instance socket-closing events or a popup asking the user if that's what they really want to do, then creating a hook for the on_request_close event is the way to go:
from kivy.config import Config
Config.set('kivy', 'exit_on_escape', '0')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.core.window import Window
class ChildApp(App):
def build(self):
Window.bind(on_request_close=self.on_request_close)
return Label(text='Child')
def on_request_close(self, *args):
self.textpopup(title='Exit', text='Are you sure?')
return True
def textpopup(self, title='', text=''):
"""Open the pop-up with the name.
:param title: title of the pop-up to open
:type title: str
:param text: main text of the pop-up to open
:type text: str
:rtype: None
"""
box = BoxLayout(orientation='vertical')
box.add_widget(Label(text=text))
mybutton = Button(text='OK', size_hint=(1, 0.25))
box.add_widget(mybutton)
popup = Popup(title=title, content=box, size_hint=(None, None), size=(600, 300))
mybutton.bind(on_release=self.stop)
popup.open()
if __name__ == '__main__':
ChildApp().run()
Courtesy of pythonic64 who created a gist on the topic in a issue way back when.

Related

os.system() don't let updating image source in kivy python

I am trying to update the image source on a changeImageSource function it changes the source instantly but when I use time.sleep() method in that function, function executes but doesn't update the source of the image. updates after time.sleep() call completed.
from kivy.app import App
from kivy.uix.image import AsyncImage
from kivy.uix.button import Button
from kivy.uix.widget import Widget
import time
# creating the App class
class MyApp(App):
def build(self):
parent = Widget()
#creating and adding image to widget
self.img = AsyncImage(
source='http://kivy.org/logos/kivy-logo-black-64.png')
self.img.pos = (400,400)
#creating btn and adding press handler
self.change_img_btn = Button(text="Change Image ")
self.change_img_btn.bind(on_press = self.changeImageSource)
#adding widget to Widget instance
parent.add_widget(self.img)
parent.add_widget(self.change_img_btn)
return parent;
def changeImageSource(self,*args):
self.img.source = "https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a"
time.sleep(4)
# run the App
MyApp().run()
One: The "return parent;" should be "return parent"
Two: Why do you need time.sleep()?
You can also try flipping line 29 and 30.

Kivy: How to blick all user interaction?

Situation: Hello, I have a kivy application with buttons/icons which have bindings like changing page, saving/loading data with AWS or local storage, calling an API REST, etc...
Problem: Some of theses actions take some time and when I click multiple time on an icon that take time to do an action, my application crash on android.
Solution: Every time a binding is called, I disable the possibility of the user to interact with the application and I display a little "charging icon" on the menu.
Real problem: I don't know how to do it ! Is there a boolean userCanInteract or 2 functions like enable_user_interaction() disable_user_interaction() ?
I don't know a way to disable all user interaction with your app but you can disable your button like this:
Python file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
class Window(Widget):
button = ObjectProperty(None)
def disable_button(self):
self.button.disabled = True
self.button.text = "Disabled"
class GUI(App):
def build(self):
return Window()
if __name__ == "__main__":
GUI().run()
.kv file:
<Window>
button: button
Button:
id: button
text: "Enabled"
disabled: False
on_release: root.disable_button()
For the crashing part, i think using threading module can solve your problem. Every time you click a button for long tasks, create a new thread. Example:
Python:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
import threading
from time import sleep
def do_something(app):
print("This function is doing something.")
sleep(1)
app.root.button.disabled = False
app.root.button.text = "Enabled"
class Window(Widget):
button = ObjectProperty(None)
def disable_button(self, app):
thread = threading.Thread(target=do_something, args=(app,))
self.button.disabled = True
self.button.text = "Disabled"
thread.start()
class GUI(App):
def build(self):
return Window()
if __name__ == "__main__":
GUI().run()
.kv file:
<Window>
button: button
Button:
id: button
text: "Enabled"
disabled: False
on_release: root.disable_button(app)
This way, your button will be disabled until the thread is done. When the thread ends, button will be enabled.

Cannot disable buttons in Kivy (Python)

I have problem with disabling of buttons in kivy library. When I disable button, it simply not disable. It waits in some strange way.
Let me show you my code:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
import time
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
time.sleep(3.0)
btn.disabled = False
app = MainApp()
app.run()
When I press button, I want to disable it for 3 sec. But instead of it program "freeze" (without disabling of button), and then after 3 secs do animation of press (button blinks with blue color). Of cource program must "freeze" because of time.sleep(3.0), but after disabling of button (Which must be gray, but it dont change color...)
How to solve it? If I put there instead time.sleep() something like for cycle (with about 10 milions of cycle) to imitate of "doing something" by program, it behaves in the same way...
So how I can solve it? How to disable button in kivy, then do something and after it is done enable button again?
Thanks!
EDIT: My problem isn't, that program freezes for 3 seconds. I understand that calling time.sleep() is blocking. What I don't understand is why button is not disabled before (and during) sleep...
The time.sleep is blocking the code. Instead you need to use Clock to enable the button after 3 seconds. Below is the corrected code to achieve your target:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from functools import partial
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
Clock.schedule_once(partial(self.btn_enable, btn), 3)
def btn_enable(self, btn, *args):
btn.disabled = False
app = MainApp()
app.run()
TL; DR
The animation happens after the press function is called. This means that you freeze the program when doing time.sleep.
What to do about it?
Instead, you need to do something non-blocking, meaning that it runs in three seconds, but it doesn't cause the program to freeze. Something that would probably work is to utilize threads (something similar to the example, but dealing with sending variables across threads).
Example
Here is an example for your code that does not work, so you can understand the gist of it. Most likely, you are going have to deal with passing variables across threads:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
# import time
import threading
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
# time.sleep(3.0)
threading.Timer(3.0, lambda: btn.disabled = False).start()
app = MainApp()
app.run()
This was inspired by this answer.

Python kivy text to speech and Popup in parallel

I am trying to implement a warning which is using text to speech and a visual warning as popup which should disappear after few seconds. I am using kivy for the user interface. At the moment the code is running with the popup and the voice warning, but the problem is, that the voice warning starts first (I would rather like the popup warning to be first) and I additionally would like both processes to start at the same time. I was not able to solve this issue using multiprocessing.Process(target.display_warning_popup).start() and multiprocessing.Process(target.display_warning_voice).start(). Followed by multiprocessing.Process(target.display_warning_popup).join() and multiprocessing.Process(target.display_warning_voice).join().
This is a snippet of the code that I am using.
# imports
import pyttsx3 # for text to speech
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.clock import Clock
class MainApp(App):
engine = pyttsx3.init()
# function for displaying popup
def display_warning_popup():
content = Label(text="Please reduce the temperature.")
content.color = (1, 1, 1, 1)
popup = Popup(title="Warning",
content=content,
size_hint=(None, None),
size=(350, 150))
popup.open()
Clock.schedule_once(popup.dismiss, 2)
# function for text to speech
def display_warning_voice(self):
self.engine.say("Temperature is too high")
self.engine.runAndWait()
# function for warning
def warning(self, is_temperature_too_high):
if is_temperatrue_too_high:
self.display_warning_popup()
self.display_warning_voice()
Binding the voice warning to the on_open event of the Popup results in both warnings appearing at about the same time. The Popup class is an extension of the ModalView class, and the documentation is here.
After incorporating this change and correcting some other minor problems with the code, here is what your code looks like:
# imports
import pyttsx3 # for text to speech
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.clock import Clock
class MainApp(App):
engine = pyttsx3.init()
def build(self):
return Button(text='click here', on_press=self.warning)
# function for displaying popup
def display_warning_popup(self):
content = Label(text="Please reduce the temperature.")
content.color = (1, 1, 1, 1)
popup = Popup(title="Warning",
content=content,
size_hint=(None, None),
size=(350, 150))
popup.bind(on_open=self.display_warning_voice)
popup.open()
Clock.schedule_once(popup.dismiss, 2)
# function for text to speech
def display_warning_voice(self, *args):
self.engine.say("Temperature is too high")
self.engine.runAndWait()
# function for warning
def warning(self, is_temperature_too_high):
self.display_warning_popup()
MainApp().run()

Momentary on screen button Kivy on Python

I have the following code which is to make an on screen momentary button to hold a motor on to roll out an awning.
I borrowed the code from an example on github hoping to modify it to my needs.
As you can see the code sets a button to act as a momentary button as opposed to a latching button, which is what I want, however, in this case, no matter how long I hold the button on, the output is only on as long as the Clock.schedule amount which is 1/10 second.
I have tried multiple different ways to get this to keep the output on as long as I hold on the button but I am unable to find a satisfactory solution?
I can make a momentary external (physical) button on a GPIO pin to do this without issue but cannot get it to work on a software button on the screen?
So in a nutshell, what I want to happen is:
While I am pushing the button on my touch screen the motor should keep operating until I take my finger off the button.
Is an anyone able to assist me please? Thanks in advance.
#Awning
if obj.text == '[size=24]Awning\n Out[/size]':
# turn on output:
GPIO.output(awnoutPin, GPIO.HIGH)
# schedule it to turn off:
Clock.schedule_once(awnout1, .1) #output stays on if this removed
#Awning - Momentary
awningOut = ToggleButton(text="[size=24]Awning\n Out[/size]",markup = True)
awningOut.bind(on_press=press_callback)
awningIn = ToggleButton(text="[size=24]Awning\n In[/size]",markup = True)
awningIn.bind(on_press=press_callback)
layout.add_widget(awningOut)
layout.add_widget(awningIn)
I would do it this way:
#!/usr/bin/env python3.5
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.clock import Clock
gui = '''
GridLayout
cols: 1
Label
text: button.name
MyButton
id: button
name: ''
'''
class MyButton(Button):
def send_signal(self, dt):
self.name = str(dt)
def on_press(self):
Clock.schedule_interval(self.send_signal, 0)
def on_release(self):
Clock.unschedule(self.send_signal)
class Test(App):
def build(self):
return Builder.load_string(gui)
Test().run()

Categories