qpuiq 0.23__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.
- PUI/PySide6/__init__.py +49 -0
- PUI/PySide6/application.py +58 -0
- PUI/PySide6/base.py +285 -0
- PUI/PySide6/button.py +21 -0
- PUI/PySide6/canvas.py +345 -0
- PUI/PySide6/checkbox.py +32 -0
- PUI/PySide6/combobox.py +85 -0
- PUI/PySide6/dialog.py +72 -0
- PUI/PySide6/divider.py +23 -0
- PUI/PySide6/image.py +48 -0
- PUI/PySide6/label.py +33 -0
- PUI/PySide6/layout.py +141 -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 +155 -0
- PUI/PySide6/splitter.py +25 -0
- PUI/PySide6/tab.py +39 -0
- PUI/PySide6/table.py +147 -0
- PUI/PySide6/text.py +35 -0
- PUI/PySide6/textfield.py +62 -0
- PUI/PySide6/toolbar.py +57 -0
- PUI/PySide6/tree.py +290 -0
- PUI/PySide6/window.py +82 -0
- PUI/__init__.py +46 -0
- PUI/common.py +26 -0
- PUI/decorator.py +20 -0
- PUI/dom.py +263 -0
- PUI/flet/__init__.py +22 -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/divider.py +14 -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 +432 -0
- PUI/state.py +711 -0
- PUI/textual/__init__.py +35 -0
- PUI/textual/application.py +82 -0
- PUI/textual/base.py +148 -0
- PUI/textual/button.py +17 -0
- PUI/textual/checkbox.py +21 -0
- PUI/textual/label.py +36 -0
- PUI/textual/layout.py +52 -0
- PUI/textual/progressbar.py +17 -0
- PUI/textual/radiobutton.py +24 -0
- PUI/textual/scroll.py +74 -0
- PUI/textual/tab.py +75 -0
- PUI/textual/text.py +32 -0
- PUI/textual/textfield.py +55 -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 +52 -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 +246 -0
- PUI/wx/button.py +16 -0
- PUI/wx/canvas.py +255 -0
- PUI/wx/checkbox.py +25 -0
- PUI/wx/combobox.py +81 -0
- PUI/wx/dialog.py +66 -0
- PUI/wx/divider.py +19 -0
- PUI/wx/label.py +18 -0
- PUI/wx/layout.py +52 -0
- PUI/wx/progressbar.py +19 -0
- PUI/wx/radiobutton.py +27 -0
- PUI/wx/scroll.py +55 -0
- PUI/wx/text.py +23 -0
- PUI/wx/textfield.py +66 -0
- PUI/wx/window.py +64 -0
- qpuiq-0.23.dist-info/LICENSE.txt +21 -0
- qpuiq-0.23.dist-info/METADATA +234 -0
- qpuiq-0.23.dist-info/RECORD +104 -0
- qpuiq-0.23.dist-info/WHEEL +5 -0
- qpuiq-0.23.dist-info/top_level.txt +1 -0
PUI/tkinter/tab.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Tabs(TkBaseWidget):
|
|
5
|
+
NORTH = "n"
|
|
6
|
+
SOUTH = "s"
|
|
7
|
+
EAST = "e"
|
|
8
|
+
WEST = "w"
|
|
9
|
+
pui_terminal = False
|
|
10
|
+
def __init__(self, tabposition=NORTH):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self.tabposition = tabposition
|
|
13
|
+
|
|
14
|
+
def destroy(self, direct):
|
|
15
|
+
if direct:
|
|
16
|
+
for w in self.widgets:
|
|
17
|
+
if w:
|
|
18
|
+
w.deleteLater()
|
|
19
|
+
super().destroy(direct)
|
|
20
|
+
|
|
21
|
+
def update(self, prev):
|
|
22
|
+
if prev and prev.ui:
|
|
23
|
+
self.ui = prev.ui
|
|
24
|
+
self.childrenui = prev.childrenui
|
|
25
|
+
else:
|
|
26
|
+
self.ui = ttk.Notebook(self.tkparent.inner)
|
|
27
|
+
self.childrenui = []
|
|
28
|
+
super().update(prev)
|
|
29
|
+
|
|
30
|
+
def addChild(self, idx, child):
|
|
31
|
+
if idx < len(self.childrenui):
|
|
32
|
+
self.ui.insert(self.childrenui[idx], child.outer, text=child.parent.label)
|
|
33
|
+
self.childrenui.insert(idx, child.outer)
|
|
34
|
+
else:
|
|
35
|
+
self.ui.add(child.outer, text=child.parent.label)
|
|
36
|
+
self.childrenui.append(child.outer)
|
|
37
|
+
|
|
38
|
+
def removeChild(self, idx, child):
|
|
39
|
+
self.ui.forget(child.outer)
|
|
40
|
+
self.childrenui.pop(idx)
|
|
41
|
+
|
|
42
|
+
def postSync(self):
|
|
43
|
+
for i,c in enumerate(self.children):
|
|
44
|
+
self.ui.tab(i, text=c.label)
|
|
45
|
+
return super().postSync()
|
|
46
|
+
|
|
47
|
+
class Tab(PUINode):
|
|
48
|
+
pui_virtual = True
|
|
49
|
+
def __init__(self, label):
|
|
50
|
+
super().__init__()
|
|
51
|
+
self.label = label
|
|
52
|
+
|
PUI/tkinter/text.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Text(TkBaseWidget):
|
|
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
|
+
self.ui.config(text = self.text)
|
|
13
|
+
else:
|
|
14
|
+
self.ui = tk.Label(self.tkparent.inner, text=self.text, anchor="w", justify="left")
|
|
15
|
+
|
|
16
|
+
class Html(Text):
|
|
17
|
+
pui_supported = False
|
|
18
|
+
|
|
19
|
+
class MarkDown(Text):
|
|
20
|
+
pui_supported = False
|
PUI/tkinter/textfield.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class TextField(TkBaseWidget):
|
|
5
|
+
def __init__(self, model, edit_model=None):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.model = model
|
|
8
|
+
self.edit_model = edit_model
|
|
9
|
+
self.editing = False
|
|
10
|
+
|
|
11
|
+
def update(self, prev):
|
|
12
|
+
model_value = str(self.model.value)
|
|
13
|
+
if prev and prev.ui:
|
|
14
|
+
self.editing = prev.editing
|
|
15
|
+
self.variable = prev.variable
|
|
16
|
+
self.ui = prev.ui
|
|
17
|
+
self.curr_value = prev.curr_value
|
|
18
|
+
if self.curr_value.set(model_value) and not self.editing:
|
|
19
|
+
self.variable.set(model_value)
|
|
20
|
+
else:
|
|
21
|
+
self.variable = tk.StringVar(self.tkparent.inner, model_value)
|
|
22
|
+
self.variable.trace_add("write", self.on_variable_changed)
|
|
23
|
+
self.curr_value = Prop(model_value)
|
|
24
|
+
self.ui = tk.Entry(self.tkparent.inner, textvariable=self.variable)
|
|
25
|
+
self.ui.bind('<Return>', self.on_return)
|
|
26
|
+
self.ui.bind('<FocusOut>', self.on_focus_out)
|
|
27
|
+
|
|
28
|
+
if self.edit_model and not self.editing:
|
|
29
|
+
self.edit_model.value = model_value
|
|
30
|
+
|
|
31
|
+
super().update(prev)
|
|
32
|
+
|
|
33
|
+
def on_variable_changed(self, var, index, mode): # editing
|
|
34
|
+
node = self.get_node()
|
|
35
|
+
node.editing = True
|
|
36
|
+
if node.edit_model:
|
|
37
|
+
node.edit_model.value = self.variable.get()
|
|
38
|
+
self._input()
|
|
39
|
+
|
|
40
|
+
def on_change(self): # finish editing
|
|
41
|
+
node = self.get_node()
|
|
42
|
+
node.editing = False
|
|
43
|
+
value = self.variable.get()
|
|
44
|
+
node.model.value = value
|
|
45
|
+
if node.edit_model:
|
|
46
|
+
node.edit_model.value = value
|
|
47
|
+
self._change()
|
|
48
|
+
|
|
49
|
+
def on_return(self, e):
|
|
50
|
+
self.on_change()
|
|
51
|
+
|
|
52
|
+
def on_focus_out(self, e):
|
|
53
|
+
self.on_change()
|
PUI/tkinter/window.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Window(TkBaseWidget):
|
|
5
|
+
pui_terminal = False
|
|
6
|
+
def __init__(self, title=None, size=None, maximize=None, fullscreen=None):
|
|
7
|
+
super().__init__()
|
|
8
|
+
self.title = title
|
|
9
|
+
self.size = size
|
|
10
|
+
self.maximize = maximize
|
|
11
|
+
self.fullscreen = fullscreen
|
|
12
|
+
|
|
13
|
+
def update(self, prev):
|
|
14
|
+
if prev and prev.ui:
|
|
15
|
+
self.ui = prev.ui
|
|
16
|
+
self.curr_size = prev.curr_size
|
|
17
|
+
self.curr_maximize = prev.curr_maximize
|
|
18
|
+
self.curr_fullscreen = prev.curr_fullscreen
|
|
19
|
+
else:
|
|
20
|
+
self.ui = tk.Toplevel(self.parent.inner)
|
|
21
|
+
# self.ui.protocol("WM_DELETE_WINDOW", self.on_closing)
|
|
22
|
+
self.curr_size = Prop()
|
|
23
|
+
self.curr_maximize = Prop()
|
|
24
|
+
self.curr_fullscreen = Prop()
|
|
25
|
+
|
|
26
|
+
if self.curr_size.set(self.size):
|
|
27
|
+
self.ui.geometry("x".join([str(v) for v in self.size]))
|
|
28
|
+
|
|
29
|
+
if self.curr_maximize.set(self.maximize):
|
|
30
|
+
# https://stackoverflow.com/a/70061942
|
|
31
|
+
try:
|
|
32
|
+
self.ui.attributes('-zoomed', True)
|
|
33
|
+
except:
|
|
34
|
+
self.ui.state("zoomed")
|
|
35
|
+
|
|
36
|
+
if self.curr_fullscreen.set(self.fullscreen):
|
|
37
|
+
self.ui.attributes('-fullscreen', True)
|
|
38
|
+
|
|
39
|
+
if not self.title is None:
|
|
40
|
+
self.ui.title(self.title)
|
|
41
|
+
|
|
42
|
+
def on_closing(self):
|
|
43
|
+
self.get_node().tkparent.ui.destroy()
|
|
44
|
+
|
|
45
|
+
def addChild(self, idx, child):
|
|
46
|
+
if idx:
|
|
47
|
+
return
|
|
48
|
+
child.outer.pack(fill=tk.BOTH, expand=True)
|
|
49
|
+
|
|
50
|
+
def removeChild(self, idx, child):
|
|
51
|
+
child.outer.pack_forget()
|
PUI/utils.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
def trbl(d):
|
|
2
|
+
# https://www.w3schools.com/css/css_padding.asp
|
|
3
|
+
if isinstance(d, int) or isinstance(d, float):
|
|
4
|
+
return (d,d,d,d)
|
|
5
|
+
elif len(d) == 4: # top, right, bottom, left
|
|
6
|
+
return d
|
|
7
|
+
elif len(d) == 3: # top, right/left, bottom
|
|
8
|
+
return (d[0], d[1], d[2], d[1])
|
|
9
|
+
elif len(d) == 2: # top/bottom, right/left
|
|
10
|
+
return (d[0], d[1], d[0], d[1])
|
|
11
|
+
else:
|
|
12
|
+
raise RuntimeError(f"Unsupported format: {d}")
|
|
13
|
+
|
|
14
|
+
def trbl2ltrb(d):
|
|
15
|
+
return (d[3], d[0], d[1], d[2])
|
PUI/view.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from .node import *
|
|
2
|
+
from .dom import *
|
|
3
|
+
from .state import StateMutationInViewBuilderError
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
DEBUG = False
|
|
7
|
+
|
|
8
|
+
class PUIView(PUINode):
|
|
9
|
+
pui_virtual = True
|
|
10
|
+
pui_isview = True
|
|
11
|
+
__ALLVIEWS__ = []
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def reload():
|
|
15
|
+
for v in PUIView.__ALLVIEWS__:
|
|
16
|
+
if DEBUG:
|
|
17
|
+
print("reload", v.key)
|
|
18
|
+
v.redraw()
|
|
19
|
+
|
|
20
|
+
def __init__(self, *args):
|
|
21
|
+
self.frames = []
|
|
22
|
+
self.dirty = False
|
|
23
|
+
self.updating = False
|
|
24
|
+
super().__init__(*args)
|
|
25
|
+
PUIView.__ALLVIEWS__.append(self)
|
|
26
|
+
|
|
27
|
+
def __enter__(self):
|
|
28
|
+
if not hasattr(tls, "puistack"):
|
|
29
|
+
tls.puistack = []
|
|
30
|
+
tls.puistack.append(self)
|
|
31
|
+
# print("enter", type(self).__name__, id(self))
|
|
32
|
+
self.root.frames.append(self)
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
def __exit__(self, ex_type, value, traceback):
|
|
36
|
+
tls.puistack.pop()
|
|
37
|
+
# print("exit", type(self).__name__, id(self))
|
|
38
|
+
self.root.frames.pop()
|
|
39
|
+
if ex_type is None: # don't consume exception
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def content(self):
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
def dump(self):
|
|
46
|
+
if DEBUG:
|
|
47
|
+
print(f"content() start", self.key)
|
|
48
|
+
start = time.time()
|
|
49
|
+
with self as scope:
|
|
50
|
+
self.content()
|
|
51
|
+
if DEBUG:
|
|
52
|
+
print(f"content() time: {time.time()-start:.5f}", self.key)
|
|
53
|
+
return scope
|
|
54
|
+
|
|
55
|
+
def destroy(self, direct):
|
|
56
|
+
try:
|
|
57
|
+
PUIView.__ALLVIEWS__.remove(self)
|
|
58
|
+
except:
|
|
59
|
+
pass
|
|
60
|
+
return super().destroy(direct)
|
|
61
|
+
|
|
62
|
+
def redraw(self):
|
|
63
|
+
if self.retired_by or self.destroyed:
|
|
64
|
+
return
|
|
65
|
+
self.dirty = True
|
|
66
|
+
if self.updating:
|
|
67
|
+
return
|
|
68
|
+
self.updating = True
|
|
69
|
+
self.sync()
|
|
70
|
+
|
|
71
|
+
def wait(self):
|
|
72
|
+
"""
|
|
73
|
+
Wait for the view to be updated.
|
|
74
|
+
"""
|
|
75
|
+
while self.updating or self.dirty:
|
|
76
|
+
time.sleep(0.0001)
|
|
77
|
+
|
|
78
|
+
# Subview update entry point
|
|
79
|
+
def sync(self):
|
|
80
|
+
if not self.pui_virtual:
|
|
81
|
+
raise VDomError(f"sync() called on non-virtual node {self.key}")
|
|
82
|
+
if self.retired_by or self.destroyed:
|
|
83
|
+
return
|
|
84
|
+
self.dirty = False
|
|
85
|
+
|
|
86
|
+
dom_parent = (self.pui_dom_parent or self).get_node()
|
|
87
|
+
if DEBUG:
|
|
88
|
+
print(f"Sync subview {self.key}@{id(self)} retired_by={id(self.retired_by) if self.retired_by else None} destroyed={self.destroyed} dom_parent={dom_parent.key}@{id(dom_parent)}")
|
|
89
|
+
found, offset = dom_parent.findDomOffsetForNode(self)
|
|
90
|
+
if DEBUG:
|
|
91
|
+
print(f" found={found} offset={offset}")
|
|
92
|
+
if not found:
|
|
93
|
+
if DEBUG:
|
|
94
|
+
print(dom_parent.serialize(show_pyid=True, show_hierarchy=True))
|
|
95
|
+
offset = 0
|
|
96
|
+
|
|
97
|
+
last_children = self.children
|
|
98
|
+
try:
|
|
99
|
+
self.update()
|
|
100
|
+
except (StateMutationInViewBuilderError, VDomError):
|
|
101
|
+
raise
|
|
102
|
+
except:
|
|
103
|
+
# prevent crash in hot-reloading
|
|
104
|
+
self.children = last_children
|
|
105
|
+
import traceback
|
|
106
|
+
print("## <ERROR OF content() >", self.key, id(self))
|
|
107
|
+
traceback.print_exc()
|
|
108
|
+
print("## </ERROR OF content()>")
|
|
109
|
+
|
|
110
|
+
start = time.time()
|
|
111
|
+
if DEBUG:
|
|
112
|
+
print("sync() start", self.key)
|
|
113
|
+
|
|
114
|
+
if DEBUG:
|
|
115
|
+
print(f"offset for {self.key}@{id(self)} on {dom_parent.key}@{id(dom_parent)} is {offset}")
|
|
116
|
+
sync(self, dom_parent, offset, last_children, self.children)
|
|
117
|
+
if DEBUG:
|
|
118
|
+
print(f"sync() time: {time.time()-start:.5f}", self.key)
|
|
119
|
+
|
|
120
|
+
self.updating = False
|
|
121
|
+
if self.dirty:
|
|
122
|
+
self.redraw()
|
|
123
|
+
|
|
124
|
+
def update(self, prev=None):
|
|
125
|
+
if self.retired_by:
|
|
126
|
+
return
|
|
127
|
+
if self.destroyed:
|
|
128
|
+
return
|
|
129
|
+
if not prev:
|
|
130
|
+
if self.setup:
|
|
131
|
+
self.setup()
|
|
132
|
+
self.setup = None
|
|
133
|
+
else:
|
|
134
|
+
self.setup = None
|
|
135
|
+
|
|
136
|
+
if DEBUG:
|
|
137
|
+
print(f"update() {self.key}@{id(self)} prev={id(prev) if prev else None}")
|
|
138
|
+
|
|
139
|
+
if prev and prev is not self:
|
|
140
|
+
prev.retired_by = self
|
|
141
|
+
try:
|
|
142
|
+
print(f"retired {prev.key}@{id(prev)}")
|
|
143
|
+
PUIView.__ALLVIEWS__.remove(prev)
|
|
144
|
+
except:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
self.children = []
|
|
148
|
+
if DEBUG:
|
|
149
|
+
print(f"content() start", self.key)
|
|
150
|
+
start = time.time()
|
|
151
|
+
with self as scope: # init frame stack
|
|
152
|
+
self.content() # V-DOM builder
|
|
153
|
+
if DEBUG:
|
|
154
|
+
print(f"content() time: {time.time()-start:.5f}", self.key)
|
|
155
|
+
|
|
156
|
+
def setup(self):
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
def run(self):
|
|
160
|
+
self.sync()
|
|
161
|
+
self.start()
|
PUI/wx/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .application import *
|
|
2
|
+
from .button import *
|
|
3
|
+
from .canvas import *
|
|
4
|
+
from .checkbox import *
|
|
5
|
+
from .combobox import *
|
|
6
|
+
from .dialog import *
|
|
7
|
+
from .divider import *
|
|
8
|
+
from .label import *
|
|
9
|
+
from .layout import *
|
|
10
|
+
from .progressbar import *
|
|
11
|
+
from .radiobutton import *
|
|
12
|
+
from .scroll import *
|
|
13
|
+
from .text import *
|
|
14
|
+
from .textfield import *
|
|
15
|
+
from .window import *
|
|
16
|
+
|
|
17
|
+
PUIView = WxPUIView
|
|
18
|
+
|
|
19
|
+
PUI_BACKEND = "wx"
|
PUI/wx/application.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Application(WxPUIView):
|
|
5
|
+
def __init__(self, icon=None):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.ui = None
|
|
8
|
+
self.icon = icon
|
|
9
|
+
|
|
10
|
+
def redraw(self):
|
|
11
|
+
if self.ui:
|
|
12
|
+
super().redraw()
|
|
13
|
+
else:
|
|
14
|
+
self.update(None)
|
|
15
|
+
|
|
16
|
+
def update(self, prev=None):
|
|
17
|
+
if not self.ui:
|
|
18
|
+
self.ui = wx.App()
|
|
19
|
+
|
|
20
|
+
super().update(prev)
|
|
21
|
+
|
|
22
|
+
def addChild(self, idx, child):
|
|
23
|
+
child.outer.Show()
|
|
24
|
+
|
|
25
|
+
def removeChild(self, idx, child):
|
|
26
|
+
child.outer.Close()
|
|
27
|
+
|
|
28
|
+
def start(self):
|
|
29
|
+
self.ui.MainLoop()
|
|
30
|
+
|
|
31
|
+
def PUIApp(func):
|
|
32
|
+
def func_wrapper(*args, **kwargs):
|
|
33
|
+
class PUIAppWrapper(Application):
|
|
34
|
+
def __init__(self, name):
|
|
35
|
+
self.name = name
|
|
36
|
+
super().__init__()
|
|
37
|
+
|
|
38
|
+
def content(self):
|
|
39
|
+
return func(*args, **kwargs)
|
|
40
|
+
|
|
41
|
+
ret = PUIAppWrapper(func.__name__)
|
|
42
|
+
return ret
|
|
43
|
+
|
|
44
|
+
return func_wrapper
|
PUI/wx/base.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
|
|
3
|
+
import wx
|
|
4
|
+
|
|
5
|
+
def int_to_wx_colour(color_int):
|
|
6
|
+
red = (color_int >> 16) & 0xFF
|
|
7
|
+
green = (color_int >> 8) & 0xFF
|
|
8
|
+
blue = color_int & 0xFF
|
|
9
|
+
return wx.Colour(red, green, blue)
|
|
10
|
+
|
|
11
|
+
def getWindow(n):
|
|
12
|
+
from .window import Window
|
|
13
|
+
from .scroll import Scroll
|
|
14
|
+
p = n
|
|
15
|
+
while p:
|
|
16
|
+
if isinstance(p, Scroll):
|
|
17
|
+
return p.ui
|
|
18
|
+
if isinstance(p, Window):
|
|
19
|
+
return p.ui
|
|
20
|
+
p = p.parent
|
|
21
|
+
raise RuntimeError("!!! getWindow returns None for", n.key if n else None)
|
|
22
|
+
|
|
23
|
+
class WxPUIView(PUIView):
|
|
24
|
+
pui_virtual = True
|
|
25
|
+
def __init__(self):
|
|
26
|
+
super().__init__()
|
|
27
|
+
|
|
28
|
+
def destroy(self, direct):
|
|
29
|
+
if self.ui: # PUIView doesn't have ui
|
|
30
|
+
self.ui.Destroy()
|
|
31
|
+
self.ui = None
|
|
32
|
+
super().destroy(direct)
|
|
33
|
+
|
|
34
|
+
def redraw(self):
|
|
35
|
+
wx.CallAfter(self.sync)
|
|
36
|
+
|
|
37
|
+
def update(self, prev=None):
|
|
38
|
+
if self.retired_by:
|
|
39
|
+
return
|
|
40
|
+
self.dirty = False
|
|
41
|
+
super().update(prev)
|
|
42
|
+
self.updating = False
|
|
43
|
+
if self.dirty:
|
|
44
|
+
self.update(prev)
|
|
45
|
+
|
|
46
|
+
class WXBase(PUINode):
|
|
47
|
+
scroll = False
|
|
48
|
+
container_x = False # axis
|
|
49
|
+
container_y = False # axis
|
|
50
|
+
expand_x_prio = 0
|
|
51
|
+
expand_y_prio = 0
|
|
52
|
+
expand_x1_children = 0
|
|
53
|
+
expand_x2_children = 0
|
|
54
|
+
expand_x3_children = 0
|
|
55
|
+
expand_x4_children = 0
|
|
56
|
+
expand_y1_children = 0
|
|
57
|
+
expand_y2_children = 0
|
|
58
|
+
expand_y3_children = 0
|
|
59
|
+
expand_y4_children = 0
|
|
60
|
+
cached_wxparent = None
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def expand_x(self):
|
|
64
|
+
return self.expand_x_prio
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def expand_y(self):
|
|
68
|
+
return self.expand_y_prio
|
|
69
|
+
|
|
70
|
+
def update(self, prev):
|
|
71
|
+
self.cached_wxparent = parent = self.wxparent
|
|
72
|
+
|
|
73
|
+
parent = self.wxparent
|
|
74
|
+
if parent:
|
|
75
|
+
if self.layout_weight:
|
|
76
|
+
if parent.container_x:
|
|
77
|
+
self.expand_x_prio = 4
|
|
78
|
+
if parent.container_y:
|
|
79
|
+
self.expand_y_prio = 4
|
|
80
|
+
|
|
81
|
+
if self.expand_x_prio >= 1:
|
|
82
|
+
parent.expand_x1_children += 1
|
|
83
|
+
if self.expand_x_prio >= 2:
|
|
84
|
+
parent.expand_x2_children += 1
|
|
85
|
+
if self.expand_x_prio >= 3:
|
|
86
|
+
parent.expand_x3_children += 1
|
|
87
|
+
if self.expand_x_prio >= 4:
|
|
88
|
+
parent.expand_x4_children += 1
|
|
89
|
+
|
|
90
|
+
if self.expand_y_prio >= 1:
|
|
91
|
+
parent.expand_y1_children += 1
|
|
92
|
+
if self.expand_y_prio >= 2:
|
|
93
|
+
parent.expand_y2_children += 1
|
|
94
|
+
if self.expand_y_prio >= 3:
|
|
95
|
+
parent.expand_y3_children += 1
|
|
96
|
+
if self.expand_y_prio >= 4:
|
|
97
|
+
parent.expand_y4_children += 1
|
|
98
|
+
|
|
99
|
+
super().update(prev)
|
|
100
|
+
|
|
101
|
+
def postUpdate(self):
|
|
102
|
+
parent = self.cached_wxparent
|
|
103
|
+
if parent:
|
|
104
|
+
if parent.container_x:
|
|
105
|
+
if self.expand_x_prio < 1 and parent.expand_x1_children > 0:
|
|
106
|
+
self.expand_x_prio = 0
|
|
107
|
+
if self.expand_x_prio < 2 and parent.expand_x2_children > 0:
|
|
108
|
+
self.expand_x_prio = 0
|
|
109
|
+
if self.expand_x_prio < 3 and parent.expand_x3_children > 0:
|
|
110
|
+
self.expand_x_prio = 0
|
|
111
|
+
if self.expand_x_prio < 4 and parent.expand_x4_children > 0:
|
|
112
|
+
self.expand_x_prio = 0
|
|
113
|
+
|
|
114
|
+
if parent.container_y:
|
|
115
|
+
if self.expand_y_prio < 1 and parent.expand_y1_children > 0:
|
|
116
|
+
self.expand_y_prio = 0
|
|
117
|
+
if self.expand_y_prio < 2 and parent.expand_y2_children > 0:
|
|
118
|
+
self.expand_y_prio = 0
|
|
119
|
+
if self.expand_y_prio < 3 and parent.expand_y3_children > 0:
|
|
120
|
+
self.expand_y_prio = 0
|
|
121
|
+
if self.expand_y_prio < 4 and parent.expand_y4_children > 0:
|
|
122
|
+
self.expand_y_prio = 0
|
|
123
|
+
|
|
124
|
+
if self._debug:
|
|
125
|
+
print("layout", self.key, f"weight={self.layout_weight}", f"expand_x={self.expand_x}", f"expand_y={self.expand_y}")
|
|
126
|
+
|
|
127
|
+
super().postUpdate()
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def wxparent(self):
|
|
131
|
+
parent = self.parent
|
|
132
|
+
while not isinstance(parent, WXBase):
|
|
133
|
+
if parent==parent.parent:
|
|
134
|
+
parent = None
|
|
135
|
+
break
|
|
136
|
+
parent = parent.parent
|
|
137
|
+
return parent
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class WxBaseWidget(WXBase):
|
|
141
|
+
pui_terminal = True
|
|
142
|
+
|
|
143
|
+
def __init__(self):
|
|
144
|
+
super().__init__()
|
|
145
|
+
self.layout_padding = (3, 3, 3, 3)
|
|
146
|
+
|
|
147
|
+
def update(self, prev):
|
|
148
|
+
super().update(prev)
|
|
149
|
+
|
|
150
|
+
if self.style_fontsize:
|
|
151
|
+
font = self.ui.GetFont()
|
|
152
|
+
font.SetPointSize(self.style_fontsize)
|
|
153
|
+
self.ui.SetFont(font)
|
|
154
|
+
|
|
155
|
+
# Set font color if specified
|
|
156
|
+
if self.style_color:
|
|
157
|
+
self.ui.SetForegroundColour(int_to_wx_colour(self.style_color))
|
|
158
|
+
|
|
159
|
+
def destroy(self, direct):
|
|
160
|
+
if self.ui is not None:
|
|
161
|
+
self.ui.Destroy()
|
|
162
|
+
self.ui = None
|
|
163
|
+
super().destroy(direct)
|
|
164
|
+
|
|
165
|
+
class WxBaseLayout(WXBase):
|
|
166
|
+
def __init__(self):
|
|
167
|
+
super().__init__()
|
|
168
|
+
self.relayout = False
|
|
169
|
+
if not isinstance(self.non_virtual_parent, WxBaseLayout):
|
|
170
|
+
self.layout_padding = (11,11,11,11)
|
|
171
|
+
|
|
172
|
+
def destroy(self, direct):
|
|
173
|
+
# self.ui.Destroy()
|
|
174
|
+
self.ui = None
|
|
175
|
+
super().destroy(direct)
|
|
176
|
+
|
|
177
|
+
def update(self, prev):
|
|
178
|
+
if prev and prev.ui:
|
|
179
|
+
self.sizerItems = prev.sizerItems
|
|
180
|
+
else:
|
|
181
|
+
self.sizerItems = []
|
|
182
|
+
super().update(prev)
|
|
183
|
+
|
|
184
|
+
def postUpdate(self):
|
|
185
|
+
super().postUpdate()
|
|
186
|
+
|
|
187
|
+
for child, si in self.sizerItems:
|
|
188
|
+
if child.expand_x or child.expand_y:
|
|
189
|
+
si.SetFlag(wx.ALL | wx.EXPAND)
|
|
190
|
+
else:
|
|
191
|
+
# si.SetFlag(wx.ALL | wx.ALIGN_CENTER)
|
|
192
|
+
si.SetFlag(wx.ALL) # XXX
|
|
193
|
+
|
|
194
|
+
weight = child.layout_weight
|
|
195
|
+
if self.container_x and child.expand_x:
|
|
196
|
+
si.SetProportion(weight if weight else 1 if child.expand_x else 0)
|
|
197
|
+
elif self.container_y and child.expand_y:
|
|
198
|
+
si.SetProportion(weight if weight else 1 if child.expand_y else 0)
|
|
199
|
+
|
|
200
|
+
p = 0
|
|
201
|
+
if child.layout_padding:
|
|
202
|
+
p = max(child.layout_padding)
|
|
203
|
+
si.SetBorder(p)
|
|
204
|
+
|
|
205
|
+
def addChild(self, idx, child):
|
|
206
|
+
from .layout import Spacer
|
|
207
|
+
self.relayout = True
|
|
208
|
+
|
|
209
|
+
weight = child.layout_weight
|
|
210
|
+
if weight is None:
|
|
211
|
+
weight = 0
|
|
212
|
+
if not weight and self.container_x and child.expand_x:
|
|
213
|
+
weight = 1
|
|
214
|
+
if not weight and self.container_y and child.expand_y:
|
|
215
|
+
weight = 1
|
|
216
|
+
|
|
217
|
+
if isinstance(child, WxBaseLayout):
|
|
218
|
+
si = self.ui.Insert(idx, child.outer)
|
|
219
|
+
self.sizerItems.insert(idx, (child, si))
|
|
220
|
+
elif isinstance(child, WxBaseWidget):
|
|
221
|
+
si = self.ui.Insert(idx, child.outer)
|
|
222
|
+
self.sizerItems.insert(idx, (child, si))
|
|
223
|
+
elif isinstance(child, Spacer):
|
|
224
|
+
si = self.ui.InsertStretchSpacer(idx)
|
|
225
|
+
self.sizerItems.insert(idx, (child, si))
|
|
226
|
+
|
|
227
|
+
def removeChild(self, idx, child):
|
|
228
|
+
self.relayout = True
|
|
229
|
+
|
|
230
|
+
from .layout import Spacer
|
|
231
|
+
if isinstance(child, WxBaseLayout):
|
|
232
|
+
self.ui.Detach(idx)
|
|
233
|
+
self.sizerItems.pop(idx)
|
|
234
|
+
elif isinstance(child, WxBaseWidget):
|
|
235
|
+
self.ui.Detach(idx)
|
|
236
|
+
self.sizerItems.pop(idx)
|
|
237
|
+
elif isinstance(child, Spacer):
|
|
238
|
+
self.ui.Detach(idx)
|
|
239
|
+
self.sizerItems.pop(idx)
|
|
240
|
+
|
|
241
|
+
def postSync(self):
|
|
242
|
+
if self.relayout and self.ui:
|
|
243
|
+
self.relayout = False
|
|
244
|
+
self.ui.Layout()
|
|
245
|
+
# self.ui.Fit(getWindow(self.parent))
|
|
246
|
+
super().postSync()
|
PUI/wx/button.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .. import *
|
|
2
|
+
from .base import *
|
|
3
|
+
|
|
4
|
+
class Button(WxBaseWidget):
|
|
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.SetLabel(self.text)
|
|
13
|
+
else:
|
|
14
|
+
self.ui = wx.Button(getWindow(self.parent), label=self.text)
|
|
15
|
+
self.ui.Bind(wx.EVT_BUTTON, self._clicked)
|
|
16
|
+
super().update(prev)
|