Google Analytics

Showing posts with label Kivy NumericProperty. Show all posts
Showing posts with label Kivy NumericProperty. Show all posts

Thursday, May 8, 2014

Learning Kivy - Part 5


Prev

Table of Contents




  • Learning Kivy - Part 1
  • Learning Kivy - Part 2
  • Learning Kivy - Part 3
  • Learning Kivy - Part 4
  • Learning Kivy - Part 5
  • Learning Kivy - Part 6




  • This is a continuation of Learning Kivy - Part 4. 

    As noted in Part 4 for Pong.py and pong.kv, we've come across an important type in the Player class, of NumericProperty, the instance of which is assigned to player1 class variable.

    With respect to this program, it is an important part of being able to dynamically update the Label's text with the changing score.

    Looking at kivy.org site to get some insight into the NumericProperty, I noticed the following:

    Theses properties implement the Observer pattern. They help you to:
    • Easily manipulate widgets defined in the Kv language
    • Automatically observe any changes and dispatch functions/code accordingly
    • Check and validate values
    • Optimize memory management

    I know from previous experience what an Observer pattern is, but in essence, it allows one part of code to inform other interested parts of code that something has changed - the change is an event, and those parties that have registered their interest through a callback function, are informed of this change.  Here's a more detailed explanation from Wikipedia: Observer Pattern

    It appears that, by setting the class PongGame's attribute player1 to an instance Player, and Player is of type Widget, and Player has an attribute of score which is of type NumericProperty, we are making use of the Observer Pattern via the NumericProperty, which will(from kivy.org) "...automatically observe any changes and dispatch functions/code accordingly".  So that appears to be the magic that let's us change.

    In other words, I'm thinking it means: when we update the 'score' attribute, since it's of type NumericProperty, then it gets updated in the pong.kv class Label text, because Label text is set to "str(root.player1.score)".  Thus the new value is displayed.

    Recommended additional info
    Here's a good question/answer thread on Kivy NumericProperty.  Here's a clip from it.


    [Alexandar Taylor]: Properties hook into kivy's event system to reference each other in automatically updated ways, dispatch events when changed etc. In comparison, a normal python class level property...doesn't. Kivy properties also take care of class uniqueness, whereas class attributes are shared by all class instances which can cause you unexpected problems if they're mutable.

    For the ReferenceListProperty, this creates a property that looks like a list, but whose elements when accessed or changed automatically reference or change the component velocity_x or velocity_y. In comparison, your example of just using a tuple does none of that - if you access velocity[0] you'll probably not even get a number but instead the NumericProperty object you defined it to point at. In contrast, the ReferenceListProperty acesses and returns the velocity_x property.

    Same goes for the NumericProperty. You could create class level python attributes if you wanted (or, more usefullly, standard instance attributes created in __init__), but then you can't bind to their changes etc. with kivy's event system. Learning more about kivy will make it clear how it ties together, you generally always will be thinking in terms of properties affecting each other via the bindings that kivy properties allow.
    The guy that wrote the above, Alexander Taylor, also has a series of youtube casts.  Here's one dealing with this issue.

    Wednesday, May 7, 2014

    Learning Kivy - Part 4


    Prev

    Table of Contents




  • Learning Kivy - Part 1
  • Learning Kivy - Part 2
  • Learning Kivy - Part 3
  • Learning Kivy - Part 4
  • Learning Kivy - Part 5
  • Learning Kivy - Part 6



    So, what did I learn from Part 3?  I learned that I can instantiate a thread, and have that thread block via a sleep, and not impact the Kivy run method.  

    That's good to know.

    And, that's all I need to know, at this point, about creating a thread for a socket outside of the main thread.

    However, that begs the question: how to get the data from the socket thread to the Kivy thread?

    Well, one way is through Python's global values.  I don't like this approach myself, but let's take a look at it anyway.

    For this example, I'm going to want Kivy to change the displayed GUI text, from
    text: "Hello World from inside simple.kv, via Box_a"
    to something more dynamic, meaning something not static - I want to be able to have the text changed that is being displayed to something originating from the thread.

    Kivy has an ObjectProperty class that allows class attributes to be exchanged between Python code and Kivy Language attributes.  Some additional information on Kivy's Properties.

    This requires a new import statement:
    from kivy.properties import ObjectProperty 

    To recap where we are, and where we're needing to go:

    • Have gotten Threads to work
    • Need to find a way to update the Kivy display
    The kivy.org site has a tutorial called Pong.  I had run this, and played around a little bit a while back.  Today it came to mind when I was trying to find a way to update displayed information.  It occurred to me that Pong has the following elements that have to be updated:
    • The paddles.  The displayed position of these has to be updated as you play the game.
    • The ball.  The displayed position of the ball is constantly changing, and as such, it has to be updated.
    • The score.  This was my main focus: it's a text item.  Checking the code, sure enough, it's a Label.  So it's worth digging deeper into this!
    After studying it, I wanted to reduce it to the bare bones, and then have it display the 'score'.  I didn't want to work through all of the code that is playing a game, no ball nor paddles, just the 'score'.  So I edited the .py code and the .kv code to a minimal point, invoked it, and sure enough - it was updating the score!

    Here's the reduced .py file:
    =====Pong.py====

    from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.properties import NumericProperty
    from kivy.clock import Clock


    class Player(Widget):
        score = NumericProperty(0)

    class PongGame(Widget):
        player1 = Player()
        
        def update(self, dt):
            self.player1.score += 1


    class PongApp(App):
        def build(self):
            game = PongGame()
            Clock.schedule_interval(game.update, 1.0/2.0)
            return PongGame()

    def main():
        PongApp().run()

    if __name__ == '__main__':

        main()

    ===============pong.kv===========
    #I've modified this for a test I'm doing on how to dynamically update
    #Label text
    :    
        
        
        Label:
            font_size: 70  
            text: str(root.player1.score)
            

    Saving these to two files in the same directory (that's very important for Kivy), running it, and I've got a numeric display that's counting up at a rate of twice per second.

    That's a demonstration of what I needed (to be able to dynamically update the text of a Label), so now how does it work?

    Here's a breakout of the code elements that are important:
    • class PongApp(App):
      • This extends the App class.  it's the class called within the main() element that starts the program: PongApp().run()
    • def build(self):
      • This is the method that is called by Kivy itself.  You don't see this call, but ultimately it's invoked by your invocation of 'run()'
    • game = PongGame()
      • This creates an instance of PongGame and assigns it to the build method variable game.
    • class PongGame(Widget):
      • This extends the Widget class, and this is important to Kivy, since everything that's displayed is a Widget.
      • It has a class variable: player1, which is of type Player.
      • Note that there is also a <PongGame>: statement in the pong.kv file.  There is a direct association between this class definition of PongGame in the Pong.py file, and the class definition in the pong.kv file.
      • player1 is instantiated as a type Player instance.  In creating this Player instance, note that Player extends Widget, and that it has a class variable of score, which is of type NumericProperty.  This is an important notation, as it will directly affect our ability to dynamically update our Label's text (Label is in the pong.kv file).
    At this point, I'm going to move to Part 5, and go into a little more detail of the NumericProperty object.