qpuiq 0.10__py3-none-any.whl
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.
Potentially problematic release.
This version of qpuiq might be problematic. Click here for more details.
- PUI/PySide6/__init__.py +49 -0
- PUI/PySide6/application.py +58 -0
- PUI/PySide6/base.py +222 -0
- PUI/PySide6/button.py +21 -0
- PUI/PySide6/canvas.py +288 -0
- PUI/PySide6/checkbox.py +32 -0
- PUI/PySide6/combobox.py +75 -0
- PUI/PySide6/dialog.py +72 -0
- PUI/PySide6/divider.py +23 -0
- PUI/PySide6/image.py +30 -0
- PUI/PySide6/label.py +33 -0
- PUI/PySide6/layout.py +72 -0
- PUI/PySide6/matplotlib.py +23 -0
- PUI/PySide6/mdi.py +33 -0
- PUI/PySide6/menu.py +85 -0
- PUI/PySide6/modal.py +132 -0
- PUI/PySide6/progressbar.py +17 -0
- PUI/PySide6/radiobutton.py +29 -0
- PUI/PySide6/scroll.py +153 -0
- PUI/PySide6/splitter.py +25 -0
- PUI/PySide6/tab.py +39 -0
- PUI/PySide6/table.py +89 -0
- PUI/PySide6/text.py +35 -0
- PUI/PySide6/textfield.py +62 -0
- PUI/PySide6/toolbar.py +57 -0
- PUI/PySide6/tree.py +120 -0
- PUI/PySide6/window.py +81 -0
- PUI/__init__.py +46 -0
- PUI/common.py +20 -0
- PUI/decorator.py +20 -0
- PUI/dom.py +238 -0
- PUI/flet/__init__.py +21 -0
- PUI/flet/application.py +42 -0
- PUI/flet/base.py +37 -0
- PUI/flet/button.py +20 -0
- PUI/flet/canvas.py +86 -0
- PUI/flet/checkbox.py +23 -0
- PUI/flet/label.py +27 -0
- PUI/flet/layout.py +50 -0
- PUI/flet/progressbar.py +21 -0
- PUI/flet/radiobutton.py +27 -0
- PUI/flet/scroll.py +83 -0
- PUI/flet/tab.py +42 -0
- PUI/flet/text.py +55 -0
- PUI/flet/textfield.py +58 -0
- PUI/flet/window.py +25 -0
- PUI/interfaces.py +97 -0
- PUI/node.py +407 -0
- PUI/state.py +698 -0
- PUI/textual/__init__.py +34 -0
- PUI/textual/application.py +82 -0
- PUI/textual/base.py +113 -0
- PUI/textual/button.py +17 -0
- PUI/textual/checkbox.py +21 -0
- PUI/textual/label.py +36 -0
- PUI/textual/layout.py +48 -0
- PUI/textual/progressbar.py +17 -0
- PUI/textual/radiobutton.py +24 -0
- PUI/textual/scroll.py +72 -0
- PUI/textual/tab.py +75 -0
- PUI/textual/text.py +32 -0
- PUI/textual/textfield.py +49 -0
- PUI/textual/window.py +7 -0
- PUI/timeline.py +36 -0
- PUI/tkinter/__init__.py +43 -0
- PUI/tkinter/application.py +49 -0
- PUI/tkinter/base.py +68 -0
- PUI/tkinter/button.py +15 -0
- PUI/tkinter/canvas.py +49 -0
- PUI/tkinter/checkbox.py +27 -0
- PUI/tkinter/label.py +17 -0
- PUI/tkinter/layout.py +114 -0
- PUI/tkinter/progressbar.py +17 -0
- PUI/tkinter/radiobutton.py +26 -0
- PUI/tkinter/scroll.py +201 -0
- PUI/tkinter/tab.py +52 -0
- PUI/tkinter/text.py +20 -0
- PUI/tkinter/textfield.py +53 -0
- PUI/tkinter/window.py +51 -0
- PUI/utils.py +15 -0
- PUI/view.py +161 -0
- PUI/wx/__init__.py +19 -0
- PUI/wx/application.py +44 -0
- PUI/wx/base.py +202 -0
- PUI/wx/button.py +16 -0
- PUI/wx/canvas.py +255 -0
- PUI/wx/checkbox.py +25 -0
- PUI/wx/combobox.py +72 -0
- PUI/wx/dialog.py +66 -0
- PUI/wx/divider.py +19 -0
- PUI/wx/label.py +18 -0
- PUI/wx/layout.py +46 -0
- PUI/wx/progressbar.py +17 -0
- PUI/wx/radiobutton.py +27 -0
- PUI/wx/scroll.py +44 -0
- PUI/wx/text.py +23 -0
- PUI/wx/textfield.py +56 -0
- PUI/wx/window.py +58 -0
- qpuiq-0.10.dist-info/LICENSE.txt +21 -0
- qpuiq-0.10.dist-info/METADATA +227 -0
- qpuiq-0.10.dist-info/RECORD +103 -0
- qpuiq-0.10.dist-info/WHEEL +5 -0
- qpuiq-0.10.dist-info/top_level.txt +1 -0
PUI/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
__version__ = "0.10"
|
|
2
|
+
|
|
3
|
+
from .node import *
|
|
4
|
+
from .view import *
|
|
5
|
+
from .state import *
|
|
6
|
+
from .timeline import *
|
|
7
|
+
from .decorator import *
|
|
8
|
+
from .common import *
|
|
9
|
+
from .interfaces import *
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import jurigged
|
|
13
|
+
w = jurigged.watch("/")
|
|
14
|
+
def postrun(path, cf):
|
|
15
|
+
PUIView.reload()
|
|
16
|
+
w.postrun.register(postrun)
|
|
17
|
+
except ImportError:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
class Prop():
|
|
21
|
+
def __init__(self, value=None):
|
|
22
|
+
self.value = value
|
|
23
|
+
|
|
24
|
+
def set(self, value):
|
|
25
|
+
changed = (self.value != value)
|
|
26
|
+
self.value = value
|
|
27
|
+
return changed
|
|
28
|
+
|
|
29
|
+
class NotImplementedNode():
|
|
30
|
+
def __init__(self, *args, **kwargs):
|
|
31
|
+
print("Not Implemented")
|
|
32
|
+
import traceback
|
|
33
|
+
import inspect
|
|
34
|
+
traceback.print_stack(inspect.currentframe().f_back, 1)
|
|
35
|
+
|
|
36
|
+
def __enter__(self):
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def __exit__(self, *args):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def layout(self, *args, **kwargs):
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
def style(self, *args, **kwargs):
|
|
46
|
+
return self
|
PUI/common.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
class Anchor(Enum):
|
|
4
|
+
LEFT_TOP = ("left" ,"top")
|
|
5
|
+
LEFT_CENTER = ("left" ,"center")
|
|
6
|
+
LEFT_BOTTOM = ("left" ,"bottom")
|
|
7
|
+
CENTER_TOP = ("center" ,"top")
|
|
8
|
+
CENTER = ("center" ,"center")
|
|
9
|
+
CENTER_BOTTOM = ("center" ,"bottom")
|
|
10
|
+
RIGHT_TOP = ("right" ,"top")
|
|
11
|
+
RIGHT_CENTER = ("right" ,"center")
|
|
12
|
+
RIGHT_BOTTOM = ("right" ,"bottom")
|
|
13
|
+
|
|
14
|
+
class MouseButton(IntEnum):
|
|
15
|
+
NONE = 0
|
|
16
|
+
LEFT = 1
|
|
17
|
+
RIGHT = 2
|
|
18
|
+
MIDDLE = 4
|
|
19
|
+
X1 = 8
|
|
20
|
+
X2 = 16
|
PUI/decorator.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .view import *
|
|
2
|
+
|
|
3
|
+
def PUI(func):
|
|
4
|
+
"""
|
|
5
|
+
PUI.PUI triggers update() directly by redraw()
|
|
6
|
+
"""
|
|
7
|
+
def func_wrapper(*args, **kwargs):
|
|
8
|
+
class PUIViewWrapper(PUIView):
|
|
9
|
+
pui_virtual = True
|
|
10
|
+
def __init__(self, name):
|
|
11
|
+
self.name = name
|
|
12
|
+
super().__init__()
|
|
13
|
+
|
|
14
|
+
def content(self):
|
|
15
|
+
return func(*args, **kwargs)
|
|
16
|
+
|
|
17
|
+
ret = PUIViewWrapper(func.__name__)
|
|
18
|
+
return ret
|
|
19
|
+
|
|
20
|
+
return func_wrapper
|
PUI/dom.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
DEBUG = False
|
|
2
|
+
|
|
3
|
+
class VDomError(Exception):
|
|
4
|
+
pass
|
|
5
|
+
|
|
6
|
+
def recur_delete(node, child, direct):
|
|
7
|
+
child.destroyed = True
|
|
8
|
+
for sc in child.children:
|
|
9
|
+
if sc is not None:
|
|
10
|
+
recur_delete(child, sc, False)
|
|
11
|
+
if DEBUG:
|
|
12
|
+
print("recur_delete", node.key, child.key, direct)
|
|
13
|
+
child.destroy(direct)
|
|
14
|
+
|
|
15
|
+
def sortGridDOMInPlace(dom):
|
|
16
|
+
dom[:] = [c for c in dom if c.grid_row is not None and c.grid_column is not None]
|
|
17
|
+
dom.sort(key=lambda c:(c.grid_row, c.grid_column, c.grid_rowspan, c.grid_columnspan))
|
|
18
|
+
|
|
19
|
+
def dom_remove_node(dom_parent, dom_offset, child):
|
|
20
|
+
if child.pui_virtual:
|
|
21
|
+
ret = [child]
|
|
22
|
+
for c in child.children:
|
|
23
|
+
ret.extend(dom_remove_node(dom_parent, dom_offset, c))
|
|
24
|
+
return ret
|
|
25
|
+
else:
|
|
26
|
+
dom_parent.removeChild(dom_offset , child)
|
|
27
|
+
return [child]
|
|
28
|
+
|
|
29
|
+
def dom_add_nodes(dom_parent, dom_offset, children):
|
|
30
|
+
for childIdx, c in enumerate([n for n in children if not n.pui_virtual]):
|
|
31
|
+
dom_parent.addChild(dom_offset+childIdx, c)
|
|
32
|
+
|
|
33
|
+
def countDomChildren(nodes):
|
|
34
|
+
num = 0
|
|
35
|
+
for c in nodes:
|
|
36
|
+
if c.pui_virtual:
|
|
37
|
+
num += countDomChildren(c.children)
|
|
38
|
+
elif not c.pui_outoforder:
|
|
39
|
+
num += 1
|
|
40
|
+
return num
|
|
41
|
+
|
|
42
|
+
def sync(node, dom_parent, dom_offset, oldVDOM, newVDOM, depth=0):
|
|
43
|
+
orig_dom_children_num = dom_children_num = countDomChildren(oldVDOM)
|
|
44
|
+
dom_children_curr = 0
|
|
45
|
+
|
|
46
|
+
if DEBUG:
|
|
47
|
+
print(f"{(depth)*' '}Syncing {node.key}@{id(node)} parent={dom_parent.key}@{id(dom_parent)} dom_offset={dom_offset} dom_children_num={dom_children_num} old={len(oldVDOM)} new={len(newVDOM)}")
|
|
48
|
+
|
|
49
|
+
if node.pui_grid_layout:
|
|
50
|
+
sortGridDOMInPlace(oldVDOM)
|
|
51
|
+
sortGridDOMInPlace(newVDOM)
|
|
52
|
+
|
|
53
|
+
if DEBUG:
|
|
54
|
+
print(f"{(depth+1)*' '}===OLD===")
|
|
55
|
+
for c in oldVDOM:
|
|
56
|
+
print(f"{(depth+1)*' '}{c.key} virtual={c.pui_virtual} ui={c.ui}")
|
|
57
|
+
|
|
58
|
+
print(f"{(depth+1)*' '}===NEW===")
|
|
59
|
+
for c in newVDOM:
|
|
60
|
+
print(f"{(depth+1)*' '}{c.key} virtual={c.pui_virtual}")
|
|
61
|
+
|
|
62
|
+
oldVMap = [x.key for x in oldVDOM]
|
|
63
|
+
newVMap = [x.key for x in newVDOM]
|
|
64
|
+
|
|
65
|
+
node.preSync()
|
|
66
|
+
|
|
67
|
+
toBeDeleted = []
|
|
68
|
+
for childIdx, new in enumerate(newVDOM):
|
|
69
|
+
if DEBUG:
|
|
70
|
+
print(f"{(depth+1)*' '}sync child {childIdx}, {new.key} dom_parent={dom_parent.key} virtual={new.pui_virtual}")
|
|
71
|
+
new.pui_dom_parent = dom_parent
|
|
72
|
+
|
|
73
|
+
while True:
|
|
74
|
+
# Step 1. just matched
|
|
75
|
+
if childIdx < len(oldVDOM) and oldVMap[childIdx] == new.key: # matched
|
|
76
|
+
if DEBUG:
|
|
77
|
+
print(f"{(depth+1)*' '}S1. MATCHED {childIdx} {new.key}")
|
|
78
|
+
old = oldVDOM[childIdx]
|
|
79
|
+
|
|
80
|
+
if old.pui_isview: # must also be virtual
|
|
81
|
+
n = countDomChildren(old.children)
|
|
82
|
+
dom_children_curr += n
|
|
83
|
+
if DEBUG:
|
|
84
|
+
print(f"{(depth+1)*' '} dom_children_curr += {n} => {dom_children_curr} dom_children_num={dom_children_num}")
|
|
85
|
+
|
|
86
|
+
old.parent = new.parent
|
|
87
|
+
old.pui_dom_parent = new.pui_dom_parent
|
|
88
|
+
newVDOM[childIdx] = old
|
|
89
|
+
new.destroy(True) # deregister old view from PUIView.__ALLVIEWS__
|
|
90
|
+
else:
|
|
91
|
+
try:
|
|
92
|
+
new.update(old)
|
|
93
|
+
except:
|
|
94
|
+
import traceback
|
|
95
|
+
print("## <ERROR OF update() >")
|
|
96
|
+
print(new.key)
|
|
97
|
+
traceback.print_exc()
|
|
98
|
+
print("## </ERROR OF update()>")
|
|
99
|
+
|
|
100
|
+
if new.pui_virtual:
|
|
101
|
+
num, delta = sync(new, node, dom_offset + dom_children_curr, old.children, new.children, depth+1)
|
|
102
|
+
dom_children_curr += num
|
|
103
|
+
dom_children_num += delta
|
|
104
|
+
if DEBUG:
|
|
105
|
+
print(f"{(depth+2)*' '}dom_children_curr += {num} => {dom_children_curr} dom_children_num += {delta} => {dom_children_num}")
|
|
106
|
+
else:
|
|
107
|
+
if not new.pui_outoforder:
|
|
108
|
+
dom_children_curr += 1
|
|
109
|
+
if DEBUG:
|
|
110
|
+
print(f"{(depth+2)*' '}dom_children_curr += 1 => {dom_children_curr} dom_children_num={dom_children_num}")
|
|
111
|
+
|
|
112
|
+
if not new.pui_terminal:
|
|
113
|
+
sync(new, new, 0, old.children, new.children, depth+1)
|
|
114
|
+
|
|
115
|
+
break # finish
|
|
116
|
+
|
|
117
|
+
# Step 2. trim removed nodes after common prefix
|
|
118
|
+
trimmed = False
|
|
119
|
+
while childIdx < len(oldVDOM) and not oldVDOM[childIdx].key in newVMap[childIdx:]: # trim old nodes
|
|
120
|
+
if DEBUG:
|
|
121
|
+
print(f"{(depth+1)*' '}S2. TRIM {childIdx} {oldVDOM[childIdx].key}")
|
|
122
|
+
old = oldVDOM.pop(childIdx)
|
|
123
|
+
oldVMap.pop(childIdx)
|
|
124
|
+
nodes = dom_remove_node(dom_parent, dom_offset + dom_children_curr, old)
|
|
125
|
+
n = len([n for n in nodes if not n.pui_virtual and not n.pui_outoforder])
|
|
126
|
+
dom_children_num -= n
|
|
127
|
+
if DEBUG:
|
|
128
|
+
print(f"{(depth+2)*' '}dom_children_num -= {n} => {dom_children_num}")
|
|
129
|
+
toBeDeleted.extend(nodes)
|
|
130
|
+
trimmed = True
|
|
131
|
+
|
|
132
|
+
if trimmed:
|
|
133
|
+
continue # restart
|
|
134
|
+
|
|
135
|
+
# Step 3. setup target node
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
matchedIdx = oldVMap[childIdx+1:].index(new.key) + childIdx + 1
|
|
139
|
+
except ValueError:
|
|
140
|
+
matchedIdx = None
|
|
141
|
+
|
|
142
|
+
## Step 3-1. new node
|
|
143
|
+
if matchedIdx is None:
|
|
144
|
+
if DEBUG:
|
|
145
|
+
print(f"{(depth+1)*' '}S3-1. NEW {childIdx} {new.key}")
|
|
146
|
+
# always populate new node regardless of isview or not
|
|
147
|
+
try:
|
|
148
|
+
new.update(None)
|
|
149
|
+
except:
|
|
150
|
+
import traceback
|
|
151
|
+
print("## <ERROR OF update() >")
|
|
152
|
+
print(new.key)
|
|
153
|
+
traceback.print_exc()
|
|
154
|
+
print("## </ERROR OF update()>")
|
|
155
|
+
|
|
156
|
+
if new.pui_virtual:
|
|
157
|
+
num, delta = sync(new, dom_parent, dom_offset + dom_children_curr, [], new.children, depth+1)
|
|
158
|
+
dom_children_curr += num
|
|
159
|
+
dom_children_num += num
|
|
160
|
+
if DEBUG:
|
|
161
|
+
print(f"{(depth+2)*' '}dom_children_curr += {num} => {dom_children_curr} dom_children_num += {num} => {dom_children_num}")
|
|
162
|
+
else:
|
|
163
|
+
if DEBUG:
|
|
164
|
+
print(f"{(depth+1)*' '}addChild", dom_parent.key, dom_offset + dom_children_curr, new.key)
|
|
165
|
+
dom_parent.addChild(dom_offset + dom_children_curr, new)
|
|
166
|
+
|
|
167
|
+
if not new.pui_outoforder:
|
|
168
|
+
dom_children_curr += 1
|
|
169
|
+
dom_children_num += 1
|
|
170
|
+
if DEBUG:
|
|
171
|
+
print(f"{(depth+2)*' '}dom_children_curr += 1 => {dom_children_curr} dom_children_num += 1 => {dom_children_num}")
|
|
172
|
+
|
|
173
|
+
if not new.pui_terminal:
|
|
174
|
+
sync(new, new, 0, [], new.children, depth+1)
|
|
175
|
+
|
|
176
|
+
oldVDOM.insert(childIdx, new) # put new node back for later findDomOffsetForNode
|
|
177
|
+
oldVMap.insert(childIdx, new.key)
|
|
178
|
+
|
|
179
|
+
## Step 3-2. existed node
|
|
180
|
+
else:
|
|
181
|
+
# if the target node is in just next position, requeue the blocker to prevent repositioning every nodes coming after
|
|
182
|
+
# eg. when an element is removed from a long list, do single 3-2-1 instead of many 3-2-2
|
|
183
|
+
|
|
184
|
+
### Step 3-2-1. yield the next position for the target node
|
|
185
|
+
if matchedIdx == childIdx + 1:
|
|
186
|
+
if DEBUG:
|
|
187
|
+
print(f"{(depth+1)*' '}S3-2-1. YIELD {childIdx} {new.key}")
|
|
188
|
+
oldVMap.pop(childIdx)
|
|
189
|
+
toBeRequeued = oldVDOM.pop(childIdx)
|
|
190
|
+
nodes = dom_remove_node(dom_parent, dom_offset + dom_children_curr, toBeRequeued)
|
|
191
|
+
n = len([n for n in nodes if not n.pui_virtual and not n.pui_outoforder])
|
|
192
|
+
dom_add_nodes(dom_parent, dom_children_num - n, nodes)
|
|
193
|
+
oldVMap.append(toBeRequeued.key)
|
|
194
|
+
oldVDOM.append(toBeRequeued)
|
|
195
|
+
|
|
196
|
+
### Step 3-2-2. move target node
|
|
197
|
+
else:
|
|
198
|
+
if DEBUG:
|
|
199
|
+
print(f"{(depth+1)*' '}S3-2-2. MOVE {childIdx} {new.key}")
|
|
200
|
+
oldVMap.pop(matchedIdx)
|
|
201
|
+
old = oldVDOM.pop(matchedIdx)
|
|
202
|
+
found, offset = dom_parent.findDomOffsetForNode(old)
|
|
203
|
+
if not found:
|
|
204
|
+
raise VDomError(f"findDomOffsetForNode() failed for {old.key} on {dom_parent.key}")
|
|
205
|
+
nodes = dom_remove_node(dom_parent, offset, old)
|
|
206
|
+
dom_add_nodes(dom_parent, dom_offset + dom_children_curr, nodes)
|
|
207
|
+
|
|
208
|
+
oldVDOM.insert(childIdx, new) # put new node back for later findDomOffsetForNode
|
|
209
|
+
oldVMap.insert(childIdx, new.key)
|
|
210
|
+
|
|
211
|
+
continue # restart, sync will be peformed in next step 1
|
|
212
|
+
|
|
213
|
+
break # finish
|
|
214
|
+
|
|
215
|
+
# Step 4. trim removed trail
|
|
216
|
+
nl = len(newVDOM)
|
|
217
|
+
while len(oldVDOM) > nl:
|
|
218
|
+
old = oldVDOM.pop(nl)
|
|
219
|
+
oldVMap.pop(nl)
|
|
220
|
+
nodes = dom_remove_node(dom_parent, dom_offset + nl, old)
|
|
221
|
+
dom_children_num -= len([n for n in nodes if not n.pui_virtual and not n.pui_outoforder])
|
|
222
|
+
toBeDeleted.extend(nodes)
|
|
223
|
+
|
|
224
|
+
for c in newVDOM:
|
|
225
|
+
c.postUpdate()
|
|
226
|
+
|
|
227
|
+
node.postSync()
|
|
228
|
+
|
|
229
|
+
# release deleted nodes
|
|
230
|
+
for old in toBeDeleted:
|
|
231
|
+
recur_delete(node, old, True)
|
|
232
|
+
|
|
233
|
+
if DEBUG:
|
|
234
|
+
print(f"{(depth)*' '}sync end {node.key} -> {dom_children_curr},{dom_children_num}")
|
|
235
|
+
|
|
236
|
+
if dom_children_curr != dom_children_num:
|
|
237
|
+
raise VDomError(f"dom_children_curr != dom_children_num for {node.key} -> {dom_children_curr},{dom_children_num}")
|
|
238
|
+
return dom_children_curr, dom_children_num - orig_dom_children_num
|
PUI/flet/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .application import *
|
|
2
|
+
from .button import *
|
|
3
|
+
from .canvas import *
|
|
4
|
+
from .checkbox import *
|
|
5
|
+
from .label import *
|
|
6
|
+
from .layout import *
|
|
7
|
+
from .progressbar import *
|
|
8
|
+
from .radiobutton import *
|
|
9
|
+
from .scroll import *
|
|
10
|
+
from .tab import *
|
|
11
|
+
from .text import *
|
|
12
|
+
from .textfield import *
|
|
13
|
+
from .window import *
|
|
14
|
+
from .. import NotImplementedNode
|
|
15
|
+
|
|
16
|
+
Combobox = NotImplementedNode
|
|
17
|
+
ComboboxItem = NotImplementedNode
|
|
18
|
+
|
|
19
|
+
PUIView = FView
|
|
20
|
+
|
|
21
|
+
PUI_BACKEND = "flet"
|
PUI/flet/application.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Application(PUIView):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.ready = False
|
|
8
|
+
|
|
9
|
+
def sync(self):
|
|
10
|
+
if not self.ready:
|
|
11
|
+
return
|
|
12
|
+
super().sync()
|
|
13
|
+
|
|
14
|
+
def flet_app(self, page: ft.Page):
|
|
15
|
+
self.ui = page
|
|
16
|
+
self.ready = True
|
|
17
|
+
self.sync()
|
|
18
|
+
|
|
19
|
+
def addChild(self, idx, child):
|
|
20
|
+
if idx > 0:
|
|
21
|
+
print("Flet backend only support single window")
|
|
22
|
+
|
|
23
|
+
def removeChild(self, idx, child):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def start(self):
|
|
27
|
+
ft.app(self.flet_app)
|
|
28
|
+
|
|
29
|
+
def PUIApp(func):
|
|
30
|
+
def func_wrapper(*args, **kwargs):
|
|
31
|
+
class PUIAppWrapper(Application):
|
|
32
|
+
def __init__(self, name):
|
|
33
|
+
self.name = name
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
def content(self):
|
|
37
|
+
return func(*args, **kwargs)
|
|
38
|
+
|
|
39
|
+
ret = PUIAppWrapper(func.__name__)
|
|
40
|
+
return ret
|
|
41
|
+
|
|
42
|
+
return func_wrapper
|
PUI/flet/base.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
import flet as ft
|
|
3
|
+
|
|
4
|
+
def _apply_params(ui, params):
|
|
5
|
+
for k,v in params.items():
|
|
6
|
+
setattr(ui, k, v)
|
|
7
|
+
|
|
8
|
+
class FView(PUIView):
|
|
9
|
+
pui_virtual = True
|
|
10
|
+
|
|
11
|
+
class FBase(PUINode):
|
|
12
|
+
def __init__(self, *args):
|
|
13
|
+
super().__init__(*args)
|
|
14
|
+
self.child_weight = None
|
|
15
|
+
if self.fparent:
|
|
16
|
+
self.layout_weight = self.fparent.child_weight
|
|
17
|
+
|
|
18
|
+
self.flet_params = {}
|
|
19
|
+
|
|
20
|
+
def flet(self, **kwargs):
|
|
21
|
+
for k,v in kwargs.items():
|
|
22
|
+
self.flet_params[k] = v
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def update(self, prev):
|
|
26
|
+
_apply_params(self.ui, self.flet_params)
|
|
27
|
+
super().update(prev)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def fparent(self):
|
|
31
|
+
parent = self.parent
|
|
32
|
+
while True:
|
|
33
|
+
if isinstance(parent, FBase):
|
|
34
|
+
return parent
|
|
35
|
+
if parent.parent == parent:
|
|
36
|
+
return None
|
|
37
|
+
parent = parent.parent
|
PUI/flet/button.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Button(FBase):
|
|
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.text = self.text
|
|
13
|
+
self.ui.on_click = self._clicked
|
|
14
|
+
try:
|
|
15
|
+
self.ui.update()
|
|
16
|
+
except:
|
|
17
|
+
pass
|
|
18
|
+
else:
|
|
19
|
+
self.ui = ft.ElevatedButton(text=self.text, on_click=self._clicked)
|
|
20
|
+
self.ui.expand = self.layout_weight
|
PUI/flet/canvas.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
import flet.canvas as cv
|
|
4
|
+
|
|
5
|
+
class Canvas(FBase):
|
|
6
|
+
def __init__(self, painter, *args):
|
|
7
|
+
super().__init__()
|
|
8
|
+
self.ui = None
|
|
9
|
+
self.painter = painter
|
|
10
|
+
self.args = args
|
|
11
|
+
|
|
12
|
+
def update(self, prev):
|
|
13
|
+
if prev and prev.ui:
|
|
14
|
+
self.ui = prev.ui
|
|
15
|
+
self.ui.puinode = self
|
|
16
|
+
else:
|
|
17
|
+
self.ui = cv.Canvas()
|
|
18
|
+
if self.layout_width:
|
|
19
|
+
self.ui.width = self.layout_width
|
|
20
|
+
else:
|
|
21
|
+
self.ui.width = float("inf")
|
|
22
|
+
if self.layout_height:
|
|
23
|
+
self.ui.height = self.layout_height
|
|
24
|
+
else:
|
|
25
|
+
self.ui.height = float("inf")
|
|
26
|
+
self.ui.shapes.clear()
|
|
27
|
+
self.painter(self, *self.args)
|
|
28
|
+
try:
|
|
29
|
+
self.ui.update()
|
|
30
|
+
except:
|
|
31
|
+
pass
|
|
32
|
+
super().update(prev)
|
|
33
|
+
|
|
34
|
+
def drawText(self, x, y, text, w=None, h=None, rotate=0, anchor=Anchor.LEFT_TOP):
|
|
35
|
+
if rotate !=0:
|
|
36
|
+
print("drawText: rotate not implemented")
|
|
37
|
+
self.ui.shapes.append(
|
|
38
|
+
cv.Text(
|
|
39
|
+
x,
|
|
40
|
+
y,
|
|
41
|
+
text,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def drawLine(self, x1, y1, x2, y2, color=None, width=None):
|
|
46
|
+
params = {}
|
|
47
|
+
if not color is None:
|
|
48
|
+
params["color"] = f"#{color:06X}"
|
|
49
|
+
if not width is None:
|
|
50
|
+
params["stroke_width"] = width
|
|
51
|
+
self.ui.shapes.append(
|
|
52
|
+
cv.Line(
|
|
53
|
+
x1,
|
|
54
|
+
y1,
|
|
55
|
+
x2,
|
|
56
|
+
y2,
|
|
57
|
+
ft.Paint(**params, style=ft.PaintingStyle.STROKE)
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def drawPolyline(self, coords, color=None, width=None):
|
|
62
|
+
params = {}
|
|
63
|
+
if not color is None:
|
|
64
|
+
params["color"] = f"#{color:06X}"
|
|
65
|
+
if not width is None:
|
|
66
|
+
params["stroke_width"] = width
|
|
67
|
+
paint = ft.Paint(**params, style=ft.PaintingStyle.STROKE)
|
|
68
|
+
for i in range(1, len(coords)):
|
|
69
|
+
self.ui.shapes.append(
|
|
70
|
+
cv.Line(
|
|
71
|
+
coords[i-1][0],
|
|
72
|
+
coords[i-1][1],
|
|
73
|
+
coords[i][0],
|
|
74
|
+
coords[i][1],
|
|
75
|
+
paint
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def drawPolygon(self, coords, fill=None, stroke=None, width=1):
|
|
80
|
+
print("drawPolygon not implemented")
|
|
81
|
+
|
|
82
|
+
def drawRect(self, x1, y1, x2, y2, fill=None, stroke=None, width=1):
|
|
83
|
+
print("drawRect not implemented")
|
|
84
|
+
|
|
85
|
+
def drawEllipse(self, x, y, rx, ry, fill=None, stroke=None, width=1):
|
|
86
|
+
print("drawEllipse not implemented")
|
PUI/flet/checkbox.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Checkbox(FBase):
|
|
5
|
+
def __init__(self, text, model):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.text = text
|
|
8
|
+
self.model = model
|
|
9
|
+
|
|
10
|
+
def update(self, prev):
|
|
11
|
+
if prev and prev.ui:
|
|
12
|
+
self.ui = prev.ui
|
|
13
|
+
self.ui.value = self.model.value
|
|
14
|
+
self.ui.on_change = self._changed
|
|
15
|
+
self.ui.update()
|
|
16
|
+
else:
|
|
17
|
+
self.ui = ft.Checkbox(label=self.text, value=self.model.value, on_change=self._changed)
|
|
18
|
+
|
|
19
|
+
super().update(prev)
|
|
20
|
+
|
|
21
|
+
def _changed(self, event):
|
|
22
|
+
node = self.get_node()
|
|
23
|
+
self.model.value = node.ui.value
|
PUI/flet/label.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Label(FBase):
|
|
5
|
+
def __init__(self, text, selectable=False):
|
|
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
|
+
else:
|
|
13
|
+
self.ui = ft.Text(spans=[])
|
|
14
|
+
self.ui.expand = self.layout_weight
|
|
15
|
+
if self._onClicked:
|
|
16
|
+
self.ui.spans = [
|
|
17
|
+
ft.TextSpan(self.text, on_click=self._clicked)
|
|
18
|
+
]
|
|
19
|
+
else:
|
|
20
|
+
self.ui.spans = [
|
|
21
|
+
ft.TextSpan(self.text)
|
|
22
|
+
]
|
|
23
|
+
try:
|
|
24
|
+
self.ui.update()
|
|
25
|
+
except:
|
|
26
|
+
pass
|
|
27
|
+
super().update(prev)
|
PUI/flet/layout.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class HBox(FBase):
|
|
5
|
+
def update(self, prev):
|
|
6
|
+
if prev and prev.ui:
|
|
7
|
+
self.ui = prev.ui
|
|
8
|
+
else:
|
|
9
|
+
self.ui = ft.Row()
|
|
10
|
+
self.ui.expand = self.layout_weight
|
|
11
|
+
super().update(prev)
|
|
12
|
+
|
|
13
|
+
def addChild(self, idx, child):
|
|
14
|
+
self.ui.controls.insert(idx, child.outer)
|
|
15
|
+
try:
|
|
16
|
+
self.ui.update()
|
|
17
|
+
except:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def removeChild(self, idx, child):
|
|
21
|
+
self.ui.controls.pop(idx)
|
|
22
|
+
self.ui.update()
|
|
23
|
+
|
|
24
|
+
class VBox(FBase):
|
|
25
|
+
def update(self, prev):
|
|
26
|
+
if prev and prev.ui:
|
|
27
|
+
self.ui = prev.ui
|
|
28
|
+
else:
|
|
29
|
+
self.ui = ft.Column()
|
|
30
|
+
self.ui.expand = self.layout_weight
|
|
31
|
+
super().update(prev)
|
|
32
|
+
|
|
33
|
+
def addChild(self, idx, child):
|
|
34
|
+
self.ui.controls.insert(idx, child.outer)
|
|
35
|
+
try:
|
|
36
|
+
self.ui.update()
|
|
37
|
+
except:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def removeChild(self, idx, child):
|
|
41
|
+
self.ui.controls.pop(idx)
|
|
42
|
+
self.ui.update()
|
|
43
|
+
|
|
44
|
+
class Spacer(FBase):
|
|
45
|
+
def update(self, prev):
|
|
46
|
+
if prev and prev.ui:
|
|
47
|
+
self.ui = prev.ui
|
|
48
|
+
else:
|
|
49
|
+
self.ui = ft.Container()
|
|
50
|
+
super().update(prev)
|
PUI/flet/progressbar.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class ProgressBar(FBase):
|
|
5
|
+
def __init__(self, progress, maximum=1):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.progress = progress
|
|
8
|
+
self.maximum = maximum
|
|
9
|
+
|
|
10
|
+
def update(self, prev):
|
|
11
|
+
if prev and prev.ui:
|
|
12
|
+
self.ui = prev.ui
|
|
13
|
+
else:
|
|
14
|
+
self.ui = ft.ProgressBar(width=300) # XXX
|
|
15
|
+
self.ui.value = max(self.progress / self.maximum, 0)
|
|
16
|
+
self.ui.expand = self.layout_weight
|
|
17
|
+
try:
|
|
18
|
+
self.ui.update()
|
|
19
|
+
except:
|
|
20
|
+
pass
|
|
21
|
+
super().update(prev)
|
PUI/flet/radiobutton.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class RadioButton(FBase):
|
|
5
|
+
def __init__(self, text, value, model):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.text = text
|
|
8
|
+
self.value = value
|
|
9
|
+
self.model = model
|
|
10
|
+
|
|
11
|
+
def update(self, prev):
|
|
12
|
+
if prev and prev.ui:
|
|
13
|
+
self.radio = prev.radio
|
|
14
|
+
self.ui = prev.ui
|
|
15
|
+
self.radio.value = self.value
|
|
16
|
+
self.ui.value = self.model.value
|
|
17
|
+
self.ui.on_change = self._changed
|
|
18
|
+
self.ui.update()
|
|
19
|
+
else:
|
|
20
|
+
self.radio = ft.Radio(value=self.value, label=self.text)
|
|
21
|
+
self.ui = ft.RadioGroup(content=self.radio, on_change=self._changed)
|
|
22
|
+
super().update(prev)
|
|
23
|
+
|
|
24
|
+
def _changed(self, event):
|
|
25
|
+
node = self.get_node()
|
|
26
|
+
if event.control.value:
|
|
27
|
+
self.model.value = node.value
|