collagraph 0.8.2__tar.gz → 0.8.4__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 (147) hide show
  1. {collagraph-0.8.2 → collagraph-0.8.4}/PKG-INFO +12 -16
  2. {collagraph-0.8.2 → collagraph-0.8.4}/README.md +11 -15
  3. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/component.py +6 -0
  4. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/layouts.py +30 -4
  5. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/sfc/parser.py +20 -1
  6. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/app_point_cloud.cgx +1 -2
  7. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/button.cgx +1 -2
  8. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/numberpad.cgx +1 -2
  9. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/point_cloud.cgx +1 -2
  10. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/big_list.cgx +1 -2
  11. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/cgx_component_example.cgx +1 -2
  12. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/counter.cgx +1 -2
  13. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/dialog_example.cgx +1 -2
  14. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/dialog_example_window.cgx +1 -2
  15. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/layout_example.cgx +1 -2
  16. collagraph-0.8.4/examples/pyside/list_and_form.cgx +46 -0
  17. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/lists_example.cgx +1 -2
  18. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/slider_example.cgx +1 -2
  19. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/tabs_example.cgx +1 -2
  20. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/todo_example.cgx +1 -2
  21. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/tree_view_example.cgx +1 -2
  22. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/tree_widget_example.cgx +1 -2
  23. {collagraph-0.8.2 → collagraph-0.8.4}/pyproject.toml +1 -1
  24. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/component/events_parent.cgx +1 -2
  25. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/component/provide_child.cgx +1 -2
  26. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/component/provide_parent.cgx +1 -2
  27. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/component/provide_root.cgx +1 -2
  28. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/pyside/app.cgx +1 -2
  29. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/pyside/test_pyside_update.cgx +1 -2
  30. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/shadow_imports.cgx +1 -2
  31. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/lists.cgx +1 -2
  32. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/test_pyside_elements.py +2 -4
  33. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/test_pyside_sfc.py +1 -2
  34. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_attributes_dynamic.py +3 -6
  35. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_component.py +3 -6
  36. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_component_life_cycle.py +27 -2
  37. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_directive_for.py +2 -4
  38. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_parser.py +28 -0
  39. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_resolve_names.py +1 -2
  40. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_tag_component.py +1 -2
  41. {collagraph-0.8.2 → collagraph-0.8.4}/.coveragerc +0 -0
  42. {collagraph-0.8.2 → collagraph-0.8.4}/.github/workflows/ci.yml +0 -0
  43. {collagraph-0.8.2 → collagraph-0.8.4}/.gitignore +0 -0
  44. {collagraph-0.8.2 → collagraph-0.8.4}/.pre-commit-config.yaml +0 -0
  45. {collagraph-0.8.2 → collagraph-0.8.4}/LICENSE +0 -0
  46. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/__init__.py +0 -0
  47. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/__main__.py +0 -0
  48. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/__pyinstaller/__init__.py +0 -0
  49. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/__pyinstaller/hook-collagraph.py +0 -0
  50. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/collagraph.py +0 -0
  51. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/constants.py +0 -0
  52. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/fragment.py +0 -0
  53. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/__init__.py +0 -0
  54. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/dict_renderer.py +0 -0
  55. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pygfx_renderer.py +0 -0
  56. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/__init__.py +0 -0
  57. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/__init__.py +0 -0
  58. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/combobox.py +0 -0
  59. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/dialogbuttonbox.py +0 -0
  60. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/dockwidget.py +0 -0
  61. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/itemmodel.py +0 -0
  62. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/listview.py +0 -0
  63. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/menu.py +0 -0
  64. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/menubar.py +0 -0
  65. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/qobject.py +0 -0
  66. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/scrollarea.py +0 -0
  67. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/splitter.py +0 -0
  68. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/standarditem.py +0 -0
  69. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/statusbar.py +0 -0
  70. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/tab.py +0 -0
  71. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/toolbar.py +0 -0
  72. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/treewidget.py +0 -0
  73. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/treewidgetitem.py +0 -0
  74. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/widget.py +0 -0
  75. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside/objects/window.py +0 -0
  76. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/renderers/pyside_renderer.py +0 -0
  77. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/sfc/__init__.py +0 -0
  78. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/sfc/compiler.py +0 -0
  79. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/sfc/importer.py +0 -0
  80. {collagraph-0.8.2 → collagraph-0.8.4}/collagraph/weak.py +0 -0
  81. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/README.md +0 -0
  82. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/combined-example.py +0 -0
  83. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/component-example.py +0 -0
  84. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/point.cgx +0 -0
  85. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/pygfx-component.cgx +0 -0
  86. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pygfx/render_widget.cgx +0 -0
  87. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/app.py +0 -0
  88. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/combobox_example.cgx +0 -0
  89. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/debug_example.cgx +0 -0
  90. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/flow_layout.py +0 -0
  91. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/item.cgx +0 -0
  92. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/todo_list.cgx +0 -0
  93. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/tree_view_item.cgx +0 -0
  94. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/tree_view_model.cgx +0 -0
  95. {collagraph-0.8.2 → collagraph-0.8.4}/examples/pyside/tree_widget_item.cgx +0 -0
  96. {collagraph-0.8.2 → collagraph-0.8.4}/tests/__init__.py +0 -0
  97. {collagraph-0.8.2 → collagraph-0.8.4}/tests/conftest.py +0 -0
  98. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/component/events_child.cgx +0 -0
  99. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/example.cgx +0 -0
  100. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/file_dunder.cgx +0 -0
  101. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/multiple_classes_right_order.cgx +0 -0
  102. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/multiple_classes_wrong_order.cgx +0 -0
  103. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/multiple_root_elements.cgx +0 -0
  104. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/no_component_class.cgx +0 -0
  105. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/simple.cgx +0 -0
  106. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/dynamic_for.cgx +0 -0
  107. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/dynamic_for_template.cgx +0 -0
  108. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/dynamic_if.cgx +0 -0
  109. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/dynamic_if_template.cgx +0 -0
  110. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/node.cgx +0 -0
  111. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/simple_tree.cgx +0 -0
  112. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/slots_implicit_default_slot.cgx +0 -0
  113. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/template.cgx +0 -0
  114. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/template_empty.cgx +0 -0
  115. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/template_implicit_default_slot.cgx +0 -0
  116. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/template_implicit_default_slot_name.cgx +0 -0
  117. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/template_partial.cgx +0 -0
  118. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/tree.cgx +0 -0
  119. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/slots/widget_with_slots.cgx +0 -0
  120. {collagraph-0.8.2 → collagraph-0.8.4}/tests/data/text/expressions.cgx +0 -0
  121. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/conftest.py +0 -0
  122. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/test_pyside_ci.py +0 -0
  123. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/test_pyside_renderer.py +0 -0
  124. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/test_update.py +0 -0
  125. {collagraph-0.8.2 → collagraph-0.8.4}/tests/pyside/test_widget.py +0 -0
  126. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_attributes_static.py +0 -0
  127. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_collagraph.py +0 -0
  128. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_component_events.py +0 -0
  129. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_component_provide_inject.py +0 -0
  130. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_directive_else.py +0 -0
  131. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_directive_else_if.py +0 -0
  132. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_directive_if.py +0 -0
  133. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_events.py +0 -0
  134. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_load_string.py +0 -0
  135. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_pygfx_renderer.py +0 -0
  136. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_reconciliation.py +0 -0
  137. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_sfc_comments.py +0 -0
  138. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_sfc_compilation.py +0 -0
  139. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_sfc_import.py +0 -0
  140. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_sfc_linting_support.py +0 -0
  141. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_sfc_shadowing.py +0 -0
  142. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_slots.py +0 -0
  143. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_slots_dynamic.py +0 -0
  144. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_tag_dynamic.py +0 -0
  145. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_tag_static.py +0 -0
  146. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_tag_template.py +0 -0
  147. {collagraph-0.8.2 → collagraph-0.8.4}/tests/test_text_expressions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: collagraph
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Reactive user interfaces
5
5
  Author-email: Berend Klein Haneveld <berendkleinhaneveld@gmail.com>, Korijn van Golen <korijn@gmail.com>
6
6
  License-Expression: MIT
@@ -30,7 +30,6 @@ Inspired by Vue and React.
30
30
  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!
31
31
 
32
32
  * Reactivity (made possible by leveraging [observ](https://github.com/fork-tongue/observ))
33
- * Function components
34
33
  * Class components with local state and life-cycle methods/hooks
35
34
  * Single-file components with Vue-like template syntax (`.cgx` files)
36
35
  * Custom renderers
@@ -39,25 +38,22 @@ Here is an example that shows a counter, made with a component with Vue-like syn
39
38
 
40
39
  Contents of `counter.cgx`:
41
40
  ```html
42
- <template>
43
- <widget>
44
- <label
45
- :text="f'Count: {count}'"
46
- />
47
- <button
48
- text="bump"
49
- @clicked="bump"
50
- />
51
- </widget>
52
- </template>
41
+ <widget>
42
+ <label
43
+ :text="f'Count: {count}'"
44
+ />
45
+ <button
46
+ text="bump"
47
+ @clicked="bump"
48
+ />
49
+ </widget>
53
50
 
54
51
  <script>
55
52
  import collagraph as cg
56
53
 
57
54
 
58
55
  class Counter(cg.Component):
59
- def __init__(self, *args, **kwargs):
60
- super().__init__(*args, **kwargs)
56
+ def init(self):
61
57
  self.state["count"] = 0
62
58
 
63
59
  def bump(self):
@@ -80,7 +76,7 @@ gui = cg.Collagraph(renderer=cg.PySideRenderer())
80
76
  # Render the component into a container
81
77
  # (in this case the app but can be another widget)
82
78
  app = QtWidgets.QApplication()
83
- gui.render(cg.h(Counter), app)
79
+ gui.render(Counter, app)
84
80
  app.exec()
85
81
  ```
86
82
 
@@ -15,7 +15,6 @@ Inspired by Vue and React.
15
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
16
 
17
17
  * Reactivity (made possible by leveraging [observ](https://github.com/fork-tongue/observ))
18
- * Function components
19
18
  * Class components with local state and life-cycle methods/hooks
20
19
  * Single-file components with Vue-like template syntax (`.cgx` files)
21
20
  * Custom renderers
@@ -24,25 +23,22 @@ Here is an example that shows a counter, made with a component with Vue-like syn
24
23
 
25
24
  Contents of `counter.cgx`:
26
25
  ```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>
26
+ <widget>
27
+ <label
28
+ :text="f'Count: {count}'"
29
+ />
30
+ <button
31
+ text="bump"
32
+ @clicked="bump"
33
+ />
34
+ </widget>
38
35
 
39
36
  <script>
40
37
  import collagraph as cg
41
38
 
42
39
 
43
40
  class Counter(cg.Component):
44
- def __init__(self, *args, **kwargs):
45
- super().__init__(*args, **kwargs)
41
+ def init(self):
46
42
  self.state["count"] = 0
47
43
 
48
44
  def bump(self):
@@ -65,7 +61,7 @@ gui = cg.Collagraph(renderer=cg.PySideRenderer())
65
61
  # Render the component into a container
66
62
  # (in this case the app but can be another widget)
67
63
  app = QtWidgets.QApplication()
68
- gui.render(cg.h(Counter), app)
64
+ gui.render(Counter, app)
69
65
  app.exec()
70
66
  ```
71
67
 
@@ -27,6 +27,12 @@ class Component:
27
27
  self._parent = ref(parent) if parent else None
28
28
  self._provided = {}
29
29
 
30
+ # Call init hook at end of __init__
31
+ self.init()
32
+
33
+ def init(self):
34
+ pass
35
+
30
36
  @property
31
37
  def props(self):
32
38
  """The incoming props of this component."""
@@ -59,8 +59,15 @@ def remove_layout(self, el):
59
59
 
60
60
  @PySideRenderer.register_remove(QFormLayout)
61
61
  def remove_form(self, el):
62
- # Layout also deletes 'el' so no need to unset parent
63
- self.removeRow(el)
62
+ # Don't use removeRow, even though it should theoratically work
63
+ # In reality it causes a hard crash...
64
+ # Instead, take the row, and then unset the parents of the
65
+ # associated widgets of the return layout item
66
+ layout_item = self.takeRow(el)
67
+ if layout_item:
68
+ layout_item.labelItem.widget().setParent(None)
69
+ layout_item.fieldItem.widget().setParent(None)
70
+ el.setParent(None)
64
71
 
65
72
 
66
73
  @PySideRenderer.register_set_attr(QLayout)
@@ -105,12 +112,31 @@ def set_grid_index(self, attr, value):
105
112
  layout.addWidget(self, *value)
106
113
 
107
114
 
108
- @PySideRenderer.register_custom_attribute("form_label", "form_index")
115
+ @PySideRenderer.register_custom_attribute("form_label")
116
+ def set_form_label(self, attr, value):
117
+ setattr(self, attr, value)
118
+ index = getattr(self, "form_index", None)
119
+ if parent := self.parent():
120
+ layout = parent.layout()
121
+ if index is not None:
122
+ label_item = layout.itemAt(index, QFormLayout.LabelRole)
123
+ label_item.widget().setText(value)
124
+
125
+
126
+ @PySideRenderer.register_custom_attribute("form_index")
109
127
  def set_form_index(self, attr, value):
128
+ old_index = getattr(self, "form_index", None)
110
129
  setattr(self, attr, value)
111
130
  if parent := self.parent():
112
131
  layout = parent.layout()
132
+ label_widget = None
133
+ if old_index is not None:
134
+ layout_item = layout.takeRow(old_index)
135
+ label_widget = layout_item.labelItem.widget()
136
+ assert layout_item.fieldItem.widget() is self
113
137
  if hasattr(self, "form_label") and hasattr(self, "form_index"):
114
- layout.insertRow(self.form_index, self.form_label, self)
138
+ layout.insertRow(
139
+ self.form_index, label_widget if label_widget else self.form_label, self
140
+ )
115
141
  elif hasattr(self, "form_label"):
116
142
  layout.addRow(self.form_label, self)
@@ -45,7 +45,7 @@ class CGXParser(HTMLParser):
45
45
  self.root = Element("root", attrs={}, location=(-1, -1))
46
46
  self.stack = [self.root]
47
47
 
48
- def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]):
48
+ def _handle_tag(self, tag: str, attrs: list[tuple[str, str | None]]) -> Element:
49
49
  # The tag parameter is lower-cased by the HTMLParser.
50
50
  # In order to figure out whether the tag indicates
51
51
  # an imported class, we need the original casing for
@@ -68,6 +68,11 @@ class CGXParser(HTMLParser):
68
68
  parent = self.stack[-1]
69
69
  parent.children.append(node)
70
70
  node.parent = ref(parent)
71
+
72
+ return node
73
+
74
+ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]):
75
+ node = self._handle_tag(tag, attrs)
71
76
  # Make the new node the last on the stack
72
77
  self.stack.append(node)
73
78
 
@@ -81,6 +86,20 @@ class CGXParser(HTMLParser):
81
86
  if node.tag.lower() == tag:
82
87
  break
83
88
 
89
+ def handle_startendtag(self, tag: str, attrs):
90
+ """Handle self-closing tags"""
91
+ node = self._handle_tag(tag, attrs)
92
+ # Get the full text and split it
93
+ text = self.get_starttag_text()
94
+ lines = text.splitlines()
95
+ # Find the location of where the tag is actually closed
96
+ column = lines[-1].index("/>")
97
+ # Figure out how many lines the tag spans
98
+ line_span_count = len(lines) - 1
99
+ line, _ = self.getpos()
100
+ # Set the end to be exactly where the '/>' is located
101
+ node.end = (line + line_span_count, column)
102
+
84
103
  def handle_data(self, data: str):
85
104
  if data.strip():
86
105
  # Add item as child to the last on the stack
@@ -18,8 +18,7 @@ from examples.pygfx.point_cloud import PointCloud
18
18
 
19
19
 
20
20
  class Example(cg.Component):
21
- def __init__(self, *args, **kwargs):
22
- super().__init__(*args, **kwargs)
21
+ def init(self):
23
22
  self.state["count"] = self.props["count"]
24
23
 
25
24
  def add(self):
@@ -32,8 +32,7 @@ class Button(cg.Component):
32
32
  ),
33
33
  }
34
34
 
35
- def __init__(self, *args, **kwargs):
36
- super().__init__(*args, **kwargs)
35
+ def init(self):
37
36
  self.state["pressed"] = False
38
37
  self.state["hovered"] = False
39
38
  self.state["scale"] = self.props.get("scale", [0.85] * 3)
@@ -9,8 +9,7 @@ from examples.pygfx.button import Button
9
9
 
10
10
 
11
11
  class NumberPad(cg.Component):
12
- def __init__(self, *args, **kwargs):
13
- super().__init__(*args, **kwargs)
12
+ def init(self):
14
13
  self.state["columns"] = 4
15
14
  self.state["rows"] = 5
16
15
 
@@ -34,8 +34,7 @@ def rand_point():
34
34
 
35
35
 
36
36
  class PointCloud(collagraph.Component):
37
- def __init__(self, *args, **kwargs):
38
- super().__init__(*args, **kwargs)
37
+ def init(self):
39
38
  self.state["positions"] = []
40
39
  self.state["hovered"] = -1
41
40
  self.state["selected"] = -1
@@ -63,8 +63,7 @@ from examples.pyside.item import Item
63
63
 
64
64
 
65
65
  class TreeWidget(cg.Component):
66
- def __init__(self, *args, **kwargs):
67
- super().__init__(*args, **kwargs)
66
+ def init(self):
68
67
  self.state["columns"] = ["text", "other"]
69
68
  self.state["items"] = [
70
69
  *[{"text": f"text-{idx}", "other": f"other-{idx}"} for idx in range(500)]
@@ -22,8 +22,7 @@ from collagraph import Component
22
22
 
23
23
 
24
24
  class Example(Component):
25
- def __init__(self, *args, **kwargs):
26
- super().__init__(*args, **kwargs)
25
+ def init(self):
27
26
  self.state["top_to_bottom"] = {
28
27
  "type": "box",
29
28
  "direction": "TopToBottom",
@@ -17,8 +17,7 @@ import collagraph as cg
17
17
 
18
18
 
19
19
  class Counter(cg.Component):
20
- def __init__(self, *args, **kwargs):
21
- super().__init__(*args, **kwargs)
20
+ def init(self):
22
21
  self.state["count"] = 0
23
22
 
24
23
  def bump(self):
@@ -40,8 +40,7 @@ import collagraph as cg
40
40
 
41
41
 
42
42
  class ListDialog(cg.Component):
43
- def __init__(self, *args, **kwargs):
44
- super().__init__(*args, **kwargs)
43
+ def init(self):
45
44
  self.state["selected"] = -1
46
45
 
47
46
  def accepted(self):
@@ -41,8 +41,7 @@ from examples.pyside.dialog_example import ListDialog
41
41
 
42
42
 
43
43
  class Window(cg.Component):
44
- def __init__(self, *args, **kwargs):
45
- super().__init__(*args, **kwargs)
44
+ def init(self):
46
45
  self.state["dialog_active"] = False
47
46
  self.state["selected_text"] = ""
48
47
  self.state["modality"] = "window"
@@ -81,8 +81,7 @@ cg.PySideRenderer.register_layout("flow", FlowLayout)
81
81
 
82
82
 
83
83
  class LayoutExample(cg.Component):
84
- def __init__(self, *args, **kwargs):
85
- super().__init__(*args, **kwargs)
84
+ def init(self):
86
85
 
87
86
  self.state["index"] = self.props.get("index", 0)
88
87
 
@@ -0,0 +1,46 @@
1
+ <widget>
2
+ <qsplitter>
3
+ <treewidget
4
+ object-name="tree-widget"
5
+ :header-labels="['content']"
6
+ :column-count="1"
7
+ :selection-mode="QtWidgets.QTreeView.ExtendedSelection"
8
+ @item-selection-changed="selection_changed"
9
+ >
10
+ <treewidgetitem
11
+ v-for="item in items"
12
+ :content="{0: item['name']}"
13
+ :obj_id="item['id']"
14
+ />
15
+ </treewidget>
16
+ <widget :layout="{'type': 'form'}">
17
+ <label v-for="item in selected_items()" v-bind="item" />
18
+ </widget>
19
+ </qsplitter>
20
+ </widget>
21
+
22
+ <script>
23
+ import collagraph as cg
24
+ from PySide6 import QtWidgets
25
+
26
+ class App(cg.Component):
27
+ def init(self):
28
+ self.state["items"] = [{"id": 0, "name": "first"}, {"id": 1, "name":"second"}]
29
+ self.state["selected"] = []
30
+
31
+ def selection_changed(self):
32
+ if not self.element:
33
+ return
34
+
35
+ tree_widget = self.element.findChild(QtWidgets.QTreeWidget, "tree-widget")
36
+ selected_items = tree_widget.selectedItems()
37
+ selected_ids = list([item.obj_id for item in selected_items])
38
+ self.state["selected"] = selected_ids
39
+
40
+ def selected_items(self):
41
+ result = [
42
+ {'text': self.state['items'][idx]["name"], 'form_label': f'Type {idx}:', 'form_index': 0}
43
+ for idx in self.state['selected']]
44
+ return result
45
+
46
+ </script>
@@ -58,8 +58,7 @@ STATE_MAP = {
58
58
 
59
59
 
60
60
  class Example(cg.Component):
61
- def __init__(self, *args, **kwargs):
62
- super().__init__(*args, **kwargs)
61
+ def init(self):
63
62
  self.state["items"] = [
64
63
  [["Item", "Value"], False],
65
64
  [["Foo", "Bar"], False],
@@ -44,8 +44,7 @@ import collagraph as cg
44
44
 
45
45
 
46
46
  class Example(cg.Component):
47
- def __init__(self, *args, **kwargs):
48
- super().__init__(*args, **kwargs)
47
+ def init(self):
49
48
  self.state["value"] = 50
50
49
  self.state["tracking"] = True
51
50
 
@@ -28,8 +28,7 @@ import collagraph as cg
28
28
 
29
29
 
30
30
  class Example(cg.Component):
31
- def __init__(self, *args, **kwargs):
32
- super().__init__(*args, **kwargs)
31
+ def init(self):
33
32
  self.state["tab_count"] = 2
34
33
 
35
34
  def bump(self):
@@ -27,8 +27,7 @@ from examples.pyside.todo_list import TodoList
27
27
 
28
28
 
29
29
  class TodoApp(cg.Component):
30
- def __init__(self, *args, **kwargs):
31
- super().__init__(*args, **kwargs)
30
+ def init(self):
32
31
  self.state["items"] = [
33
32
  "Groceries",
34
33
  "Laundry",
@@ -60,8 +60,7 @@ def row(text: str, /, children=None, **kwargs):
60
60
 
61
61
 
62
62
  class TreeView(cg.Component):
63
- def __init__(self, *args, **kwargs):
64
- super().__init__(*args, **kwargs)
63
+ def init(self):
65
64
  # Adding columns seems to work relatively OK, but removing columns
66
65
  # has some problems...
67
66
  self.state["columns"] = ["text", "other"]
@@ -73,8 +73,7 @@ class Row:
73
73
 
74
74
 
75
75
  class TreeWidget(cg.Component):
76
- def __init__(self, *args, **kwargs):
77
- super().__init__(*args, **kwargs)
76
+ def init(self):
78
77
  self.state["columns"] = ["text", "other"]
79
78
  self.state["items"] = [
80
79
  Row(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "collagraph"
3
- version = "0.8.2"
3
+ version = "0.8.4"
4
4
  description = "Reactive user interfaces"
5
5
  authors = [
6
6
  { name = "Berend Klein Haneveld", email = "berendkleinhaneveld@gmail.com" },
@@ -14,8 +14,7 @@ from tests.data.component.events_child import Child
14
14
 
15
15
 
16
16
  class Parent(cg.Component):
17
- def __init__(self, *args, **kwargs):
18
- super().__init__(*args, **kwargs)
17
+ def init(self):
19
18
  self.state["count"] = 0
20
19
 
21
20
  def bump(self):
@@ -9,8 +9,7 @@ import collagraph as cg
9
9
 
10
10
 
11
11
  class Child(cg.Component):
12
- def __init__(self, *args, **kwargs):
13
- super().__init__(*args, **kwargs)
12
+ def init(self):
14
13
  self.state["injected_value"] = self.inject("value")
15
14
  self.state["injected_non_existing"] = self.inject("non_existing")
16
15
  self.state["injected_default"] = self.inject("other", default="bar")
@@ -8,7 +8,6 @@ from tests.data.component.provide_child import Child
8
8
 
9
9
 
10
10
  class Parent(cg.Component):
11
- def __init__(self, *args, **kwargs):
12
- super().__init__(*args, **kwargs)
11
+ def init(self):
13
12
  self.provide(key="value", value="foo")
14
13
  </script>
@@ -8,7 +8,6 @@ from tests.data.component.provide_parent import Parent
8
8
 
9
9
 
10
10
  class Root(cg.Component):
11
- def __init__(self, *args, **kwargs):
12
- super().__init__(*args, **kwargs)
11
+ def init(self):
13
12
  self.provide(key="other", value="baz")
14
13
  </script>
@@ -90,8 +90,7 @@ def quit():
90
90
 
91
91
 
92
92
  class Window(cg.Component):
93
- def __init__(self, *args, **kwargs):
94
- super().__init__(*args, **kwargs)
93
+ def init(self):
95
94
  self.state["toggles"] = {
96
95
  "show_dock": True,
97
96
  "show_dock_title": True,
@@ -33,8 +33,7 @@ from PySide6 import QtCore, QtWidgets
33
33
 
34
34
 
35
35
  class TreeView(cg.Component):
36
- def __init__(self, *args, **kwargs):
37
- super().__init__(*args, **kwargs)
36
+ def init(self):
38
37
  self.state["items"] = ["Foo", "Bar", "Bla"]
39
38
  self.state["selected"] = []
40
39
 
@@ -6,8 +6,7 @@ import tests
6
6
 
7
7
 
8
8
  class Example(cg.Component):
9
- def __init__(self, *args, **kwargs):
10
- super().__init__(*args, **kwargs)
9
+ def init(self):
11
10
  self.state["tests"] = "foo"
12
11
 
13
12
  @property
@@ -55,8 +55,7 @@ import collagraph as cg
55
55
  from observ import to_raw
56
56
 
57
57
  class ListsExample(cg.Component):
58
- def __init__(self, *args, **kwargs):
59
- super().__init__(*args, **kwargs)
58
+ def init(self):
60
59
  self.state["values"] = to_raw(self.props["items"])
61
60
 
62
61
  def items_content(self):
@@ -101,8 +101,7 @@ def test_layouts(qapp, qtbot, parse_source):
101
101
  import collagraph as cg
102
102
 
103
103
  class LayoutExample(cg.Component):
104
- def __init__(self, *args, **kwargs):
105
- super().__init__(*args, **kwargs)
104
+ def init(self):
106
105
  # Data to fill the box layout
107
106
  self.box = []
108
107
  for i in range(1, 5):
@@ -207,8 +206,7 @@ def test_lists(qapp, qtbot, qtmodeltester, parse_source):
207
206
  import collagraph as cg
208
207
 
209
208
  class ListsExample(cg.Component):
210
- def __init__(self, *args, **kwargs):
211
- super().__init__(*args, **kwargs)
209
+ def init(self):
212
210
  self.state["values"] = self.props["items"]
213
211
 
214
212
  def items_content(self):
@@ -21,8 +21,7 @@ def test_pyside_sfc_event_handlers(qtbot, parse_source):
21
21
  import collagraph as cg
22
22
 
23
23
  class Buttons(cg.Component):
24
- def __init__(self, *args, **kwargs):
25
- super().__init__(*args, **kwargs)
24
+ def init(self):
26
25
  self.state["count"] = 0
27
26
 
28
27
  def increase(self):
@@ -191,8 +191,7 @@ def test_dynamic_attribute_basic_dict(parse_source):
191
191
 
192
192
 
193
193
  class Labels(cg.Component):
194
- def __init__(self, *args, **kwargs):
195
- super().__init__(*args, **kwargs)
194
+ def init(self):
196
195
  self.state["label_text"] = self.props.get("text", "Label")
197
196
  </script>
198
197
  """
@@ -317,8 +316,7 @@ def test_dynamic_attribute_full(parse_source):
317
316
  import collagraph as cg
318
317
 
319
318
  class Labels(cg.Component):
320
- def __init__(self, *args, **kwargs):
321
- super().__init__(*args, **kwargs)
319
+ def init(self):
322
320
  self.state["other"] = "bar"
323
321
  </script>
324
322
  """
@@ -360,8 +358,7 @@ def test_dynamic_attribute_typo(parse_source):
360
358
  import collagraph as cg
361
359
 
362
360
  class App(cg.Component):
363
- def __init__(self, *args, **kwargs):
364
- super().__init__(*args, **kwargs)
361
+ def init(self):
365
362
  self.state["text"] = "Foo"
366
363
  </script>
367
364
  """
@@ -26,8 +26,7 @@ def test_component_no_override(parse_source, attr):
26
26
  import collagraph as cg
27
27
 
28
28
  class Item(cg.Component):
29
- def __init__(self, *args, **kwargs):
30
- super().__init__(*args, **kwargs)
29
+ def init(self):
31
30
  self.{attr} = {{}}
32
31
  </script>
33
32
  """
@@ -49,8 +48,7 @@ def test_component_props_read_only(parse_source):
49
48
  import collagraph as cg
50
49
 
51
50
  class Item(cg.Component):
52
- def __init__(self, *args, **kwargs):
53
- super().__init__(*args, **kwargs)
51
+ def init(self):
54
52
  self.props["foo"] = "bar"
55
53
  </script>
56
54
  """
@@ -115,8 +113,7 @@ def test_component_callback(parse_source):
115
113
  import collagraph as cg
116
114
 
117
115
  class Counter(cg.Component):
118
- def __init__(self, *args, **kwargs):
119
- super().__init__(*args, **kwargs)
116
+ def init(self):
120
117
  self.state["count"] = self.props.get("count", 0)
121
118
  self.state["step_size"] = self.props.get("step_size", 1)
122
119