2011-09-12 6 views
1

特定の既存のCocoaウィンドウを閉じて、独自のハンドラを追加しようとします(本当に閉じるか、または別の何かをする可能性があります)。オーバーライドウィンドウのクローズ動作

私はこれを行うにあたって、さまざまな解決策を思いついていました。 1つは、

実行時に既存のCocoaウィンドウのウィンドウの閉じるボタンを独自の閉じるウィジェットで置き換えて、独自のコードを追加できるようにしたいと考えています。

import objc 
_NSThemeCloseWidget = objc.lookUpClass("_NSThemeCloseWidget") 

def find_close_widget(window): 
    contentView = window.contentView() 
    grayFrame = contentView.superview() 
    for i in range(len(grayFrame.subviews())): 
     v = grayFrame.subviews()[i] 
     if isinstance(v, _NSThemeCloseWidget): 
      return v, i, grayFrame 

class CustomCloseWidget(_NSThemeCloseWidget): 
    pass 

def replace_close_widget(window, clazz=CustomCloseWidget): 
    v, i, grayFrame = find_close_widget(window) 
    newv = clazz.alloc().init() 
    grayFrame.subviews()[i] = newv 

しかし、これはかなり右いないようです:

は今のところ、私はこのコードを持っています。 (クラッシュします)

+1

NSWindowDelegateプロトコルを使用しない理由はありますか?閉じる動作を変更する場合は、windowShouldCloseまたはwindowWillCloseを使用します。 –

+0

私は同意します - それが代表者のためのものです。だからあなたは意思決定パイプラインにフックすることができます... – bryanmac

+0

私はそれを必要としている時点で新しいデリゲートを設定することはできません。しかし、私はwindowShouldClose程度にフックすることができます。しかし、どうやって実際にその行動を変えることができますか? (場合によってはそれを閉じないようにするだけでなく、時には何らかのアクションを実行する必要があります) – Albert

答えて

0

私はもう別のルートに行きます。これは部分的にChrome関連ですが、他の場所でも簡単に採用できます。私は、他のクリーンアップを避けるためにできるだけ早くウィンドウを閉じるためのいくつかのアクションをキャッチしたり、ウィンドウが奇妙な状態に陥ったりすることを望んでいました。

def check_close_callback(obj): 
    # check ... 
    return True # or: 
    return False 

import objc 
BrowserWindowController = objc.lookUpClass("BrowserWindowController") 

# copied from objc.signature to avoid warning 
def my_signature(signature, **kw): 
    from objc._objc import selector 
    kw['signature'] = signature 
    def makeSignature(func): 
     return selector(func, **kw) 
    return makeSignature 

windowWillCloseSig = "[email protected]:[email protected]" # BrowserWindowController.windowWillClose_.signature 
commandDispatchSig = "[email protected]:[email protected]" 
class BrowserWindowController(objc.Category(BrowserWindowController)): 
    @my_signature(windowWillCloseSig) 
    def myWindowShouldClose_(self, sender): 
     print "myWindowShouldClose", self, sender 
     if not check_close_callback(self): return objc.NO 
     return self.myWindowShouldClose_(sender) # this is no recursion when we exchanged the methods 

    @my_signature(commandDispatchSig) 
    def myCommandDispatch_(self, cmd): 
     try: print "myCommandDispatch_", self, cmd 
     except: pass # like <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\u2026' in position 37: ordinal not in range(128) 
     if cmd.tag() == 34015: # IDC_CLOSE_TAB 
      if not check_close_callback(self): return   
     self.myCommandDispatch_(cmd) 

from ctypes import * 
capi = pythonapi 

# id objc_getClass(const char *name) 
capi.objc_getClass.restype = c_void_p 
capi.objc_getClass.argtypes = [c_char_p] 

# SEL sel_registerName(const char *str) 
capi.sel_registerName.restype = c_void_p 
capi.sel_registerName.argtypes = [c_char_p] 

def capi_get_selector(name): 
    return c_void_p(capi.sel_registerName(name)) 

# Method class_getInstanceMethod(Class aClass, SEL aSelector) 
# Will also search superclass for implementations. 
capi.class_getInstanceMethod.restype = c_void_p 
capi.class_getInstanceMethod.argtypes = [c_void_p, c_void_p] 

# void method_exchangeImplementations(Method m1, Method m2) 
capi.method_exchangeImplementations.restype = None 
capi.method_exchangeImplementations.argtypes = [c_void_p, c_void_p] 

def method_exchange(className, origSelName, newSelName): 
    clazz = capi.objc_getClass(className) 
    origMethod = capi.class_getInstanceMethod(clazz, capi_get_selector(origSelName)) 
    newMethod = capi.class_getInstanceMethod(clazz, capi_get_selector(newSelName)) 
    capi.method_exchangeImplementations(origMethod, newMethod) 

def hook_into_windowShouldClose(): 
    method_exchange("BrowserWindowController", "windowShouldClose:", "myWindowShouldClose:") 

def hook_into_commandDispatch(): 
    method_exchange("BrowserWindowController", "commandDispatch:", "myCommandDispatch:") 

このコードはherehereからです。

1

閉じるウィジェットは、ウィンドウを閉じる唯一の方法ではありません。ウィジェットを取得するための公開APIがあるので、フレームビューのサブビューを巡る必要はありませんが、それは間違ったパスです。

正しい方法は、オブジェクトをウィンドウのデリゲートにすることです。interfere with the window's closureです。理想的には、ウインドウを作成してから注文するまでの間に、ウインドウのデリゲートを設定する必要があります。

+0

私のケースでは、ウィンドウの代理人を変更することはできません。私はそれが何を変えるかについてのコントロールを持たない他のコードがあることを保証することはできません。 – Albert

関連する問題