class Store: def __init__(self, reducer: Dict, init_value: Dict): self._reducer = reducer #Default Setter if "SET_VALUE" in self._reducer: raise KeyError("Reducer name 'SET_VALUE' is reserved") self._reducer["SET_VALUE"] = set_value_reducer self._state = BehaviorSubject({}) self._state.on_next(init_value) def dispatch(self, name: str, payload: Dict = None): if name in self._reducer: self._state.on_next(self._reducer[name](self.state, payload)) def get_reducers(self): return {name: partial(self.dispatch, name) for name in self._reducer} def select_compiled(self, comp, built_in=None, logger=None): def wrapper(state): built_in.update(state) try: return eval_compiled(comp, variables=built_in) except: # Could not evaluate, probably wrong binding if logger: logger.warn(f"Error could not evaluate binding") logger.warn(format_exc(limit=2)) return None return self.select([wrapper]) def select(self, selectors: List): if len(selectors) == 1 and callable(selectors[0]): return self._state.pipe(rxop.map(selectors[0]), rxop.distinct_until_changed()) else: return self._state.pipe( rxop.map(lambda data: safe_get(data, selectors)), rxop.distinct_until_changed()) def select_by_path(self, path): return self.select(path.split(".")) def subscribe(self, subscriber): return self._state.subscribe(subscriber) @property def state(self) -> Dict: return self._state.value
def _add_obs(self, name, default=None): subject = BehaviorSubject(default) uniq_subject = subject.pipe(ops.distinct_until_changed()) def getobs(self): return getattr(self, f"_obs_{name}").value def setobs(self, v): getattr(self, f"_obs_{name}").on_next(v) setattr(self, f"_obs_{name}", subject) setattr(self, f"obs_{name}", uniq_subject) setattr(self.__class__, name, property(getobs, setobs)) self._data_fields.append(name)
class BlenderContext(Context): window_size: RV[Dimension] = rv.new_view() batch: RV[GPUBatch] = window_size.map(lambda c, s: c.create_batch(s)) buffer: RV[GPUBatch] = window_size.map(lambda c, s: c.create_batch(s)) def __init__(self, toolkit: BlenderToolkit, look_and_feel: Optional[LookAndFeel] = None, font_options: Optional[FontOptions] = None, window_manager: Optional[WindowManager] = None, error_handler: Optional[ErrorHandler] = None) -> None: super().__init__(toolkit, look_and_feel, font_options, window_manager, error_handler) resolution = Dimension(bge.render.getWindowWidth(), bge.render.getWindowHeight()) self._resolution = BehaviorSubject(resolution) # noinspection PyTypeChecker self.window_size = self._resolution.pipe(ops.distinct_until_changed()) self._shader = cast(GPUShader, gpu.shader.from_builtin("2D_IMAGE")) if use_viewport_render: # noinspection PyArgumentList self._draw_handler = SpaceView3D.draw_handler_add( self.process, (), "WINDOW", "POST_PIXEL") else: self._draw_handler = None bge.logic.getCurrentScene().post_draw.append(self.process) # noinspection PyTypeChecker self._texture = Buffer(bgl.GL_INT, 1) bgl.glGenTextures(1, self.texture) @property def shader(self) -> GPUShader: return self._shader @property def texture(self) -> Buffer: return self._texture def create_batch(self, size: Dimension) -> GPUBatch: if size is None: raise ValueError("Argument 'size' is required.") points = Bounds(0, 0, size.width, size.height).points vertices = tuple(map(lambda p: p.tuple, map(self.translate, points))) coords = ((0, 0), (1, 0), (1, 1), (0, 1)) indices = {"pos": vertices, "texCoord": coords} return batch_for_shader(self.shader, "TRI_FAN", indices) def translate(self, point: Point) -> Point: if point is None: raise ValueError("Argument 'point' is required.") return point.copy(y=self.window_size.height - point.y) # noinspection PyTypeChecker def process_draw(self) -> None: width = bge.render.getWindowWidth() height = bge.render.getWindowHeight() self._resolution.on_next(Dimension(width, height)) super().process_draw() data = self.surface.get_data() source = bgl.Buffer(bgl.GL_BYTE, width * height * 4, data) bgl.glEnable(bgl.GL_BLEND) bgl.glActiveTexture(bgl.GL_TEXTURE0) # noinspection PyUnresolvedReferences bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0]) bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_SRGB_ALPHA, width, height, 0, bgl.GL_BGRA, bgl.GL_UNSIGNED_BYTE, source) bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST) bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST) self.shader.bind() self.shader.uniform_int("image", 0) self.batch.draw(self.shader) bgl.glDeleteBuffers(1, source) def dispose(self) -> None: if self._draw_handler: # noinspection PyArgumentList SpaceView3D.draw_handler_remove(self._draw_handler, "WINDOW") else: bge.logic.getCurrentScene().post_draw.remove(self.process) bgl.glDeleteTextures(1, self.texture) # noinspection PyTypeChecker bgl.glDeleteBuffers(1, self.texture) super().dispose()
class LobbyConnection: """ GUARANTEES: - After calling connect, connection will leave disconnected state. """ def __init__(self, host, port): self.socket = QtNetwork.QTcpSocket() self.socket.stateChanged.connect(self._on_socket_state_change) self.socket.readyRead.connect(self.read) self.socket.error.connect(self._on_error) self.socket.setSocketOption(QtNetwork.QTcpSocket.KeepAliveOption, 1) self._host = host self._port = port self.block_size = 0 self._obs_state = BehaviorSubject(ConnectionState.DISCONNECTED) self.obs_state = self._obs_state.pipe(ops.distinct_until_changed()) self.message_stream = Subject() def _socket_state(self, s): qas = QtNetwork.QAbstractSocket if s in [qas.ConnectedState, qas.ClosingState]: return ConnectionState.CONNECTED elif s in [qas.BoundState, qas.HostLookupState, qas.ConnectingState]: return ConnectionState.CONNECTING else: return ConnectionState.DISCONNECTED @property def state(self): return self._obs_state.value def _on_socket_state_change(self, state): s = self._socket_state(state) if s is ConnectionState.DISCONNECTED: self.block_size = 0 self._obs_state.on_next(s) # Conflict with PySide2 signals! # Idempotent def connect_(self): if self.state is not ConnectionState.DISCONNECTED: return self.socket.connectToHost(self._host, self._port) def disconnect_(self): self.socket.disconnectFromHost() def read(self): ins = QtCore.QDataStream(self.socket) ins.setVersion(QtCore.QDataStream.Qt_4_2) while not ins.atEnd(): if self.block_size == 0: if self.socket.bytesAvailable() < 4: return self.block_size = ins.readUInt32() if self.socket.bytesAvailable() < self.block_size: return data = ins.readQString() self.block_size = 0 self.message_stream.on_next(data) def write(self, data): block = QtCore.QByteArray() out = QtCore.QDataStream(block, QtCore.QIODevice.ReadWrite) out.setVersion(QtCore.QDataStream.Qt_4_2) out.writeUInt32(2 * len(data) + 4) out.writeQString(data) if self.socket.state() == QtNetwork.QAbstractSocket.ConnectedState: self.socket.write(block) def _on_error(self): self.disconnect_()