• JonB

    I think where this is usually used is when the module has a long name, and that you use a lot within your code, so for readability you use an abbreviation

    import numpy as np
    from matplotlib import pyplot as plt

    posted in Pythonista read more
  • JonB

    Rereading your question ... I guess maybe I read more into your question than you originally wanted...

    class GameLoop(Scene):
        def __init__(self):
             self.go_list = [];
        def add_gameobject(self, go):
             self.go_list.append(go)
             self.add_child(go)
        def remove_gameobject(self,go):
             #... remove from go_list and remove_child
        def setup(self):
             for i in range(40):
                self.add_gameobject( GameObject() )
        def update(self):
             for go in self.go_list:
                 go.update()
    

    Doesn't something like this accomplish what you are after, without a lot of muss & fuss?

    Now, I think you could accomplish various types of physics behaviours using decorators -- for instance, you could have a projectile decorator which simply takes current velocity and adjusts position. Then, you could have a affected_by_gravity decorator which adjusts velocity in each time step by g*dt in y. A bouncy decorator that looks for collisions and then inverts velocity, etc. Each decorator would have the form

    def affected_by_gravity(cls):
        originalupdate = cls.__dict__.get('update)
        def update(self, dt):
              self.velocity[1] = self.parent.gravity * dt
              if callable(originalupdate):
                  originalupdate(self,dt)
        setattr(cls,'update',update)
        return cls
    

    then you could have

    @affected_by_gravity
    @projectile_physics
    @bouncy
    class Ball(ShapeNode):
        def __init__(self):
             pass
    

    posted in Pythonista read more
  • JonB

    I was saying you simply have something like this

    def update(self):
       for node in self.children:
            if hasattr(node, 'update') and callable(node.update):
                node.update()
    

    You could actually do something like this via a class decorator

    def auto_update_children(cls):
       originalupdate  = cls.__dict__.get('update')  # Possibly None.
       def update(self):
          for node in self.children:
             if hasattr(node, 'update') and callable(node.update):
                node.update()
          #finally, if the class already had an update... call it. so you can define classes with an update method, it will update children, then call your specific code..
          if callable(originalupdate):
             originalupdate(self)
       setattr(cls, 'update',update)
       return cls
    
    @auto_update_children
    class Game(Scene):
       def update(self):
           #do any specific game logic... children have all been update'd when this gets called
           check_collsitions()
           etc()
    #...
    
    

    Likewise, you could then use said decorator on different "layers" that manage their own game state and children.

    The other approach is that you don't have to literally call update on every object. You probably have classes of objects -- player, enemy, projectiles, powerups, whatever.
    Keep those as lists inside your scene, and then loop through the lists.
    The advantage of that is you can do things like check collisions of every enemy with player, or projectiles with player.

    def add_enemy(self):
               enemy = EnemyNode(....)           # ... some code to add a random enemy... 
               self.enemies.append(enemy)
               self.add_child(enemy)
    def add_projectile(self, initial_position, initial_velocity):
               projectile = Projectile(initial_position, initial_velocity)
               self.projectiles.append(projectile)
               self.add_child(projectile)
    def update(self):
       for enemy in self.enemies:
             enemy.update()
       player.update()
       for projectile in self.projectiles:
             projectile.update()
             if projectile.bbox.intersects(player.bbox):
                #kill player,  ...
    

    So, you don't have to have a separate named object for everything in your scene, you have categories of objects that get added so that they can start being updated.

    posted in Pythonista read more
  • JonB

    There is nothing wrong with that approach. That is how many of the examples are written -- you keep a list of objects, and each run an update function.

    Depending on what you are doing, you could also make use of Actions.

    posted in Pythonista read more
  • JonB

    Ahh, I see you are trying to make sure the callback gets run on the main thread... The above code doesn't do that--it just sets the callback on the main thread.

    If things within the callback must be done on the main thread, you need such code inside the callback handler itself. For instance, you could use a decorator on the handler.

    Another common problem is calling ObjCInstance on some non object. @shinyformica had a thread a while back about a wierd seg fault, that we were able to fix I think by prechecking some condition in a loop, and only proceeding once things were correct. The details evade me.

    posted in Pythonista read more
  • JonB

    on_main_thread(itemProvider.loadObjectOfClass_completionHandler_(ObjCClass('UIImage'),handler_block))
    

    Try instead:

    on_main_thread(itemProvider.loadObjectOfClass_completionHandler_)(ObjCClass('UIImage'),handler_block)
    

    In other words, on_main_thread returns a method, which you call using arguments. The way you had it, you were calling a function, and passing it's output, which was not a function, to on_main_thread.

    on_main_thread(original_func)(args)
    

    I would also question whether you need to be calling on_main_thread at all here.

    posted in Pythonista read more
  • JonB

    The reason is that you have defined lb inside a function -- main(), not as a global.

    Either use globals, or use attributes directly accessible from your UI elements. Sender in your code will be the button--so sender.superview leads to your main view in this case, and sender.superview.lb.text='test' etc let's you store info in your own custom attribute names. Just be sure they are unique names that are not already attributes of ui.View.

    When you are trying into to access subviews you can do

    sender.superview['label1'].text='test'
    

    if you originally define view names when you add them to your main view.
    Or, you can add attributes of the button to point to it's target label,

    B=ui.Button(...)
    L=ui.Label()
    
    B.target_label=L
    ...
    #Then in your callback
    sender.target_label.text='...'
    

    posted in Pythonista read more
  • JonB

    When you post code, can you add in the three backticks before and after, like this

    ```
    import something
    def main():
       ...
    
    ```
    

    That way, we can see the proper indenting.

    posted in Pythonista read more
  • JonB

    https://forum.omz-software.com/topic/4528/help-me-understand-pythonista-s-threads

    Basically to iOS, the only things non the main thread are user interface code and callbacks. Everything else is in the background queue. By default, there are just these two threads, and in_background gets queued onto the background thread -- meaning your main code must exit before anything from in_background will run. That leads to strange scenarios -- you just never have code in the main script that blocks waiting for something from a in_background function, since the in_background code won't execute until the main script is done. Also, never have blocking dependencies between two in_background items. It is often better to create your own thread if you plan on having code wait in the background for other conditions to be true.

    Callbacks -- button presses, etc are called on the main thread.

    posted in Pythonista read more
  • JonB

    Is that a full on crash or a normal traceback? If a normal traceback, print the full traceback so we can get the offending line.

    If it is a crash, install @dgelessus 's faulthandler.
    https://github.com/dgelessus/pythonista_startup

    posted in Pythonista read more

Internal error.

Oops! Looks like something went wrong!