views:

251

answers:

2

I was trying to hack up a tool to visualize shaders for my game and I figured I would try using python and cocoa. I have ran into a brick wall of sorts though. Maybe its my somewhat poor understand of objective c but I can not seem to get this code for a view I was trying to write working:

from objc import YES, NO, IBAction, IBOutlet
from Foundation import *
from AppKit import *
import gv

class SceneView(NSOpenGLView):
    def __init__(self):
        NSOpenGLView.__init__(self)
        self.renderer = None

    def doinit(self):
        self.renderer = gv.CoreRenderer()


    def initWithFrame_(self, frame):
        self = super(SceneView, self).initWithFrame_(frame)

        if self:
            self.doinit()
            print self.__dict__

        return self

    def drawRect_(self, rect):
        clearColor = [0.0,0.0,0.0,0.0]
        print self.__dict__
        self.renderer.clear(CF_Target|CF_ZBuffer,clearColor)

It outputs this when executed:

{'renderer': <gv.CoreRenderer; proxy of <Swig Object of type 'GV::CoreRenderer *' at 0x202c7d0> >}
{}
2009-04-03 19:13:30.941 geom-view-edit[50154:10b] An exception has occured:
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjCTools/AppHelper.py", line 235, in runEventLoop
  File "/mnt/gilead/amcharg/projects/geom-view-edit/build/Debug/geom-view-edit.app/Contents/Resources/SceneView.py", line 37, in drawRect_
    self.renderer.clear(CF_Target|CF_ZBuffer,clearColor)
AttributeError: 'SceneView' object has no attribute 'renderer'

It seems to be losing my renderer variable which is not that surprising considering how funky the initWithFrame_ code is but this was something xcode seemed to write which I suppose makes sense since objective C has the init separate from alloc idiom. It is still strange seeing it python however.

Is there anyways to salvage this or should I take it out behind the code shed shoot it and use QT or wxPython? I considered using objective-c but I want to test out these nifty swig bindings I just compiled =)

+2  A: 

Depending on what's happening elsewhere in your app, your instance might actually be getting copied.

In this case, implement the copyWithZone method to ensure that the new copy gets the renderer as well. (Caveat, while I am a Python developer, and an Objective-C cocoa developer, I haven't used PyObjC myself, so I can't say for certain if you should be implementing copyWithZone or __copy__).

In fact, shoving a copyWithZone method into the class with a print will allow you to tell if the method is being called and if that's the reason renderer appears to have vanished.


Edit: Base on your feedback, I've pasted your code into a blank xcode python project (just substituting something else for gv.CoreRenderer, since I don't have that), and it works fine with some minor modifications. How are you instantiating your SceneView?

In my case I:

  • Created a blank xcode project using the Cocoa-Python template
  • Created a new file called SceneView.py. I pasted in your code.
  • Opened the MainMenu.xib file, and dragged an NSOpenGLView box onto the window.
  • With the NSOpenGLView box selected, I went to the attributes inspector and changed the class of the box to SceneView
  • Back in xcode, I added import SceneView in the imports in main.py so that the class would be available when the xib file is loaded
  • I implemented an awakeFromNib method in SceneView.py to handle setting up self.renderer. Note that __init__, and initWithFrame are not called for nib objects during your program execution... they are considered "serialized" into the nib file, and therefore already instantiated. I'm glossing over some details, but this is why awakeFromNib exists.
  • Everything worked on run. The __dict__ had appropriate values in the drawRect_ call, and such.

Here's the awakeFromNib function:

def awakeFromNib(self):
    print "Awake from nib"
    self.renderer = gv.CoreRenderer()

So, I'm guessing there are just some crossed wires somewhere in how your object is being instantiated and/or added to the view. Are you using Interface Builder for your object, or are you manually creating it and adding it to a view later? I'm curious to see that you are getting loggin outputs from initWithFrame, which is why I'm asking how you are creating the SceneView.

Jarret Hardie
These don't seem to help :( Note that I am adding the renderer field when I construct it and then it magically disappears. Methods seem to stay for some reason though.
fuzzy-waffle
The awakeFromNib code did the trick! Thanks!
fuzzy-waffle
+2  A: 

Even if they weren't serialized, the __init__-constructor of python isn't supported by the ObjectiveC-bridge. So one needs to overload e.g. initWithFrame: for self-created Views.

deets
Thanks deets... appreciate you plugging the holes in my knowledge of the bridge!
Jarret Hardie
Don't forget to re-read my answer, as I just now found out that the markdown-formatting killed my double underscores :)
deets
Initially I ran into the problem without the constructor but I will remove it and try some more.
fuzzy-waffle
the point is that you don't have the constructor available - you need to find hooks that run before draw_. Jarret showed you one, as did I.
deets