AttributeError: 'float' object has no attribute 'root' - attributes

i have a issue which i cant solve.
When i run the following code i get this error
AttributeError: 'float' object has no attribute 'root'
Everything works fine till i ad a Clock.schedule
Code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.clock import Clock
global C
C = 0
class Example(App):
def build(self):
global C
C = C + 1
y = str(C)
self.root = FloatLayout()
self.label = Label(text=y, pos=(0,0), size_hint=(1.0,1.0), halign="left")
self.label.text_size = self.label.size
self.root.add_widget(self.label)
return self.root
Clock.schedule_once(build, 0.1)
Example().run()
For Example: When i want to update the time, i call a method every second with
#Clock.schedule_interval(test, 1)
So i call the Method every second
But if i want to update the time and send it to a lable with the ID time i get the same error....
def test(self):
z = time.strftime("%H:%M:%S")
self.ids.time.text = (y)
Clock.schedule_interval(test, 1)
My goal in this projekt is to show the time on every page and update the status of different inputs on different screens to lables for example every second.
i tried the following command to but i guess it doesnt work for more then one screen
self.the_time.text = MyTime

build is a method, and as such be called as self.build, also, it should accept a float argument for the elapsed time since the scheduling.
I assume what happens is that the method is called unbound (no implicit self) and self is assigned that float value, so the method tries to work with that float as self, which doesn't work for pretty obvious reasons.
TL&DR: use Clock.schedule_once(self.build, 0.1) and change build declaration to something like def build(self, dt=0):.

Related

How to get a list of class instance attributes via a generic pythonic approach?

The goal is to retrieve a list of class instance attributes. Perferably would like to find some way to do this without having to instantiate an instance of the class (some kind of pre-instantiation inspection). However, this may not be possible. Alternatively, what would be the most pythonic way to do this, given the need to apply it to generic classes.
Here is what I have put together so far and an example of the hurdles to get past to achieve the goal:
First, a basic class and a function that instantiates a class instance, inspects it and returns the attribues successfully:
from typing import List
import inspect
import datetime as dt
class Apple:
def __init__(self,color:str,size:int) -> None:
self.log_time = dt.datetime.now()
self.color = color
self.size = size
def get_attributes(class_obj:object) -> List[str]:
'''Returns instantiated class attributes.'''
params = list(inspect.signature(class_obj).parameters.keys())
kwargs = {param:None for param in params}
instance = class_obj(**kwargs)
attributes = list(vars(instance))
return attributes
get_attributes(Apple)
>>>
['log_time', 'color', 'size']
This works fine since there are no issues generating attributes (with None populating the parameters) in the init method.
However, for another class:
class Orange:
def __init__(self,color:str,size:int) -> None:
self.log_time = dt.datetime.now()
self.color = color
self.size = size
self.another_attribute = size * 2
get_attributes(Orange)
>>>
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[63], line 1
----> 1 get_attributes(Orange)
Cell In[60], line 7, in get_attributes(class_obj)
5 params = list(inspect.signature(class_obj).parameters.keys())
6 kwargs = {param:None for param in params}
----> 7 instance = class_obj(**kwargs)
8 attributes = list(vars(instance))
10 return attributes
Cell In[62], line 8, in Orange.__init__(self, color, size)
6 self.color = color
7 self.size = size
----> 8 self.another_attribute = size * 2
TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
The deployment of None creates the issue. Entering specific dummy parameters that explicitly permit the class to generate is not the route that I want to take, since this is not a generic enough solution.
Looking for solutions as to how to achieve the goal generically and in the most pythonic manner.
Thanks!
Knowing that general class instance can be dynamically assigned with a new attributes during its life cycle we shouldn't chase an instantiated object with inspect to get its original state.But in context of inspection of initialization phase (instance varnames/attributes) of potential "designed" instance you can apply the following approach (digging into inspect.getmembers):
from typing import List
import inspect
import datetime as dt
class Apple:
def __init__(self,color:str,size:int) -> None:
self.log_time = dt.datetime.now()
self.color = color
self.size = size
def get_init_varnames(class_obj:object) -> List[str]:
'''Finds instanse varnames/attributes (on initialization phase).'''
init_code_obj = next(filter(lambda m: m[0] == '__init__', inspect.getmembers(class_obj)))[1]
init_code = init_code_obj.__code__
return init_code.co_names[-init_code.co_nlocals:]
print(get_init_varnames(Apple))
('log_time', 'color', 'size')

Kivy progressbar value won't update

I'm trying to program a progress bar which counts down from 60 to 0 seconds as soon as the screen it is on opens, however I can't work out why the value of the bar isn't updating.
I believed self.ids.pb.value was a valid way to do this, which leads me to think I'm making a mistake elsewhere.
Thanks in advance.
Error
KeyError: 'pb'
AttributeError: 'super' object has no attribute '__getattr__'
.py file
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, CardTransition
from kivy.uix.progressbar import ProgressBar
class CountDown(ProgressBar):
def count(self):
self.ids.pb.value = 60
seconds = 60
def count_it(seconds):
if seconds == 0:
return
seconds -= 1
self.ids.pb.value = seconds
Clock.schedule_once( lambda dt: count_it(seconds), 1)
Clock.schedule_once( lambda dt: count_it(60), 1)
class EasyMode(Screen):
pass
class TutorialEasy(Screen):
pass
class GIFapp(App):
countdown = CountDown()
def build(self):
return Builder.load_file("testkivy.kv")
Kv file
<TutorialEasy>:
Button:
on_release: app.countdown.count()
on_release: app.root.current = "EasyMode"
<EasyMode>:
CountDown:
id: pb
max:60
Update
Value wasn't updating once errors were fixed because I missed declaring the value in Kv lang ie.
<EasyMode>:
CountDown:
id: pb
max:60
value: app.countdown.value
The value is already in the CountDown class.
So you can set the value with self.value = seconds
If you are in the EasyMode class, it would work with self.ids.pb.value = seconds

PyQtGraph SpotItem returns 'NoneType' when calling user data

I am trying to get the associated user data of a point (which is a SpotItem instance) in a scatter plot when clicked on it. While methods listed in the documentation (like pos() or size()) seem to work fine, I recieve a NoneType object when I apply the data() method. I actually expected it to return my user data, but it doesn't.
So, how can I retrieve my associated original data that I put in?
What I actually need is something like an index i of the original input lists for a clicked point that would allow me to track back the corresponding x[i] y[i] set.
Here is my code sample:
import pyqtgraph as pg
#some dummy data
x=[0,1,2,3,4,5,3.5,3.4]
y=[5,4,3,2,1,0,3.4,3.5]
win=pg.GraphicsWindow()
p1=win.addPlot(row=1, col=1)
my_data=pg.ScatterPlotItem(x,y,symbol='o',size=30)
p1.addItem(my_data)
def clicked(items,points):
print("point data: ",points[0].data())
my_data.sigClicked.connect(clicked)
I am using Python 3.6 (with Spyder 3.1.4), Qt 5.6 and PyQt 5
sigClicked gave us the item (ScatterPlotItem) that has been pressed and the points (SpotItem) where they have been pressed, with the seconds we can obtain the element Point() that gives us the position, and this has the methods x() y y() that return the coordinates. From item we can get all the x and y that you have initially placed through data['x'] and data['y'], respectively, then we have the points pressed and all the points possible so to find the indexes we use np.argwhere() and then we intersect the values with np.intersect1d(), at the end we eliminate the repeated points with set.
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
app = QtGui.QApplication([])
x=[0,1,2,3,4,5,3.5,3.4, 3.4]
y=[5,4,3,2,1,0,3.4,3.5, 3.5]
win=pg.GraphicsWindow()
p1=win.addPlot(row=1, col=1)
my_data=pg.ScatterPlotItem(x,y,symbol='o',size=30)
p1.addItem(my_data)
def clicked(item, points):
indexes = []
for p in points:
p = p.pos()
x, y = p.x(), p.y()
lx = np.argwhere(item.data['x'] == x)
ly = np.argwhere(item.data['y'] == y)
i = np.intersect1d(lx, ly).tolist()
indexes += i
indexes = list(set(indexes))
print(indexes)
my_data.sigClicked.connect(clicked)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
like this:
def clicked(items,obj, points):
now_point = point[0].pos()
x = now_point[0]
y = now_point[1]
Then you can get what you want.

Return output of the function executed 'on_click'

How to get the output of the function executed on_click by ipywidgets.Button
outside the function to use in next steps? For example, I want to get back the value of a after every click to use in the next cell of the jupyter-notebook. So far I only get None.
from ipywidgets import Button
def add_num(ex):
a = b+1
print('a = ', a)
return a
b = 1
buttons = Button(description="Load/reload file list")
a = buttons.on_click(add_num)
display(buttons)
print(a)
The best way that I have found to do this type of thing is by creating a subclass of widgets.Button and then add a new traitlet attribute . That way you can load the value(s) that you want to perform operations on when you create a new button object. You can access this new traitlet attribute whenever you want inside or outside of the function. Here is an example;
from ipywidgets import widgets
from IPython.display import display
from traitlets import traitlets
class LoadedButton(widgets.Button):
"""A button that can holds a value as a attribute."""
def __init__(self, value=None, *args, **kwargs):
super(LoadedButton, self).__init__(*args, **kwargs)
# Create the value attribute.
self.add_traits(value=traitlets.Any(value))
def add_num(ex):
ex.value = ex.value+1
print(ex.value)
lb = LoadedButton(description="Loaded", value=1)
lb.on_click(add_num)
display(lb)
Hope that helps. Please comment below if this does not solve your problem.

Class Attribute Variable is not updating

I am creating a game where the player is a ship that has to dodge meteors that fall. I have 2 classes, the ship and the meteors. The meteors "fall" by having their canvas objects moved down their y axis and their y coordinates are subtracted by the number as the move function. I have an if statement that detects whether the meteors have fallen passed the border of the canvas, and it deletes those meteors, and creates new meteors at the top of the screen, thus making it seem that there are multiple meteors that are falling. The ship class has a similar function that detects if the ship has gone passed the sides of the canvas, which triggers the death function. I have a function in the meteor class that detects whether it is overlapping, with the help from Sneaky Turtle's answer. Now, it's almost finished, I just have one problem. After 3 "rounds" the meteors should get faster. How I implemented this was by having an if statement that checks if a variable is over 3. If not, the variable adds 1. When it reaches 3, it resets and adds (speed amount) to the speed attribute of the meteor,which is used when it moves. the problem is it only works on the first "wave" after that, the speed attribute stays the same. All the sound functions are commented off so that I don't have to upload the files.
Code:
from random import *
from tkinter import *
from time import *
print('''****Meteor Run****
Don't let the meteors hit you!
A-Left D-Right ''')
sleep(1.25)
#from game_sounds import*
root=Tk()
c = Canvas(width=800,height=600,bg="#37061a")
c.pack()
m1=0
m2=0
m3=0
m4=0
m5=0
m6=0
m7=0
m8=0
direction=0
speed=0
score = 0
cont=True
class ship:
def __init__(self,x1,y1,x2,y2):
self.x1=x1
self.y1=y1
self.x2=x2
self.y2=y2
self.hitbox3=387.5 + x1
self.shape=c.create_polygon(353+x1,380+y1,387.5+x1,310+y1,
420+x1,380+y1,fill="Blue")
def move(self):
global direction
if direction=="L":
self.x1 = self.x1-10
self.hitbox3 = self.hitbox3-10
c.move(self.shape,-10,0)
sleep(0.001)
root.update()
if direction=="R":
self.x1 = self.x1+10
self.hitbox3 = self.hitbox3+10
c.move(self.shape,10,0)
root.update()
self.test_lost_in_space()
sleep(0.001)
def death(self):
root.destroy()
print("You Lost!")
print("Score:",score)
# death_sound()
def test_lost_in_space(self):
if self.hitbox3<=0:
self.death()
if self.hitbox3 >=800:
self.death()
def ship_explode(self):
overlap = c.find_overlapping(353+self.x1,380+self.y1,420+self.x1,310+self.y1)
if overlap != (self.shape,):
self.death()
class meteor:
def __init__(self,x1,y1):
self.x1=x1
self.y1=y1
self.hitbox=89+x1
self.speed=.75
self.shape =c.create_polygon(1+x1,50+y1,34+x1,23+y1,67+x1,23+y1,
89+x1,57+y1,64+x1,71+y1,27+x1,71+y1,fill="brown")
def meteor_return(self):
global m1
global m2
global m3
global m4
global m5
global m6
global m7
global m8
global speed
global score
if self.y1 >=600:
c.delete(self)
m1=meteor(randrange(0,700),randrange(6,12))
m2=meteor(randrange(0,700),randrange(6,12))
m3=meteor(randrange(0,700),randrange(6,12))
m4=meteor(randrange(0,700),randrange(6,12))
m5=meteor(randrange(0,700),randrange(6,12))
m6=meteor(randrange(0,700),randrange(6,12))
m7=meteor(randrange(0,700),randrange(6,12))
m8=meteor(randrange(0,700),randrange(6,12))
if speed!=3:
speed=speed +1
score = score + 1
# lvl_up()
if speed==3:
speed=0
self.speed= self.speed + .5
print(self.speed)
score = score + 5
# lvl_up_2()
def meteor_fall(self):
global speed
self.y1 = self.y1 + self.speed
c.move(self.shape,0,self.speed)
root.update()
self.meteor_return()
# ship1.ship_explode()
def ship_move(event):
global direction
if event.keysym=="a":
direction="L"
ship1.move()
if event.keysym=="d":
direction="R"
ship1.move()
ship1 =ship(0,0,0,0)
m1=meteor(randrange(0,200),randrange(6,12))
m2=meteor(randrange(200,400),randrange(6,12))
m3 =meteor(randrange(400,600),randrange(6,12))
m4=meteor(randrange(600,800),randrange(6,12))
m5 =meteor(randrange(400,600),randrange(6,12))
m6=meteor(randrange(600,800),randrange(6,12))
m7 =meteor(randrange(400,600),randrange(6,12))
m8=meteor(randrange(600,800),randrange(6,12))
c.bind_all("<KeyPress-a>",ship_move)
c.bind_all("<KeyPress-d>",ship_move)
while cont ==True:
m1.meteor_fall()
m2.meteor_fall()
m3.meteor_fall()
m4.meteor_fall()
m5.meteor_fall()
m6.meteor_fall()
m7.meteor_fall()
m8.meteor_fall()
c.bind_all("<KeyPress-a>",ship_move)
c.bind_all("<KeyPress-d>",ship_move)
ship1.death()
Instead of comparing the x and y coordinates of each meteor and seeing whether they are within the bounds of the ships co-ordinates, I would use find_overlapping to detect what actually overlaps the Ship.
If you have nothing on your canvas except the meteors and ship, you could implement something like:
ship_coords = c.coords(self.shape)
overlap = c.find_overlapping(*ship_coords)
if overlap != (self.shape, ):
#Code to run when the meteors collide with the ship.
...
Where (self.shape, ) is the tuple returned from the coordinates you pass to find_overlapping. I recommend reading documentation on the Tkinter canvas, it seems like you have just started learning! Hopefully this helps for the moment however.
If you need to specifically detect what items are overlapping with your ship, then there are plenty of other questions and answers on Stack Overflow about find_overlapping.

Resources