py10x-universe 0.1.3__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.
- core_10x/__init__.py +42 -0
- core_10x/backbone/__init__.py +0 -0
- core_10x/backbone/backbone_store.py +59 -0
- core_10x/backbone/backbone_traitable.py +30 -0
- core_10x/backbone/backbone_user.py +66 -0
- core_10x/backbone/bound_data_domain.py +49 -0
- core_10x/backbone/namespace.py +101 -0
- core_10x/backbone/vault.py +38 -0
- core_10x/code_samples/__init__.py +0 -0
- core_10x/code_samples/_package_manifest.py +3 -0
- core_10x/code_samples/directories.py +181 -0
- core_10x/code_samples/person.py +76 -0
- core_10x/concrete_traits.py +356 -0
- core_10x/conftest.py +12 -0
- core_10x/curve.py +321 -0
- core_10x/data_domain.py +48 -0
- core_10x/data_domain_binder.py +45 -0
- core_10x/directory.py +250 -0
- core_10x/entity.py +8 -0
- core_10x/entity_filter.py +5 -0
- core_10x/environment_variables.py +147 -0
- core_10x/exec_control.py +84 -0
- core_10x/experimental/__init__.py +0 -0
- core_10x/experimental/data_protocol_ex.py +34 -0
- core_10x/global_cache.py +121 -0
- core_10x/manual_tests/__init__.py +0 -0
- core_10x/manual_tests/calendar_test.py +35 -0
- core_10x/manual_tests/ctor_update_bug.py +58 -0
- core_10x/manual_tests/debug_graph_on.py +17 -0
- core_10x/manual_tests/debug_graphoff_inside_graph_on.py +28 -0
- core_10x/manual_tests/enum_bits_test.py +17 -0
- core_10x/manual_tests/env_vars_trivial_test.py +12 -0
- core_10x/manual_tests/existing_traitable.py +33 -0
- core_10x/manual_tests/k10x_test1.py +13 -0
- core_10x/manual_tests/named_constant_test.py +121 -0
- core_10x/manual_tests/nucleus_trivial_test.py +42 -0
- core_10x/manual_tests/polars_test.py +14 -0
- core_10x/manual_tests/py_class_test.py +4 -0
- core_10x/manual_tests/rc_test.py +42 -0
- core_10x/manual_tests/rdate_test.py +12 -0
- core_10x/manual_tests/reference_serialization_bug.py +19 -0
- core_10x/manual_tests/resource_trivial_test.py +10 -0
- core_10x/manual_tests/store_uri_test.py +6 -0
- core_10x/manual_tests/trait_definition_test.py +19 -0
- core_10x/manual_tests/trait_filter_test.py +15 -0
- core_10x/manual_tests/trait_flag_modification_test.py +42 -0
- core_10x/manual_tests/trait_modification_bug.py +26 -0
- core_10x/manual_tests/traitable_as_of_test.py +82 -0
- core_10x/manual_tests/traitable_heir_test.py +39 -0
- core_10x/manual_tests/traitable_history_test.py +41 -0
- core_10x/manual_tests/traitable_serialization_test.py +54 -0
- core_10x/manual_tests/traitable_trivial_test.py +71 -0
- core_10x/manual_tests/trivial_graph_test.py +16 -0
- core_10x/manual_tests/ts_class_association_test.py +64 -0
- core_10x/manual_tests/ts_trivial_test.py +35 -0
- core_10x/named_constant.py +425 -0
- core_10x/nucleus.py +81 -0
- core_10x/package_manifest.py +85 -0
- core_10x/package_refactoring.py +153 -0
- core_10x/py_class.py +431 -0
- core_10x/rc.py +155 -0
- core_10x/rdate.py +339 -0
- core_10x/resource.py +189 -0
- core_10x/roman_number.py +67 -0
- core_10x/testlib/__init__.py +0 -0
- core_10x/testlib/test_store.py +240 -0
- core_10x/testlib/traitable_history_tests.py +787 -0
- core_10x/testlib/ts_tests.py +280 -0
- core_10x/trait.py +377 -0
- core_10x/trait_definition.py +176 -0
- core_10x/trait_filter.py +205 -0
- core_10x/trait_method_error.py +36 -0
- core_10x/traitable.py +1082 -0
- core_10x/traitable_cli.py +153 -0
- core_10x/traitable_heir.py +33 -0
- core_10x/traitable_id.py +31 -0
- core_10x/ts_store.py +172 -0
- core_10x/ts_store_type.py +26 -0
- core_10x/ts_union.py +147 -0
- core_10x/ui_hint.py +153 -0
- core_10x/unit_tests/test_concrete_traits.py +156 -0
- core_10x/unit_tests/test_converters.py +51 -0
- core_10x/unit_tests/test_curve.py +157 -0
- core_10x/unit_tests/test_directory.py +54 -0
- core_10x/unit_tests/test_documentation.py +172 -0
- core_10x/unit_tests/test_environment_variables.py +15 -0
- core_10x/unit_tests/test_filters.py +239 -0
- core_10x/unit_tests/test_graph.py +348 -0
- core_10x/unit_tests/test_named_constant.py +98 -0
- core_10x/unit_tests/test_rc.py +11 -0
- core_10x/unit_tests/test_rdate.py +484 -0
- core_10x/unit_tests/test_trait_method_error.py +80 -0
- core_10x/unit_tests/test_trait_modification.py +19 -0
- core_10x/unit_tests/test_traitable.py +959 -0
- core_10x/unit_tests/test_traitable_history.py +1 -0
- core_10x/unit_tests/test_ts_store.py +1 -0
- core_10x/unit_tests/test_ts_union.py +369 -0
- core_10x/unit_tests/test_ui_nodes.py +81 -0
- core_10x/unit_tests/test_xxcalendar.py +471 -0
- core_10x/vault/__init__.py +0 -0
- core_10x/vault/sec_keys.py +133 -0
- core_10x/vault/security_keys_old.py +168 -0
- core_10x/vault/vault.py +56 -0
- core_10x/vault/vault_traitable.py +56 -0
- core_10x/vault/vault_user.py +70 -0
- core_10x/xdate_time.py +136 -0
- core_10x/xnone.py +71 -0
- core_10x/xxcalendar.py +228 -0
- infra_10x/__init__.py +0 -0
- infra_10x/manual_tests/__init__.py +0 -0
- infra_10x/manual_tests/test_misc.py +16 -0
- infra_10x/manual_tests/test_prepare_filter_and_pipeline.py +25 -0
- infra_10x/mongodb_admin.py +111 -0
- infra_10x/mongodb_store.py +346 -0
- infra_10x/mongodb_utils.py +129 -0
- infra_10x/unit_tests/conftest.py +13 -0
- infra_10x/unit_tests/test_mongo_db.py +36 -0
- infra_10x/unit_tests/test_mongo_history.py +1 -0
- py10x_universe-0.1.3.dist-info/METADATA +406 -0
- py10x_universe-0.1.3.dist-info/RECORD +214 -0
- py10x_universe-0.1.3.dist-info/WHEEL +4 -0
- py10x_universe-0.1.3.dist-info/licenses/LICENSE +21 -0
- ui_10x/__init__.py +0 -0
- ui_10x/apps/__init__.py +0 -0
- ui_10x/apps/collection_editor_app.py +100 -0
- ui_10x/choice.py +212 -0
- ui_10x/collection_editor.py +135 -0
- ui_10x/concrete_trait_widgets.py +220 -0
- ui_10x/conftest.py +8 -0
- ui_10x/entity_stocker.py +173 -0
- ui_10x/examples/__init__.py +0 -0
- ui_10x/examples/_guess_word_data.py +14076 -0
- ui_10x/examples/collection_editor.py +17 -0
- ui_10x/examples/date_selector.py +14 -0
- ui_10x/examples/entity_stocker.py +18 -0
- ui_10x/examples/guess_word.py +392 -0
- ui_10x/examples/message_box.py +20 -0
- ui_10x/examples/multi_choice.py +17 -0
- ui_10x/examples/py_data_browser.py +66 -0
- ui_10x/examples/radiobox.py +29 -0
- ui_10x/examples/single_choice.py +31 -0
- ui_10x/examples/style_sheet.py +47 -0
- ui_10x/examples/trivial_entity_editor.py +18 -0
- ui_10x/platform.py +20 -0
- ui_10x/platform_interface.py +517 -0
- ui_10x/py_data_browser.py +249 -0
- ui_10x/qt6/__init__.py +0 -0
- ui_10x/qt6/conftest.py +8 -0
- ui_10x/qt6/manual_tests/__init__.py +0 -0
- ui_10x/qt6/manual_tests/basic_test.py +35 -0
- ui_10x/qt6/platform_implementation.py +275 -0
- ui_10x/qt6/utils.py +665 -0
- ui_10x/rio/__init__.py +0 -0
- ui_10x/rio/apps/examples/examples/__init__.py +22 -0
- ui_10x/rio/apps/examples/examples/components/__init__.py +3 -0
- ui_10x/rio/apps/examples/examples/components/collection_editor.py +15 -0
- ui_10x/rio/apps/examples/examples/pages/collection_editor.py +21 -0
- ui_10x/rio/apps/examples/examples/pages/login_page.py +88 -0
- ui_10x/rio/apps/examples/examples/pages/style_sheet.py +21 -0
- ui_10x/rio/apps/examples/rio.toml +14 -0
- ui_10x/rio/component_builder.py +497 -0
- ui_10x/rio/components/__init__.py +9 -0
- ui_10x/rio/components/group_box.py +31 -0
- ui_10x/rio/components/labeled_checkbox.py +18 -0
- ui_10x/rio/components/line_edit.py +37 -0
- ui_10x/rio/components/radio_button.py +32 -0
- ui_10x/rio/components/separator.py +24 -0
- ui_10x/rio/components/splitter.py +121 -0
- ui_10x/rio/components/tree_view.py +75 -0
- ui_10x/rio/conftest.py +35 -0
- ui_10x/rio/internals/__init__.py +0 -0
- ui_10x/rio/internals/app.py +192 -0
- ui_10x/rio/manual_tests/__init__.py +0 -0
- ui_10x/rio/manual_tests/basic_test.py +24 -0
- ui_10x/rio/manual_tests/splitter.py +27 -0
- ui_10x/rio/platform_implementation.py +91 -0
- ui_10x/rio/style_sheet.py +53 -0
- ui_10x/rio/unit_tests/test_collection_editor.py +68 -0
- ui_10x/rio/unit_tests/test_internals.py +630 -0
- ui_10x/rio/unit_tests/test_style_sheet.py +37 -0
- ui_10x/rio/widgets/__init__.py +46 -0
- ui_10x/rio/widgets/application.py +109 -0
- ui_10x/rio/widgets/button.py +48 -0
- ui_10x/rio/widgets/button_group.py +60 -0
- ui_10x/rio/widgets/calendar.py +23 -0
- ui_10x/rio/widgets/checkbox.py +24 -0
- ui_10x/rio/widgets/dialog.py +137 -0
- ui_10x/rio/widgets/group_box.py +27 -0
- ui_10x/rio/widgets/layout.py +34 -0
- ui_10x/rio/widgets/line_edit.py +37 -0
- ui_10x/rio/widgets/list.py +105 -0
- ui_10x/rio/widgets/message_box.py +70 -0
- ui_10x/rio/widgets/scroll_area.py +31 -0
- ui_10x/rio/widgets/spacer.py +6 -0
- ui_10x/rio/widgets/splitter.py +45 -0
- ui_10x/rio/widgets/text_edit.py +28 -0
- ui_10x/rio/widgets/tree.py +89 -0
- ui_10x/rio/widgets/unit_tests/test_button.py +101 -0
- ui_10x/rio/widgets/unit_tests/test_button_group.py +33 -0
- ui_10x/rio/widgets/unit_tests/test_calendar.py +114 -0
- ui_10x/rio/widgets/unit_tests/test_checkbox.py +109 -0
- ui_10x/rio/widgets/unit_tests/test_group_box.py +158 -0
- ui_10x/rio/widgets/unit_tests/test_label.py +43 -0
- ui_10x/rio/widgets/unit_tests/test_line_edit.py +140 -0
- ui_10x/rio/widgets/unit_tests/test_list.py +146 -0
- ui_10x/table_header_view.py +305 -0
- ui_10x/table_view.py +174 -0
- ui_10x/trait_editor.py +189 -0
- ui_10x/trait_widget.py +131 -0
- ui_10x/traitable_editor.py +200 -0
- ui_10x/traitable_view.py +131 -0
- ui_10x/unit_tests/conftest.py +8 -0
- ui_10x/unit_tests/test_platform.py +9 -0
- ui_10x/utils.py +661 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
2
|
+
|
|
3
|
+
from core_10x.rc import RC, RC_TRUE
|
|
4
|
+
from core_10x.xnone import XNone
|
|
5
|
+
|
|
6
|
+
from ui_10x.utils import UxDialog, ux
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PyDataBrowser:
|
|
10
|
+
class Item(ux.TreeItem):
|
|
11
|
+
def __init__(self, parent_node, key, value):
|
|
12
|
+
super().__init__(parent_node)
|
|
13
|
+
self.key = key
|
|
14
|
+
self.value = value
|
|
15
|
+
self.new_value = value
|
|
16
|
+
|
|
17
|
+
def path(self) -> list:
|
|
18
|
+
res = []
|
|
19
|
+
node = self
|
|
20
|
+
while node:
|
|
21
|
+
res.insert(0, node.key)
|
|
22
|
+
node = node.parent()
|
|
23
|
+
|
|
24
|
+
return res
|
|
25
|
+
|
|
26
|
+
def accept_edit(self, results):
|
|
27
|
+
n = self.child_count()
|
|
28
|
+
if not n:
|
|
29
|
+
if self.value != self.new_value:
|
|
30
|
+
path = self.path()
|
|
31
|
+
x = results
|
|
32
|
+
for key in path[:-1]:
|
|
33
|
+
x = x[key]
|
|
34
|
+
x[path[-1]] = self.new_value
|
|
35
|
+
else:
|
|
36
|
+
for i in range(n):
|
|
37
|
+
child = self.child(i)
|
|
38
|
+
child.accept_edit(results)
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
data,
|
|
43
|
+
on_select = None,
|
|
44
|
+
edit = False,
|
|
45
|
+
custom_editor = None,
|
|
46
|
+
max_label_len = 50,
|
|
47
|
+
max_value_len = 70
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Browse any python data.
|
|
51
|
+
:param data:
|
|
52
|
+
:param on_select: a callable on item selection. If given, will call on_select( the_browser, path_to_item: list, item_value )
|
|
53
|
+
:param edit: allow editing if True
|
|
54
|
+
:param custom_editor: if given, is used for editing - fn( the_browser, item: PyDataBrowser: Item )
|
|
55
|
+
:param max_label_len: max label width (if exceeds, gets truncated showing '...' and the end)
|
|
56
|
+
:param max_value_len: max value width (if exceeds, gets truncated showing '...' and the end)
|
|
57
|
+
Tooltip (full label/value, if possible) is shown as usual
|
|
58
|
+
"""
|
|
59
|
+
self.data = data
|
|
60
|
+
self.edit = edit
|
|
61
|
+
self.custom_editor = custom_editor
|
|
62
|
+
self.select_cb = on_select
|
|
63
|
+
self.max_label_len = max_label_len
|
|
64
|
+
self.max_value_len = max_value_len
|
|
65
|
+
|
|
66
|
+
def fill(self, value, node: ux.TreeItem, label = True):
|
|
67
|
+
if label:
|
|
68
|
+
i = 0
|
|
69
|
+
n = self.max_label_len
|
|
70
|
+
tip = True
|
|
71
|
+
else:
|
|
72
|
+
i = 1
|
|
73
|
+
n = self.max_value_len
|
|
74
|
+
tip = isinstance(value, str)
|
|
75
|
+
text = str(value)
|
|
76
|
+
if len(text) > n:
|
|
77
|
+
show = text[: n] + '...'
|
|
78
|
+
else:
|
|
79
|
+
show = text
|
|
80
|
+
node.set_text(i, show)
|
|
81
|
+
if tip:
|
|
82
|
+
node.set_tool_tip(i, text)
|
|
83
|
+
|
|
84
|
+
s_bool_vmap = {
|
|
85
|
+
'True': True,
|
|
86
|
+
'False': False,
|
|
87
|
+
'1': True,
|
|
88
|
+
'0': False
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
s_use_as_is = {
|
|
92
|
+
type: None,
|
|
93
|
+
type(None): None,
|
|
94
|
+
type(XNone): None,
|
|
95
|
+
bool: lambda x: PyDataBrowser.s_bool_vmap.get(x, bool(x)),
|
|
96
|
+
str: lambda x: x,
|
|
97
|
+
int: lambda x: int(x),
|
|
98
|
+
float: lambda x: float(x),
|
|
99
|
+
datetime: lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S'),
|
|
100
|
+
date: lambda x: datetime.strptime(x, '%Y-%m-%d').date(),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
s_dict_handler = lambda data: data.items()
|
|
104
|
+
s_list_handler = lambda data: enumerate(data)
|
|
105
|
+
s_type_handlers = {
|
|
106
|
+
tuple: s_list_handler,
|
|
107
|
+
list: s_list_handler,
|
|
108
|
+
dict: s_dict_handler,
|
|
109
|
+
}
|
|
110
|
+
def create_tree(self, key, item, parent_node):
|
|
111
|
+
node = self.Item(parent_node, key, item)
|
|
112
|
+
self.fill(key, node, label = True)
|
|
113
|
+
self.fill(item, node, label = False)
|
|
114
|
+
|
|
115
|
+
dtype = type(item)
|
|
116
|
+
if dtype in self.s_use_as_is:
|
|
117
|
+
pass
|
|
118
|
+
else:
|
|
119
|
+
handler = self.s_type_handlers.get(dtype)
|
|
120
|
+
if not handler:
|
|
121
|
+
try:
|
|
122
|
+
text = item.__repr__()
|
|
123
|
+
node.set_text(1, text)
|
|
124
|
+
except Exception:
|
|
125
|
+
node.set_text(1, '???')
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
item = item.to_dict()
|
|
130
|
+
handler = self.__class__.s_dict_handler
|
|
131
|
+
except Exception:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
for key, value in handler(item):
|
|
135
|
+
self.create_tree(key, value, node)
|
|
136
|
+
|
|
137
|
+
def widget(self) -> ux.TreeWidget:
|
|
138
|
+
self.tree = tree = ux.TreeWidget()
|
|
139
|
+
tree.set_column_count(2)
|
|
140
|
+
tree.set_header_labels(['Key', 'Value'])
|
|
141
|
+
|
|
142
|
+
data = self.data
|
|
143
|
+
dtype = type(data)
|
|
144
|
+
handler = self.s_type_handlers.get(dtype)
|
|
145
|
+
if not handler:
|
|
146
|
+
data = { str(dtype): data }
|
|
147
|
+
handler = self.s_dict_handler
|
|
148
|
+
|
|
149
|
+
for key, item in handler(data):
|
|
150
|
+
self.create_tree(key, item, tree)
|
|
151
|
+
|
|
152
|
+
if len(data) == 1:
|
|
153
|
+
tree.top_level_item(0).set_expanded(True)
|
|
154
|
+
|
|
155
|
+
tree.resize_column_to_contents(0)
|
|
156
|
+
tree.resize_column_to_contents(1)
|
|
157
|
+
|
|
158
|
+
tree.item_expanded_connect(lambda item: tree.resize_column_to_contents(0))
|
|
159
|
+
tree.item_pressed_connect(self.on_item_pressed)
|
|
160
|
+
tree.item_changed_connect(self.on_item_changed)
|
|
161
|
+
|
|
162
|
+
return tree
|
|
163
|
+
|
|
164
|
+
def on_item_pressed(self, item: Item, col: int):
|
|
165
|
+
if item:
|
|
166
|
+
if col == 0 or not self.edit:
|
|
167
|
+
if self.select_cb:
|
|
168
|
+
self.select_cb(self, item.path(), item.value)
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
if self.custom_editor:
|
|
172
|
+
self.custom_editor(self, item)
|
|
173
|
+
else:
|
|
174
|
+
if not item.child_count():
|
|
175
|
+
self.generic_editor(item)
|
|
176
|
+
|
|
177
|
+
def on_item_changed(self, item: Item, col: int):
|
|
178
|
+
if col != 1:
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
dtype = type(item.value)
|
|
182
|
+
from_str_handler = self.s_use_as_is.get(dtype)
|
|
183
|
+
if from_str_handler:
|
|
184
|
+
text = item.text(col)
|
|
185
|
+
try:
|
|
186
|
+
item.new_value = from_str_handler(text)
|
|
187
|
+
except Exception:
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
def generic_editor(self, item: Item):
|
|
191
|
+
self.tree.open_persistent_editor(item, 1)
|
|
192
|
+
self.tree.edit_item(item, 1)
|
|
193
|
+
|
|
194
|
+
def accept_edit(self) -> RC:
|
|
195
|
+
for i in range(self.tree.top_level_item_count()):
|
|
196
|
+
item = self.tree.top_level_item(i)
|
|
197
|
+
item.accept_edit(self.data)
|
|
198
|
+
return RC_TRUE
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def show(cls, data, on_select = None, title = '', w = 1000, h = 800):
|
|
202
|
+
if not title:
|
|
203
|
+
title = 'Python Data Browser'
|
|
204
|
+
|
|
205
|
+
br = cls(data, on_select = on_select)
|
|
206
|
+
d = UxDialog(
|
|
207
|
+
br.widget(),
|
|
208
|
+
title = title,
|
|
209
|
+
ok = '',
|
|
210
|
+
cancel = 'Close',
|
|
211
|
+
min_width = w,
|
|
212
|
+
min_height = h
|
|
213
|
+
)
|
|
214
|
+
d.exec()
|
|
215
|
+
|
|
216
|
+
@classmethod
|
|
217
|
+
def edit(cls, data, on_select = None, custom_editor = None, title = '', w = 1000, h = 800) -> bool:
|
|
218
|
+
if not title:
|
|
219
|
+
title = 'Python Data Editor'
|
|
220
|
+
|
|
221
|
+
br = cls(data, on_select = on_select, edit = True, custom_editor = custom_editor)
|
|
222
|
+
d = UxDialog(
|
|
223
|
+
br.widget(),
|
|
224
|
+
accept_callback = br.accept_edit,
|
|
225
|
+
title = title,
|
|
226
|
+
min_width = w,
|
|
227
|
+
min_height = h
|
|
228
|
+
)
|
|
229
|
+
rc = d.exec()
|
|
230
|
+
return bool(rc)
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def path_to_str(cls, path: list, delims = ('/', '/')) -> str:
|
|
234
|
+
res = []
|
|
235
|
+
for i, key in enumerate(path):
|
|
236
|
+
delim = delims[ isinstance(key, int) ]
|
|
237
|
+
n = len(delim)
|
|
238
|
+
if n == 0:
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
if n == 1:
|
|
242
|
+
key = f'{delim}{key}' if i > 0 else str(key)
|
|
243
|
+
else:
|
|
244
|
+
key = f'{delim[0]}{key}{delim[1]}'
|
|
245
|
+
|
|
246
|
+
res.append(key)
|
|
247
|
+
|
|
248
|
+
return ''.join(res)
|
|
249
|
+
|
ui_10x/qt6/__init__.py
ADDED
|
File without changes
|
ui_10x/qt6/conftest.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# from PyQt6.QtWidgets import QWidget, QLayout, QBoxLayout, QVBoxLayout, QHBoxLayout, QFormLayout, QApplication
|
|
2
|
+
# from PyQt6.QtWidgets import QLabel, QCalendarWidget, QMessageBox, QGroupBox, QButtonGroup, QRadioButton
|
|
3
|
+
# from PyQt6.QtWidgets import QListWidgetItem, QListWidget, QLineEdit, QPlainTextEdit, QTreeWidgetItem, QTreeWidget, QCheckBox, QComboBox
|
|
4
|
+
# from PyQt6.QtWidgets import QFrame, QSplitter, QDialog, QScrollArea, QPushButton, QStyle, QSizePolicy
|
|
5
|
+
# from PyQt6.QtCore import Qt, QObject, QThread, pyqtSignal, QBuffer, QIODevice, QPoint #, QByteArray
|
|
6
|
+
# from PyQt6.QtGui import QColor, QGuiApplication, QPixmap, QMouseEvent
|
|
7
|
+
#
|
|
8
|
+
# import platform
|
|
9
|
+
#
|
|
10
|
+
#
|
|
11
|
+
# Object = QObject
|
|
12
|
+
#
|
|
13
|
+
# signal_decl = pyqtSignal
|
|
14
|
+
#
|
|
15
|
+
# class MouseEvent(QMouseEvent):
|
|
16
|
+
# def is_left_button(self) -> bool: return self.button() == Qt.MouseButton.LeftButton
|
|
17
|
+
# def is_right_button(self) -> bool: return self.button() == Qt.MouseButton.RightButton
|
|
18
|
+
|
|
19
|
+
from ui_10x.platform import ux
|
|
20
|
+
|
|
21
|
+
if __name__ == '__main__':
|
|
22
|
+
app = ux.init()
|
|
23
|
+
|
|
24
|
+
w = ux.Label('a')
|
|
25
|
+
w = ux.PushButton('b')
|
|
26
|
+
w = ux.LineEdit()
|
|
27
|
+
w = ux.RadioButton()
|
|
28
|
+
w = ux.CheckBox('Check')
|
|
29
|
+
w = ux.ListWidget()
|
|
30
|
+
w = ux.TreeWidget()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Callable, Any
|
|
3
|
+
|
|
4
|
+
from PyQt6.QtWidgets import QWidget, QLayout, QBoxLayout, QVBoxLayout, QHBoxLayout, QFormLayout, QApplication
|
|
5
|
+
from PyQt6.QtWidgets import QLabel, QCalendarWidget, QMessageBox, QGroupBox, QButtonGroup, QRadioButton
|
|
6
|
+
from PyQt6.QtWidgets import QListWidgetItem, QListWidget, QLineEdit, QPlainTextEdit, QTreeWidgetItem, QTreeWidget, QCheckBox, QComboBox
|
|
7
|
+
from PyQt6.QtWidgets import QFrame, QSplitter, QDialog, QScrollArea, QPushButton, QStyle, QSizePolicy, QStyleOptionHeader
|
|
8
|
+
from PyQt6.QtCore import Qt, QObject, QThread, pyqtSignal, QBuffer, QIODevice, QPoint #, QByteArray
|
|
9
|
+
from PyQt6.QtGui import QColor, QGuiApplication, QPixmap, QMouseEvent, QFontMetrics, QPalette, QPainter
|
|
10
|
+
|
|
11
|
+
import platform
|
|
12
|
+
|
|
13
|
+
from core_10x.global_cache import cache
|
|
14
|
+
|
|
15
|
+
def missing_attr(self, item):
|
|
16
|
+
t = item.title()
|
|
17
|
+
item = t[0].lower() + t[1:].replace('_', '')
|
|
18
|
+
return QObject.__getattribute__(self, item)
|
|
19
|
+
|
|
20
|
+
Object = QObject
|
|
21
|
+
|
|
22
|
+
QueuedConnection = Qt.ConnectionType.QueuedConnection
|
|
23
|
+
AutoConnection = Qt.ConnectionType.AutoConnection
|
|
24
|
+
DirectConnection = Qt.ConnectionType.DirectConnection
|
|
25
|
+
UniqueConnection = Qt.ConnectionType.UniqueConnection
|
|
26
|
+
BlockingQueuedConnection = Qt.ConnectionType.BlockingQueuedConnection
|
|
27
|
+
|
|
28
|
+
signal_decl = pyqtSignal
|
|
29
|
+
|
|
30
|
+
MouseEvent = QMouseEvent
|
|
31
|
+
MouseEvent.is_left_button = lambda self: self.button() == Qt.MouseButton.LeftButton
|
|
32
|
+
MouseEvent.is_right_button = lambda self: self.button() == Qt.MouseButton.RightButton
|
|
33
|
+
|
|
34
|
+
Point = QPoint
|
|
35
|
+
|
|
36
|
+
SizePolicy = QSizePolicy
|
|
37
|
+
SizePolicy.MINIMUM_EXPANDING = QSizePolicy.Policy.MinimumExpanding
|
|
38
|
+
SizePolicy.PREFERRED = QSizePolicy.Policy.Preferred
|
|
39
|
+
|
|
40
|
+
Color = QColor
|
|
41
|
+
|
|
42
|
+
FontMetrics = QFontMetrics
|
|
43
|
+
FontMetrics.__getattr__ = missing_attr
|
|
44
|
+
|
|
45
|
+
Widget = QWidget
|
|
46
|
+
Widget.__getattr__ = missing_attr
|
|
47
|
+
|
|
48
|
+
Horizontal = Qt.Orientation.Horizontal
|
|
49
|
+
Vertical = Qt.Orientation.Vertical
|
|
50
|
+
|
|
51
|
+
Layout = QLayout
|
|
52
|
+
|
|
53
|
+
BoxLayout = QBoxLayout
|
|
54
|
+
BoxLayout.__getattr__ = missing_attr
|
|
55
|
+
|
|
56
|
+
HBoxLayout = QHBoxLayout
|
|
57
|
+
VBoxLayout = QVBoxLayout
|
|
58
|
+
|
|
59
|
+
FormLayout = QFormLayout
|
|
60
|
+
FormLayout.__getattr__ = missing_attr
|
|
61
|
+
|
|
62
|
+
Label = QLabel
|
|
63
|
+
Label.set_text = QLabel.setText
|
|
64
|
+
|
|
65
|
+
Splitter = QSplitter
|
|
66
|
+
|
|
67
|
+
Style = QStyle
|
|
68
|
+
Style.State_Active = QStyle.StateFlag.State_Active
|
|
69
|
+
|
|
70
|
+
Style.__getattr__ = missing_attr
|
|
71
|
+
|
|
72
|
+
PushButton = QPushButton
|
|
73
|
+
PushButton.clicked_connect = lambda self, bound_method: self.clicked.connect(bound_method)
|
|
74
|
+
|
|
75
|
+
LineEdit = QLineEdit
|
|
76
|
+
LineEdit.text_edited_connect = lambda self, bound_method: self.textEdited.connect(bound_method)
|
|
77
|
+
LineEdit.editing_finished_connect = lambda self, bound_method: self.editingFinished.connect(bound_method)
|
|
78
|
+
LineEdit.set_password_mode = lambda self: self.setEchoMode(QLineEdit.EchoMode.Password)
|
|
79
|
+
|
|
80
|
+
TextEdit = QPlainTextEdit
|
|
81
|
+
|
|
82
|
+
CheckBox = QCheckBox
|
|
83
|
+
CheckBox.state_changed_connect = lambda self, bound_method: self.stateChanged.connect(bound_method)
|
|
84
|
+
|
|
85
|
+
ComboBox = QComboBox
|
|
86
|
+
|
|
87
|
+
GroupBox = QGroupBox
|
|
88
|
+
|
|
89
|
+
RadioButton = QRadioButton
|
|
90
|
+
|
|
91
|
+
ButtonGroup = QButtonGroup
|
|
92
|
+
ButtonGroup.__getattr__ = missing_attr
|
|
93
|
+
|
|
94
|
+
ListItem = QListWidgetItem
|
|
95
|
+
ListItem.__getattr__ = missing_attr
|
|
96
|
+
|
|
97
|
+
MatchExactly = Qt.MatchFlag.MatchExactly
|
|
98
|
+
|
|
99
|
+
ListWidget = QListWidget
|
|
100
|
+
ListWidget.clicked_connect = lambda self, bound_method: self.clicked.connect(bound_method)
|
|
101
|
+
|
|
102
|
+
TreeItem = QTreeWidgetItem
|
|
103
|
+
TreeItem.__getattr__ = missing_attr
|
|
104
|
+
|
|
105
|
+
TreeWidget = QTreeWidget
|
|
106
|
+
TreeWidget.item_clicked_connect = lambda self, bound_method: self.itemClicked.connect(bound_method)
|
|
107
|
+
TreeWidget.item_expanded_connect = lambda self, bound_method: self.itemExpanded.connect(bound_method)
|
|
108
|
+
TreeWidget.item_pressed_connect = lambda self, bound_method: self.itemPressed.connect(bound_method)
|
|
109
|
+
TreeWidget.item_changed_connect = lambda self, bound_method: self.itemChanged.connect(bound_method)
|
|
110
|
+
|
|
111
|
+
CalendarWidget = QCalendarWidget
|
|
112
|
+
CalendarWidget.selected_date = lambda self: self.selectedDate().toPyDate()
|
|
113
|
+
|
|
114
|
+
class Dialog(QDialog):
|
|
115
|
+
s_open_dialogs = set()
|
|
116
|
+
def __init__(self,parent=None,*args,**kwargs):
|
|
117
|
+
super().__init__(parent or init().activeModalWidget(),*args,**kwargs)
|
|
118
|
+
|
|
119
|
+
def _on_finished(self):
|
|
120
|
+
open_dialogs = self.s_open_dialogs
|
|
121
|
+
in_open_dialogs = self in open_dialogs
|
|
122
|
+
print(f'finishing {self}: {in_open_dialogs}')
|
|
123
|
+
|
|
124
|
+
if in_open_dialogs:
|
|
125
|
+
self.s_open_dialogs.remove(self)
|
|
126
|
+
|
|
127
|
+
def show(self):
|
|
128
|
+
print(f'showing {self}')
|
|
129
|
+
self.s_open_dialogs.add(self)
|
|
130
|
+
self.finished.connect(self._on_finished)
|
|
131
|
+
super().show()
|
|
132
|
+
self.raise_()
|
|
133
|
+
self.activateWindow()
|
|
134
|
+
self.s_open_dialogs.add(self)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class MessageBox(QMessageBox):
|
|
138
|
+
s_boxes_by_parent = {}
|
|
139
|
+
def __init__(self, on_close: Callable[[Any],None] = None, parent: Widget = None):
|
|
140
|
+
super().__init__(parent)
|
|
141
|
+
self._on_close = on_close
|
|
142
|
+
self.finished.connect(self._handle_finished)
|
|
143
|
+
|
|
144
|
+
# Set the default values for the message box
|
|
145
|
+
self.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.WindowCloseButtonHint|Qt.WindowType.WindowStaysOnTopHint)
|
|
146
|
+
self.setStandardButtons(QMessageBox.StandardButton.Ok)
|
|
147
|
+
self.setDefaultButton(QMessageBox.StandardButton.Ok)
|
|
148
|
+
self.setEscapeButton(QMessageBox.StandardButton.Ok)
|
|
149
|
+
|
|
150
|
+
self.setModal(True)
|
|
151
|
+
self.s_boxes_by_parent[self.parent()] = self
|
|
152
|
+
|
|
153
|
+
def _handle_finished(self, result):
|
|
154
|
+
if self._on_close:
|
|
155
|
+
self._on_close(result)
|
|
156
|
+
self.s_boxes_by_parent.pop(self.parent())
|
|
157
|
+
self.deleteLater()
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def _create_message_box(cls, parent: Widget, title, message, on_close: Callable[[Any],None], icon) -> MessageBox:
|
|
161
|
+
msg_box = cls(on_close,parent)
|
|
162
|
+
msg_box.setWindowTitle(title)
|
|
163
|
+
msg_box.setText(message)
|
|
164
|
+
msg_box.setIcon(icon)
|
|
165
|
+
return msg_box
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def question(cls, parent: Widget, title: str, message: str, on_close: Callable[[Any],None]) -> MessageBox.StandardButton:
|
|
169
|
+
msg_box = cls._create_message_box(
|
|
170
|
+
parent=parent,
|
|
171
|
+
title=title,
|
|
172
|
+
message=message,
|
|
173
|
+
on_close=on_close,
|
|
174
|
+
icon=QMessageBox.Icon.Question,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|
178
|
+
msg_box.setDefaultButton(QMessageBox.StandardButton.No)
|
|
179
|
+
msg_box.setEscapeButton(QMessageBox.StandardButton.No)
|
|
180
|
+
|
|
181
|
+
if on_close:
|
|
182
|
+
msg_box.show()
|
|
183
|
+
else:
|
|
184
|
+
return msg_box.exec()
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def warning(cls, parent: Widget, title: str, message: str, on_close: Callable[[Any],None]) -> MessageBox.StandardButton:
|
|
188
|
+
msg_box = cls._create_message_box(
|
|
189
|
+
parent=parent,
|
|
190
|
+
title=title,
|
|
191
|
+
message=message,
|
|
192
|
+
on_close=on_close,
|
|
193
|
+
icon=QMessageBox.Icon.Warning,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if on_close:
|
|
197
|
+
msg_box.show()
|
|
198
|
+
else:
|
|
199
|
+
return msg_box.exec()
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def information(cls, parent: Widget, title: str, message: str, on_close: Callable[[Any],None]) -> MessageBox.StandardButton:
|
|
203
|
+
msg_box = cls._create_message_box(
|
|
204
|
+
parent=parent,
|
|
205
|
+
title=title,
|
|
206
|
+
message=message,
|
|
207
|
+
on_close=on_close,
|
|
208
|
+
icon=QMessageBox.Icon.Information,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if on_close:
|
|
212
|
+
msg_box.show()
|
|
213
|
+
else:
|
|
214
|
+
return msg_box.exec()
|
|
215
|
+
|
|
216
|
+
@classmethod
|
|
217
|
+
def is_yes_button(cls, sb: QMessageBox.StandardButton) -> bool:
|
|
218
|
+
return sb == QMessageBox.StandardButton.Yes
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
Application = QApplication
|
|
222
|
+
|
|
223
|
+
class TEXT_ALIGN:
|
|
224
|
+
TOP = Qt.AlignmentFlag.AlignTop
|
|
225
|
+
V_CENTER = Qt.AlignmentFlag.AlignVCenter
|
|
226
|
+
BOTTOM = Qt.AlignmentFlag.AlignBottom
|
|
227
|
+
LEFT = Qt.AlignmentFlag.AlignLeft
|
|
228
|
+
CENTER = Qt.AlignmentFlag.AlignCenter
|
|
229
|
+
RIGHT = Qt.AlignmentFlag.AlignRight
|
|
230
|
+
|
|
231
|
+
class SCROLL:
|
|
232
|
+
OFF = Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
|
233
|
+
ON = Qt.ScrollBarPolicy.ScrollBarAlwaysOn
|
|
234
|
+
AS_NEEDED = Qt.ScrollBarPolicy.ScrollBarAsNeeded
|
|
235
|
+
|
|
236
|
+
ScrollArea = QScrollArea
|
|
237
|
+
|
|
238
|
+
ForegroundRole = Qt.ItemDataRole.ForegroundRole
|
|
239
|
+
BackroundRole = Qt.ItemDataRole.BackgroundRole
|
|
240
|
+
|
|
241
|
+
Palette = QPalette
|
|
242
|
+
Palette.ButtonText = QPalette.ColorRole.ButtonText
|
|
243
|
+
Palette.Button = QPalette.ColorRole.Button
|
|
244
|
+
Palette.Window = QPalette.ColorRole.Window
|
|
245
|
+
|
|
246
|
+
StyleOptionHeader = QStyleOptionHeader
|
|
247
|
+
Separator = QLabel
|
|
248
|
+
|
|
249
|
+
@cache
|
|
250
|
+
def init(style = '') -> QApplication:
|
|
251
|
+
if not style:
|
|
252
|
+
style = platform.system()
|
|
253
|
+
|
|
254
|
+
app = QApplication([])
|
|
255
|
+
app.setStyle(style)
|
|
256
|
+
return app
|
|
257
|
+
|
|
258
|
+
def to_clipboard(text: str, **kwargs):
|
|
259
|
+
cp = QGuiApplication.clipboard()
|
|
260
|
+
cp.setText(text)
|
|
261
|
+
|
|
262
|
+
def from_clipboard(**kwargs) -> str:
|
|
263
|
+
cp = QGuiApplication.clipboard()
|
|
264
|
+
return cp.text()
|
|
265
|
+
|
|
266
|
+
def separator(horizontal = True) -> Separator:
|
|
267
|
+
direction = QFrame.Shape.HLine if horizontal else QFrame.Shape.VLine
|
|
268
|
+
sep = QLabel()
|
|
269
|
+
sep.setFrameStyle(direction | QFrame.Shadow.Sunken)
|
|
270
|
+
return sep
|
|
271
|
+
|
|
272
|
+
def is_ui_thread() -> bool:
|
|
273
|
+
return QThread.currentThread() is QGuiApplication.instance().thread()
|
|
274
|
+
|
|
275
|
+
|