You cannot invoke regular methods, only signals and slots. That is why it is not working for you. See the QMetaObject documentation for details about it:
Invokes the member (a signal or a slot name) on the object obj. Returns true if the member could be invoked. Returns false if there is no such member or the parameters did not match.
Try this decorator:
...
@QtCore.Slot()
def beep(self):
print('beep')
...
See the following documentation for details as well as this one:
Using QtCore.Slot()
Slots are assigned and overloaded using the decorator QtCore.Slot(). Again, to define a signature just pass the types like the QtCore.Signal() class. Unlike the Signal() class, to overload a function, you don’t pass every variation as tuple or list. Instead, you have to define a new decorator for every different signature. The examples section below will make it clearer.
Another difference is about its keywords. Slot() accepts a name and a result. The result keyword defines the type that will be returned and can be a C or Python type. name behaves the same way as in Signal(). If nothing is passed as name then the new slot will have the same name as the function that is being decorated.