Kivy/Python Countdown App project kivy has no attribute 'built' error - python

Question: What is a 'has no attribute 'built' error, and what do I need to do to correct this code so that it can take in a datetime object and display the count down? Sorry for the long post.
I've provided the code and a link to the .kv file.
I tried to create a countdown clock that takes a datetime object as a parameter and counts down to that date (using python and kivy). It's basically an slight adaptation of Adam Giermanowski's countdown timer tutorial.
Here's my code:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
import datetime
#datetime object
b= datetime.datetime(2016,9,12,3,5)
class Counter_Timer(BoxLayout):
days = StringProperty()
hours = StringProperty()
minutes = StringProperty()
seconds = StringProperty()
def __init__(self, datetimeOBJ):
self.datetimeOBJ = datetimeOBJ
def update(self, dt):
#the difference in time
delta = self.datetimeOBJ - datetime.datetime.now()
self.days = str(delta.days)
hour_string = str(delta).split(', ')[1]
self.hours = hour_string.split(':')[0]
self.minutes = hour_string.split(':')[1]
self.seconds = hour_string.split(':')[2].split('.')[0]
class Counter(App):
#takes a datetime object as a parameter
def __init__(self, datetimeOBJ):
self.datetimeOBJ = datetimeOBJ
def build(self):
Counter = Counter_Timer(self.datetimeOBJ)
Clock.schedule_interval(Counter.update, 1.0)
return Counter
if __name__=='__main__':
Counter(b).run()
Here's the error on the Counter(b).run() line:
AttributeError: 'Counter' object has no attribute 'built'

You have to call the superclasses constructor when you override __init__, so that all of the things that that constructor does in order to have the other methods of the class work gets done. Your init method should be this:
def __init__(self, datetimeOBJ):
App.init(self)
self.datetimeOBJ = datetimeOBJ

Related

Cant wrap my head around Classes and Kivy

I am trying to use kivy.clock object more specifically Clock.schedule_interval to take temperature readings every couple seconds.
I create a method in my main screen class (MyTerrLayout(Screen)) called clockAction. now when I type in Clock.schedule_interval(clockAction, 2) I get:
NameError: clockAction is not defined
So I tired to do self.clockAction but that didn't work either. I tried various methods to get it going like moving Clock.schedule_interval(clockAction, 2) to its own class but I get other errors. Do i have to create an instance of the method clockAction? Or since Clock.schedule_interval(clockAction, 2) might be a class attribute it needs to be called a different way.
Or is it because Clock.schedule_interval(clockAction, 2) is a class attribute that I need to call clockAction a different way? I can't seem to make the connection.
If someone could point me in the right direction that would be awesome. Also does anyone know where I can practice more complex examples of OOP and class manipulation? Or does someone have some old homework regarding class manipulation and OOP concepts?
Here is my code
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.properties import ObjectProperty
from random import randint
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
import threading
Window.size = (600,250)
tep = 75
hum = 85
# Window Manager Class
class WindowManager(ScreenManager):
pass
# Main home menu Class
class MyTerrLayout(Screen):
internal_read = ObjectProperty(None)
external_read = ObjectProperty(None)
target_temp = ObjectProperty(None)
target_hum = ObjectProperty(None)
Clock.schedule_interval(clockAction, 2) #Clock Schedule
def set_temp(self):
global tep
self.ids._Target_Temp.text = str(tep) + " F"
def set_hum(self):
global hum
self.ids._Target_Hum.text = str(hum) + "%"
def clockAction(self, dt=0): ####method I am trying to run
self.ids.external_reading.text = str(randint(1,10))
print('test')
# Temprature Screen Class / Window
class TempWindow(Screen):
def sub_temp(self):
global tep
tep = tep - 1
self.temp_menu.text = str(tep)
def add_temp(self):
global tep
tep = tep + 1
self.temp_menu.text = str(tep)
def set_temp(self):
terrlayout = self.manager.get_screen('main')
terrlayout.set_temp()
class HumWindow(Screen):
def sub_hum(self):
global hum
hum = hum - 1
self.ids.set_hum_menu.text = str(hum)
def add_hum(self):
global hum
hum = hum + 1
self.ids.set_hum_menu.text = str(hum)
def set_temp(self):
terrlayout = self.manager.get_screen('main')
terrlayout.set_hum()
class AC(Screen):
def build(self):
Clock.schedule_interval(self.Callback_Clock, 2)
def Callback_Clock(self, dt):
terrlayout = self.manager.get_screen('main')
terrlayout.clockAction()
# Builder Section
kv = Builder.load_file('terrlayoutV1.kv')
class TerrLayout(App):
def build(self):
return kv
if __name__=="__main__":
TerrLayout().run()
The problem is here:
class MyTerrLayout(Screen):
[...]
Clock.schedule_interval(clockAction, 2)
[...]
That code is in the class part of MyTerrLayout. In other languages, we call that static. clockAction has self as a parameter, which means it is not static.
In a static context, you can't access members of an object, because there is no object to refer to.
IMHO this should be done when the object is created:
def __init__(self, **kw):
super().__init__(**kw)
Clock.schedule_interval(self.clockAction, 2) # Clock Schedule
Note that the __init__ method has self and you can refer to self.clockAction.

A clock with kivy python

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()

python/kivy: need function clock() but crash because of recursion

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()

Kivy Python very basic Binding of Label Text

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):
...

Takes no arguments, 2 given: Kivy Button bind to def

Error:
TypeError: changetxt() takes no arguments but 2 given, or global name play_btn not defined.
I tried adding instance, self as well into the def args but still have the same error.
import kivy
from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.lang import Builder
class launchScreenMenu(FloatLayout):
def __init__(self, **kwargs):
super(launchScreenMenu, self).__init__(**kwargs)
menuanchor = AnchorLayout(anchor_x='left',anchor_y='bottom')
menu = StackLayout(orientation='bt-lr',size_hint=(0.5,1))
about_btn = Button(text='About',size_hint=(0.3,0.1))
help_btn = Button(text='Settings',size_hint=(0.3,0.1))
settings_btn = Button(text='Help',size_hint=(0.3,0.1))
menu.add_widget(about_btn)
menu.add_widget(help_btn)
menu.add_widget(settings_btn)
menuanchor.add_widget(menu)
return self.add_widget(menuanchor)
class launchScreenBtn(AnchorLayout):
def __init__(self, **kwargs):
super(launchScreenBtn, self).__init__(**kwargs)
play_btn = Button(text="Play")
self.anchor_x = 'center'
self.anchor_y = 'center'
self.size_hint = 0.2,0.2
self.add_widget(play_btn)
play_btn.bind(on_press=self.changetxt)
def changetxt():
play_btn.text = 'Game Over'
class GameApp(App):
def build(self):
root = AnchorLayout()
root.add_widget(launchScreenMenu())
root.add_widget(launchScreenBtn())
return root
if __name__=='__main__':
GameApp().run()
All instance methods should always has self as their first argument:
def changetxt(self, *args):
self.play_btn = 'Game Over'
*args is just to be secure, in case it wasn't you who pass the argument.
Also, change all play_btn inside the class to self.play_btn:
self.play_btn = Button(text="Play")
Well, hope this helps!
The first argument of instance methods (think any function in a class, but there are exceptions) is a link to the instance itself. By convention this is called self. Reference
I don't know with 100% certainty what your second argument is. It may be play_btn since that's what's calling the function. That would be convenient since you're trying to reference that anyway, and it otherwise would be undefined.
How you want your def to read is like this:
def changetxt(self, play_btn):

Categories

Resources