ronaldoussoren / pyobjc

The Python <-> Objective-C Bridge with bindings for macOS frameworks
https://pyobjc.readthedocs.io
553 stars 47 forks source link

Unreco #601

Closed demonguy closed 5 months ago

demonguy commented 5 months ago

Describe the bug I tried to write a program to listen "Screen Lock" event of my mac. So far it can be triggered, but I failed to write corresponding handler and get error "unrecognized selector sent to instance 0x1496c8d90" when I lock screen. Did I make anything wrong with my code?

from Foundation import NSDistributedNotificationCenter, NSObject
import objc

class ScreenLockerListener(NSObject):
    def screen_locked_(self, *args):
        print("Screen locked")

    def screen_unlocked_(self, *args):
        print("Screen unlocked")

sll = ScreenLockerListener.new()

# Subscribe to lock and unlock events
notification_center = NSDistributedNotificationCenter.defaultCenter()
notification_center.addObserver_selector_name_object_(
    sll, 
    "screen_locked:",
    "com.apple.screenIsLocked",
    None
)
notification_center.addObserver_selector_name_object_(
    None, 
    screen_unlocked,
    "com.apple.screenIsUnlocked",
    None
)

# Run the main loop to keep the script running
from PyObjCTools import AppHelper
AppHelper.runConsoleEventLoop(installInterrupt=True)
cy@CYs-MacBook-Pro /tmp % *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ScreenLockerListener screen_locked:]: unrecognized selector sent to instance 0x1496c8d90'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000018474accc __exceptionPreprocess + 176
    1   libobjc.A.dylib                     0x0000000184232788 objc_exception_throw + 60
    2   CoreFoundation                      0x00000001847fd02c -[NSObject(NSObject) __retain_OA] + 0
    3   CoreFoundation                      0x00000001846b4cdc ___forwarding___ + 1580
    4   CoreFoundation                      0x00000001846b45f0 _CF_forwarding_prep_0 + 96
    5   CoreFoundation                      0x00000001846cab1c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 148
    6   CoreFoundation                      0x000000018475edb8 ___CFXRegistrationPost_block_invoke + 88
    7   libdispatch.dylib                   0x0000000184444750 _dispatch_call_block_and_release + 32
    8   libdispatch.dylib                   0x00000001844463e8 _dispatch_client_callout + 20
    9   libdispatch.dylib                   0x0000000184454bb8 _dispatch_main_queue_drain + 988
    10  libdispatch.dylib                   0x00000001844547cc _dispatch_main_queue_callback_4CF + 44
    11  CoreFoundation                      0x00000001847174ac __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
    12  CoreFoundation                      0x00000001846d4c30 __CFRunLoopRun + 1996
    13  CoreFoundation                      0x00000001846d3e0c CFRunLoopRunSpecific + 608
    14  Foundation                          0x0000000185807028 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212
    15  libffi.dylib                        0x00000001955ac050 ffi_call_SYSV + 80
    16  libffi.dylib                        0x00000001955b4ae0 ffi_call_int + 1212
    17  _objc.cpython-311-darwin.so         0x0000000104b61620 PyObjCFFI_Caller_SimpleSEL + 336
    18  _objc.cpython-311-darwin.so         0x0000000104b83ed4 objcsel_vectorcall_simple + 516
    19  Python                              0x0000000105caa990 _PyEval_EvalFrameDefault + 53096
    20  Python                              0x0000000105c9c774 PyEval_EvalCode + 276
    21  Python                              0x0000000105d1a7d4 pyrun_file + 240
    22  Python                              0x0000000105d19fa4 _PyRun_SimpleFileObject + 288
    23  Python                              0x0000000105d19518 _PyRun_AnyFileObject + 232
    24  Python                              0x0000000105d43288 pymain_run_file_obj + 220
    25  Python                              0x0000000105d42a6c pymain_run_file + 72
    26  Python                              0x0000000105d42280 Py_RunMain + 1828
    27  Python                              0x0000000105d43404 pymain_main + 52
    28  Python                              0x0000000105d43c3c Py_BytesMain + 40
    29  dyld                                0x000000018426e0e0 start + 2360
)
libc++abi: terminating due to uncaught exception of type NSException

[1]  + abort      python3 test.py
ronaldoussoren commented 5 months ago

You need to follow the Cocoa naming convention for methods, e.g.:

class ScreenLockerListener(NSObject):
    def screenLocked_(self, sender):
        print("Screen locked")

    def screenUnlocked_(self, sender):
        print("Screen unlocked")

...

notification_center.addObserver_selector_name_object_(
    sll, 
    "screenLocked:",
    "com.apple.screenIsLocked",
    None
)
notification_center.addObserver_selector_name_object_(
    None, 
    screenUnlocked:,
    "com.apple.screenIsUnlocked",
    None
)

PyObjC's default translation from Python method names to ObjC selectors changes all underscores to colons, and requires that there is an argument for every colon. *args is supported, but makes it easier to hide code problems.