Objective c 覆盖窗口关闭行为

Objective c 覆盖窗口关闭行为,objective-c,cocoa,pyobjc,Objective C,Cocoa,Pyobjc,我想捕获所有试图关闭某个特定的现有Cocoa窗口并添加一些自己的处理程序(这可能真的会关闭它或做一些不同的事情)的尝试 我想到了不同的解决方案。一个是: 我想在运行时用自己的close小部件替换现有Cocoa窗口的WindowClose按钮,在这里我可以添加一些自己的代码 现在,我有以下代码: import objc _NSThemeCloseWidget = objc.lookUpClass("_NSThemeCloseWidget") def find_close_widget(windo

我想捕获所有试图关闭某个特定的现有Cocoa窗口并添加一些自己的处理程序(这可能真的会关闭它或做一些不同的事情)的尝试

我想到了不同的解决方案。一个是:

我想在运行时用自己的close小部件替换现有Cocoa窗口的WindowClose按钮,在这里我可以添加一些自己的代码

现在,我有以下代码:

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

然而,这似乎并不完全正确。(它崩溃了。)

关闭窗口小部件不是关闭窗口的唯一方法。有一个公共API来获取小部件,因此您不需要遍历框架视图的子视图,但这是错误的路径


正确的方法是使一个对象成为窗口的委托,然后在那里。理想情况下,您应该在创建窗口和排序窗口之间设置窗口的委托。

关闭窗口小部件不是关闭窗口的唯一方法。有一个公共API来获取小部件,因此您不需要遍历框架视图的子视图,但这是错误的路径


正确的方法是使一个对象成为窗口的委托,然后在那里。理想情况下,您应该在创建窗口和排序窗口之间设置窗口的委托。

我现在要走另一条路线。这部分与铬有关,但很容易在其他地方采用。我想捕捉一些尽早关闭窗口的操作,以避免任何其他清理操作,从而导致窗口处于奇怪的状态

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 = "c12@0:4@8" # BrowserWindowController.windowWillClose_.signature
commandDispatchSig = "v12@0:4@8"
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:")
def检查关闭回调(obj):
#检查。。。
返回真值#或:
返回错误
导入objc
BrowserWindowController=objc.lookUpClass(“BrowserWindowController”)
#从objc.signature复制以避免警告
def我的签名(签名,**kw):
从objc.\u objc导入选择器
kw['signature']=签名
def生成签名(func):
返回选择器(func,**千瓦)
返回签名
windowWillCloseSig=”c12@0:4@8“#浏览器WindowController.windowWillClose#签名
commandDispatchSig=”v12@0:4@8"
类BrowserWindowController(对象类别(BrowserWindowController)):
@我的签名(windowWillCloseSig)
def MyWindowsShouldClose(自身、发件人):
打印“myWindowShouldClose”、自我、发件人
如果没有,请检查\u关闭\u回调(self):返回objc.NO
返回self.myWindowsShouldClose(发送方)#当我们交换方法时,这不是递归
@我的签名(commandDispatchSig)
def myCommandDispatch(self,cmd):
尝试:打印“myCommandDispatch”,self,cmd
除了:pass#like:'ascii'编解码器无法对位置37处的字符u'\u2026'进行编码:序号不在范围内(128)
如果cmd.tag()==34015:#IDC_关闭_选项卡
如果未选中\u关闭\u回调(self):返回
self.myCommandDispatch(cmd)
从ctypes导入*
capi=蟒蛇
#id objc_getClass(常量字符*名称)
capi.objc_getClass.restype=c_void_p
capi.objc_getClass.argtypes=[c_char\u p]
#选择注册表名(const char*str)
capi.sel\u registerName.restype=c\u void\u p
capi.sel\u registerName.argtypes=[c\u char\u p]
def capi_get_选择器(名称):
返回c_void_p(capi.sel_注册表名(名称))
#方法类\u getInstanceMethod(类aClass,选择选择器)
#还将在超类中搜索实现。
capi.class\u getInstanceMethod.restype=c\u void\u p
capi.class\u getInstanceMethod.argtypes=[c\u void\u p,c\u void\u p]
#无效方法(方法m1、方法m2)
capi.method_exchangeimplements.restype=None
capi.method_exchangeimplements.argtypes=[c_void_p,c_void_p]
def方法_交换(类名、origSelName、newSelName):
clazz=capi.objc_getClass(类名)
origMethod=capi.class\u getInstanceMethod(clazz,capi\u get\u选择器(origSelName))
newMethod=capi.class\u getInstanceMethod(clazz,capi\u get\u选择器(newSelName))
capi.方法交换实施(原始方法、新方法)
def将_挂接到_windowShouldClose():
方法交换(“BrowserWindowController”、“windowShouldClose:”、“myWindowShouldClose:”)
def将_挂接到_命令分派()中:
方法交换(“BrowserWindowController”、“commandDispatch:”、“myCommandDispatch:”)

此代码来自和。

我现在要走另一条路线。这部分与铬有关,但很容易在其他地方采用。我想捕捉一些尽早关闭窗口的操作,以避免任何其他清理操作,从而导致窗口处于奇怪的状态

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 = "c12@0:4@8" # BrowserWindowController.windowWillClose_.signature
commandDispatchSig = "v12@0:4@8"
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:")
def检查关闭回调(obj):
#检查。。。
返回真值#或:
返回错误
导入objc
BrowserWindowController=objc.lookUpClass(“BrowserWindowController”)
#从objc.signature复制以避免警告
def我的签名(签名,**kw):
从objc.\u objc导入选择器
kw['signature']=签名
def生成签名(func):
返回选择器(func,**千瓦)
返回签名
windowWillCloseSig=”c12@0:4@8“#浏览器WindowController.windowWillClose#签名
commandDispatchSig=”v12@0:4@8"
类BrowserWindowController(对象类别(BrowserWindowController)):
@我的签名(windowWillCloseSig)
def MyWindowsShouldClose(自身、发件人):
打印“myWindowShouldClose”、自我、发件人
如果没有,请检查\u关闭\u回调(self):返回objc.NO
返回self.myWindowsShouldClose(发送方)#当我们交换方法时,这不是递归
@我的签名(commandDispatchSig)
def myCommandDispatch(self,cmd):
尝试:打印“myCommandDispatch”,self,cmd
除了:pass#like:'ascii'编解码器无法对位置37处的字符u'\u2026'进行编码:序号不在范围内(128)
如果cmd.tag()==34015:#IDC_关闭_选项卡
如果未选中\u关闭\u回调(self):返回
self.myCommandDispatch(cmd)
从ctypes导入*
capi=蟒蛇
#id objc_getClass(常量字符*名称)
capi.objc_getClass.restype=c_void_p
capi.objc_getClass.argtypes=[c_char\u p]
#选择注册表名(const char*str)
capi.sel_registerName.res