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,305 @@
|
|
|
1
|
+
from PyQt5.QtCore import QModelIndex, QPointF, QRect, QSize, Qt, QVariant
|
|
2
|
+
from PyQt5.QtGui import QFont, QFontMetrics, QPainter, QPaintEvent
|
|
3
|
+
from PyQt5.QtWidgets import QStyle, QStyleOptionHeader
|
|
4
|
+
|
|
5
|
+
from ui_10x.platform import ux
|
|
6
|
+
from ui_10x.traitable_view import TraitableView
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# ruff: noqa: N802 # Function name should be lowercase
|
|
10
|
+
class HeaderModel(ux.StandardItemModel):
|
|
11
|
+
def _create_subtree(self, traits: dict, root: ux.StandardItem, tree: dict):
|
|
12
|
+
col = 0
|
|
13
|
+
for subtree_name, subtree in tree.items():
|
|
14
|
+
if isinstance(subtree, str):
|
|
15
|
+
trait = traits.get(subtree_name)
|
|
16
|
+
if not trait:
|
|
17
|
+
continue
|
|
18
|
+
label = subtree
|
|
19
|
+
subtree = None
|
|
20
|
+
elif isinstance(subtree, dict):
|
|
21
|
+
label = subtree_name
|
|
22
|
+
else:
|
|
23
|
+
continue
|
|
24
|
+
|
|
25
|
+
item = ux.StandardItem(label)
|
|
26
|
+
if subtree:
|
|
27
|
+
self._create_subtree(traits, item, subtree)
|
|
28
|
+
|
|
29
|
+
if root:
|
|
30
|
+
root.append_column((item, ))
|
|
31
|
+
else:
|
|
32
|
+
self.set_item(0, col, item)
|
|
33
|
+
|
|
34
|
+
col += 1
|
|
35
|
+
|
|
36
|
+
def __init__(self, traits: dict, header_structure: dict):
|
|
37
|
+
super().__init__()
|
|
38
|
+
self.traits = traits
|
|
39
|
+
self.create(header_structure)
|
|
40
|
+
|
|
41
|
+
def create(self, header_structure: dict):
|
|
42
|
+
self._create_subtree(self.traits, None, header_structure)
|
|
43
|
+
|
|
44
|
+
def leaf_index(self, section_index: int) -> ux.ModelIndex:
|
|
45
|
+
current_leaf_index = -1
|
|
46
|
+
for i in range(self.column_count()):
|
|
47
|
+
res, current_leaf_index = self.find_leaf(self.index( 0, i), section_index, current_leaf_index)
|
|
48
|
+
if res.is_valid():
|
|
49
|
+
return res
|
|
50
|
+
|
|
51
|
+
return ux.ModelIndex()
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def leftmost_leaf(index: ux.ModelIndex) -> ux.ModelIndex:
|
|
55
|
+
prev_index = index
|
|
56
|
+
while index.is_valid():
|
|
57
|
+
prev_index = index
|
|
58
|
+
index = index.child(0, 0)
|
|
59
|
+
return prev_index
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def find_leaf(current_index: ux.ModelIndex, section_index: int, current_leaf_index: int) -> tuple: #-- ( index, current_leaf_index )
|
|
63
|
+
if current_index.is_valid():
|
|
64
|
+
child_count = current_index.model().column_count(current_index)
|
|
65
|
+
if child_count:
|
|
66
|
+
for i in range(child_count):
|
|
67
|
+
res, current_leaf_index = HeaderModel.find_leaf(current_index.child(0, i), section_index, current_leaf_index)
|
|
68
|
+
if res.is_valid():
|
|
69
|
+
return (res, current_leaf_index)
|
|
70
|
+
else:
|
|
71
|
+
current_leaf_index += 1
|
|
72
|
+
if current_leaf_index == section_index:
|
|
73
|
+
return (current_index, current_leaf_index)
|
|
74
|
+
|
|
75
|
+
return (ux.ModelIndex(), current_leaf_index)
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def search_leaves(current_index: ux.ModelIndex) -> list:
|
|
79
|
+
res = []
|
|
80
|
+
if current_index.is_valid():
|
|
81
|
+
if not current_index.child(0, 0).is_valid():
|
|
82
|
+
res.append(current_index)
|
|
83
|
+
else:
|
|
84
|
+
c = 0
|
|
85
|
+
child = current_index.child(0, c)
|
|
86
|
+
while child.is_valid():
|
|
87
|
+
found = HeaderModel.search_leaves(child)
|
|
88
|
+
res.extend(found)
|
|
89
|
+
c += 1
|
|
90
|
+
child = current_index.child(0, c)
|
|
91
|
+
|
|
92
|
+
return res
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def leaves(search_index: ux.ModelIndex ) -> list:
|
|
96
|
+
leaves = []
|
|
97
|
+
if search_index.is_valid():
|
|
98
|
+
child_count = search_index.model().column_count(search_index)
|
|
99
|
+
for i in range(child_count):
|
|
100
|
+
found = HeaderModel.search_leaves(search_index.child(0, i))
|
|
101
|
+
leaves.extend(found)
|
|
102
|
+
|
|
103
|
+
return leaves
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def find_root_index(index: ux.ModelIndex) -> ux.ModelIndex:
|
|
107
|
+
while index.parent().is_valid():
|
|
108
|
+
index = index.parent()
|
|
109
|
+
return index
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def parent_indexes(index: ux.ModelIndex) -> list:
|
|
113
|
+
indexes = []
|
|
114
|
+
while index.is_valid():
|
|
115
|
+
indexes.insert(0, index)
|
|
116
|
+
index = index.parent()
|
|
117
|
+
return indexes
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class HeaderView(ux.HeaderView):
|
|
121
|
+
def __init__(self, view: TraitableView, parent: ux.Widget = None):
|
|
122
|
+
super().__init__(ux.Horizontal, parent = parent)
|
|
123
|
+
self.model = HeaderModel(view.ui_hints, view.header)
|
|
124
|
+
self.set_model(self.model)
|
|
125
|
+
|
|
126
|
+
self.painted_cells = set()
|
|
127
|
+
self.section_resized_connect(self.on_section_resized)
|
|
128
|
+
|
|
129
|
+
def set_foreground_brush(self, opt: ux.StyleOptionHeader, index: ux.ModelIndex ):
|
|
130
|
+
fgb = index.data(ux.ForegroundRole)
|
|
131
|
+
if fgb:
|
|
132
|
+
opt.palette.set_brush(ux.Palette.ButtonText, fgb)
|
|
133
|
+
|
|
134
|
+
def set_background_brush(self, opt: ux.StyleOptionHeader, index: ux.ModelIndex ):
|
|
135
|
+
bgb = index.data(ux.BackgroundRole)
|
|
136
|
+
if bgb:
|
|
137
|
+
opt.palette.set_brush(ux.Palette.Button, bgb)
|
|
138
|
+
opt.palette.set_brush(ux.Palette.Window, bgb)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# s_selected_position = (
|
|
142
|
+
# ux.StyleOptionHeader.NotAdjacent, # prev - 0, next 0
|
|
143
|
+
# ux.StyleOptionHeader.NextIsSelected, # prev - 0, next 1
|
|
144
|
+
# ux.StyleOptionHeader.PreviousIsSelected, # prev - 1, next 0
|
|
145
|
+
# ux.StyleOptionHeader.NextAndPreviousAreSelected # prev - 1, next 1
|
|
146
|
+
# )
|
|
147
|
+
def style_option_for_cell(self, logical_index: int) -> ux.StyleOptionHeader:
|
|
148
|
+
opt = ux.StyleOptionHeader()
|
|
149
|
+
# self.init_style_option(opt)
|
|
150
|
+
# if self.window().is_active_window():
|
|
151
|
+
# opt.state |= ux.Style.State_Active
|
|
152
|
+
# opt.text_alignment = ux.AlignCenter
|
|
153
|
+
# opt.icon_alignment = ux.AlignVCenter
|
|
154
|
+
# opt.section = logical_index
|
|
155
|
+
#
|
|
156
|
+
# visual = self.visual_index(logical_index)
|
|
157
|
+
#
|
|
158
|
+
# if self.count() == 1:
|
|
159
|
+
# opt.position = ux.StyleOptionHeader.OnlyOneSection
|
|
160
|
+
# else:
|
|
161
|
+
# if visual == 0:
|
|
162
|
+
# opt.position = QStyleOptionHeader.Beginning
|
|
163
|
+
# else:
|
|
164
|
+
# opt.position = QStyleOptionHeader.End if visual == ( self.count() - 1 ) else QStyleOptionHeader.Middle
|
|
165
|
+
#
|
|
166
|
+
# if self.sectionsClickable():
|
|
167
|
+
# if self.highlightSections() and self.selectionMode():
|
|
168
|
+
# if self.selectionModel().columnIntersectsSelection( logical_index, self.rootIndex() ):
|
|
169
|
+
# opt.state |= QStyle.State_On
|
|
170
|
+
# if self.selectionModel().isColumnSelected( logical_index, self.rootIndex() ):
|
|
171
|
+
# opt.state |= QStyle.State_Sunken
|
|
172
|
+
#
|
|
173
|
+
# if self.selectionModel():
|
|
174
|
+
# prev_selected = self.selectionModel().isColumnSelected( self.logicalIndex( visual - 1 ), self.rootIndex() )
|
|
175
|
+
# next_selected = self.selectionModel().isColumnSelected( self.logicalIndex( visual + 1 ), self.rootIndex() )
|
|
176
|
+
# code = 2 * prev_selected + next_selected
|
|
177
|
+
# opt.selectedPosition = self.s_selectedPosition[ code ]
|
|
178
|
+
|
|
179
|
+
return opt
|
|
180
|
+
|
|
181
|
+
def cell_size(self, leaf_index: ux.ModelIndex, style_options: ux.StyleOptionHeader) -> ux.Size:
|
|
182
|
+
res = ux.Size()
|
|
183
|
+
variant = QVariant(leaf_index.data(Qt.SizeHintRole))
|
|
184
|
+
if variant.isValid():
|
|
185
|
+
res = variant # TODO: cast?
|
|
186
|
+
fnt = QFont(self.font())
|
|
187
|
+
var = QVariant(leaf_index.data(Qt.FontRole))
|
|
188
|
+
if var.isValid():
|
|
189
|
+
fnt = var
|
|
190
|
+
fnt.setBold(True)
|
|
191
|
+
fm = QFontMetrics(fnt)
|
|
192
|
+
size = QSize(fm.size(0, leaf_index.data(Qt.DisplayRole)))
|
|
193
|
+
if leaf_index.data(Qt.UserRole): # isValid()
|
|
194
|
+
size.transpose()
|
|
195
|
+
decoration_size = self.style().sizeFromContents(QStyle.CT_HeaderSection, style_options, QSize(), self)
|
|
196
|
+
empty_text_size = QSize(fm.size( 0, '' ))
|
|
197
|
+
return res.expandedTo(size + decoration_size - empty_text_size)
|
|
198
|
+
|
|
199
|
+
def current_cell_width(self, searched_index: QModelIndex, leaf_index: QModelIndex, section_index: int) -> int:
|
|
200
|
+
leaves = HeaderModel.leaves(searched_index)
|
|
201
|
+
if not leaves:
|
|
202
|
+
return self.section_size(section_index)
|
|
203
|
+
|
|
204
|
+
first_leaf_section_index = section_index - leaves.index(leaf_index)
|
|
205
|
+
return sum(self.section_size(first_leaf_section_index + i) for i in range(len(leaves)))
|
|
206
|
+
|
|
207
|
+
def current_cell_left(self, searched_index: QModelIndex, leaf_index: QModelIndex, section_index: int, left: int) -> int:
|
|
208
|
+
leaves = HeaderModel.leaves(searched_index)
|
|
209
|
+
if leaves:
|
|
210
|
+
n = leaves.index(leaf_index)
|
|
211
|
+
first_leaf_section_index = section_index - n
|
|
212
|
+
n -= 1
|
|
213
|
+
left -= sum(self.section_size(first_leaf_section_index + i) for i in range(n))
|
|
214
|
+
return left
|
|
215
|
+
|
|
216
|
+
def paint_horizontal_cell(self, painter: QPainter, cell_index: QModelIndex, leaf_index: QModelIndex, logical_index: int, style_options: QStyleOptionHeader, section_rect: QRect, top: int) -> int:
|
|
217
|
+
uniopt = QStyleOptionHeader(style_options)
|
|
218
|
+
self.set_foreground_brush(uniopt, cell_index)
|
|
219
|
+
self.set_background_brush(uniopt, cell_index) # TODO!
|
|
220
|
+
|
|
221
|
+
height = self.cell_size(cell_index, uniopt).height()
|
|
222
|
+
|
|
223
|
+
if cell_index == leaf_index:
|
|
224
|
+
height = section_rect.height() - top
|
|
225
|
+
|
|
226
|
+
left = self.current_cell_left(cell_index, leaf_index, logical_index, section_rect.left())
|
|
227
|
+
width = self.current_cell_width(cell_index, leaf_index, logical_index)
|
|
228
|
+
|
|
229
|
+
if cell_index not in self.painted_cells:
|
|
230
|
+
self.painted_cells.add(cell_index)
|
|
231
|
+
r = QRect(left, top, width, height)
|
|
232
|
+
|
|
233
|
+
uniopt.text = cell_index.data(Qt.DisplayRole)
|
|
234
|
+
painter.save()
|
|
235
|
+
uniopt.rect = r
|
|
236
|
+
self.style().drawControl(QStyle.CE_Header, uniopt, painter, self)
|
|
237
|
+
|
|
238
|
+
painter.restore()
|
|
239
|
+
|
|
240
|
+
return top + height
|
|
241
|
+
|
|
242
|
+
def paintEvent(self, event: QPaintEvent):
|
|
243
|
+
self.painted_cells = set()
|
|
244
|
+
super().paintEvent(event)
|
|
245
|
+
|
|
246
|
+
def paintSection(self, painter: QPainter, rect: QRect, logical_index: int):
|
|
247
|
+
if rect.isValid():
|
|
248
|
+
leaf_index = self.model.leaf_index(logical_index)
|
|
249
|
+
if leaf_index.is_valid():
|
|
250
|
+
old_bo = QPointF(painter.brushOrigin())
|
|
251
|
+
top = rect.y()
|
|
252
|
+
indexes = HeaderModel.parent_indexes(leaf_index)
|
|
253
|
+
style_options = self.style_option_for_cell(logical_index)
|
|
254
|
+
for i in range(len(indexes)):
|
|
255
|
+
real_style_options = QStyleOptionHeader(style_options)
|
|
256
|
+
state = int(real_style_options.state)
|
|
257
|
+
if i < (len(indexes) - 1) and (bool(state & QStyle.State_Sunken) or bool(state & QStyle.State_On)):
|
|
258
|
+
real_style_options.state &= ~(QStyle.State_Sunken | QStyle.State_On)
|
|
259
|
+
|
|
260
|
+
cell = indexes[i]
|
|
261
|
+
top = self.paint_horizontal_cell(painter, cell, leaf_index, logical_index, real_style_options, rect, top)
|
|
262
|
+
|
|
263
|
+
painter.setBrushOrigin(old_bo)
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
super().paintSection(painter, rect, logical_index)
|
|
267
|
+
|
|
268
|
+
def sectionSizeFromContents(self, logical_index: int ) -> QSize:
|
|
269
|
+
cur_leaf_index = QModelIndex(self.model.leaf_index(logical_index))
|
|
270
|
+
if not cur_leaf_index.is_valid():
|
|
271
|
+
return super().sectionSizeFromContents(logical_index)
|
|
272
|
+
|
|
273
|
+
style_option = QStyleOptionHeader(self.style_option_for_cell(logical_index ))
|
|
274
|
+
s = QSize(self.cell_size(cur_leaf_index, style_option))
|
|
275
|
+
cur_leaf_index = cur_leaf_index.parent()
|
|
276
|
+
while cur_leaf_index.is_valid():
|
|
277
|
+
s.setHeight(s.height() + self.cell_size(cur_leaf_index, style_option).height())
|
|
278
|
+
cur_leaf_index = cur_leaf_index.parent()
|
|
279
|
+
|
|
280
|
+
return s
|
|
281
|
+
|
|
282
|
+
def on_section_resized(self, logical_index: int, i2, i3 ):
|
|
283
|
+
if self.is_section_hidden(logical_index):
|
|
284
|
+
return
|
|
285
|
+
|
|
286
|
+
leaf_index = QModelIndex(self.model.leaf_index(logical_index))
|
|
287
|
+
if leaf_index.is_valid():
|
|
288
|
+
root_index = HeaderModel.find_root_index(leaf_index)
|
|
289
|
+
leaves = HeaderModel.leaves(root_index)
|
|
290
|
+
try:
|
|
291
|
+
n = leaves.index(leaf_index)
|
|
292
|
+
while n > 0:
|
|
293
|
+
logical_index -= 1
|
|
294
|
+
|
|
295
|
+
w = self.viewport().width()
|
|
296
|
+
h = self.viewport().height()
|
|
297
|
+
pos = self.sectionViewportPosition(logical_index)
|
|
298
|
+
r = QRect(pos, 0, w - pos, h)
|
|
299
|
+
if self.isRightToLeft():
|
|
300
|
+
r.setRect(0, 0, pos + self.sectionSize(logical_index), h)
|
|
301
|
+
|
|
302
|
+
self.viewport().update( r.normalized() )
|
|
303
|
+
n -= 1
|
|
304
|
+
except Exception:
|
|
305
|
+
pass
|
ui_10x/table_view.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
|
|
3
|
+
from core_10x.traitable import Trait, Traitable
|
|
4
|
+
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt, QVariant
|
|
5
|
+
from PyQt5.QtWidgets import QAbstractScrollArea, QTableView
|
|
6
|
+
|
|
7
|
+
from ui_10x.table_header_view import HeaderModel, HeaderView
|
|
8
|
+
from ui_10x.traitable_editor import TraitableEditor
|
|
9
|
+
from ui_10x.traitable_view import TraitableView
|
|
10
|
+
from ui_10x.utils import UxStyleSheet, ux_text_alignment
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# ruff: noqa: N802 # Function name should be lowercase
|
|
14
|
+
class Model(QAbstractTableModel):
|
|
15
|
+
def __init__(self, model: HeaderModel, data, parent = None):
|
|
16
|
+
super().__init__(parent = parent)
|
|
17
|
+
self.model = model
|
|
18
|
+
self.data = data
|
|
19
|
+
self.col2name = { i: name for i, name in enumerate( model.traits ) }
|
|
20
|
+
self.name2col = { name: i for i, name in self.col2name.items() }
|
|
21
|
+
|
|
22
|
+
def trait_name(self, col: int) -> str:
|
|
23
|
+
return self.col2name.get(col)
|
|
24
|
+
|
|
25
|
+
def rowCount(self, parent = None, *args, **kwargs):
|
|
26
|
+
return len(self.data)
|
|
27
|
+
|
|
28
|
+
def columnCount( self, parent = None, *args, **kwargs ):
|
|
29
|
+
if not self.data:
|
|
30
|
+
return 0
|
|
31
|
+
return len(self.col2name)
|
|
32
|
+
|
|
33
|
+
def data(self, index: QModelIndex, role: int = None):
|
|
34
|
+
if role == Qt.UserRole:
|
|
35
|
+
return self.model
|
|
36
|
+
|
|
37
|
+
entity: Traitable = self.data[index.row()]
|
|
38
|
+
trait_name = self.col2name.get(index.column())
|
|
39
|
+
|
|
40
|
+
cls = entity.__class__
|
|
41
|
+
trait: Trait = cls.trait(trait_name)
|
|
42
|
+
if role == Qt.DisplayRole and index.isValid():
|
|
43
|
+
if trait:
|
|
44
|
+
value = entity.get_value(trait)
|
|
45
|
+
text = trait.to_str(value)
|
|
46
|
+
else:
|
|
47
|
+
text = ''
|
|
48
|
+
return text
|
|
49
|
+
|
|
50
|
+
if role == Qt.ForegroundRole:
|
|
51
|
+
sh = entity.get_style_sheet(trait)
|
|
52
|
+
return UxStyleSheet.fg_color(sh)
|
|
53
|
+
|
|
54
|
+
if role == Qt.BackgroundRole:
|
|
55
|
+
sh = entity.get_style_sheet(trait)
|
|
56
|
+
return UxStyleSheet.bg_color(sh)
|
|
57
|
+
|
|
58
|
+
if role == Qt.TextAlignmentRole:
|
|
59
|
+
return ux_text_alignment(trait.ui_hint.param('align_h', 0), horizontal = True)
|
|
60
|
+
|
|
61
|
+
return QVariant()
|
|
62
|
+
|
|
63
|
+
def set_data(self, i: QModelIndex, dummy, role = None) -> bool:
|
|
64
|
+
self.dataChanged.emit(i, i)
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
def change_data(self, row: int, trait_name: str):
|
|
68
|
+
col = self.name2col.get(trait_name)
|
|
69
|
+
i = QModelIndex(row, col)
|
|
70
|
+
self.set_data(i, None, role = None)
|
|
71
|
+
|
|
72
|
+
def insertRows(self, row: int, num_rows: int, parent = None, *args, **kwargs) -> bool:
|
|
73
|
+
#self.beginInsertRows( QModelIndex(), row, row + num_rows )
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
def extendData(self, data) -> bool:
|
|
77
|
+
"""
|
|
78
|
+
:param data: an iterable of entities
|
|
79
|
+
:return: True if model reset happened as well
|
|
80
|
+
"""
|
|
81
|
+
if data:
|
|
82
|
+
the_data = self.data
|
|
83
|
+
is_empty = not bool(the_data)
|
|
84
|
+
if is_empty:
|
|
85
|
+
self.beginResetModel()
|
|
86
|
+
|
|
87
|
+
first = len(the_data)
|
|
88
|
+
last = first + len(data) - 1
|
|
89
|
+
self.beginInsertRows(QModelIndex(), first, last)
|
|
90
|
+
the_data.extend(data)
|
|
91
|
+
self.insertRows(first, len( data))
|
|
92
|
+
self.endInsertRows()
|
|
93
|
+
|
|
94
|
+
if is_empty:
|
|
95
|
+
self.endResetModel()
|
|
96
|
+
return is_empty
|
|
97
|
+
|
|
98
|
+
def entity(self, row: int) -> Traitable:
|
|
99
|
+
return self.data[row]
|
|
100
|
+
|
|
101
|
+
class TableView(QTableView):
|
|
102
|
+
def __init__(self, entities_or_class, view: TraitableView = None):
|
|
103
|
+
assert entities_or_class, 'first arg must not be empty'
|
|
104
|
+
|
|
105
|
+
if inspect.isclass(entities_or_class):
|
|
106
|
+
assert issubclass(entities_or_class, Traitable), 'first arg is a class, but not a subclass of Traitable'
|
|
107
|
+
proto = entities_or_class
|
|
108
|
+
entities = []
|
|
109
|
+
else:
|
|
110
|
+
entities = entities_or_class
|
|
111
|
+
proto = entities[0]
|
|
112
|
+
|
|
113
|
+
if not view:
|
|
114
|
+
view = TraitableView.default(proto)
|
|
115
|
+
|
|
116
|
+
super().__init__()
|
|
117
|
+
self.wheel_enabled = True
|
|
118
|
+
|
|
119
|
+
hv = HeaderView(view, parent = self)
|
|
120
|
+
hv.setStretchLastSection(True)
|
|
121
|
+
|
|
122
|
+
self.setHorizontalHeader(hv)
|
|
123
|
+
model = Model(hv.model(), entities, self)
|
|
124
|
+
self.setModel(model)
|
|
125
|
+
|
|
126
|
+
self.setAlternatingRowColors(True)
|
|
127
|
+
self.resizeColumnsToContents()
|
|
128
|
+
|
|
129
|
+
self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
|
|
130
|
+
|
|
131
|
+
def wheelEvent(self, *args, **kwargs):
|
|
132
|
+
if self.wheel_enabled:
|
|
133
|
+
QTableView.wheelEvent(self, args[0])
|
|
134
|
+
|
|
135
|
+
def keyPressEvent(self, *args, **kwargs):
|
|
136
|
+
if self.wheel_enabled:
|
|
137
|
+
QTableView.keyPressEvent(self, args[0])
|
|
138
|
+
|
|
139
|
+
def mousePressEvent(self, *args, **kwargs):
|
|
140
|
+
mouse_event = args[0]
|
|
141
|
+
row = self.rowAt(mouse_event.y())
|
|
142
|
+
col = self.columnAt(mouse_event.x())
|
|
143
|
+
if row == -1 or col == -1:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
entity = self.model().entity(row)
|
|
147
|
+
trait_name = self.model().trait_name(col)
|
|
148
|
+
if not trait_name:
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
editor_class = TraitableEditor.findEditorClass(entity.__class__) #-- no alternative packages, look under ui subdir only
|
|
152
|
+
mouse_btn = mouse_event.button()
|
|
153
|
+
if mouse_btn == Qt.LeftButton:
|
|
154
|
+
cb = editor_class.leftMouseCallback(trait_name)
|
|
155
|
+
elif mouse_btn == Qt.RightButton:
|
|
156
|
+
cb = editor_class.rightMouseCallback(trait_name)
|
|
157
|
+
else:
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if cb:
|
|
161
|
+
cb(self, entity)
|
|
162
|
+
else:
|
|
163
|
+
super().mousePressEvent(*args, **kwargs)
|
|
164
|
+
|
|
165
|
+
def render_entity(self, row: int, entity: Traitable):
|
|
166
|
+
last_col = len(self.model().col2name) - 1
|
|
167
|
+
first_index = self.model().create_index(row, 0)
|
|
168
|
+
last_index = self.model().create_index(row, last_col)
|
|
169
|
+
self.model().dataChanged.emit(first_index, last_index)
|
|
170
|
+
|
|
171
|
+
def extendData( self, data ):
|
|
172
|
+
if self.model().extendData(data):
|
|
173
|
+
self.resizeColumnsToContents()
|
|
174
|
+
#self.resizeRowsToContents()
|
ui_10x/trait_editor.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import nullcontext
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from core_10x.concrete_traits import date_trait, dict_trait, flags_trait, list_trait
|
|
8
|
+
from core_10x.trait import T, Ui
|
|
9
|
+
from core_10x.traitable import traitable_trait
|
|
10
|
+
|
|
11
|
+
import ui_10x.concrete_trait_widgets # do not remove -- this registers trait widgets
|
|
12
|
+
from ui_10x.choice import MultiChoice
|
|
13
|
+
from ui_10x.py_data_browser import PyDataBrowser
|
|
14
|
+
from ui_10x.trait_widget import TraitWidget
|
|
15
|
+
from ui_10x.utils import UxDialog, UxStyleSheet, ux, ux_pick_date
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
|
|
20
|
+
from core_10x.entity import Entity
|
|
21
|
+
from core_10x.exec_control import BTP
|
|
22
|
+
from core_10x.rc import RC
|
|
23
|
+
from core_10x.trait import Trait
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class EntityWrapper:
|
|
28
|
+
entity: Entity
|
|
29
|
+
traitable_processor: Callable[[], BTP]
|
|
30
|
+
|
|
31
|
+
def set_value(self, trait: Trait, value) -> RC:
|
|
32
|
+
with self.traitable_processor() or nullcontext():
|
|
33
|
+
return self.entity.set_value(trait, value)
|
|
34
|
+
|
|
35
|
+
def get_value(self, trait: Trait):
|
|
36
|
+
with self.traitable_processor() or nullcontext():
|
|
37
|
+
value = self.entity.get_value(trait)
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
def is_valid(self, trait: Trait) -> bool:
|
|
41
|
+
with self.traitable_processor() or nullcontext():
|
|
42
|
+
return self.entity.is_valid(trait)
|
|
43
|
+
|
|
44
|
+
def invalidate_value(self, trait: Trait) -> None:
|
|
45
|
+
with self.traitable_processor() or nullcontext():
|
|
46
|
+
return self.entity.invalidate_value(trait)
|
|
47
|
+
|
|
48
|
+
def get_style_sheet(self, trait: Trait):
|
|
49
|
+
with self.traitable_processor() or nullcontext():
|
|
50
|
+
return self.entity.get_style_sheet(trait)
|
|
51
|
+
|
|
52
|
+
def get_choices(self, trait: Trait):
|
|
53
|
+
with self.traitable_processor() or nullcontext():
|
|
54
|
+
return self.entity.get_choices(trait)
|
|
55
|
+
|
|
56
|
+
def create_ui_node(self, trait: Trait, callback):
|
|
57
|
+
with self.traitable_processor() or nullcontext():
|
|
58
|
+
return self.entity.bui_class().create_ui_node(self.entity, trait, callback)
|
|
59
|
+
|
|
60
|
+
def update_ui_node(self, trait: Trait):
|
|
61
|
+
with self.traitable_processor() or nullcontext():
|
|
62
|
+
return self.entity.bui_class().update_ui_node(self.entity, trait)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TraitEditor:
|
|
66
|
+
def __init__(self, entity, trait: Trait, ui_hint: Ui, custom_callback: Callable[[], None] = None, traitable_processor: Callable[[], BTP] = None):
|
|
67
|
+
self.entity = EntityWrapper(entity, traitable_processor)
|
|
68
|
+
self.trait = trait
|
|
69
|
+
self.widget: TraitWidget | None = None
|
|
70
|
+
self.ui_hint = ui_hint
|
|
71
|
+
self.trait_callback = self._establish_callback(custom_callback)
|
|
72
|
+
|
|
73
|
+
def is_read_only(self) -> bool:
|
|
74
|
+
return self.ui_hint.flags_on(Ui.READ_ONLY)
|
|
75
|
+
|
|
76
|
+
def _establish_callback(self, custom_callback: Callable[[], None]):
|
|
77
|
+
if custom_callback:
|
|
78
|
+
return custom_callback
|
|
79
|
+
|
|
80
|
+
if self.is_read_only():
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
return self.builtin_callback()
|
|
84
|
+
|
|
85
|
+
def new_label(self) -> ux.Widget:
|
|
86
|
+
if self.ui_hint.param('right_label', False):
|
|
87
|
+
return ux.Label(' ')
|
|
88
|
+
|
|
89
|
+
if self.trait_callback:
|
|
90
|
+
label = ux.PushButton()
|
|
91
|
+
label.set_flat(True)
|
|
92
|
+
label.set_style_sheet('text-align: left')
|
|
93
|
+
label.set_text(self.ui_hint.label + '...')
|
|
94
|
+
label.clicked_connect(lambda v: self.trait_callback(self))
|
|
95
|
+
if self.trait.flags_on(T.EXPENSIVE):
|
|
96
|
+
UxStyleSheet.modify(label, {Ui.FG_COLOR: 'purple', Ui.FONT_STYLE: 'italic'})
|
|
97
|
+
label.set_tool_tip('Reaction on clicking this button may take considerable time...')
|
|
98
|
+
else:
|
|
99
|
+
label = ux.Label(self.ui_hint.label)
|
|
100
|
+
|
|
101
|
+
return label
|
|
102
|
+
|
|
103
|
+
def new_widget(self, update_self=True) -> TraitWidget:
|
|
104
|
+
tw: TraitWidget = TraitWidget.instance(self)
|
|
105
|
+
assert tw, f'{self.entity.entity.__class__}.{self.trait.name} - unknown trait widget class'
|
|
106
|
+
|
|
107
|
+
avg_char_width = tw.font_metrics().average_char_width()
|
|
108
|
+
min_width = self.ui_hint.param('min_width', 0)
|
|
109
|
+
if min_width > 0:
|
|
110
|
+
tw.set_minimum_width(min_width * avg_char_width)
|
|
111
|
+
|
|
112
|
+
max_width = self.ui_hint.param('max_width', 0)
|
|
113
|
+
if max_width > 0:
|
|
114
|
+
tw.set_maximum_width(max_width * avg_char_width)
|
|
115
|
+
|
|
116
|
+
if update_self:
|
|
117
|
+
self.widget = tw
|
|
118
|
+
|
|
119
|
+
return tw
|
|
120
|
+
|
|
121
|
+
# ---- Built-in Callbacks
|
|
122
|
+
|
|
123
|
+
def date_cb(self):
|
|
124
|
+
ux_pick_date(
|
|
125
|
+
title=f'Pick a date for {self.ui_hint.label}',
|
|
126
|
+
show_date=self.entity.get_value(self.trait),
|
|
127
|
+
on_accept=lambda value: self.entity.set_value(self.trait, value),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def list_cb(self):
|
|
131
|
+
choices = self.entity.get_value(self.trait)
|
|
132
|
+
mc = MultiChoice(choices=choices)
|
|
133
|
+
w = mc.widget()
|
|
134
|
+
if not w:
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
UxDialog(
|
|
138
|
+
w,
|
|
139
|
+
title=f'Choose one or more values for {self.ui_hint.label}',
|
|
140
|
+
accept_callback=lambda ctx: self.entity.set_value(self.trait, mc.values_selected),
|
|
141
|
+
).show()
|
|
142
|
+
|
|
143
|
+
def dict_cb(self):
|
|
144
|
+
data = dict(self.entity.get_value(self.trait))
|
|
145
|
+
rc = PyDataBrowser.edit(data, title=f'Edit {self.trait.ui_hint.label}') # TODO: callback
|
|
146
|
+
if rc:
|
|
147
|
+
self.entity.set_value(self.trait, data)
|
|
148
|
+
|
|
149
|
+
def flags_cb(self):
|
|
150
|
+
flags = self.entity.get_value(self.trait)
|
|
151
|
+
bits_class = self.trait.data_type
|
|
152
|
+
tags_selected = bits_class.data_type.names_from_value(flags)
|
|
153
|
+
mc = MultiChoice(*tags_selected, choices=bits_class.choose_from())
|
|
154
|
+
w = mc.widget()
|
|
155
|
+
if not w:
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
UxDialog(
|
|
159
|
+
w,
|
|
160
|
+
title=f'Choose one or more flags for {self.ui_hint.label}',
|
|
161
|
+
accept_callback=lambda ctx: self.entity.set_value(self.trait, mc.values_selected),
|
|
162
|
+
).show()
|
|
163
|
+
|
|
164
|
+
def traitable_cb(self):
|
|
165
|
+
# -- EntityEditor - popup
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
def expensive_cb(self):
|
|
169
|
+
value = self.entity.get_value(self.trait)
|
|
170
|
+
self.widget.set_widget_value(value)
|
|
171
|
+
self.widget.style_sheet.restore()
|
|
172
|
+
|
|
173
|
+
def builtin_callback(self):
|
|
174
|
+
cb = self.s_builtin_callbacks.get(self.trait.__class__)
|
|
175
|
+
if not cb:
|
|
176
|
+
if self.trait.flags_on(T.EXPENSIVE):
|
|
177
|
+
cb = self.__class__.expensive_cb
|
|
178
|
+
|
|
179
|
+
return cb
|
|
180
|
+
|
|
181
|
+
# fmt: off
|
|
182
|
+
s_builtin_callbacks = {
|
|
183
|
+
date_trait: date_cb,
|
|
184
|
+
list_trait: list_cb,
|
|
185
|
+
dict_trait: dict_cb,
|
|
186
|
+
flags_trait: flags_cb,
|
|
187
|
+
traitable_trait: traitable_cb,
|
|
188
|
+
}
|
|
189
|
+
# fmt: on
|