Skip to content

Commit 96bd635

Browse files
Fine-grained reactivity (#119)
This PR completely does away with the React way of having a virtual DOM and comparing trees for figuring out which changes are needed. In its place there is now a 'fine-grained reactivity' system that updates just what is needed and does away with the overhead of having to compare virtual DOMs. This results in improved performance in scenarios with lots of moving parts¹. Notable API changes: the create_element (h) methods are gone: currently only possible to create components with template syntax. This might be coming back at a later stage signature of Collagraph.render method changed: takes a Component class instead of a virtual VNode added state that gets passed to the component (created from the given class) removed callback method in favor of subscribing to changes directly from the renderer text elements (moustaches) are not supported (yet) Changes to the template syntax: No need to have a single root item to render in a component No need for enclosing template tag: all tags outside the script tag are rendered These changes should make the template syntax a little more concise (thanks to Svelte for the inspiration). Other notable changes: * Some small fixes for PySide6 updates * Bump version to 0.8.0 ¹ There is no official benchmark to prove or quantify the performance improvements
1 parent 28ee8e6 commit 96bd635

160 files changed

Lines changed: 7079 additions & 5602 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright © 2022 collagraph authors
3+
Copyright © 2022-2025 collagraph authors
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ To try out Collagraph or start development, run:
9595

9696
```sh
9797
# Basic dev setup (no pygfx or pyside)
98-
uv install
98+
uv sync
9999
# Full dev setup
100-
uv install --all-groups
100+
uv sync --all-groups
101101
# Run example:
102102
uv run python examples/pyside/layout-example.py
103103
# Run test suite:

collagraph/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
from importlib.metadata import version
22

3-
from .collagraph import Collagraph, create_element, render_slot # noqa: F401
3+
from .collagraph import Collagraph # noqa: F401
44
from .component import Component # noqa: F401
5+
from .constants import EventLoopType # noqa: F401
56
from .renderers import * # noqa: F403
6-
from .types import EventLoopType, VNode # noqa: F401
77
from .sfc import importer # noqa: F401
88

99
__version__ = version("collagraph")
10-
11-
h = create_element
12-
s = render_slot

collagraph/__main__.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import argparse
24
import importlib
35
import json
@@ -26,7 +28,9 @@ def available_renderers():
2628
def init_collagraph(
2729
renderer_type: str, component_path: Path, state: dict | None = None
2830
):
29-
component_class, _ = cg.sfc.load(component_path)
31+
file_as_module = ".".join([*component_path.parts[:-1], component_path.stem])
32+
component_module = importlib.import_module(file_as_module)
33+
component_class = component_module.__component_class
3034
props = reactive(state or {})
3135

3236
if renderer_type == "pygfx":
@@ -36,43 +40,41 @@ def init_collagraph(
3640
canvas = WgpuCanvas(size=(600, 400))
3741
wgpu_renderer = gfx.renderers.WgpuRenderer(canvas)
3842

39-
camera = gfx.PerspectiveCamera(70, 16 / 9)
40-
camera.position.z = 15
43+
camera = gfx.PerspectiveCamera(70)
44+
camera.local.z = 15
45+
camera.show_pos((0, 0, 0))
4146

42-
controls = gfx.OrbitController(camera.position.clone())
43-
controls.add_default_event_handlers(wgpu_renderer, camera)
47+
controls = gfx.OrbitController(camera)
48+
controls.register_events(wgpu_renderer)
4449

4550
container = gfx.Scene()
4651

4752
def animate():
48-
controls.update_camera(camera)
4953
wgpu_renderer.render(container, camera)
5054

51-
gui = cg.Collagraph(renderer=cg.PygfxRenderer())
52-
gui.render(
53-
cg.h(component_class, props),
54-
container,
55-
callback=lambda: canvas.request_draw(animate),
56-
)
55+
renderer = cg.PygfxRenderer()
56+
renderer.add_on_change_handler(lambda: canvas.request_draw(animate))
57+
gui = cg.Collagraph(renderer=renderer)
58+
gui.render(component_class, container, state=props)
5759

5860
run()
5961
elif renderer_type == "pyside":
6062
from PySide6 import QtWidgets
6163

6264
app = QtWidgets.QApplication()
6365
gui = cg.Collagraph(renderer=cg.PySideRenderer())
64-
gui.render(cg.h(component_class, props), app)
66+
gui.render(component_class, app, state=props)
6567
app.exec()
6668
elif renderer_type == "dict":
6769
container = {"root": None}
6870
gui = cg.Collagraph(
6971
renderer=cg.DictRenderer(),
7072
event_loop_type=cg.EventLoopType.SYNC,
7173
)
72-
gui.render(cg.h(component_class, props), container)
74+
gui.render(component_class, container, state=props)
7375
# Start debugger to allow for inspection of container
7476
# and manipulation of props
75-
breakpoint() # noqa T100
77+
breakpoint() # noqa: T100
7678

7779

7880
def existing_component_file(value):

0 commit comments

Comments
 (0)