2011-09-12 4 views
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

지금 다른 경로로갑니다. 이것은 부분적으로 크롬과 관련이 있지만 쉽게 다른 곳에서 채택 될 수 있습니다. 다른 정리 작업을 피하기 위해 최대한 빨리 창을 닫는 몇 가지 작업을 잡아서 창을 이상한 상태로 만들려고했습니다.

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

관련 문제