I'm new to python and kivy and try to learn from code snippets and trial and error. But now I'm stuck.
To displays weather and garbage information on an raspberry I used kivy.
To grab these information I use the function URLRequest. This function needs the clock-function
while not req.is_finished:
Clock.tick()
return req.result
So the program works, displays the infos but crashed regularly after some 20 minutes or so wit hthe error "RuntimeError: maximum recursion depth exceeded
But I don't understand how I can get rid of the recursion by still getting things working :(
Here's mor of the code in context. Can anyone help?
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.network.urlrequest import UrlRequest
from time import gmtime, strftime, localtime, sleep
class garbage:
def garbage_text(garbage):
req = UrlRequest('http://192.168.1.1:8083/fhem?cmd={ReadingsVal(%22ABFALL%22,%22next_text%22,0)}&XHR=1&fwcsrf=password')
while not req.is_finished:
Clock.tick()
return req.result
class weather:
def weather_db1(weather):
req = UrlRequest('http://192.168.1.1:8083/fhem?cmd={ReadingsVal(%22netatmo_M01_00_00_3f_1d_1a%22,%22temperature%22,0)}&XHR=1&fwcsrf=password')
while not req.is_finished:
Clock.tick()
return req.result
class MyBox(BoxLayout):
def update(self, *args):
uweather = weather()
aktw = uweather.weather_db1()
ggarbage = garbage()
garbagetext = ggarbage.garbage_text()
self.ids.temp_ist.text = str(aktw)
self.ids.uhrzeit_top.text = strftime("%H:%M", localtime())
self.ids.datum_top.text = strftime("%d.%m.%Y", localtime())
self.ids.garbage_std.text = garbagetext+" rausstellen "
class ControlApp(App):
def build(self):
actclock = MyBox()
Clock.schedule_interval(actclock.update, 1)
return actclock
if __name__ == "__main__":
ControlApp().run()
Here is a modified version of your code that uses callbacks instead of looping or waiting:
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock, mainthread
from kivy.uix.boxlayout import BoxLayout
from kivy.network.urlrequest import UrlRequest
from time import strftime, localtime
class garbage:
def garbage_text(self, *args):
req = UrlRequest('http://192.168.1.1:8083/fhem?cmd={ReadingsVal(%22netatmo_M01_00_00_3f_1d_1a%22,%22temperature%22,0)}&XHR=1&fwcsrf=password',
on_success=self.update, on_failure=self.failure)
#mainthread
def update(self, req, result):
# update the GUI
garbage_std = App.get_running_app().root.ids.garbage_std
garbage_std.text = str(result)+" rausstellen "
# schedule the next update
Clock.schedule_once(self.garbage_text, 1)
def failure(self):
print('garbage failed')
class weather:
def weather_db1(self, *args):
req = UrlRequest('http://192.168.1.1:8083/fhem?cmd={ReadingsVal(%22netatmo_M01_00_00_3f_1d_1a%22,%22temperature%22,0)}&XHR=1&fwcsrf=password',
on_success=self.update, on_failure=self.failure)
#mainthread
def update(self, req, result):
# update the GUI
temp_ist = App.get_running_app().root.ids.temp_ist
temp_ist.text = str(result)
# schedule the next update
Clock.schedule_once(self.weather_db1, 1)
def failure(self):
print('weather failed')
class MyBox(BoxLayout):
def update(self, *args):
self.ids.uhrzeit_top.text = strftime("%H:%M", localtime())
self.ids.datum_top.text = strftime("%d.%m.%Y", localtime())
class ControlApp(App):
def build(self):
actclock = MyBox()
self.weather = weather()
self.garbage = garbage()
# start the time updates
Clock.schedule_interval(actclock.update, 1)
# start the other updates
Clock.schedule_once(self.weather_update)
Clock.schedule_once(self.garbage_update)
return actclock
def weather_update(self, dt):
self.weather.weather_db1()
def garbage_update(self, dt):
self.garbage.garbage_text()
Builder.load_string('''
<MyBox>:
orientation: 'vertical'
Label:
id: temp_ist
Label:
id: uhrzeit_top
Label:
id: datum_top
Label:
id: garbage_std
''')
if __name__ == "__main__":
ControlApp().run()
Related
I would like to display the time with kivy. I don't want that the label use the whole page. How can I do ?
I want it to appear the same as in the following photo:
from kivy.app import App
from datetime import datetime
from datetime import timedelta
from kivy.clock import Clock
from kivy.uix.label import Label
class myApp(App):
def build(self):
self.now = datetime.now()
# Schedule the self.update_clock function to be called once a second
Clock.schedule_interval(self.update_clock, 1)
self.my_label = Label(text= self.now.strftime('%H:%M:%S'))
return self.my_label # The label is the only widget in the interface
def update_clock(self, *args):
# Called once a second using the kivy.clock module
# Add one second to the current time and display it on the label
self.now = self.now + timedelta(seconds = 1)
self.my_label.text = self.now.strftime('%H:%M:%S')
myApp().run()
I have the below code in test.py and test.kv.
The MDProgressBar UI does not move until end of the program. I've read similar questions in stackoverflow but the solutions posted were only snippets.
Assuming self.process_some_data() is a function that takes a long while to complete, the MDProgressBar UI instance self.progress_bar is supposed to reflect current progress. Based on solutions from other forum posts, I've
created a thread for self.process_some_data() function.
used Clock.schedule_once() for self.update_progress_bar() function which updates self.progress_bar.value in main thread at different stages of completion.
included decorator #mainthread for self.update_progress_bar() function which does not seem to make any difference.
Feel free to edit the code and repost the full solution. Thanks.
main.py
from kivymd.app import MDApp
from kivymd.uix.progressbar import MDProgressBar
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivymd.uix.dialog import MDDialog
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
import time
import threading
from kivy.clock import mainthread
from functools import partial
class Screen1(Screen):
def show_popup(self):
self.progress_bar = MDProgressBar()
self.popup = Popup(
title ='Progress',
content = self.progress_bar,
auto_dismiss = False, # dialog does NOT close if click outside it
size_hint = (None, None),
size = (400, 400)
)
self.popup.bind( on_open = lambda x: self.run_thread() )
# self.progress_bar.max = 100
self.progress_bar.value = 10
self.popup.open()
print(self.progress_bar.value)
#mainthread
def update_progress_bar(self, val, _):
self.progress_bar.value = val
print(self.progress_bar.value)
if val >= 100:
# self.popup.dismiss()
dialog = MDDialog(title="Status", text="Completed")
dialog.open()
def run_thread(self):
t1 = threading.Thread(target=self.process_some_data())
t1.start()
# t1.join()
def process_some_data(self):
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 25), 0)
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 50), 0)
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 75), 0)
time.sleep(1) # simulate program is running something
Clock.schedule_once(partial(self.update_progress_bar, 100), 0)
# Create the App class
class MyApp(MDApp):
def build(self):
return Builder.load_file("test.kv")
# run the App
if __name__ in ("__main__"):
MyApp().run()
test.kv
Screen1:
Button:
text: "Run program"
on_release: root.show_popup()
size_hint: 0.4, 0.1
pos_hint: {"center_x": 0.5, "center_y": 0.5}
In your run_thread() method, the line:
t1 = threading.Thread(target=self.process_some_data())
runs the process_some_data() method, then sets the target of the created thread to the return of that method. I think you just need to remove the () from that method, so that the target is set to the process_some_data method and not its return:
t1 = threading.Thread(target=self.process_some_data)
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
import json
import re
class Grid(GridLayout):
def __init__(self, **kwargs):
super(Grid, self).__init__(**kwargs)
# Columns for the main view
self.cols = 1
# Start of the Inside
self.inside = GridLayout()
self.inside.rows = 3
self.inside.add_widget(Label(text = 'Enter the Brand Name for its Products: '))
self.brand_input = TextInput(multiline = False)
self.inside.add_widget(self.brand_input)
# Add the things in inside to the main
self.add_widget(self.inside)
self.submit = Button(text = 'Submit', font_size = 20)
self.submit.bind(on_press = self.pressed)
self.add_widget(self.submit)
def pressed(self, instance):
# Pull the text inside the textInput
brand_name = self.brand_input.text
with open('mcg_app/brands.json', 'r') as f:
brands_dict = json.load(f)
request = brands_dict[brand_name]
modified_result = re.sub(r',\s(?![^(]*\))', "\n", str(request))
self.inside.add_widget(Label(text = modified_result))
# Clear out the fields after submitting
self.brand_input.text = ''
class Mcg(App):
def build(self):
return Grid()
if __name__ == '__main__':
Mcg().run()
Hello, this program here has a text input box and the user inputs a brand name for example "Dove" after that he presses the submit button, and the button is connected to the pressed() function. Everything about the program is actually working fine the only problem is after I print the products of the entered brand let's say he inputs a different brand in that case the old output is still there so the program outputs new products with the old ones already there. How can I fix this? Thanks in advance
I can't really test it without a json data file, but check out this:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
import json
import re
class Grid(GridLayout):
def __init__(self, **kwargs):
super(Grid, self).__init__(**kwargs)
# Columns for the main view
self.cols = 1
# Start of the Inside
self.inside = GridLayout()
self.inside.rows = 3
self.inside.add_widget(Label(text='Enter the Brand Name for its Products: '))
self.brand_input = TextInput(multiline=False)
self.inside.add_widget(self.brand_input)
# Add the things in inside to the main
self.add_widget(self.inside)
self.submit = Button(text='Submit', font_size=20)
self.submit.bind(on_press=self.pressed)
self.add_widget(self.submit)
def pressed(self, instance):
# Clear out the fields after submitting
self.brand_input.text = ''
self.inside.clear_widgets()
self.inside.add_widget(Label(text='Enter the Brand Name for its Products: '))
self.inside.add_widget(self.brand_input)
# Pull the text inside the textInput
brand_name = self.brand_input.text
with open('mcg_app/brands.json', 'r') as f:
brands_dict = json.load(f)
request = brands_dict[brand_name]
modified_result = re.sub(r',\s(?![^(]*\))', "\n", str(request))
self.inside.add_widget(Label(text=modified_result))
class Mcg(App):
def build(self):
return Grid()
if __name__ == '__main__':
Mcg().run()
It resets the self.inside GridLayout on every button press...
I'm quite new to python programming and I'm currently building a photobooth using Kivy and Python.
In general it is working (I can press the button and it starts the function to take 3 pictures and updates the tumbnail on the screen) , but I'm not able to change the label text (actionLabel) to show a countdown before the takePhotos function starts.
import os, time, Image, sys, datetime, subprocess,glob
import kivy
kivy.require('1.10.0') # replace with your current kivy version !
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.image import Image as kivyImage
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
import RPi.GPIO as GPIO
#GPIO varialbes
#buttonPin = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(23,GPIO.IN)
GPIO_status = False
# Some variables
photoTitle = "My first Photobox!"
total_photos = 3 #Number of photos to be takes
#Function for photo taking
def takePhotos():
#Take first picture - Folder for inbound pictures /home/pi/PB_Inbox/photobooth%H%M%S.jpg
time.sleep(3)
subprocess.call("gphoto2 --capture-image-and-download --filename /home/pi/PB_Inbox/photobooth%H%M%S.jpg", shell=True)
#Take all other picture
for x in range (0,total_photos-1):
subprocess.call("gphoto2 --capture-image-and-download --filename /home/pi/PB_Inbox/photobooth%H%M%S.jpg", shell=True)
#Process pictures
subprocess.call("sudo sh /home/pi/Documents/Photo_Booth/Image_prep_3", shell=True)
print('done')
class MyApp(App):
# Display the latest thumbnail
photo = kivyImage(source="/home/pi/PB_Thumb/Thumb.png")
actionLabel = Label(text="Push the button", size_hint=(1, 0.2),color=[1,0,0,1],font_size='40sp')
def build(self):
# Set up the layout
photobox = GridLayout(rows=3, spacing=10, padding=10)
# Create the UI objects (and bind them to callbacks, if necessary)
headerLabel = Label(text="The Greatest Photobox", size_hint=(1, 0.1),font_size='40sp') # Button: 20% width, 100% height
# Add the UI elements to the layout
photobox.add_widget(headerLabel)
photobox.add_widget(self.photo)
photobox.add_widget(self.actionLabel)
# Periodically refresh the displayed photo using the callback function
Clock.schedule_interval(self.callback, 0.3)
return photobox
# Callback for thumbnail refresh and listening to GPIO for input
def callback(self, instance):
self.photo.reload()
if self.readSensor() == False:
pass
#print('waiting')
else:
#provided as an argument
takePhotos()
#Read status of the sensor and return True if Buzzer has been pushed
def readSensor(self):
sensor = GPIO.input(23)
if sensor == 1:
return True
else:
return False
if __name__ == '__main__':
MyApp().run()
Can someone show me how to do this?
Thx
Use Clock.create_trigger to trigger the count down.
Remove the time.sleep(3) in function, takePhotos().
Use Clock.schedule_once to invoke function takePhotos() i.e. Clock.schedule_once(takePhotos, 3)
Separate the Kivy App into Python Script and kv file.
Programming Guide ยป Events and Properties
In Kivy applications, you have to avoid long/infinite loops or sleeping.
Example
main.py
import os, time, sys, datetime, subprocess, glob
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.properties import ObjectProperty, NumericProperty
import RPi.GPIO as GPIO
# GPIO variables
# buttonPin = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.IN)
GPIO_status = False
# Some variables
total_photos = 3 # Number of photos to be takes
# Function for photo taking
def takePhotos(dt):
# Take 3 picture
for x in range(total_photos):
subprocess.call("gphoto2 --capture-image-and-download --filename /home/pi/PB_Inbox/photobooth%H%M%S.jpg", shell=True)
# Process pictures
subprocess.call("sudo sh /home/pi/Documents/Photo_Booth/Image_prep_3", shell=True)
print('done')
class PhotoBox(BoxLayout):
photo = ObjectProperty(None)
action_button = ObjectProperty(None)
count_down_trigger = ObjectProperty(None)
count = NumericProperty(3)
def __init__(self, **kwargs):
super(PhotoBox, self).__init__(**kwargs)
# Display the latest thumbnail
self.photo.source = "/home/pi/PB_Thumb/Thumb.png"
self.count_down_trigger = Clock.create_trigger(self.count_down, 1)
def on_press_camera_button(self):
self.count = 3
self.count_down_trigger()
def count_down(self, dt):
self.action_button.text = str(self.count)
self.count -= 1
if self.count >= 0:
self.count_down_trigger()
else:
self.action_button.text = "Say Cheese!"
# Periodically refresh the displayed photo using the callback function
# Clock.schedule_interval(self.callback, 0.3) # infinite loop
Clock.schedule_once(self.callback, 0.3)
self.action_button.text = "Push the button" # reset text
# Callback for thumbnail refresh and listening to GPIO for input
def callback(self, dt):
self.photo.reload()
if self.readSensor():
# provided as an argument
Clock.schedule_once(takePhotos, 3) # call takePhotos method once after 3 seconds
else:
pass
#print('waiting')
# Read status of the sensor and return True if Buzzer has been pushed
def readSensor(self):
sensor = True # GPIO.input(23)
if sensor == 1:
return True
else:
return False
class MyApp(App):
title = "My first Photobox!"
def build(self):
return PhotoBox()
if __name__ == '__main__':
MyApp().run()
my.kv
#:kivy 1.10.0
<PhotoBox>:
photo: photo
action_button: action_button
orientation:'vertical'
spacing: 10
padding: 10
Label:
text: "The Greatest Photobox"
size_hint: (1, 0.1)
font_size: '40sp'
AsyncImage:
id: photo
Button:
id: action_button
text: "Push the button"
size_hint: (1, 0.2)
color: [1,0,0,1]
font_size: '40sp'
on_press: root.on_press_camera_button()
Output
I know this might be a very basic question, but after spending hours wrapping my head around it I still can't figure it out.
I basically just want to bind the text of a label to a variable in the python code. lets call it value. however it should get updated everytime I run a loop Clock.schedule_interval(RootWidget.update, 1.0/1.0)
here is the python, simplified so its basically just the time, which is also printed just to see if it is actually working.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.core.window import Window
import time
class RootWidget(FloatLayout):
def update(self, *args):
value = time.time()
print value
self.ids.value_label.text = str(value)
class MainApp(App):
def build(self):
Window.size = (800, 480)
r = RootWidget()
Clock.schedule_interval(r.update, 1)
print 'build running'
return r
def on_pause(self):
return True
if __name__ == '__main__':
MainApp().run()
the kv file looks as such:
<RootWidget>:
Label:
id: value_label
text:
y: 20.0
x: 0.0
width: 100.0
italic: False
height: 50.0
Clock.schedule_interval(RootWidget.update, 1.0/1.0)
You need to schedule the update method of an instance, not the class itself.
For instance:
r = RootWidget()
Clock.schedule_interval(r.update, 1)
return r
The clock will pass some arguments by default, so you should also declare the update method to accept these. If you don't want to use them then you can just do:
def update(self, *args):
...