Question

I'm porting a little library of Objective-C/Cocoa into Python/PyObjC. And I'm confusing about the way to implement.

Original code starts like below:

@implementation HUDWindow
- (id)initWithContentRect:(NSRect)contentRect 
                styleMask:(unsigned int)styleMask 
                  backing:(NSBackingStoreType)bufferingType 
                    defer:(BOOL)flag 
{
    if (self = [super initWithContentRect:contentRect 
                                styleMask:NSBorderlessWindowMask 
                                  backing:bufferingType 
                                    defer:flag]) {
        // initialize code here
             :
             :
             :
             :
             :
             :
        return self;
    }
}

And in header file says that HUDWindow class inherits NSPanel. As far as I read the code, initialize is progress like this:

  1. HUDWindow's initContentRect overrides NSPanel's initContentRect.
  2. Inside of HUDWindow's initContentRect, NSPanel's initContentRect is called and get the result as "self".

First, I write python code like this. It's a translation of word-by-word:

class HUDWindow(NSPanel):
    def initWithContentRect_styleMask_backing_defer_(self, rect, style, buf, flag):
        self = NSPanel.initWithContentRect_styleMask_backing_defer_(
            rect, NSBorderlessWindowMask, buf, flag)
        # initialize code here
             :
             :
             :
             :
             :
             :
        return self

It didn't work because self is only a instance of NSPanel. I can't call any addictional functions I've ported from original obj-c code.

So I changed my code like this:

class HUDWindow(NSPanel):
    def initWithContentRect_styleMask_backing_defer_(self, rect, style, buf, flag):
        self.window = NSPanel.initWithContentRect_styleMask_backing_defer_(
            rect, NSBorderlessWindowMask, buf, flag)
        # initialize code here
             :
             :
             :
             :
             :
             :
        return self.window

It worked a little better. At least, I could call other functions in this initialize function. But outside of initialize function, I can't call other functions ported from original code.

So I tried like this:

class HUDWindow(NSPanel):
    def initWithContentRect_styleMask_backing_defer_(self, rect, style, buf, flag):
        self.window = NSPanel.initWithContentRect_styleMask_backing_defer_(
            rect, NSBorderlessWindowMask, buf, flag)
        # initialize code here
             :
             :
             :
             :
             :
             :
         self.window.setTitle_ = self.setTitle_
         return self.window

It didn't work. I can't overwrite or add functions after making instance.

Now I'm feeling that I'm doing totally wrong. Is there any common way to inherit a class like NSPanel or NPWindow?

Was it helpful?

Solution

Your first solution is on the right path. In the second, you'd need to either use NSProxy or write your own explicit delegation, which is a pain (and the third isn't the way to do that—you can do something similar through swizzling, but don't).

However, there are a few problems.

First, this code:

self = NSPanel.initWithContentRect_styleMask_backing_defer_(rect, NSBorderlessWindowMask, buf, flag)

… is equivalent to this ObjC code:

self = [NSPanel initWithContentRect:rect 
                          styleMask:NSBorderlessWindowMask
                            backing:buf
                              defer:flag]

… when what you want is:

self = [super initWithContentRect:rect 
                        styleMask:NSBorderlessWindowMask
                          backing:buf
                            defer:flag]

The PyObjC equivalent of the latter is:

self = super().initWithContentRect_styleMask_backing_defer_(rect, NSBorderlessWindowMask, buf, flag)

Or, in 2.x:

self = super(HUDWindow, self).initWithContentRect_styleMask_backing_defer_(rect, NSBorderlessWindowMask, buf, flag)

In ObjC, super isn't the superclass, but a special magic object which is effectively "self as an instance of the superclass". It's almost exactly like Python 3's super(), or the less-magical Python 2 super, and PyObjC does the extra work to mean you can ignore that "almost".

What you were doing was (trying to) initialize the NSPanel class object (or maybe the class object of the PyObjC bridge to NSPanel, I'm not sure which), or possibly even trying to initialize the rect as if it were an NSPanel. Whatever you're doing, it can't possibly do anything to initialize your self instance. (And assigning the result to self doesn't do the same thing in Python as in ObjC. It's still usually idiomatic style in PyObjC, but don't be misled by it; the only effect it has is if you use self again within the initializer method. At any rate, at best that would cover up the problem in ObjC, not fix it…) So, after returning from this method, your self is still the allocated but uninitialized NSPanel instance that it was before you called anything.

This is mostly explained pretty early in the tutorial, in Two Phase Instantiation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top