collagraph 0.5.2__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. collagraph-0.7.0/PKG-INFO +144 -0
  2. collagraph-0.7.0/README.md +122 -0
  3. collagraph-0.7.0/collagraph/__main__.py +131 -0
  4. collagraph-0.7.0/collagraph/__pyinstaller/__init__.py +8 -0
  5. collagraph-0.7.0/collagraph/__pyinstaller/hook-collagraph.py +57 -0
  6. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/cgx/cgx.py +123 -89
  7. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/collagraph.py +13 -35
  8. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/component.py +31 -3
  9. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/__init__.py +8 -0
  10. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pygfx_renderer.py +17 -15
  11. collagraph-0.7.0/collagraph/renderers/pyside/objects/scrollarea.py +11 -0
  12. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/widget.py +7 -3
  13. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/utils.py +1 -0
  14. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside_renderer.py +17 -17
  15. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/types.py +1 -2
  16. collagraph-0.7.0/pyproject.toml +50 -0
  17. collagraph-0.5.2/PKG-INFO +0 -87
  18. collagraph-0.5.2/README.md +0 -66
  19. collagraph-0.5.2/pyproject.toml +0 -35
  20. collagraph-0.5.2/setup.py +0 -39
  21. {collagraph-0.5.2 → collagraph-0.7.0}/LICENSE +0 -0
  22. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/__init__.py +0 -0
  23. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/cgx/__init__.py +0 -0
  24. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/cgx/importer.py +0 -0
  25. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/compare.py +0 -0
  26. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/dict_renderer.py +0 -0
  27. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/dom_renderer.py +0 -0
  28. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/__init__.py +0 -0
  29. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/__init__.py +0 -0
  30. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/combobox.py +0 -0
  31. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/dialogbuttonbox.py +0 -0
  32. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/dockwidget.py +0 -0
  33. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/itemmodel.py +0 -0
  34. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/listview.py +0 -0
  35. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/menu.py +0 -0
  36. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/menubar.py +0 -0
  37. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/qobject.py +0 -0
  38. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/splitter.py +0 -0
  39. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/standarditem.py +0 -0
  40. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/statusbar.py +0 -0
  41. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/tab.py +0 -0
  42. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/toolbar.py +0 -0
  43. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/treewidget.py +0 -0
  44. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/treewidgetitem.py +0 -0
  45. {collagraph-0.5.2 → collagraph-0.7.0}/collagraph/renderers/pyside/objects/window.py +0 -0
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.1
2
+ Name: collagraph
3
+ Version: 0.7.0
4
+ Summary: Reactive user interfaces
5
+ Home-page: https://github.com/fork-tongue/collagraph
6
+ License: MIT
7
+ Author: Berend Klein Haneveld
8
+ Author-email: berendkleinhaneveld@gmail.com
9
+ Requires-Python: >=3.9
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Provides-Extra: pygfx
16
+ Provides-Extra: pyside
17
+ Requires-Dist: observ (>=0.14.1)
18
+ Requires-Dist: pygfx (>=0.1.17) ; extra == "pygfx"
19
+ Requires-Dist: pyside6_essentials (>=6.6,<6.7) ; (python_version < "3.13") and (extra == "pyside")
20
+ Description-Content-Type: text/markdown
21
+
22
+ [![PyPI version](https://badge.fury.io/py/collagraph.svg)](https://badge.fury.io/py/collagraph)
23
+ [![CI status](https://github.com/fork-tongue/collagraph/workflows/CI/badge.svg)](https://github.com/fork-tongue/collagraph/actions)
24
+
25
+ # Collagraph 📓
26
+
27
+ Reactive user interfaces.
28
+
29
+ > The word [Collagraphy](https://en.wikipedia.org/wiki/Collagraphy) is derived from the Greek word _koll_ or _kolla_, meaning glue, and graph, meaning the activity of drawing.
30
+
31
+ Inspired by Vue and React.
32
+
33
+
34
+ ## Features
35
+
36
+ Write your Python interfaces in a declarative manner with plain render functions, component classes or even single-file components using Vue-like syntax, but with Python!
37
+
38
+ * Reactivity (made possible by leveraging [observ](https://github.com/fork-tongue/observ))
39
+ * Function components
40
+ * Class components with local state and life-cycle methods/hooks
41
+ * Single-file components with Vue-like template syntax (`.cgx` files)
42
+ * Custom renderers
43
+
44
+ Here is an example that shows a counter, made with a component with Vue-like syntax:
45
+
46
+ Contents of `counter.cgx`:
47
+ ```html
48
+ <template>
49
+ <widget>
50
+ <label
51
+ :text="f'Count: {count}'"
52
+ />
53
+ <button
54
+ text="bump"
55
+ @clicked="bump"
56
+ />
57
+ </widget>
58
+ </template>
59
+
60
+ <script>
61
+ import collagraph as cg
62
+
63
+
64
+ class Counter(cg.Component):
65
+ def __init__(self, *args, **kwargs):
66
+ super().__init__(*args, **kwargs)
67
+ self.state["count"] = 0
68
+
69
+ def bump(self):
70
+ self.state["count"] += 1
71
+ </script>
72
+ ```
73
+
74
+ Contents of `app.py`:
75
+ ```python
76
+ from PySide6 import QtWidgets
77
+ import collagraph as cg
78
+
79
+ # After importing collagraph, it's possible to import
80
+ # components directly from .cgx files
81
+ from counter import Counter
82
+
83
+ # Create a Collagraph instance with a PySide renderer
84
+ # and register with the Qt event loop
85
+ gui = cg.Collagraph(renderer=cg.PySideRenderer())
86
+ # Render the component into a container
87
+ # (in this case the app but can be another widget)
88
+ app = QtWidgets.QApplication()
89
+ gui.render(cg.h(Counter), app)
90
+ app.exec()
91
+ ```
92
+
93
+ Which looks something like this:
94
+
95
+ ![collagraph example](https://github.com/fork-tongue/collagraph/assets/1000968/4ebae92e-d7be-48ea-b76a-c6eab8d62112)
96
+
97
+ Instead of using a python file as an entry point to run components, you can run them directly using the collagraph CLI:
98
+
99
+ ```sh
100
+ poetry run collagraph examples/pyside/counter.cgx
101
+ ```
102
+
103
+ For more examples, please take a look at the [examples folder](examples).
104
+
105
+ Currently there are three renderers:
106
+
107
+ * [PysideRenderer](collagraph/renderers/pyside_renderer.py): for rendering PySide6 applications
108
+ * [PygfxRenderer](collagraph/renderers/pygfx_renderer.py): for rendering 3D graphic scenes with [Pygfx](https://github.com/pygfx/pygfx)
109
+ * [DomRenderer](collagraph/renderers/dom_renderer.py): for rendering to browser DOM through [PyScript](https://pyscript.net) (or rather [Pyodide](https://pyodide.org/en/stable/))
110
+
111
+ It is possible to create a custom Renderer using the [Renderer](collagraph/renderers/__init__.py) interface, to render to other UI frameworks, for instance wxPython.
112
+
113
+
114
+ ## Development
115
+
116
+ To try out Collagraph or start development, run:
117
+
118
+ ```sh
119
+ # Basic dev setup (no pygfx or pyside)
120
+ poetry install
121
+ # Full dev setup
122
+ poetry install --with pyside --extras pyside --extras pygfx
123
+ # Run example:
124
+ poetry run python examples/pyside/layout-example.py
125
+ # Run test suite:
126
+ poetry run pytest
127
+ # Install git pre-commit hooks to make sure tests/linting passes before committing
128
+ poetry run pre-commit install
129
+ ```
130
+
131
+
132
+ ### Syntax Highlighting
133
+
134
+ Syntax highlighting for single-file components (`.cgx`) is supported for VSCode and Sublime Text:
135
+
136
+ * [CGX syntax highlight for Sublime Text](https://github.com/fork-tongue/cgx-syntax-highlight-sublime)
137
+ * [CGX syntax highlight for VSCode](https://github.com/fork-tongue/cgx-syntax-highlight-vscode)
138
+
139
+
140
+ ### Formatting and linting
141
+
142
+ Linting cgx files is possible with a flake8 plugin: [flake8-cgx](https://github.com/fork-tongue/flake8-cgx).
143
+ Formatting the contents of the script tag can be done with [black-cgx](https://github.com/fork-tongue/black-cgx).
144
+
@@ -0,0 +1,122 @@
1
+ [![PyPI version](https://badge.fury.io/py/collagraph.svg)](https://badge.fury.io/py/collagraph)
2
+ [![CI status](https://github.com/fork-tongue/collagraph/workflows/CI/badge.svg)](https://github.com/fork-tongue/collagraph/actions)
3
+
4
+ # Collagraph 📓
5
+
6
+ Reactive user interfaces.
7
+
8
+ > The word [Collagraphy](https://en.wikipedia.org/wiki/Collagraphy) is derived from the Greek word _koll_ or _kolla_, meaning glue, and graph, meaning the activity of drawing.
9
+
10
+ Inspired by Vue and React.
11
+
12
+
13
+ ## Features
14
+
15
+ Write your Python interfaces in a declarative manner with plain render functions, component classes or even single-file components using Vue-like syntax, but with Python!
16
+
17
+ * Reactivity (made possible by leveraging [observ](https://github.com/fork-tongue/observ))
18
+ * Function components
19
+ * Class components with local state and life-cycle methods/hooks
20
+ * Single-file components with Vue-like template syntax (`.cgx` files)
21
+ * Custom renderers
22
+
23
+ Here is an example that shows a counter, made with a component with Vue-like syntax:
24
+
25
+ Contents of `counter.cgx`:
26
+ ```html
27
+ <template>
28
+ <widget>
29
+ <label
30
+ :text="f'Count: {count}'"
31
+ />
32
+ <button
33
+ text="bump"
34
+ @clicked="bump"
35
+ />
36
+ </widget>
37
+ </template>
38
+
39
+ <script>
40
+ import collagraph as cg
41
+
42
+
43
+ class Counter(cg.Component):
44
+ def __init__(self, *args, **kwargs):
45
+ super().__init__(*args, **kwargs)
46
+ self.state["count"] = 0
47
+
48
+ def bump(self):
49
+ self.state["count"] += 1
50
+ </script>
51
+ ```
52
+
53
+ Contents of `app.py`:
54
+ ```python
55
+ from PySide6 import QtWidgets
56
+ import collagraph as cg
57
+
58
+ # After importing collagraph, it's possible to import
59
+ # components directly from .cgx files
60
+ from counter import Counter
61
+
62
+ # Create a Collagraph instance with a PySide renderer
63
+ # and register with the Qt event loop
64
+ gui = cg.Collagraph(renderer=cg.PySideRenderer())
65
+ # Render the component into a container
66
+ # (in this case the app but can be another widget)
67
+ app = QtWidgets.QApplication()
68
+ gui.render(cg.h(Counter), app)
69
+ app.exec()
70
+ ```
71
+
72
+ Which looks something like this:
73
+
74
+ ![collagraph example](https://github.com/fork-tongue/collagraph/assets/1000968/4ebae92e-d7be-48ea-b76a-c6eab8d62112)
75
+
76
+ Instead of using a python file as an entry point to run components, you can run them directly using the collagraph CLI:
77
+
78
+ ```sh
79
+ poetry run collagraph examples/pyside/counter.cgx
80
+ ```
81
+
82
+ For more examples, please take a look at the [examples folder](examples).
83
+
84
+ Currently there are three renderers:
85
+
86
+ * [PysideRenderer](collagraph/renderers/pyside_renderer.py): for rendering PySide6 applications
87
+ * [PygfxRenderer](collagraph/renderers/pygfx_renderer.py): for rendering 3D graphic scenes with [Pygfx](https://github.com/pygfx/pygfx)
88
+ * [DomRenderer](collagraph/renderers/dom_renderer.py): for rendering to browser DOM through [PyScript](https://pyscript.net) (or rather [Pyodide](https://pyodide.org/en/stable/))
89
+
90
+ It is possible to create a custom Renderer using the [Renderer](collagraph/renderers/__init__.py) interface, to render to other UI frameworks, for instance wxPython.
91
+
92
+
93
+ ## Development
94
+
95
+ To try out Collagraph or start development, run:
96
+
97
+ ```sh
98
+ # Basic dev setup (no pygfx or pyside)
99
+ poetry install
100
+ # Full dev setup
101
+ poetry install --with pyside --extras pyside --extras pygfx
102
+ # Run example:
103
+ poetry run python examples/pyside/layout-example.py
104
+ # Run test suite:
105
+ poetry run pytest
106
+ # Install git pre-commit hooks to make sure tests/linting passes before committing
107
+ poetry run pre-commit install
108
+ ```
109
+
110
+
111
+ ### Syntax Highlighting
112
+
113
+ Syntax highlighting for single-file components (`.cgx`) is supported for VSCode and Sublime Text:
114
+
115
+ * [CGX syntax highlight for Sublime Text](https://github.com/fork-tongue/cgx-syntax-highlight-sublime)
116
+ * [CGX syntax highlight for VSCode](https://github.com/fork-tongue/cgx-syntax-highlight-vscode)
117
+
118
+
119
+ ### Formatting and linting
120
+
121
+ Linting cgx files is possible with a flake8 plugin: [flake8-cgx](https://github.com/fork-tongue/flake8-cgx).
122
+ Formatting the contents of the script tag can be done with [black-cgx](https://github.com/fork-tongue/black-cgx).
@@ -0,0 +1,131 @@
1
+ import argparse
2
+ import importlib
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from observ import reactive
7
+
8
+ import collagraph as cg
9
+
10
+
11
+ def available_renderers():
12
+ result = set()
13
+ for renderer_type, name in {
14
+ "PySideRenderer": "pyside",
15
+ "PygfxRenderer": "pygfx",
16
+ "DictRenderer": "dict",
17
+ # TODO: add support for DomRenderer
18
+ # "DomRenderer": "dom",
19
+ }.items():
20
+ try:
21
+ importlib.import_module("collagraph", renderer_type)
22
+ result.add(name)
23
+ except ImportError:
24
+ continue
25
+ return result
26
+
27
+
28
+ def init_collagraph(renderer_type: str, component_path: Path, state: dict = None):
29
+ Component, _ = cg.cgx.cgx.load(component_path)
30
+ props = reactive(state or {})
31
+
32
+ if renderer_type == "pygfx":
33
+ import pygfx as gfx
34
+ from wgpu.gui.auto import run, WgpuCanvas
35
+
36
+ canvas = WgpuCanvas(size=(600, 400))
37
+ wgpu_renderer = gfx.renderers.WgpuRenderer(canvas)
38
+
39
+ camera = gfx.PerspectiveCamera(70, 16 / 9)
40
+ camera.position.z = 15
41
+
42
+ controls = gfx.OrbitController(camera.position.clone())
43
+ controls.add_default_event_handlers(wgpu_renderer, camera)
44
+
45
+ container = gfx.Scene()
46
+
47
+ def animate():
48
+ controls.update_camera(camera)
49
+ wgpu_renderer.render(container, camera)
50
+
51
+ gui = cg.Collagraph(renderer=cg.PygfxRenderer())
52
+ gui.render(
53
+ cg.h(Component, props),
54
+ container,
55
+ callback=lambda: canvas.request_draw(animate),
56
+ )
57
+
58
+ run()
59
+ elif renderer_type == "pyside":
60
+ from PySide6 import QtWidgets
61
+
62
+ app = QtWidgets.QApplication()
63
+ gui = cg.Collagraph(renderer=cg.PySideRenderer())
64
+ gui.render(cg.h(Component, props), app)
65
+ app.exec()
66
+ elif renderer_type == "dict":
67
+ container = {"root": None}
68
+ gui = cg.Collagraph(
69
+ renderer=cg.DictRenderer(),
70
+ event_loop_type=cg.EventLoopType.SYNC,
71
+ )
72
+ gui.render(cg.h(Component, props), container)
73
+ # Start debugger to allow for inspection of container
74
+ # and manipulation of props
75
+ breakpoint()
76
+
77
+
78
+ def existing_component_file(value):
79
+ path = Path(value)
80
+ if not path.exists():
81
+ raise argparse.ArgumentTypeError(f"{value} does not exist")
82
+ if not path.is_file():
83
+ raise argparse.ArgumentTypeError(f"{value} is not a file")
84
+ if path.suffix != ".cgx":
85
+ raise argparse.ArgumentTypeError(f"{value} is not a collagraph component")
86
+ return path
87
+
88
+
89
+ def json_contents(value):
90
+ path = Path(value)
91
+ if path.is_file():
92
+ with path.open(mode="r", encoding="utf-8") as fh:
93
+ try:
94
+ return json.load(fh)
95
+ except Exception:
96
+ raise argparse.ArgumentTypeError(f"{value} is not valid json")
97
+ else:
98
+ try:
99
+ return json.loads(value)
100
+ except Exception:
101
+ raise argparse.ArgumentTypeError(f"{value} is not valid json")
102
+
103
+
104
+ def run():
105
+ parser = argparse.ArgumentParser(
106
+ description="Run collagraph components directly",
107
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
108
+ )
109
+ parser.add_argument(
110
+ "--renderer",
111
+ default="pyside",
112
+ choices=available_renderers(),
113
+ help="The type of renderer to use",
114
+ )
115
+ parser.add_argument(
116
+ "--state",
117
+ type=json_contents,
118
+ help="Optional state/props to load (json file or string)",
119
+ )
120
+ parser.add_argument(
121
+ "component",
122
+ type=existing_component_file,
123
+ help="Path to component to render",
124
+ )
125
+ args = parser.parse_args()
126
+
127
+ init_collagraph(args.renderer, args.component, args.state)
128
+
129
+
130
+ if __name__ == "__main__":
131
+ run()
@@ -0,0 +1,8 @@
1
+ from os.path import dirname
2
+
3
+
4
+ HERE = dirname(__file__)
5
+
6
+
7
+ def get_hook_dirs():
8
+ return [HERE]
@@ -0,0 +1,57 @@
1
+ import ast
2
+ from pathlib import Path
3
+
4
+ from PyInstaller.utils.hooks import collect_data_files
5
+
6
+ from collagraph.cgx.cgx import CGXParser, get_script_ast
7
+
8
+
9
+ def hook(hook_api):
10
+ collagraph_uses = hook_api.analysis.graph.get_code_using("collagraph")
11
+
12
+ hidden_imports = set()
13
+ datas = []
14
+ for package, code in collagraph_uses.items():
15
+ filename = Path(code.co_filename)
16
+ hidden_imports |= collect_hidden_imports(filename.parent)
17
+ datas += collect_data_files(package, includes=["**/*.cgx"])
18
+
19
+ hook_api.add_imports(*hidden_imports)
20
+ hook_api.add_datas(datas)
21
+
22
+
23
+ def collect_hidden_imports(folder):
24
+ folder = Path(folder)
25
+
26
+ hidden_imports = set()
27
+ for path in folder.glob("**/*.cgx"):
28
+ template = path.read_text()
29
+ # Parse the file component into a tree of Node instances
30
+ parser = CGXParser()
31
+ parser.feed(template)
32
+
33
+ # Get the AST from the script tag
34
+ script_tree = get_script_ast(parser, path)
35
+
36
+ # Find a list of imported names (or aliases, if any)
37
+ # Those names don't have to be wrapped by `_lookup`
38
+ imported_names = ImportsCollector()
39
+ imported_names.visit(script_tree)
40
+
41
+ hidden_imports |= imported_names.names
42
+
43
+ return hidden_imports
44
+
45
+
46
+ class ImportsCollector(ast.NodeVisitor):
47
+ def __init__(self):
48
+ self.names = set()
49
+
50
+ def visit_ImportFrom(self, node):
51
+ for alias in node.names:
52
+ self.names.add(".".join([node.module, alias.name]))
53
+ self.names.add(node.module)
54
+
55
+ def visit_Import(self, node):
56
+ for alias in node.names:
57
+ self.names.add(alias.name)