Created
August 25, 2022 21:22
-
-
Save SuzanneSoy/3630eaf0cd4158a1d82d9d89fb85469a to your computer and use it in GitHub Desktop.
Reactive model→view function (full of bugs)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if True: | |
from PySide import QtGui | |
from PySide import QtCore | |
import threading | |
from collections import defaultdict | |
# resizing a window too fast seems to allow for the window manager to drop some requests. | |
# for experimentation, we resize a label within a fixed-size window. | |
w = QtGui.QLabel() | |
w.show() | |
w.setGeometry(QtCore.QRect(100,100,400,400)) | |
propagators = { | |
'width': lambda src, dest: | |
dest.__self__.setGeometry(QtCore.QRect(dest.__self__.x(), dest.__self__.y(), src.value, dest.__self__.height())), | |
'height': lambda src, dest: | |
dest.__self__.setGeometry(QtCore.QRect(dest.__self__.x(), dest.__self__.y(), dest.__self__.width(), src.value)) | |
} | |
def propagate(src, dest): | |
propagators[dest.__name__](src, dest) | |
def connect(src, dest): | |
src.register('set', lambda _: propagate(src, dest)) | |
class Model: | |
def __init__(self): | |
self._lock = threading.Lock() | |
self._eventTypeToCallbacks = defaultdict(dict) # key: string i.e. event type, value: {callback} | |
self._eventTypeToCurrent = dict() | |
self._nextId = 0 | |
def notify(self, eventType, info): | |
with self._lock: | |
self._eventTypeToCurrent[eventType] = info | |
# avoid race conditions by caching the list of callbacks, that way if | |
# one of the callbacks modifies the list of callbacks, it won't cause | |
# issues. Also, it prevents deadlocks if one of the callbacks tries to | |
# perform an operation which would try to acquire the lock. | |
cachedCallbacks = [c for c in self._eventTypeToCallbacks[eventType].values()] | |
for c in cachedCallbacks: | |
c(info) | |
def register(self, eventType, callback): | |
with self._lock: | |
callbackId = self._nextId | |
self._nextId = self._nextId + 1 | |
self._eventTypeToCallbacks[eventType][callbackId] = callback | |
cachedHasCurrent = False | |
if eventType in self._eventTypeToCurrent: | |
cachedCurrent = self._eventTypeToCurrent[eventType] | |
cachedHasCurrent = True | |
if cachedHasCurrent: | |
callback(cachedCurrent) | |
return callbackId | |
def unregister(self, event, callbackId): | |
with self._lock: | |
del self._eventTypeToCallbacks[eventType][callbackId] | |
class Connection: | |
def __init__(self, src, dest): | |
self.src = src | |
self.dest = dest | |
class ModelFragment(Model): | |
def __init__(self, name, v): | |
super(ModelFragment, self).__init__() | |
self.name = name | |
if isinstance(v, ModelFragment): | |
self.value = v.value | |
else: | |
self.value = v | |
self.notify('set', self.value) | |
def setValue(self, v): | |
self.value = v | |
self.notify('set', self.value) | |
def __rshift__(self, other): | |
return Connection(self, other) | |
class ModelPropertySet(Model): | |
def _getter(self, p): | |
def real_getter(self): | |
return self._properties[p] | |
return real_getter | |
def _setter(self, p): | |
def real_setter(self, v): | |
if isinstance(v, Connection): | |
connect(v.src, v.dest) | |
else: | |
self._properties[p].setValue(v) | |
return real_setter | |
def __init__(self, theClass, properties): | |
super(ModelPropertySet, self).__init__() | |
self._properties = {} | |
for (p, v) in properties.items(): | |
self._properties[p] = ModelFragment(p, v) | |
setattr(theClass, p, property(self._getter(p), self._setter(p))) | |
class ModelNode(ModelPropertySet): | |
def __init__(self): | |
super(ModelNode, self).__init__(ModelNode, { | |
'width': 200, | |
'height': 200 | |
}) | |
m = ModelNode() | |
l = QtGui.QLabel() | |
l.setStyleSheet("QLabel { background-color : rgba(0,0,255,100%); color : black; }") | |
l.setParent(w) | |
l.show() | |
l.setGeometry(QtCore.QRect(100,100,100,100)) | |
m.width >>= l.width | |
m.height >>= l.height | |
m.width = 50 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment