To trigger the acquisition of an image, I use the on_release-function of a kivy-button.
So, whenever this button is clicked (or pressed -- since using a touchscreen) a camera is triggered using gphoto2.
The issue:
From time to time, the function is executed multiple times (taking multiple images), while it was clearly pressed only a single time.
According to the logs, I'm confident, that it's a kivy-related issue (not related to the camera, etc.): Logging entries within the on_release-function appear multiple times within the logs.
I'm running an app with kivy (version 1.9.0) and python (version 2.7.6) under Ubuntu 14.04 LTS (64bit) using a touchscreen.
Any hint on how to debug or fix the issue is welcome.
I was stuck at the same problem for days!
Do you have this line in your code?
Config.set('input', 'mouse', 'mouse, multitouch_on_demand')
If yes, then remove it.
On touch devices, touch gets triggered multiple times.
Hope it helps someone with the same problem.
You could disable the button when the first release event starts, and in the end of the thread where the picture is taken, enable the button again. Then you will not have multiple events from that button executing, but still the main app thread is allowed to continue.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
import time
import threading
Builder.load_string('''
<MyLayout>:
Button:
text: 'Print to terminal'
on_release:
root.button_released(self)
''')
class MyLayout(BoxLayout):
def button_released(self,button):
button.disabled = True
print("{} pressed!".format(button))
threading.Thread(target=self.take_picture, args=(button,)).start()
def take_picture(self,button):
time.sleep(1) # taking picture or whatever
button.disabled = False
class MyApp(App):
def build(self):
return MyLayout()
MyApp().run()
Related
I am facing a problem with the Kivy widget Switch and was not able to find a solution. Each topic on the Internet deals with "Working with the active property", which is understandable to me. But I want to set/initializie the start-up active value depending on the current environment within the program.
In my case: I have a Wifi Power-Plug which can be already running. So in this case when the app starts I want the switch with active: True. If the plug is deactivated, the switch shall start with active: False
Normaly you can do this from the main.py with sth. like:
if (getWifiState) == "OFF":
self.ids[widgetName].active = False
else:
self.ids[widgetName].active = True
Generally spoken this works and changes the state.
But here the problem: as soon as you are changing the switch value this way it behaves as if you were clicking on the switch, because the default value = 0 → change to 1 → on_active: function() will be called. But I need a solution which allows me just to change the start value without running the on_active property.
Potential solution:
Probably I have to put logic into my .kv file so that during the switch initialisation the correct start parameter will be set. But why?
Or is there another way to do so?
Appreciate your help
Tried to put logic to my active property in .kv-File, but this did not work.
My solution:
import random
from kivy.app import App
from kivy.lang import Builder
kv = '''
BoxLayout:
Switch:
active: app.get_wifi_state()
on_active: print(self.active)
'''
class Test(App):
# method which return wifi status (replace implementation with your own)
def get_wifi_state(self):
return random.choice((True, False))
def build(self):
return Builder.load_string(kv)
Test().run()
I have an application that at a certain point needs to check if a variable within its Python code has one value or another, and also requires a button capable of changing that variable from one value to another to also save that information in a text file and that the variable loads it the next time the application is started.
Here I prepared a small program to exemplify what I am looking for:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang.builder import Builder
from kivy.core.window import Window
Window.size=(200,200)
data=open('data.txt','r') #Let's say there is a text file called "data" that contains the number 0
Builder.load_string("""
<Screen>
BoxLayout:
orientation:'vertical'
size:root.width,root.height
Label:
text:'Switch On' if app.var==1 else 'Switch Off' #Initially then the label should say "Switch Off"
Button:
text:'Switcher'
on_release: #Change app.var from 0 to 1 or from 1 to 0
""")
class Screen(Widget):
pass
class App(App):
def build(self):
self.var=data.read()
#Save the changes made by the Button
return Screen()
if __name__=='__main__':
App().run()
How can I do this or something similar? A better and simpler way?
You should define a function and bind it to a button. The documentation can really help you here! you can bind your function using my_button.bind(on_press=on_button_click) where:
def on_button_click():
this.my_var += 1
Let me know if you think the documentation is unclear!
I'm making an app/game and the user is able to play a sound when pressing the sound button, and the same sound is played after the user finishes a level. The next level contains a new sound.
For some reason, my code doesn't act how I want it and the results aren't consistent: sometimes it works only once, sometimes it works twice or more. Sometimes the next level contains the new sound but most of the time it doesn't play anything.
It probably has something to do with the way I load/try to unload audio files because the code returns 'play' when printing out the audio state.
Here is a simplified version of the code:
import time
import kivy
from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.lang import Builder
from kivy.uix.widget import Widget
root_widget = Builder.load_file('app.kv')
class ExampleWidget(Widget):
def play_sound(self):
file = 'correct.wav'
print('playing ', file)
click_sound = SoundLoader.load(file)
click_sound.play()
time.sleep(2)
print(click_sound.state)
click_sound.stop()
print(click_sound.state)
#click_sound.unload() # Crashes the app
click_sound = ''
class MyApp(App):
def build(self):
return ExampleWidget()
MyApp().run()
app.kv:
<ExampleWidget>:
id: ew
GridLayout:
col: 2
rows: 3
size: root.size
Button:
text: 'play sound'
size: self.size
on_release: ew.play_sound()
I also tried to empty the click_sound variable by assigning it a None value, using 'del click_sound', tried emptying the variable at the start of the function, end or both and I also tried using the unload() function but that returns the following error:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
I'm also getting the following warning. I don't think it's a part of my problem but I'm adding it just in case:
[WARNING] [ffpyplayer ] [wav # 0x7fc738908e00] Discarding ID3 tags because more suitable tags were found.
Hopefully someone can help me out, I thank you in advance.
EDIT:
As ApuCoder said, the code works fine, just not on my macbook. Just tested it on a Windows pc and there were no problems. Does anybody know a way to fix this on Apple computers?
I am new to Kivy and trying to find my way around. Whenever I create and run an app, it displays as a full-screen that I am unable to close without disconnecting the power (which I know is not ideal, but that's exactly why I am desperate to fix it!).
Shortcuts that are suggested to work (Esc, Ctrl+C, Ctrl+Alt+break) don't. I have attempted changing the config settings at the beginning of the script as follows:
from kivy.config import Config
Config.set('graphics', 'fullscreen', 0)
Config.write()
I've also tried variations on the theme - 0 as a string, 1 as both an integer and string (and trying to provide a width and height for the window) but with no perceivable change. Even if this did work, it would not be the ideal fix given that I would probably want to be able to run things full-screen in the end!
Given that each time I've tried changing something I've had to restart the pi by disconnecting the power, playing around has been quite time-consuming!
Does anybody have any suggestions about how I should proceed?
I'm currently using:
Raspberry Pi 2 Model B connected to normal TV (many people having problems have been using a touchscreen, but that is not true for me)
Raspbian Jessie, Linux 8
Python 2.7
I'm afraid I don't know how to check details about the Kivy module I have downloaded.
I'm very new to this, so apologies if I don't manage to provide all of the relevant information.
Full code I am trying to run (excluding the above config changes):
import kivy
kivy.require('1.9.2') #may be part of the problem - not 100% sure this is correct
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
return Label(text='Hello world')
if __name__ == '__main__':
MyApp().run()
As a temporary workaround you could just do :
def build(self):
button = Button(text = 'Exit', size_hint = (.1, .05),
pos_hint = {'x':0, 'y':0})
button.bind(on_press = self.on_quit)
self.layout = FloatLayout()
self.layout.add_widget(button)
return self.layout
def on_quit(self):
exit()
Which would provide you with an exit button. For your fullscreen problem it's weird, can you provide some more code ?
EDIT:
Can you try this ? :
from kivy.config import Config
Config.set('graphics', 'borderless', 0)
Config.write()
To work around this issue, you can change the full screen to fake so kivy can exit on Ctrl+C.
from kivy.config import Config
Config.set('graphics', 'fullscreen', 'fake')
Config.write()
Also, try to run the code in command line prompt. Avoid raspberry pi's desktop environment while running kivy apps. This will free up pi's memory for running kivy.
I have a Python script that performs some intensive processing of user's files and can take some time. I've build a user interface to it using Kivy, that allows the user to select the file, processing mode and shows them some messages as the process goes on.
My problem is that when the main Kivy loop passes calls the underlying user interface, the window freezes.
From what I've understood, the proper way of resolving this is to create a separate process to which the script would be off-loaded and from which it would send the updates to the user interface.
However, I was not able to find an example of how to do this or any specification on how to send messages from a separate thread back into application.
Could someone please give an example of how to do this properly or point me to the documentation pertaining to the subject?
Update:
For the sake of keeping the program maintainable I would like to avoid calling the elements of loops of processor from the main thread and instead call one long process that comes back to updated elements of the GUI, such as the progress bar or a text field. It looks like those elements can be modified only from the main kivy thread. How do I gain access to them from the outside?
Use publisher/consumer model as described here. Here's an example from that link modified to use separate threads:
from kivy.app import App
from kivy.clock import Clock, _default_time as time # ok, no better way to use the same clock as kivy, hmm
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.button import Button
from kivy.properties import ListProperty
from threading import Thread
from time import sleep
MAX_TIME = 1/60.
kv = '''
BoxLayout:
ScrollView:
GridLayout:
cols: 1
id: target
size_hint: 1, None
height: self.minimum_height
MyButton:
text: 'run'
<MyLabel#Label>:
size_hint_y: None
height: self.texture_size[1]
'''
class MyButton(Button):
def on_press(self, *args):
Thread(target=self.worker).start()
def worker(self):
sleep(5) # blocking operation
App.get_running_app().consommables.append("done")
class PubConApp(App):
consommables = ListProperty([])
def build(self):
Clock.schedule_interval(self.consume, 0)
return Builder.load_string(kv)
def consume(self, *args):
while self.consommables and time() < (Clock.get_time() + MAX_TIME):
item = self.consommables.pop(0) # i want the first one
label = Factory.MyLabel(text=item)
self.root.ids.target.add_widget(label)
if __name__ == '__main__':
PubConApp().run()
I think it's worth providing a 2022 update. Kivy apps can now be run via Python's builtin asyncio library and utilities. Previously, the problem was there was no way to return control to the main Kivy event loop when an async function call finished, hence you could not update the GUI. Now, Kivy runs in the same event loop as any other asyncio awaitables (relevant docs).
To run the app asynchronously, replace the YourAppClass().run() at the bottom of your main.py with this:
import asyncio
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(
YourAppClass().async_run()
)
loop.close()
And that's about it. With regards to the docs:
It is fully safe to interact with any kivy object from other
coroutines running within the same async event loop. This is because
they are all running from the same thread and the other coroutines are
only executed when Kivy is idling.
Similarly, the kivy callbacks may safely interact with objects from
other coroutines running in the same event loop. Normal single
threaded rules apply to both case.
If explicitly need to create a new thread, #Nykakin 's approach is what you want. I'd recommend using Queues to pass data between threads, instead, because they're simpler to implement and more robust, being specifically designed for this purpose. If you just want asynchronicity, async_run() is your best friend.
BE WARNED: While modifying a kivy property from another thread nominally works, there is every indication that this is not a thread safe operation. (Use a debugger and step through the append function in the background thread.) Altering a kivy property from another thread states that you should not modify a property in this way.