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()
Related
It seems to me that the color change when clicking a button with a mouse only occurs if I hold the button pressed for a short time (< 1 second). If I only perform a very short click, it doesn't happen. Did anyone else experience this and is there a way to change this behaviour, so the button color changes immediately (preferably directly in the kv file)?
I am using Kivy 2.0.0.
Edit:
Meanwhile I noticed that this happens when the operations triggered by the button press take some time. Here is an example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
import time
import kivy
kivy.require('2.0.0')
Builder.load_string("""
<MyLayout>
Button:
text: "Button 1"
on_press: root.button1_press()
Button:
text: "Button 2"
on_release: root.button2_press()
""")
class MyLayout(BoxLayout):
def button1_press(self):
time.sleep(3)
print("Result Button 1")
def button2_press(self):
time.sleep(3)
print("Result Button 2")
class MyApp(App):
def build(self):
return MyLayout()
if __name__ == '__main__':
MyApp().run()
To change the color of button 1 I need to press it for at least 3 seconds (the sleep time), while button 2 stays on for 3 seconds cause on_release it used instead of on_press for binding.
Is there a way to just have a button change color immediately like a short "blink" to give the feedback that it has been pressed?
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.
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.
my kivy simple hello world app is not closing I'm using raspberry pi B and I can't close it I must unplug my raspberry pi 5v adapter to close it
I'm using rasbian jessie
this is the very simple code
import kivy
from kivy.app import App
from kivy.uix.label import Label
class mamdouh(App):
def build(self):
return Label(text='mamdouh')
if __name__=='__main__':
mamdouh().run()
What I was trying to say in the comment was. That you need to have some action to cause the quit, as the run method will run in a loop indefinitely otherwise.
If you now click on the Button labeled 'paul' it will quit.
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
class mamdouh(App):
def build(self):
lbl = Label(text='paul')
btn = Button(text='mamdouh')
btn.bind(on_press=lambda b: app.stop())
lbl.add_widget(btn)
return lbl
if __name__ == '__main__':
app = mamdouh()
app.run()
I know nothing about kivy but I can see that this should allow you to quit, whether you want a Button in your app is another question.
Another way to kill it and avoid the reboot is simply to go back to the prompt where you launched your app and do CTRL + C. This will only work from the prompt though, not from the app window itself.
I cannot understand how to update kivy screen.
This is my python file:
import kivy
kivy.require('1.1.1')
from kivy.app import App
from kivy.uix.widget import Widget
import time
class PongGame(Widget):
labe = ObjectProperty(None)
def settext(self,x):
self.labe.text = str(x)
print "DONE"
class PongApp(App):
def build(self):
game = PongGame()
game.settext(1)
time.sleep(3)
game.settext(5)
time.sleep(3)
game.settext(87)
time.sleep(3)
game.settext(5)
return game
if __name__ == '__main__':
PongApp().run()
this is my kv file
#:kivy 1.0.9
<PongGame>:
labe:lab
Label:
id: lab
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str("Hello")
When I run it all it freezes. Then all I see is a 5.
How can i get the others to show up?
def build(self):
game = PongGame()
game.settext(1)
time.sleep(3)
game.settext(5)
time.sleep(3)
game.settext(87)
time.sleep(3)
game.settext(5)
return game
Your problem is that sleep blocks the entire thread - your whole program just stops doing anything for the duration of the call. That includes any graphical interface that has been drawn, it can't update or even receive input because it's running in the same thread and doesn't get run again until your blocking function call has stopped.
Actually, you also have the problem that all these changes take place before the gui is even drawn (the game is displayed in the kivy window only after it's returned from build).
You have to instead think in terms of kivy's main loop and clock - in the background kivy is trying to run certain functions like touch detection and graphical updates as frequently as possible. If you run some code in this loop that takes a long time, none of the rest of kivy can work until your code terminates. This is normal in gui programming, you always have to be careful not to block the main program loop.
The easiest way in kivy to schedule something regularly is to create a function and hook into this event loop, telling the clock to run your function after some interval or after every repeat of some time period. In your case, you want to change the text every 3 seconds.
Here is a short example of how to achieve exactly what you want:
from kivy.app import App
from kivy.uix.widget import Widget
import time
from kivy.properties import ObjectProperty
from functools import partial
from kivy.lang import Builder
from kivy.clock import Clock
Builder.load_string('''
<PongGame>:
labe:lab
Label:
id: lab
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str("Hello")
''')
class PongGame(Widget):
labe = ObjectProperty(None)
def settext(self, x, *args):
self.labe.text = str(x)
print "DONE"
class PongApp(App):
def build(self):
game = PongGame()
game.settext(1)
Clock.schedule_once(partial(game.settext, 5), 3)
Clock.schedule_once(partial(game.settext, 87), 6)
Clock.schedule_once(partial(game.settext, 5), 9)
return game
if __name__ == '__main__':
PongApp().run()
There are a few important notes. One is that I used functools.partial() because you need to pass a function to Clock.schedule_once, and partial creates a function from an existing function (here game.settext) and some default arguments to use (here the numbers for the label). It was also important to add *args to PongGame.settext because the clock automatically passes some extra arguments that we don't care about.
If the meaning of that is not clear to you, experiment with these parameters to see what happens.