qpuiq 0.10__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 (107) hide show
  1. qpuiq-0.10/LICENSE.txt +21 -0
  2. qpuiq-0.10/PKG-INFO +227 -0
  3. qpuiq-0.10/PUI/PySide6/__init__.py +49 -0
  4. qpuiq-0.10/PUI/PySide6/application.py +58 -0
  5. qpuiq-0.10/PUI/PySide6/base.py +222 -0
  6. qpuiq-0.10/PUI/PySide6/button.py +21 -0
  7. qpuiq-0.10/PUI/PySide6/canvas.py +288 -0
  8. qpuiq-0.10/PUI/PySide6/checkbox.py +32 -0
  9. qpuiq-0.10/PUI/PySide6/combobox.py +75 -0
  10. qpuiq-0.10/PUI/PySide6/dialog.py +72 -0
  11. qpuiq-0.10/PUI/PySide6/divider.py +23 -0
  12. qpuiq-0.10/PUI/PySide6/image.py +30 -0
  13. qpuiq-0.10/PUI/PySide6/label.py +33 -0
  14. qpuiq-0.10/PUI/PySide6/layout.py +72 -0
  15. qpuiq-0.10/PUI/PySide6/matplotlib.py +23 -0
  16. qpuiq-0.10/PUI/PySide6/mdi.py +33 -0
  17. qpuiq-0.10/PUI/PySide6/menu.py +85 -0
  18. qpuiq-0.10/PUI/PySide6/modal.py +132 -0
  19. qpuiq-0.10/PUI/PySide6/progressbar.py +17 -0
  20. qpuiq-0.10/PUI/PySide6/radiobutton.py +29 -0
  21. qpuiq-0.10/PUI/PySide6/scroll.py +153 -0
  22. qpuiq-0.10/PUI/PySide6/splitter.py +25 -0
  23. qpuiq-0.10/PUI/PySide6/tab.py +39 -0
  24. qpuiq-0.10/PUI/PySide6/table.py +89 -0
  25. qpuiq-0.10/PUI/PySide6/text.py +35 -0
  26. qpuiq-0.10/PUI/PySide6/textfield.py +62 -0
  27. qpuiq-0.10/PUI/PySide6/toolbar.py +57 -0
  28. qpuiq-0.10/PUI/PySide6/tree.py +120 -0
  29. qpuiq-0.10/PUI/PySide6/window.py +81 -0
  30. qpuiq-0.10/PUI/__init__.py +46 -0
  31. qpuiq-0.10/PUI/common.py +20 -0
  32. qpuiq-0.10/PUI/decorator.py +20 -0
  33. qpuiq-0.10/PUI/dom.py +238 -0
  34. qpuiq-0.10/PUI/flet/__init__.py +21 -0
  35. qpuiq-0.10/PUI/flet/application.py +42 -0
  36. qpuiq-0.10/PUI/flet/base.py +37 -0
  37. qpuiq-0.10/PUI/flet/button.py +20 -0
  38. qpuiq-0.10/PUI/flet/canvas.py +86 -0
  39. qpuiq-0.10/PUI/flet/checkbox.py +23 -0
  40. qpuiq-0.10/PUI/flet/label.py +27 -0
  41. qpuiq-0.10/PUI/flet/layout.py +50 -0
  42. qpuiq-0.10/PUI/flet/progressbar.py +21 -0
  43. qpuiq-0.10/PUI/flet/radiobutton.py +27 -0
  44. qpuiq-0.10/PUI/flet/scroll.py +83 -0
  45. qpuiq-0.10/PUI/flet/tab.py +42 -0
  46. qpuiq-0.10/PUI/flet/text.py +55 -0
  47. qpuiq-0.10/PUI/flet/textfield.py +58 -0
  48. qpuiq-0.10/PUI/flet/window.py +25 -0
  49. qpuiq-0.10/PUI/interfaces.py +97 -0
  50. qpuiq-0.10/PUI/node.py +407 -0
  51. qpuiq-0.10/PUI/state.py +698 -0
  52. qpuiq-0.10/PUI/textual/__init__.py +34 -0
  53. qpuiq-0.10/PUI/textual/application.py +82 -0
  54. qpuiq-0.10/PUI/textual/base.py +113 -0
  55. qpuiq-0.10/PUI/textual/button.py +17 -0
  56. qpuiq-0.10/PUI/textual/checkbox.py +21 -0
  57. qpuiq-0.10/PUI/textual/label.py +36 -0
  58. qpuiq-0.10/PUI/textual/layout.py +48 -0
  59. qpuiq-0.10/PUI/textual/progressbar.py +17 -0
  60. qpuiq-0.10/PUI/textual/radiobutton.py +24 -0
  61. qpuiq-0.10/PUI/textual/scroll.py +72 -0
  62. qpuiq-0.10/PUI/textual/tab.py +75 -0
  63. qpuiq-0.10/PUI/textual/text.py +32 -0
  64. qpuiq-0.10/PUI/textual/textfield.py +49 -0
  65. qpuiq-0.10/PUI/textual/window.py +7 -0
  66. qpuiq-0.10/PUI/timeline.py +36 -0
  67. qpuiq-0.10/PUI/tkinter/__init__.py +43 -0
  68. qpuiq-0.10/PUI/tkinter/application.py +49 -0
  69. qpuiq-0.10/PUI/tkinter/base.py +68 -0
  70. qpuiq-0.10/PUI/tkinter/button.py +15 -0
  71. qpuiq-0.10/PUI/tkinter/canvas.py +49 -0
  72. qpuiq-0.10/PUI/tkinter/checkbox.py +27 -0
  73. qpuiq-0.10/PUI/tkinter/label.py +17 -0
  74. qpuiq-0.10/PUI/tkinter/layout.py +114 -0
  75. qpuiq-0.10/PUI/tkinter/progressbar.py +17 -0
  76. qpuiq-0.10/PUI/tkinter/radiobutton.py +26 -0
  77. qpuiq-0.10/PUI/tkinter/scroll.py +201 -0
  78. qpuiq-0.10/PUI/tkinter/tab.py +52 -0
  79. qpuiq-0.10/PUI/tkinter/text.py +20 -0
  80. qpuiq-0.10/PUI/tkinter/textfield.py +53 -0
  81. qpuiq-0.10/PUI/tkinter/window.py +51 -0
  82. qpuiq-0.10/PUI/utils.py +15 -0
  83. qpuiq-0.10/PUI/view.py +161 -0
  84. qpuiq-0.10/PUI/wx/__init__.py +19 -0
  85. qpuiq-0.10/PUI/wx/application.py +44 -0
  86. qpuiq-0.10/PUI/wx/base.py +202 -0
  87. qpuiq-0.10/PUI/wx/button.py +16 -0
  88. qpuiq-0.10/PUI/wx/canvas.py +255 -0
  89. qpuiq-0.10/PUI/wx/checkbox.py +25 -0
  90. qpuiq-0.10/PUI/wx/combobox.py +72 -0
  91. qpuiq-0.10/PUI/wx/dialog.py +66 -0
  92. qpuiq-0.10/PUI/wx/divider.py +19 -0
  93. qpuiq-0.10/PUI/wx/label.py +18 -0
  94. qpuiq-0.10/PUI/wx/layout.py +46 -0
  95. qpuiq-0.10/PUI/wx/progressbar.py +17 -0
  96. qpuiq-0.10/PUI/wx/radiobutton.py +27 -0
  97. qpuiq-0.10/PUI/wx/scroll.py +44 -0
  98. qpuiq-0.10/PUI/wx/text.py +23 -0
  99. qpuiq-0.10/PUI/wx/textfield.py +56 -0
  100. qpuiq-0.10/PUI/wx/window.py +58 -0
  101. qpuiq-0.10/QPUIQ.egg-info/PKG-INFO +227 -0
  102. qpuiq-0.10/QPUIQ.egg-info/SOURCES.txt +109 -0
  103. qpuiq-0.10/QPUIQ.egg-info/dependency_links.txt +1 -0
  104. qpuiq-0.10/QPUIQ.egg-info/top_level.txt +1 -0
  105. qpuiq-0.10/README.md +217 -0
  106. qpuiq-0.10/setup.cfg +4 -0
  107. qpuiq-0.10/setup.py +17 -0
qpuiq-0.10/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 buganini@b612.tw
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
qpuiq-0.10/PKG-INFO ADDED
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.1
2
+ Name: qpuiq
3
+ Version: 0.10
4
+ Summary: "PUI" Python Declarative UI Framework
5
+ Home-page: https://github.com/buganini/PUI
6
+ Author: Buganini Chiu
7
+ Author-email: buganini@b612.tw
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE.txt
10
+
11
+ # What is PUI
12
+ PUI is a reactive/declarative UI framework with two-way data binding.
13
+ PUI doesn't do UI itself, it turns imperative UI libraries into reactive/declarative flavor with virtual DOM and aims to maintain interoperability.
14
+
15
+ [Slides for SciWork 2023](https://speakerdeck.com/buganini/pui-declarative-ui-framework-for-python)
16
+
17
+ [CPPUI: Experimental C++ Version](https://github.com/buganini/CPPUI)
18
+
19
+ # Installation
20
+ ```
21
+ pip install QPUIQ
22
+ ```
23
+
24
+ # Get Started
25
+ ## Hello World
26
+ ```python
27
+ # example/hello_world.py
28
+ from PUI.PySide6 import *
29
+
30
+ @PUIApp
31
+ def Example():
32
+ with Window(title="test", size=(320,240)):
33
+ Label("Hello world")
34
+
35
+ root = Example()
36
+ root.run()
37
+ ```
38
+ ![Hello World](screenshots/hello_world.png)
39
+
40
+ ## State & Data Binding
41
+ ```python
42
+ # example/generic_textfield.py
43
+ from PUI.PySide6 import *
44
+
45
+ data = State()
46
+ class Example(Application):
47
+ def __init__(self):
48
+ super().__init__()
49
+ data.var = 0
50
+
51
+ def content(self):
52
+ with Window(title="blah"):
53
+ with VBox():
54
+ with HBox():
55
+ Button("-").click(self.on_minus)
56
+ Label(f"{data.var}")
57
+ Button("+").click(self.on_plus)
58
+
59
+ TextField(data("var")) # binding
60
+
61
+ def on_minus(self):
62
+ data.var -= 1
63
+
64
+ def on_plus(self):
65
+ data.var += 1
66
+
67
+ root = Example()
68
+ root.run()
69
+ ```
70
+ ![State & Data Binding](screenshots/pyside6_textfield.png)
71
+
72
+ ## View Component
73
+ ```python
74
+ # example/bleak_list.py
75
+
76
+ ....
77
+
78
+ @PUI # View Component
79
+ def DeviceView(device, advertising_data):
80
+ Label(f"{device.address} {device.name} {advertising_data.rssi}")
81
+
82
+ class GUI(Application):
83
+ def __init__(self, state):
84
+ super().__init__()
85
+ self.state = state
86
+
87
+ def content(self):
88
+ with Window(title="BLE List"):
89
+ with VBox():
90
+ Label(f"Found {len(self.state.scanned_devices)} devices")
91
+ for device, advertising_data in self.state.scanned_devices:
92
+ DeviceView(device, advertising_data)
93
+
94
+ ....
95
+ ```
96
+ ![View Component](screenshots/bleak_list.png)
97
+
98
+ ## Layout & Styling
99
+ ```python
100
+ # example/pyside6_feedparser.py
101
+
102
+ ...
103
+ with VBox():
104
+ Label(title).qt(StyleSheet={"font-weight":"bold"}) # QT-specific
105
+
106
+ with HBox():
107
+ with Scroll():
108
+ with VBox():
109
+ for i,e in enumerate(entries):
110
+ Label(e.title).click(self.entry_selected, i)
111
+ Spacer()
112
+
113
+ with Scroll().layout(weight=1): # Generic Layout Parameter
114
+ if 0 <= selected and selected < len(entries):
115
+ (Text(entries[selected].description)
116
+ .layout(padding=10) # Generic Layout Parameter
117
+ .qt(StyleSheet={"background-color":"white", "color":"black"})) # QT-specific
118
+ ...
119
+ ```
120
+ ![Layout & Styling](screenshots/feed_parser_padding.png)
121
+
122
+
123
+ ## Canvas
124
+ ```python
125
+ # example/generic_canvas.py
126
+
127
+ from PUI.PySide6 import *
128
+
129
+ data = State()
130
+ class Example(Application):
131
+ def __init__(self):
132
+ super().__init__()
133
+ data.var = 50
134
+
135
+ def content(self):
136
+ with Window(title="blah", size=(640,480)):
137
+ with VBox():
138
+ Canvas(self.painter, data.var)
139
+ with HBox():
140
+ Button("-").click(self.on_minus)
141
+ Label(f"{data.var}").layout(weight=1)
142
+ Button("+").click(self.on_plus)
143
+
144
+ @staticmethod
145
+ def painter(canvas, var):
146
+ canvas.drawText(var, var/2, f"blah {var}")
147
+ canvas.drawLine(var, var, var*2, var*3, color=0xFFFF00)
148
+
149
+ def on_minus(self):
150
+ data.var -= 1
151
+
152
+ def on_plus(self):
153
+ data.var += 1
154
+
155
+ root = Example()
156
+ root.run()
157
+ ```
158
+ ![Canvas](screenshots/pyside6_canvas.gif)
159
+
160
+ ## Cookbook
161
+ `python -m cookbook PySide6` (requires pygments for syntax highlight)
162
+
163
+ ![Cookbook 1](screenshots/cookbook1.png)
164
+ ![Cookbook 2](screenshots/cookbook2.png)
165
+
166
+ `python -m cookbook textual`
167
+ ![Cookbook textual](screenshots/cookbook_textual.png)
168
+
169
+ `python -m cookbook flet`
170
+ ![Cookbook flet](screenshots/cookbook_flet.png)
171
+
172
+ `python -m cookbook tkinter`
173
+ ![Cookbook tkinter](screenshots/cookbook_tkinter.png)
174
+
175
+ # Hot Reload
176
+ ``` shell
177
+ pip install jurigged
178
+ ```
179
+ Then PUI will take care of view update [(code)](https://github.com/buganini/PUI/blob/main/PUI/__init__.py#L11)
180
+
181
+
182
+ # Backends
183
+ ## Tier-1
184
+ * PySide6
185
+ ## Lower Priority
186
+ * wx
187
+ * tkinter
188
+ * or https://github.com/rdbende/Sun-Valley-ttk-theme
189
+ * flet
190
+ * textual (Text Mode)
191
+ * no canvas
192
+
193
+ # Components
194
+ [Reference](REFERENCE.md)
195
+
196
+ # Used by
197
+ * https://github.com/buganini/kikit-ui
198
+ * https://github.com/solvcon/modmesh
199
+
200
+ # TODO
201
+ * Merge node and view
202
+ * Dump with layout parameters and add test cases
203
+ * [Toga](https://beeware.org/project/projects/libraries/toga/)
204
+ * [ISSUE] textual layout sizing (cookbook scroll example)
205
+ * [ISSUE] flet layout sizing (cookbook scroll example)
206
+ * nested state trigger
207
+ * set state in PUIView __init__
208
+ * set state in setup() ?
209
+ * Tabs(`tabposition`)
210
+ * Lazy List
211
+ * StateObject decorator
212
+ * UI Flow
213
+ * Navigation Stack
214
+ * View Router
215
+ * Model Window/Dialog
216
+ * Layout
217
+ * ZBox
218
+ * SwiftUI style overlay ??
219
+ * Canvas
220
+ * Rect
221
+ * Arc
222
+ * Image
223
+ * ...
224
+ * Table
225
+ * Tree
226
+ * Dialog
227
+ * State with Pydantic support?
@@ -0,0 +1,49 @@
1
+ from PySide6.QtWidgets import QSizePolicy, QLayout
2
+
3
+ from .application import *
4
+ from .button import *
5
+ from .canvas import *
6
+ from .checkbox import *
7
+ from .combobox import *
8
+ from .dialog import *
9
+ from .divider import *
10
+ from .image import *
11
+ from .label import *
12
+ from .layout import *
13
+ from .modal import *
14
+ from .matplotlib import *
15
+ from .progressbar import *
16
+ from .radiobutton import *
17
+ from .scroll import *
18
+ from .splitter import *
19
+ from .table import *
20
+ from .tab import *
21
+ from .text import *
22
+ from .textfield import *
23
+ from .tree import *
24
+ from .window import *
25
+ from .mdi import *
26
+ from .toolbar import *
27
+
28
+ PUIView = QtPUIView
29
+
30
+ def PUI(func):
31
+ """
32
+ PUI.PySide6.PUI triggers update() by signal/slot
33
+ """
34
+ def func_wrapper(*args, **kwargs):
35
+ class PUIViewWrapper(QtPUIView):
36
+ pui_virtual = True
37
+ def __init__(self, name):
38
+ self.name = name
39
+ super().__init__()
40
+
41
+ def content(self):
42
+ return func(*args, **kwargs)
43
+
44
+ ret = PUIViewWrapper(func.__name__)
45
+ return ret
46
+
47
+ return func_wrapper
48
+
49
+ PUI_BACKEND = "PySide6"
@@ -0,0 +1,58 @@
1
+ from .. import *
2
+ from .base import *
3
+
4
+ class QtApplicationSignal(QtCore.QObject):
5
+ quit = QtCore.Signal()
6
+
7
+ class Application(QtPUIView):
8
+ def __init__(self, icon=None):
9
+ super().__init__()
10
+ self.ui = None
11
+ self.icon = icon
12
+ self._qtappsignal = QtApplicationSignal()
13
+ self._qtappsignal.quit.connect(self._quit, QtCore.Qt.ConnectionType.QueuedConnection) # Use QueuedConnection to prevent nested trigger
14
+
15
+ def redraw(self):
16
+ if self.ui:
17
+ super().redraw()
18
+ else:
19
+ self.sync()
20
+
21
+ def update(self, prev=None):
22
+ if not self.ui:
23
+ from PySide6 import QtWidgets
24
+ self.ui = QtWidgets.QApplication([])
25
+ if self.icon:
26
+ self.ui.setWindowIcon(QtGui.QIcon(self.icon))
27
+
28
+ super().update(prev)
29
+
30
+ def addChild(self, idx, child):
31
+ child.outer.show()
32
+
33
+ def removeChild(self, idx, child):
34
+ child.outer.close()
35
+
36
+ def start(self):
37
+ self.ui.exec()
38
+
39
+ def quit(self):
40
+ self._qtappsignal.quit.emit()
41
+
42
+ def _quit(self):
43
+ self.ui.quit()
44
+
45
+ def PUIApp(func):
46
+ def func_wrapper(*args, **kwargs):
47
+ class PUIAppWrapper(Application):
48
+ def __init__(self, name):
49
+ self.name = name
50
+ super().__init__()
51
+
52
+ def content(self):
53
+ return func(*args, **kwargs)
54
+
55
+ ret = PUIAppWrapper(func.__name__)
56
+ return ret
57
+
58
+ return func_wrapper
@@ -0,0 +1,222 @@
1
+ from .. import *
2
+ from PySide6 import QtCore, QtWidgets, QtGui
3
+
4
+ class QtViewSignal(QtCore.QObject):
5
+ redraw = QtCore.Signal()
6
+
7
+ def _apply_params(ui, node):
8
+ styles = {}
9
+ if node.style_fontsize:
10
+ styles["font"] = f"{node.style_fontsize}pt"
11
+ if node.style_fontfamily:
12
+ styles["font-family"] = node.style_fontfamily
13
+ if node.style_color:
14
+ styles["color"] = f"#{node.style_color:06X}"
15
+ if node.style_bgcolor:
16
+ styles["background-color"] = f"#{node.style_bgcolor:06X}"
17
+
18
+ style = node.qt_params.get("Style")
19
+ if not style is None:
20
+ ui.setStyle(style)
21
+
22
+ HorizontalPolicy = node.qt_params.get("HorizontalPolicy")
23
+ if not HorizontalPolicy is None:
24
+ ui.sizePolicy().setHorizontalPolicy(HorizontalPolicy)
25
+
26
+ VerticalPolicy = node.qt_params.get("VerticalPolicy")
27
+ if not VerticalPolicy is None:
28
+ ui.sizePolicy().setVerticalPolicy(VerticalPolicy)
29
+
30
+ SizeConstraint = node.qt_params.get("SizeConstraint")
31
+ if not SizeConstraint is None:
32
+ ui.setSizeConstraint(SizeConstraint)
33
+
34
+ StyleSheet = node.qt_params.get("StyleSheet", {})
35
+ for k,v in StyleSheet.items():
36
+ styles[k] = v
37
+
38
+ if hasattr(ui, "setStyleSheet"):
39
+ ui.setStyleSheet("".join([f"{k}:{v};" for k,v in styles.items()]))
40
+
41
+ if node.layout_padding:
42
+ ui.setContentsMargins(*trbl2ltrb(node.layout_padding))
43
+
44
+ class QtPUIView(PUIView):
45
+ pui_virtual = True
46
+ def __init__(self):
47
+ super().__init__()
48
+ self.qt_params = {}
49
+ self._qtsignal = QtViewSignal()
50
+ self._qtsignal.redraw.connect(self.sync, QtCore.Qt.ConnectionType.QueuedConnection) # Use QueuedConnection to prevent nested trigger
51
+
52
+ def destroy(self, direct):
53
+ if direct:
54
+ if self.ui: # PUIView doesn't have ui
55
+ self.ui.deleteLater()
56
+ self.ui = None
57
+ super().destroy(direct)
58
+
59
+ def redraw(self):
60
+ self.dirty = True
61
+ if self.updating:
62
+ return
63
+ self.updating = True
64
+ self._qtsignal.redraw.emit()
65
+
66
+ def update(self, prev=None):
67
+ super().update(prev)
68
+ _apply_params(self.ui, self)
69
+
70
+ def qt(self, **kwargs):
71
+ for k,v in kwargs.items():
72
+ self.qt_params[k] = v
73
+ return self
74
+
75
+ class QtBaseWidget(PUINode):
76
+ pui_terminal = True
77
+
78
+ def __init__(self):
79
+ super().__init__()
80
+ self.qt_params = {}
81
+
82
+ def destroy(self, direct):
83
+ if direct:
84
+ if self.ui:
85
+ self.ui.deleteLater()
86
+ self.ui = None
87
+ super().destroy(direct)
88
+
89
+ def update(self, prev=None):
90
+ super().update(prev)
91
+
92
+ sizePolicy = self.ui.sizePolicy()
93
+ if self.layout_width is not None:
94
+ sizePolicy.setHorizontalPolicy(QtWidgets.QSizePolicy.Preferred)
95
+ if self.layout_height is not None:
96
+ sizePolicy.setVerticalPolicy(QtWidgets.QSizePolicy.Preferred)
97
+ self.ui.setSizePolicy(sizePolicy)
98
+
99
+ if not hasattr(self.ui, "origSizeHint"):
100
+ self.ui.origSizeHint = self.ui.sizeHint
101
+ self.ui.sizeHint = self.qtSizeHint
102
+
103
+ _apply_params(self.ui, self)
104
+
105
+ def qtSizeHint(self):
106
+ node = self.get_node()
107
+ if not node.ui:
108
+ return QtCore.QSize(0, 0)
109
+ sh = node.ui.origSizeHint()
110
+ w = sh.width()
111
+ h = sh.height()
112
+ if not node.layout_width is None:
113
+ w = node.layout_width
114
+ if not node.layout_height is None:
115
+ h = node.layout_height
116
+ return QtCore.QSize(w, h)
117
+
118
+ def qt(self, **kwargs):
119
+ for k,v in kwargs.items():
120
+ self.qt_params[k] = v
121
+ return self
122
+
123
+ class QtBaseLayout(PUINode):
124
+ def __init__(self):
125
+ super().__init__()
126
+ self.qt_params = {}
127
+ if not isinstance(self.non_virtual_parent, QtBaseLayout):
128
+ self.layout_padding = (11,11,11,11)
129
+
130
+ @property
131
+ def outer(self):
132
+ return self.ui
133
+
134
+ @property
135
+ def inner(self):
136
+ return self.layout
137
+
138
+ def destroy(self, direct):
139
+ if direct:
140
+ if self.ui:
141
+ self.ui.deleteLater()
142
+ self.layout = None
143
+ self.ui = None
144
+ super().destroy(direct)
145
+
146
+ def update(self, prev=None):
147
+ super().update(prev)
148
+ _apply_params(self.ui, self)
149
+
150
+ def addChild(self, idx, child):
151
+ from .modal import Modal
152
+ from .layout import Spacer
153
+ if isinstance(child, Spacer):
154
+ self.layout.insertItem(idx, child.outer)
155
+ elif isinstance(child, Modal):
156
+ pass
157
+ elif isinstance(child, QtBaseWidget) or isinstance(child, QtBaseLayout):
158
+ params = {}
159
+ if not child.layout_weight is None:
160
+ params["stretch"] = child.layout_weight
161
+ self.layout.insertWidget(idx, child.outer, **params)
162
+
163
+ def removeChild(self, idx, child):
164
+ from .modal import Modal
165
+ from .layout import Spacer
166
+ if isinstance(child, Spacer):
167
+ self.layout.removeItem(child.outer)
168
+ elif isinstance(child, Modal):
169
+ pass
170
+ elif isinstance(child, QtBaseWidget) or isinstance(child, QtBaseLayout):
171
+ child.outer.setParent(None)
172
+
173
+ def qt(self, **kwargs):
174
+ for k,v in kwargs.items():
175
+ self.qt_params[k] = v
176
+ return self
177
+
178
+ class QtBaseFrame(QtBaseWidget):
179
+ pui_terminal = False
180
+
181
+ def destroy(self, direct):
182
+ if direct:
183
+ if self.ui:
184
+ self.ui.deleteLater()
185
+ self.ui = None
186
+
187
+ def addChild(self, idx, child):
188
+ if idx != 0:
189
+ return
190
+ if isinstance(child, QtBaseWidget) or isinstance(child, QtBaseLayout):
191
+ self.ui.setWidget(child.outer)
192
+ elif child.children:
193
+ self.addChild(idx, child.children[0])
194
+
195
+ def removeChild(self, idx, child):
196
+ if idx != 0:
197
+ return
198
+ if isinstance(child, QtBaseWidget) or isinstance(child, QtBaseLayout):
199
+ child.outer.setParent(None)
200
+ elif child.children:
201
+ self.removeChild(idx, child.children[0])
202
+
203
+ class QtInPui(QtBaseWidget):
204
+ def __init__(self, widget, *args):
205
+ self._internal_tag = str(id(widget))
206
+ super().__init__(*args)
207
+ self.ui = widget
208
+
209
+ def destroy(self, direct):
210
+ pass
211
+
212
+ class PuiInQt(QtPUIView):
213
+ def __init__(self, ui):
214
+ super().__init__()
215
+ self.ui = ui
216
+ self.ui.update()
217
+
218
+ def addChild(self, idx, child):
219
+ self.ui.addChild(idx, child)
220
+
221
+ def removeChild(self, idx, child):
222
+ self.ui.removeChild(idx, child)
@@ -0,0 +1,21 @@
1
+ from .. import *
2
+ from .base import *
3
+
4
+ class Button(QtBaseWidget):
5
+ def __init__(self, text):
6
+ super().__init__()
7
+ self.text = text
8
+
9
+ def update(self, prev):
10
+ if prev and prev.ui:
11
+ self.ui = prev.ui
12
+ self.ui.setText(self.text)
13
+ try:
14
+ self.ui.clicked.disconnect()
15
+ except:
16
+ pass
17
+ prev.callback = None
18
+ else:
19
+ self.ui = QtWidgets.QPushButton(text=self.text)
20
+ self.ui.clicked.connect(self._clicked)
21
+ super().update(prev)