-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] Signal System #268
Comments
@jevexendo I would like your thoughts on this when you get a chance. |
As soon as I wrote that I realized this was for Amulet Core, not Amulet Editor. I'll take a look at this when I get home from work later today. |
The core library can be used independently of the GUI. |
I think an event system is a good approach, but I'm not sure if I'm on board using PySide as an event backend since it seems a little bloated to install a whole UI package in what's supposed to be a non-UI library just for an event system. Instead it looks like this package accomplishes the same goal and we can still keep our fallback implementation just in case: https://pypi.org/project/Events/ |
I agree that if you aren't already using PySide6 it is unnecessary bloat but the system I wrote isn't dependent on PySide6. |
Oh yep I stand corrected, I see now that the fallback is just mimicking the PySide event functions, I initially didn't catch that in my first look through and was seeing the imports in the try/catch and thought that was the default behavior. I think this is a really good way to approach it |
One concern I have is that PySide signals and slots only work in QObjects when the Qt main application thread is running. With that being the case, I think it would be better if we required the user to explicitly define which version they would like to use. It would probably help avoid confusion since a custom signal/slot implementation isn't going to be a drop-in replacement for Qt's system. |
It would be the job of the application using the library to request the Signal provider that is used or provide their own. |
Another concern I have relates to object inheritance. If the user says they want to use PySide6 Signals and Slots, all Amulet classes that emit Signals would need to inherit from QObject. I'm not particularly sure how one dynamically modifies class inheritance, but it doesn't sound like a particularly good idea. Also, since QObject is a metaclass, any classes in Amulet that inherit from an abstract base class cannot also directly inherit from QObject or you'll get an error like:
I did manage to find a workaround for this issue on Stack Overflow, but I think that allowing the dynamically swapping between simple callbacks and signals/slots may be more annoying to implement than it's worth. from abc import ABC, ABCMeta
from PySide6.QtCore import QObject
from PySide6.QtWidgets import QWidget
QObjectMeta = type(QObject)
QWidgetMeta = type(QWidget)
class _ABCQObjectMeta(QObjectMeta, ABCMeta):...
class _ABCQWidgetMeta(QObjectMeta, ABCMeta):...
class ABCQObject(QObject, ABC, metaclass=_ABCQObjectMeta):...
class ABCQWidget(QWidget, ABC, metaclass=_ABCQWidgetMeta):... |
Now admittedly, I must just be misunderstanding something about your sample code since the PySide6 signal instance appears to be working fine despite being used within an abstract base class that doesn't inherit from QObject. Especially since it clearly thinks there's a QObject since this code exists: if not isinstance(obj, QObject):
raise RuntimeError What am I missing here? |
The PySide6 SignalInstance constructor needs a QObject to exist but the class we end up setting it on doesn't need to be an instance of QObject. Here is the important part def get_pyside6_signal_instance_constructor() -> SignalInstanceConstructor:
try:
from PySide6.QtCore import QObject, Signal as PySide6_Signal, SignalInstance as PySide6_SignalInstance
except ImportError as e:
raise ImportError("Could not import PySide6") from e
def pyside6_signal_instance_constructor(
*,
types: tuple[type, ...],
name: Optional[str],
arguments: Optional[str],
signal: Signal,
instance: Any,
owner: Any
) -> PySide6_SignalInstance:
if isinstance(instance, QObject):
return PySide6_Signal(*types, name=name, arguments=arguments).__get__(instance, QObject)
else:
try:
obj = instance._qobject
except AttributeError:
obj = instance._qobject = QObject()
if not isinstance(obj, QObject):
raise RuntimeError
return PySide6_Signal(*types, name=name, arguments=arguments).__get__(obj, QObject)
return pyside6_signal_instance_constructor I would suggest looking into the descriptor protocol if you don't know what it is. It is the mechanic how properties work.
|
This has an API based on signals from PySide #268
There are aspects of the library where it would be useful to get a push notification when something happens.
For example when using the library with a 3D renderer it would be useful to know when a chunk changed.
Currently we a system storing modification time and a thread that regularly queries the chunks but that is inefficient.
I would like to be able to directly use the PySide6 signal system and have a fallback when it is not installed.
Here is my current prototype which allows registering a signal back-end to enable hooking into other compatible signal systems.
Edit: cache the SignalInstance on the instance using the Signal as a lookup. This was previously cached on the Signal but there is one Signal instance for all instances of the class which meant they all shared the same SignalInstance.
Switched storage variables to more obscure names.
The text was updated successfully, but these errors were encountered: