In kv-string: 'pos: tt.pos'- NameError: name 'tt' is not defined

106 views
Skip to first unread message

Henrik R.

unread,
Nov 23, 2019, 12:23:51 PM11/23/19
to Kivy users support
Hi! Again I seem to have misunderstood something about 'id' in the kivy language. The kivy-string below is part of a Python-program, but when I try running it I get the following error. What is wrong?:

 BuilderException: Parser: File "<inline>", line 23:
 ...
      21:
      22:<Arrow>:
 >>   23:    pos: tt.pos
      24:   
 ...
 NameError: name 'tt' is not defined



Builder.load_string('''
#:kivy 1.11.1
#:import kivy kivy

<layout>:
id: tt
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 0, 0, 0
Line: # Vertical line:
points: [self.center_x, self.center_y-0.45*min(self.width,self.height), \
self.center_x, self.center_y+0.45*min(self.width,self.height)] \
if min(self.width,self.height)<mm(100) \
else [self.center_x, self.center_y-mm(45), self.center_x, self.center_y+mm(45)]
width: mm(0.25)

<Arrow>:
pos: tt.pos

''')


embryo

unread,
Nov 23, 2019, 1:17:37 PM11/23/19
to Kivy users support
You can use ids to access elements only inside the same rule.
So, the tt id is accessible only inside the layout rule.

The exceptions to that is the root rule and the app.

Elliot Garbus

unread,
Nov 23, 2019, 3:32:33 PM11/23/19
to kivy-...@googlegroups.com

This is the difference between defining a class and instancing a class.

<Arrow>, defines the class arrow.

Arrow:, instances the class arrow.

 

Referring to your code below, below the class definitions add, class instances:

 

Layout1:  # I changed the name of your layout class, by convention, classes start with capital letters

    id:tt

    Arrow: # instance the arrow class

        pos: tt.pos

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/be010be5-4858-4471-b689-cd62ef9f714d%40googlegroups.com.

 

Elliot Garbus

unread,
Nov 23, 2019, 3:39:47 PM11/23/19
to kivy-...@googlegroups.com

This is the difference between a rule (Class Definition) and an Instance.

 

 

From: embryo
Sent: Saturday, November 23, 2019 11:17 AM
To: Kivy users support

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Henrik R.

unread,
Nov 26, 2019, 12:04:59 PM11/26/19
to Kivy users support
I am sorry for being so 'cluesless'!
I added the 4 lines you, Elliot, suggested, to the kv-string, but this only leads to new errors.
I have attached 2 files: "arrow_test.py" is the one I am running. "arrow.py" defines the Arrow-class and is imported by "arrow_test.py".
Please help.

Here is the traceback:

 Traceback (most recent call last):
   File "/mnt/4AF15A0435E762B4/DataDoc/OneDrive/PycharmProjects/2d-graphics/kivy-arrow-master/arrow_test.py", line 68, in <module>
     ''')
   File "/usr/lib/python3/dist-packages/kivy/lang/builder.py", line 399, in load_string
     widget = Factory.get(parser.root.name)(__no_builder=True)
   File "/usr/lib/python3/dist-packages/kivy/factory.py", line 131, in __getattr__
     raise FactoryException('Unknown class <%s>' % name)
 kivy.factory.FactoryException: Unknown class <Layout1>


lørdag den 23. november 2019 kl. 21.39.47 UTC+1 skrev Elliot Garbus:

This is the difference between a rule (Class Definition) and an Instance.

 

 

From: embryo
Sent: Saturday, November 23, 2019 11:17 AM
To: Kivy users support
Subject: [kivy-users] Re: In kv-string: 'pos: tt.pos'- NameError: name 'tt' isnot defined

 

You can use ids to access elements only inside the same rule.

So, the tt id is accessible only inside the layout rule.

 

The exceptions to that is the root rule and the app.

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-...@googlegroups.com.

arrow.py
arrow_test.py

Elliot Garbus

unread,
Nov 26, 2019, 12:47:06 PM11/26/19
to kivy-...@googlegroups.com

I made some changes… this gets you to no error, but just a black box.

I changed the name Layout to Layout1 to match what you had below.  Layout is defined in kivy, so there would be a conflict.  I made Layout1 a BoxLayout.

 

 

 

Builder.load_string('''
<Layout1@BoxLayout>:  # I changed the name and made this a boxlayout

    id: tt
    canvas:
        Color:
            rgb: 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size
        Color:
            rgb: 0, 0, 0
        Line: # Vertical line:
            points: [self.center_x, self.center_y-0.45*min(self.width,self.height),
\
                self.center_x, self.center_y+0.45*min(self.width,self.height)] \
                if min(self.width,self.height)<mm(100) \
                else [self.center_x, self.center_y-mm(45), self.center_x, self.center_y+mm(45)]
            width: mm(0.25)
        Rectangle: # Horizontal line:
            pos: (self.center_x-0.45*min(self.width,self.height),self.center_y-mm(0.25))
\
                if min(self.width,self.height)<mm(100) \
                else (self.center_x-mm(45),self.center_y-mm(0.25))
            size: ((0.9*min(self.width,self.height)),mm(0.5))
\
                if min(self.width,self.height)<mm(100) else (mm(90),mm(0.5))

<Arrow>:
    #pos: tt.pos


Layout1:  # I changed the name of your layout class, by convention, classes start with capital letters
    id: tt
    Arrow: # instance the arrow class
        pos: tt.pos

''')

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/e755eb82-ede6-4c9d-ae07-728d49e97685%40googlegroups.com.

 

Henrik R.

unread,
Nov 27, 2019, 5:43:22 AM11/27/19
to Kivy users support
I didn't get to a 'no errors' situation. :-(
But I realized that the author of "arrow_test.py" had created the layout-widget in which the arrows are drawn as a property (or attribute?) of the App-class. And according to my understanding a widget has to be defined as a class in Python in order to refer to it when creating a Kivy-rule for it in the Kivy-string. So I rewrote "arrow_test.py" - see below. But that didn't change anything! I get exactly the same error.
Can any one help?:

from kivy.app import App
from kivy.uix.widget import Widget
from arrow import *
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from random import random
from kivy.clock import Clock

from kivy.lang import Builder


Builder.load_string('''
#:kivy 1.11.1
#:import kivy kivy

<ArrowLayout>:
    id: tt
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 0, 0, 0
Line: # Vertical line:
points: [self.center_x, self.center_y-0.45*min(self.width,self.height), \
self.center_x, self.center_y+0.45*min(self.width,self.height)] \
if min(self.width,self.height)<mm(100) \
else [self.center_x, self.center_y-mm(45), self.center_x, self.center_y+mm(45)]
width: mm(0.25)
Rectangle: # Horizontal line:
pos: (self.center_x-0.45*min(self.width,self.height),self.center_y-mm(0.25)) \
if min(self.width,self.height)<mm(100) \
else (self.center_x-mm(45),self.center_y-mm(0.25))
size: ((0.9*min(self.width,self.height)),mm(0.5)) \
if min(self.width,self.height)<mm(100) else (mm(90),mm(0.5))

<Arrow>:
# pos: tt.pos

ArrowLayout:  # I changed the name of your layout class, by convention, classes start with capital letters
    id:tt
Arrow: # instance the arrow class
pos: tt.pos

''')


class ArrowLayout(FloatLayout):

def __init__(self):
super().__init__()
self.arrows = []
self.dtsum = 0.0
self.dtcounter = 0.0
# self.layout = FloatLayout()
# Clock.schedule_interval(self.move_arrows, 1/34.0)
Clock.schedule_interval(self.add_random_arrow, 0.5)
# self.layout

def add_random_arrow(self,*args):
newarrow = Arrow(
main_color=[0,0,0,1],
# outline_color=[random()/2.0,random()/2.0,random()/2.0,1],
o_x= 0.5*self.layout.width,
o_y= 0.5*self.layout.height,
#to_x=random()*self.layout.width,
#to_y=random()*self.layout.height,
angle=random()*360,
distance=150+random()*150,
fletching_radius=cm(0.1), # root-end
# distortions=[random() * -0.2, random() *0.3] if random()>0.5 else [],
head_angle=60
)
self.add_widget(newarrow)
self.arrows.append(newarrow)

# def debug_dt(self,*args):
# print ("DT avg ",self.dtsum/float(self.dtcounter))


class ArrowTest(App):

def build(self):
return ArrowLayout()


if __name__ == '__main__':
ArrowTest().run()

Elliot Garbus

unread,
Nov 27, 2019, 10:16:01 AM11/27/19
to kivy-...@googlegroups.com

This is running…

Missing: **kwargs in constructor

def __init__(self, **kwargs):  # kwargs was missing

I moved builder load string…   I don’t think your done, but you’re closer.

 

 

from kivy.app import App
from kivy.uix.widget import Widget
from arrow import *
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from random import random
from kivy.clock import Clock

from kivy.lang import
Builder

kv =
'''

#:kivy 1.11.1
#:import kivy kivy

<ArrowLayout>:
   # id: tt

    canvas:
        Color:
            rgb: 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size
        Color:
            rgb: 0, 0, 0
        Line: # Vertical line:
            points: [self.center_x, self.center_y-0.45*min(self.width,self.height),
\
                self.center_x, self.center_y+0.45*min(self.width,self.height)] \
                if min(self.width,self.height)<mm(100) \
                else [self.center_x, self.center_y-mm(45), self.center_x, self.center_y+mm(45)]
            width: mm(0.25)
        Rectangle: # Horizontal line:
            pos: (self.center_x-0.45*min(self.width,self.height),self.center_y-mm(0.25))
\
                if min(self.width,self.height)<mm(100) \
                else (self.center_x-mm(45),self.center_y-mm(0.25))
            size: ((0.9*min(self.width,self.height)),mm(0.5))
\
                if min(self.width,self.height)<mm(100) else (mm(90),mm(0.5))

<Arrow>:

ArrowLayout:  # I changed the name of your layout class, by convention, classes start with capital letters
    id:tt
    Arrow: # instance the arrow class
        pos: tt.pos

'''


class ArrowLayout(FloatLayout):

   
def __init__(self, **kwargs):  # kwargs was missing
       
super().__init__(**kwargs)
       
self.arrows = []
       
self.dtsum = 0.0
       
self.dtcounter = 0.0
       
self.layout = FloatLayout()
       
# Clock.schedule_interval(self.move_arrows, 1/34.0)
       
Clock.schedule_interval(self.add_random_arrow, 0.5)
       
# self.layout

   
def add_random_arrow(self,*args):
        newarrow = Arrow(
                        
main_color=[0,0,0,1],
#                         outline_color=[random()/2.0,random()/2.0,random()/2.0,1],
                        
o_x= 0.5*self.layout.width,
                        
o_y= 0.5*self.layout.height,
                        
#to_x=random()*self.layout.width,
                         #to_y=random()*self.layout.height,
                         
angle=random()*360,
                        
distance=150+random()*150,
                        
fletching_radius=cm(0.1), # root-end
#                         distortions=[random() * -0.2, random() *0.3] if random()>0.5 else [],
                         
head_angle=60
                       
)
       
self.add_widget(newarrow)
       
self.arrows.append(newarrow)

#    def debug_dt(self,*args):
#        print ("DT avg ",self.dtsum/float(self.dtcounter))


class ArrowTest(App):

   
def build(self
):
       
return Builder.load_string(kv)


if __name__ == '__main__':
    ArrowTest().run()

 

 

 

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/5956f0e8-9694-40df-8954-5efe998eedec%40googlegroups.com.

 

Henrik R.

unread,
Nov 27, 2019, 11:17:56 AM11/27/19
to Kivy users support
OK...

Changing from:
    Builder.load_string('''
to:
    kv = '''

And from:
def build(self):
return ArrowLayout()
to:
def build(self):
return Builder.load_string(kv)

Makes it run, and almost do what I want.

But that is NOT how I understand the documentation on kivy.org... See for instance this:

class TouchtracerApp(App):

def build(self):
self.title = 'Touchtracer - Henrik text in py-file'
self.icon = 'icon.png'
return Touchtracer()

And why did:
  Builder.load_string('''
work fine, when I used it in that project?

Henrik R.

unread,
Nov 27, 2019, 11:41:14 AM11/27/19
to Kivy users support
I made other small changes. Here is the whole file "arrow_test.py":

#!/usr/bin/python3

from kivy.app import App
from kivy.uix.widget import Widget
from arrow import Arrow

from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from random import random
from kivy.clock import Clock
from kivy.metrics import cm
from kivy.lang import Builder

# Builder.load_string('''
kv = '''
#:kivy 1.11.1
#:import kivy kivy

<ArrowLayout>:
    id: tt
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 0, 0, 0
Line: # Vertical line:
points: [self.center_x, self.center_y-0.45*min(self.width,self.height), \
self.center_x, self.center_y+0.45*min(self.width,self.height)] \
if min(self.width,self.height)<mm(100) \
else [self.center_x, self.center_y-mm(45), self.center_x, self.center_y+mm(45)]
width: mm(0.25)
Rectangle: # Horizontal line:
pos: (self.center_x-0.45*min(self.width,self.height),self.center_y-mm(0.25)) \
if min(self.width,self.height)<mm(100) \
else (self.center_x-mm(45),self.center_y-mm(0.25))
size: ((0.9*min(self.width,self.height)),mm(0.5)) \
if min(self.width,self.height)<mm(100) else (mm(90),mm(0.5))

# <Arrow>:
#    pos: tt.pos

ArrowLayout: # I changed the name of your layout class, by convention, classes start with capital letters
id:tt
Arrow: # instance the arrow class
pos: tt.pos

'''


class ArrowLayout(FloatLayout):

def __init__(self, **kwargs): # kwargs was missing
super().__init__(**kwargs)
self.arrows = []
self.dtsum = 0.0
self.dtcounter = 0.0
        # self.layout = FloatLayout()
# Clock.schedule_interval(self.move_arrows, 1/34.0)
Clock.schedule_interval(self.add_random_arrow, 0.5)
# self.layout

def add_random_arrow(self,*args):
newarrow = Arrow(
main_color=[0,0,0,1],
# outline_color=[random()/2.0,random()/2.0,random()/2.0,1],
                         o_x= 0.5*self.width,
o_y= 0.5*self.height,

#to_x=random()*self.layout.width,
#to_y=random()*self.layout.height,
angle=random()*360,
distance=150+random()*150,
fletching_radius=cm(0.1), # root-end
# distortions=[random() * -0.2, random() *0.3] if random()>0.5 else [],
head_angle=60
)
self.add_widget(newarrow)
self.arrows.append(newarrow)

# def debug_dt(self,*args):
# print ("DT avg ",self.dtsum/float(self.dtcounter))

class ArrowTest(App):

def build(self):
return Builder.load_string(kv)
#        return ArrowLayout()


if __name__ == '__main__':
ArrowTest().run()

Elliot Garbus

unread,
Nov 27, 2019, 12:27:27 PM11/27/19
to kivy-...@googlegroups.com

In touchtracer the .kv  file only defines the style of the touchtracer class.  In the ArrowLayout, we are instancing the objects in the KV file.

So in the touch tracer example TouchTracer is the root widget, and is instanced in python.  In the code below, the root widget is defined in the kv file.

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/be8e5c54-4ed5-4279-8947-f3fa2a49e770%40googlegroups.com.

 

Elliot Garbus

unread,
Nov 27, 2019, 12:28:51 PM11/27/19
to kivy-...@googlegroups.com

Is it doing what you want, or do you have a question?

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/998d9135-fd3a-4142-82c5-7bba9d514bfb%40googlegroups.com.

 

Henrik R.

unread,
Nov 27, 2019, 1:59:56 PM11/27/19
to Kivy users support
Thank you for asking!:
I was trying to 'chain' the arrows to the canvas of the 'ArrowLayout' so the arrows would move together with the coordinate system, when you change the size of the window.
That still doesn't work. :-)

You say:
In this code "the root widget is defined in the kv file."
But I specifically changed the code to the following, to change that(?):

class ArrowTest(App):

def build(self):
return ArrowLayout()

I look forward to hear from you. :-)

Elliot Garbus

unread,
Nov 28, 2019, 12:14:25 PM11/28/19
to kivy-...@googlegroups.com

If you remove the lines that instance widgets in the kv file, it will work just fine to instance the root widget in the return of the build as you show below.

 

Looking at the behavior of the app now, the app is correctly tracking the changes to window size as it draws new arrows, the old arrows are not repositioned. 

 

This could be the result of the way the arrows are drawn, or a missing bind on resize in Arrow.   

An alternative (not sure this will work) is detect the resize in ArrowLayout and remove all of the ‘old’ widgets, and then re-add them.

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/e66836f7-5104-4b41-ab58-8a79ce0f3575%40googlegroups.com.

 

Henrik R.

unread,
Nov 28, 2019, 4:12:29 PM11/28/19
to Kivy users support
Thank you for your help!
I will try to look into if I can 'bind on resize in Arrow'.

Elliot Garbus

unread,
Nov 28, 2019, 6:02:13 PM11/28/19
to kivy-...@googlegroups.com
Take a look at the Window event on_draw. 
This might help a little. 



Sent from my iPhone

On Nov 28, 2019, at 2:15 PM, Henrik R. <henrik.r...@gmail.com> wrote:


To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/f370190e-2329-4ada-882e-ce71e27f132d%40googlegroups.com.

Henrik R.

unread,
Nov 29, 2019, 6:32:28 AM11/29/19
to Kivy users support
OK. Thank you!



fredag den 29. november 2019 kl. 00.02.13 UTC+1 skrev Elliot Garbus:
Take a look at the Window event on_draw. 
This might help a little. 



Sent from my iPhone

Henrik R.

unread,
Dec 1, 2019, 9:33:45 AM12/1/19
to Kivy users support
I found a solution. It's not very elegant, but it works. :-)

The most elegant solution would be to declare something in the kv-string/file that automatically updates itself, when the window-size changes. With my limited kivy-skills, I couldn't make that work.

The next-most elegant solution would be if class Arrow(Widget,KVector) can discover that it's parent - class ArrowLayout(FloatLayout) - has changed size. I haven't searched through the whole documentation of Widget (and EventDispatcher) to look for that possibility.

But in 'class ArrowLayout(FloatLayout)' I more or less added the following, and that works.
(I guess '*args' can be deleted.):

def on_size(self, *args):
if self.size_changes>0:
# self.arrow_generator.cancel()
self.redraw_arrows(self,*args)
else:
self.size_changes += 1

def redraw_arrows(self,*args):
for an_arrow in self.arrows:
self.remove_widget(an_arrow)
# print(an_arrow) # Why is this run 5 times per arrow?
an_arrow.o_x = 0.5*self.width
an_arrow.o_y = 0.5*self.height
for an_arrow in self.arrows:
self.add_widget(an_arrow)

Henrik R.

unread,
Dec 1, 2019, 10:07:17 AM12/1/19
to Kivy users support
Well... This looks nicer:

an_arrow.o_x = self.center_x
an_arrow.o_y = self.center_y

Instead of:

an_arrow.o_x = 0.5*self.width
an_arrow.o_y = 0.5*self.height

Elliot Garbus

unread,
Dec 1, 2019, 10:10:53 AM12/1/19
to kivy-...@googlegroups.com

That looks good to me….

 

You can access the parent class of a widget by using ‘self.parent’.

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/48c67c43-dfc7-4661-af18-ae0facb33b53%40googlegroups.com.

 

Henrik R.

unread,
Dec 1, 2019, 11:12:07 AM12/1/19
to Kivy users support
How would you use ‘self.parent’ to do this more elegantly?

Elliot Garbus

unread,
Dec 1, 2019, 6:39:12 PM12/1/19
to kivy-...@googlegroups.com

In arrow.py:

  1. Add size to the bind statement
a.  size=self.set_pos,  # added to set position to center
  1. create  set_pos to update the drawing
 
def set_pos(self, *args):              # on size change update drawing
   
self.o_x, self.o_y = self.parent.center

 

I took a guess and got lucky that o_x and o_y were the origin used in the drawing.

 

 

------------------------------------------------------------------------


   
def __init__(self,*args, **kwargs):

        Widget.
__init__(self,*args,**kwargs)
#        KVector.__init__(self,*args,**kwargs)
       
KVector.__init__(self)

       
with self.canvas:
           
self.icolor = Color(rgba=self.main_color)
           
self.head = Mesh(mode='triangle_fan',indices=[0,1,2])
           
self.shaft = Line(width=self.shaft_width)
           
self.fletching = Ellipse()

           
self.ocolor = Color(rgba=self.outline_color)
           
self.head_outline = Line(width=self.outline_width)
           
self.shaft_outline_left = Line(width=self.outline_width)
           
self.shaft_outline_right = Line(width=self.outline_width)
           
self.fletching_outline = Line()

       
self.bind(
                  
o_x=self.update_dims,
                 
o_y=self.update_dims,
                 
to_x=self.update_dims,
                 
to_y=self.update_dims,
                 
head_size=self.update_dims,
                 
head_angle=self.update_dims,
                  
shaft_width=self.update_shaft_width,
                 
outline_color=self.update_outline_color,
                 
main_color=self.update_color,
                 
outline_width=self.update_outline_width,
                 
distortions=self.update_dims,
                  
size=self.set_pos,  # ****added to set position to center****
                 
)
       
self.update_dims()
       
self.update_shaft_width()
       
self.update_color()

   
def set_pos(self, *args):              # ****on size change update drawing*****
        self.o_x, self.o_y = self.parent.center

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/3f5084a9-befd-432e-8db3-adfb2271cc71%40googlegroups.com.

 

Elliot Garbus

unread,
Dec 1, 2019, 7:16:19 PM12/1/19
to kivy-...@googlegroups.com

Interesting…this works as well:

def set_pos(self, *args):              # on size change update drawing
   
self.o_x, self.o_y = self.center
 

 

From: Elliot Garbus
Sent: Sunday, December 1, 2019 4:40 PM
To: kivy-...@googlegroups.com
Subject: RE: [kivy-users] Re: In kv-string: 'pos: tt.pos'- NameError:name'tt'isnotdefined

 

In arrow.py:

1)      Add size to the bind statement

a.  size=self.set_pos,  # added to set position to center

2)      create  set_pos to update the drawing

Reply all
Reply to author
Forward
0 new messages