cengal.user_interface.gui.tkinter.components.aggregator_view.versions.v_0.aggregator_view
1#!/usr/bin/env python 2# coding=utf-8 3 4# Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space> 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17 18 19__all__ = ['AggregatorAppendFormatter', 'AggregatorView'] 20 21 22""" 23Module Docstring 24Docstrings: http://www.python.org/dev/peps/pep-0257/ 25""" 26 27__author__ = "ButenkoMS <gtalk@butenkoms.space>" 28__copyright__ = "Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space>" 29__credits__ = ["ButenkoMS <gtalk@butenkoms.space>", ] 30__license__ = "Apache License, Version 2.0" 31__version__ = "4.4.1" 32__maintainer__ = "ButenkoMS <gtalk@butenkoms.space>" 33__email__ = "gtalk@butenkoms.space" 34# __status__ = "Prototype" 35__status__ = "Development" 36# __status__ = "Production" 37 38 39from math import ceil 40import ttkbootstrap as ttkb 41from ttkbootstrap.constants import INSERT as ttkb__INSERT, CURRENT as ttkb__CURRENT, SEL_FIRST as ttkb__SEL_FIRST, SEL_LAST as ttkb__SEL_LAST, SEL as ttkb__SEL, END as ttkb__END 42from ttkbootstrap import Style 43from typing import Callable, Hashable, Optional, Tuple, Any 44 45from cengal.parallel_execution.coroutines.coro_standard_services.tkinter import * 46from cengal.parallel_execution.coroutines.coro_standard_services.sleep import Sleep 47from cengal.parallel_execution.coroutines.coro_standard_services.fast_aggregator import * 48from cengal.user_interface.gui.tkinter.components.read_only_text import * 49 50 51class AggregatorAppendFormatter: 52 def __init__(self, initial_text: str) -> None: 53 self.initial_text: str = initial_text 54 self._items_num: int = 0 55 self.initiated: bool = False 56 57 def reset(self): 58 self._items_num = 0 59 self.initiated = False 60 61 def __call__(self, data: Any) -> Any: 62 self._items_num += 1 63 64 if self.initiated: 65 return f'{data}' 66 else: 67 self.initiated = True 68 return f'{current_interface().coro_id}. {self.initial_text}:\n{data}' 69 70 71class AggregatorView(ttkb.Frame): 72 def __init__(self, append_data: bool, default_auto_scroll: bool, aggregator_key: Hashable, updating_interval: float, data_formatter_func: Optional[Callable], width, height, *args, **kwargs): 73 self.append_data: bool = append_data 74 self.updating_interval: float = updating_interval 75 self.aggregator_key: Hashable = aggregator_key 76 self._default_data_formatter_func: Callable = lambda x: f'{x}' 77 self.data_formatter_func: Optional[Callable] = data_formatter_func or self._default_data_formatter_func 78 self._stop: bool = False 79 self.updates_num: int = 0 80 self.max_len: int = None 81 super().__init__(*args, **kwargs) 82 83 text_area_config = { 84 'highlightcolor': Style.instance.colors.primary, 85 'highlightbackground': Style.instance.colors.border, 86 'highlightthickness': 1, 87 'wrap': 'none', 88 } 89 if width is not None: 90 text_area_config.update({ 91 'width': width, 92 }) 93 94 if height is not None: 95 text_area_config.update({ 96 'height': height, 97 }) 98 99 self.text_area = ReadOnlyText(self, **text_area_config) 100 self.text_area.pack(fill='both', expand='yes') 101 102 self.text_area._text['yscrollcommand'] = self.yscroll 103 self.text_area._vbar.config(command=self.yview) 104 self.auto_scroll: bool = default_auto_scroll 105 self.last_yscroll: Tuple[str, str] = None 106 self.last_lines_num: int = None 107 self.last_yview: Tuple[str] = None 108 self.last_line: int = 0 109 self.desired_line: int = 0 110 self.selection_started = False 111 self.selection_first = None 112 self.selection_last = None 113 self.selection = None 114 self.previously_was_mouse_b1_double = False 115 116 self.text_area._text.bind("<Button-3>", self.mouse_b3_down) 117 if not append_data: 118 self.text_area._text.bind("<<Selection>>", self.selection_changed) 119 self.text_area._text.bind("<Double-Button-1>", self.mouse_b1_double) 120 self.text_area._text.bind("<Button-1>", self.mouse_b1_down) 121 self.text_area._text.bind("<B1-Motion>", self.mouse_b1_motion) 122 self.text_area._text.bind("<ButtonRelease-1>", self.mouse_b1_up) 123 self.text_area._text.bind("<Control-a>", self.ctrl_a) 124 125 def ctrl_a(self, event): 126 self.clear_selection() 127 self.selection_first = self.text_area._text.index('@%s,%s' % (1, 0)) 128 self.selection_last = 'end' 129 self.selection = (self.selection_first, self.selection_last) 130 self.apply_selection() 131 132 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 133 self.text_area._text.tag_add(ttkb__INSERT, self.selection_last) 134 self.text_area._text.mark_unset(ttkb__INSERT) 135 self.text_area._text.mark_set(ttkb__INSERT, self.selection_last) 136 return 'break' 137 138 def clear_selection(self): 139 self.text_area._text.tag_remove(ttkb__SEL, '1.0', 'end') 140 141 def normalize_selection(self): 142 if self.selection is not None: 143 if 'end' == self.selection[0]: 144 self.selection = (self.selection[1], self.selection[0]) 145 elif 'end' == self.selection[1]: 146 pass 147 else: 148 sel_left = [int(x) for x in self.selection[0].split('.')] 149 sel_right = [int(x) for x in self.selection[1].split('.')] 150 if sel_left > sel_right: 151 self.selection = (self.selection[1], self.selection[0]) 152 153 def apply_selection(self): 154 if self.selection is not None: 155 if self.selection[0] == self.selection[1]: 156 return 157 158 self.normalize_selection() 159 self.text_area._text.tag_remove(ttkb__SEL, '1.0', 'end') 160 self.text_area._text.tag_add(ttkb__SEL, *self.selection) 161 162 def mouse_b1_down(self, event): 163 self.previously_was_mouse_b1_double = False 164 self.selection_first = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 165 self.selection_last = None 166 self.clear_selection() 167 168 def mouse_b1_up(self, event): 169 selection_last = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 170 if self.selection_first is not None: 171 if self.selection_last is None: 172 if self.selection_first != selection_last: 173 self.selection = (self.selection_first, selection_last) 174 self.apply_selection() 175 self.selection_first = None 176 else: 177 self.selection = None 178 else: 179 self.selection = (self.selection_first, self.selection_last) 180 self.apply_selection() 181 182 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 183 self.text_area._text.tag_add(ttkb__INSERT, selection_last) 184 self.text_area._text.mark_unset(ttkb__INSERT) 185 self.text_area._text.mark_set(ttkb__INSERT, selection_last) 186 return 'break' 187 188 def mouse_b1_motion(self, event): 189 selection_last = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 190 if self.selection_first is not None: 191 self.selection = (self.selection_first, selection_last) 192 self.apply_selection() 193 194 return 'break' 195 196 def mouse_b1_double(self, event): 197 if self.previously_was_mouse_b1_double: 198 self.selection_first = self.text_area._text.index('@%s,%s linestart' % (event.x, event.y)) 199 self.selection_last = self.text_area._text.index('@%s,%s lineend' % (event.x, event.y)) 200 self.selection = (self.selection_first, self.selection_last) 201 self.apply_selection() 202 self.previously_was_mouse_b1_double = False 203 else: 204 self.selection_first = self.text_area._text.index('@%s,%s wordstart' % (event.x, event.y)) 205 self.selection_last = self.text_area._text.index('@%s,%s wordend' % (event.x, event.y)) 206 self.selection = (self.selection_first, self.selection_last) 207 self.apply_selection() 208 self.previously_was_mouse_b1_double = True 209 210 return 'break' 211 212 def mouse_b3_down(self, event): 213 self.text_area._text.mark_unset(ttkb__INSERT) 214 selection = None 215 if self.selection is not None: 216 self.normalize_selection() 217 selection = self.selection 218 else: 219 current_selection = self.text_area._text.index(ttkb__SEL) 220 current_selection_first = self.text_area._text.index(ttkb__SEL_FIRST) 221 current_selection_last = self.text_area._text.index(ttkb__SEL_LAST) 222 if current_selection_first and current_selection_last: 223 selection = (current_selection_first, current_selection_last) 224 225 if selection is not None: 226 self.text_area._text.clipboard_clear() 227 text_content = self.text_area._text.get(selection[0], selection[1]) 228 self.text_area._text.clipboard_append(text_content) 229 self.selection_last = None 230 self.selection = None 231 self.previously_was_mouse_b1_double = False 232 self.clear_selection() 233 234 def selection_changed(self, event): 235 return 236 237 def put(self, text: str): 238 lines_num_before = self.index_to_line_number(self.text_area._text.index('end')) 239 view_fractions_before = self.text_area._text.yview() 240 line_before = int(ceil(lines_num_before * float(view_fractions_before[0]))) 241 tag_insert_before = self.text_area._text.index(ttkb__INSERT) 242 tag_current_before = self.text_area._text.index(ttkb__CURRENT) 243 244 if not self.append_data: 245 self.clear_context() 246 247 self.text_area._text.insert('end', text) 248 249 if self.auto_scroll: 250 self.text_area._text.yview(ttkb__END) 251 252 if not self.append_data: 253 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 254 self.text_area._text.tag_add(ttkb__INSERT, tag_insert_before) 255 self.text_area._text.tag_remove(ttkb__CURRENT, '1.0', 'end') 256 self.text_area._text.tag_add(ttkb__CURRENT, tag_current_before) 257 self.text_area._text.mark_unset(ttkb__INSERT) 258 self.text_area._text.mark_set(ttkb__INSERT, tag_insert_before) 259 self.apply_selection() 260 261 lines_num_after = self.index_to_line_number(self.text_area._text.index('end')) 262 view_fractions_after = self.text_area._text.yview() 263 line_after = int(ceil(lines_num_after * float(view_fractions_after[0]))) 264 265 line_before = line_before or 1 266 line_after = line_after or 1 267 if line_after != line_before: 268 movement = line_before - line_after 269 self.text_area._text.yview_scroll(movement, 'units') 270 271 def index_to_line_number(self, index: str): 272 return int(index.split('.')[0]) 273 274 def clear_context(self): 275 self.text_area._text.delete('1.0', 'end') 276 277 def yscroll(self, *args): 278 first, last = args 279 lines_num = self.index_to_line_number(self.text_area._text.index('end')) - 1 280 last_line_index = lines_num - 1 281 first_visible_line_index = int(ceil(lines_num * float(first))) 282 last_visible_line_index = int(ceil(lines_num * float(last))) - 1 283 284 if self.auto_scroll: 285 if (0 < first_visible_line_index) and (last_visible_line_index < last_line_index): 286 self.auto_scroll = False 287 else: 288 if (0 <= last_line_index) and (0 <= last_visible_line_index) and (0 < first_visible_line_index) and (last_visible_line_index >= first_visible_line_index) and (last_visible_line_index == last_line_index): 289 self.auto_scroll = True 290 291 self.text_area._vbar.set(*args) 292 293 def yview(self, *args): 294 self.text_area._text.yview(*args) 295 296 def start(self, wr: TkObjWrapper): 297 wr.put_coro(self._updater) 298 299 def stop(self): 300 self._stop = True 301 302 def _updater(self, i: Interface): 303 while not self._stop: 304 try: 305 self.updates_num += 1 306 if (self.max_len is not None) and (self.updates_num >= self.max_len): 307 if isinstance(self.data_formatter_func, AggregatorAppendFormatter): 308 self.updates_num = 1 309 self.data_formatter_func.reset() 310 auto_scroll_buff = self.auto_scroll 311 self.clear_context() 312 self.auto_scroll = auto_scroll_buff 313 314 data = i(FastAggregator, self.aggregator_key) 315 if self.append_data: 316 text = '\n'.join([self.data_formatter_func(item) for item in data]) + '\n' 317 else: 318 text = self.data_formatter_func(data[-1]) 319 320 self.put(text) 321 except KeyError: 322 pass 323 324 i(Sleep, self.updating_interval)
class
AggregatorAppendFormatter:
52class AggregatorAppendFormatter: 53 def __init__(self, initial_text: str) -> None: 54 self.initial_text: str = initial_text 55 self._items_num: int = 0 56 self.initiated: bool = False 57 58 def reset(self): 59 self._items_num = 0 60 self.initiated = False 61 62 def __call__(self, data: Any) -> Any: 63 self._items_num += 1 64 65 if self.initiated: 66 return f'{data}' 67 else: 68 self.initiated = True 69 return f'{current_interface().coro_id}. {self.initial_text}:\n{data}'
class
AggregatorView(tkinter.ttk.Frame):
72class AggregatorView(ttkb.Frame): 73 def __init__(self, append_data: bool, default_auto_scroll: bool, aggregator_key: Hashable, updating_interval: float, data_formatter_func: Optional[Callable], width, height, *args, **kwargs): 74 self.append_data: bool = append_data 75 self.updating_interval: float = updating_interval 76 self.aggregator_key: Hashable = aggregator_key 77 self._default_data_formatter_func: Callable = lambda x: f'{x}' 78 self.data_formatter_func: Optional[Callable] = data_formatter_func or self._default_data_formatter_func 79 self._stop: bool = False 80 self.updates_num: int = 0 81 self.max_len: int = None 82 super().__init__(*args, **kwargs) 83 84 text_area_config = { 85 'highlightcolor': Style.instance.colors.primary, 86 'highlightbackground': Style.instance.colors.border, 87 'highlightthickness': 1, 88 'wrap': 'none', 89 } 90 if width is not None: 91 text_area_config.update({ 92 'width': width, 93 }) 94 95 if height is not None: 96 text_area_config.update({ 97 'height': height, 98 }) 99 100 self.text_area = ReadOnlyText(self, **text_area_config) 101 self.text_area.pack(fill='both', expand='yes') 102 103 self.text_area._text['yscrollcommand'] = self.yscroll 104 self.text_area._vbar.config(command=self.yview) 105 self.auto_scroll: bool = default_auto_scroll 106 self.last_yscroll: Tuple[str, str] = None 107 self.last_lines_num: int = None 108 self.last_yview: Tuple[str] = None 109 self.last_line: int = 0 110 self.desired_line: int = 0 111 self.selection_started = False 112 self.selection_first = None 113 self.selection_last = None 114 self.selection = None 115 self.previously_was_mouse_b1_double = False 116 117 self.text_area._text.bind("<Button-3>", self.mouse_b3_down) 118 if not append_data: 119 self.text_area._text.bind("<<Selection>>", self.selection_changed) 120 self.text_area._text.bind("<Double-Button-1>", self.mouse_b1_double) 121 self.text_area._text.bind("<Button-1>", self.mouse_b1_down) 122 self.text_area._text.bind("<B1-Motion>", self.mouse_b1_motion) 123 self.text_area._text.bind("<ButtonRelease-1>", self.mouse_b1_up) 124 self.text_area._text.bind("<Control-a>", self.ctrl_a) 125 126 def ctrl_a(self, event): 127 self.clear_selection() 128 self.selection_first = self.text_area._text.index('@%s,%s' % (1, 0)) 129 self.selection_last = 'end' 130 self.selection = (self.selection_first, self.selection_last) 131 self.apply_selection() 132 133 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 134 self.text_area._text.tag_add(ttkb__INSERT, self.selection_last) 135 self.text_area._text.mark_unset(ttkb__INSERT) 136 self.text_area._text.mark_set(ttkb__INSERT, self.selection_last) 137 return 'break' 138 139 def clear_selection(self): 140 self.text_area._text.tag_remove(ttkb__SEL, '1.0', 'end') 141 142 def normalize_selection(self): 143 if self.selection is not None: 144 if 'end' == self.selection[0]: 145 self.selection = (self.selection[1], self.selection[0]) 146 elif 'end' == self.selection[1]: 147 pass 148 else: 149 sel_left = [int(x) for x in self.selection[0].split('.')] 150 sel_right = [int(x) for x in self.selection[1].split('.')] 151 if sel_left > sel_right: 152 self.selection = (self.selection[1], self.selection[0]) 153 154 def apply_selection(self): 155 if self.selection is not None: 156 if self.selection[0] == self.selection[1]: 157 return 158 159 self.normalize_selection() 160 self.text_area._text.tag_remove(ttkb__SEL, '1.0', 'end') 161 self.text_area._text.tag_add(ttkb__SEL, *self.selection) 162 163 def mouse_b1_down(self, event): 164 self.previously_was_mouse_b1_double = False 165 self.selection_first = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 166 self.selection_last = None 167 self.clear_selection() 168 169 def mouse_b1_up(self, event): 170 selection_last = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 171 if self.selection_first is not None: 172 if self.selection_last is None: 173 if self.selection_first != selection_last: 174 self.selection = (self.selection_first, selection_last) 175 self.apply_selection() 176 self.selection_first = None 177 else: 178 self.selection = None 179 else: 180 self.selection = (self.selection_first, self.selection_last) 181 self.apply_selection() 182 183 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 184 self.text_area._text.tag_add(ttkb__INSERT, selection_last) 185 self.text_area._text.mark_unset(ttkb__INSERT) 186 self.text_area._text.mark_set(ttkb__INSERT, selection_last) 187 return 'break' 188 189 def mouse_b1_motion(self, event): 190 selection_last = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 191 if self.selection_first is not None: 192 self.selection = (self.selection_first, selection_last) 193 self.apply_selection() 194 195 return 'break' 196 197 def mouse_b1_double(self, event): 198 if self.previously_was_mouse_b1_double: 199 self.selection_first = self.text_area._text.index('@%s,%s linestart' % (event.x, event.y)) 200 self.selection_last = self.text_area._text.index('@%s,%s lineend' % (event.x, event.y)) 201 self.selection = (self.selection_first, self.selection_last) 202 self.apply_selection() 203 self.previously_was_mouse_b1_double = False 204 else: 205 self.selection_first = self.text_area._text.index('@%s,%s wordstart' % (event.x, event.y)) 206 self.selection_last = self.text_area._text.index('@%s,%s wordend' % (event.x, event.y)) 207 self.selection = (self.selection_first, self.selection_last) 208 self.apply_selection() 209 self.previously_was_mouse_b1_double = True 210 211 return 'break' 212 213 def mouse_b3_down(self, event): 214 self.text_area._text.mark_unset(ttkb__INSERT) 215 selection = None 216 if self.selection is not None: 217 self.normalize_selection() 218 selection = self.selection 219 else: 220 current_selection = self.text_area._text.index(ttkb__SEL) 221 current_selection_first = self.text_area._text.index(ttkb__SEL_FIRST) 222 current_selection_last = self.text_area._text.index(ttkb__SEL_LAST) 223 if current_selection_first and current_selection_last: 224 selection = (current_selection_first, current_selection_last) 225 226 if selection is not None: 227 self.text_area._text.clipboard_clear() 228 text_content = self.text_area._text.get(selection[0], selection[1]) 229 self.text_area._text.clipboard_append(text_content) 230 self.selection_last = None 231 self.selection = None 232 self.previously_was_mouse_b1_double = False 233 self.clear_selection() 234 235 def selection_changed(self, event): 236 return 237 238 def put(self, text: str): 239 lines_num_before = self.index_to_line_number(self.text_area._text.index('end')) 240 view_fractions_before = self.text_area._text.yview() 241 line_before = int(ceil(lines_num_before * float(view_fractions_before[0]))) 242 tag_insert_before = self.text_area._text.index(ttkb__INSERT) 243 tag_current_before = self.text_area._text.index(ttkb__CURRENT) 244 245 if not self.append_data: 246 self.clear_context() 247 248 self.text_area._text.insert('end', text) 249 250 if self.auto_scroll: 251 self.text_area._text.yview(ttkb__END) 252 253 if not self.append_data: 254 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 255 self.text_area._text.tag_add(ttkb__INSERT, tag_insert_before) 256 self.text_area._text.tag_remove(ttkb__CURRENT, '1.0', 'end') 257 self.text_area._text.tag_add(ttkb__CURRENT, tag_current_before) 258 self.text_area._text.mark_unset(ttkb__INSERT) 259 self.text_area._text.mark_set(ttkb__INSERT, tag_insert_before) 260 self.apply_selection() 261 262 lines_num_after = self.index_to_line_number(self.text_area._text.index('end')) 263 view_fractions_after = self.text_area._text.yview() 264 line_after = int(ceil(lines_num_after * float(view_fractions_after[0]))) 265 266 line_before = line_before or 1 267 line_after = line_after or 1 268 if line_after != line_before: 269 movement = line_before - line_after 270 self.text_area._text.yview_scroll(movement, 'units') 271 272 def index_to_line_number(self, index: str): 273 return int(index.split('.')[0]) 274 275 def clear_context(self): 276 self.text_area._text.delete('1.0', 'end') 277 278 def yscroll(self, *args): 279 first, last = args 280 lines_num = self.index_to_line_number(self.text_area._text.index('end')) - 1 281 last_line_index = lines_num - 1 282 first_visible_line_index = int(ceil(lines_num * float(first))) 283 last_visible_line_index = int(ceil(lines_num * float(last))) - 1 284 285 if self.auto_scroll: 286 if (0 < first_visible_line_index) and (last_visible_line_index < last_line_index): 287 self.auto_scroll = False 288 else: 289 if (0 <= last_line_index) and (0 <= last_visible_line_index) and (0 < first_visible_line_index) and (last_visible_line_index >= first_visible_line_index) and (last_visible_line_index == last_line_index): 290 self.auto_scroll = True 291 292 self.text_area._vbar.set(*args) 293 294 def yview(self, *args): 295 self.text_area._text.yview(*args) 296 297 def start(self, wr: TkObjWrapper): 298 wr.put_coro(self._updater) 299 300 def stop(self): 301 self._stop = True 302 303 def _updater(self, i: Interface): 304 while not self._stop: 305 try: 306 self.updates_num += 1 307 if (self.max_len is not None) and (self.updates_num >= self.max_len): 308 if isinstance(self.data_formatter_func, AggregatorAppendFormatter): 309 self.updates_num = 1 310 self.data_formatter_func.reset() 311 auto_scroll_buff = self.auto_scroll 312 self.clear_context() 313 self.auto_scroll = auto_scroll_buff 314 315 data = i(FastAggregator, self.aggregator_key) 316 if self.append_data: 317 text = '\n'.join([self.data_formatter_func(item) for item in data]) + '\n' 318 else: 319 text = self.data_formatter_func(data[-1]) 320 321 self.put(text) 322 except KeyError: 323 pass 324 325 i(Sleep, self.updating_interval)
Ttk Frame widget is a container, used to group other widgets together.
AggregatorView( append_data: bool, default_auto_scroll: bool, aggregator_key: typing.Hashable, updating_interval: float, data_formatter_func: typing.Union[typing.Callable, NoneType], width, height, *args, **kwargs)
73 def __init__(self, append_data: bool, default_auto_scroll: bool, aggregator_key: Hashable, updating_interval: float, data_formatter_func: Optional[Callable], width, height, *args, **kwargs): 74 self.append_data: bool = append_data 75 self.updating_interval: float = updating_interval 76 self.aggregator_key: Hashable = aggregator_key 77 self._default_data_formatter_func: Callable = lambda x: f'{x}' 78 self.data_formatter_func: Optional[Callable] = data_formatter_func or self._default_data_formatter_func 79 self._stop: bool = False 80 self.updates_num: int = 0 81 self.max_len: int = None 82 super().__init__(*args, **kwargs) 83 84 text_area_config = { 85 'highlightcolor': Style.instance.colors.primary, 86 'highlightbackground': Style.instance.colors.border, 87 'highlightthickness': 1, 88 'wrap': 'none', 89 } 90 if width is not None: 91 text_area_config.update({ 92 'width': width, 93 }) 94 95 if height is not None: 96 text_area_config.update({ 97 'height': height, 98 }) 99 100 self.text_area = ReadOnlyText(self, **text_area_config) 101 self.text_area.pack(fill='both', expand='yes') 102 103 self.text_area._text['yscrollcommand'] = self.yscroll 104 self.text_area._vbar.config(command=self.yview) 105 self.auto_scroll: bool = default_auto_scroll 106 self.last_yscroll: Tuple[str, str] = None 107 self.last_lines_num: int = None 108 self.last_yview: Tuple[str] = None 109 self.last_line: int = 0 110 self.desired_line: int = 0 111 self.selection_started = False 112 self.selection_first = None 113 self.selection_last = None 114 self.selection = None 115 self.previously_was_mouse_b1_double = False 116 117 self.text_area._text.bind("<Button-3>", self.mouse_b3_down) 118 if not append_data: 119 self.text_area._text.bind("<<Selection>>", self.selection_changed) 120 self.text_area._text.bind("<Double-Button-1>", self.mouse_b1_double) 121 self.text_area._text.bind("<Button-1>", self.mouse_b1_down) 122 self.text_area._text.bind("<B1-Motion>", self.mouse_b1_motion) 123 self.text_area._text.bind("<ButtonRelease-1>", self.mouse_b1_up) 124 self.text_area._text.bind("<Control-a>", self.ctrl_a)
Constructs a Ttk Widget with the parent master.
STANDARD OPTIONS
class, cursor, takefocus, style
SCROLLABLE WIDGET OPTIONS
xscrollcommand, yscrollcommand
LABEL WIDGET OPTIONS
text, textvariable, underline, image, compound, width
WIDGET STATES
active, disabled, focus, pressed, selected, background,
readonly, alternate, invalid
def
ctrl_a(self, event):
126 def ctrl_a(self, event): 127 self.clear_selection() 128 self.selection_first = self.text_area._text.index('@%s,%s' % (1, 0)) 129 self.selection_last = 'end' 130 self.selection = (self.selection_first, self.selection_last) 131 self.apply_selection() 132 133 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 134 self.text_area._text.tag_add(ttkb__INSERT, self.selection_last) 135 self.text_area._text.mark_unset(ttkb__INSERT) 136 self.text_area._text.mark_set(ttkb__INSERT, self.selection_last) 137 return 'break'
def
normalize_selection(self):
142 def normalize_selection(self): 143 if self.selection is not None: 144 if 'end' == self.selection[0]: 145 self.selection = (self.selection[1], self.selection[0]) 146 elif 'end' == self.selection[1]: 147 pass 148 else: 149 sel_left = [int(x) for x in self.selection[0].split('.')] 150 sel_right = [int(x) for x in self.selection[1].split('.')] 151 if sel_left > sel_right: 152 self.selection = (self.selection[1], self.selection[0])
def
mouse_b1_up(self, event):
169 def mouse_b1_up(self, event): 170 selection_last = self.text_area._text.index('@%s,%s' % (event.x, event.y)) 171 if self.selection_first is not None: 172 if self.selection_last is None: 173 if self.selection_first != selection_last: 174 self.selection = (self.selection_first, selection_last) 175 self.apply_selection() 176 self.selection_first = None 177 else: 178 self.selection = None 179 else: 180 self.selection = (self.selection_first, self.selection_last) 181 self.apply_selection() 182 183 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 184 self.text_area._text.tag_add(ttkb__INSERT, selection_last) 185 self.text_area._text.mark_unset(ttkb__INSERT) 186 self.text_area._text.mark_set(ttkb__INSERT, selection_last) 187 return 'break'
def
mouse_b1_double(self, event):
197 def mouse_b1_double(self, event): 198 if self.previously_was_mouse_b1_double: 199 self.selection_first = self.text_area._text.index('@%s,%s linestart' % (event.x, event.y)) 200 self.selection_last = self.text_area._text.index('@%s,%s lineend' % (event.x, event.y)) 201 self.selection = (self.selection_first, self.selection_last) 202 self.apply_selection() 203 self.previously_was_mouse_b1_double = False 204 else: 205 self.selection_first = self.text_area._text.index('@%s,%s wordstart' % (event.x, event.y)) 206 self.selection_last = self.text_area._text.index('@%s,%s wordend' % (event.x, event.y)) 207 self.selection = (self.selection_first, self.selection_last) 208 self.apply_selection() 209 self.previously_was_mouse_b1_double = True 210 211 return 'break'
def
mouse_b3_down(self, event):
213 def mouse_b3_down(self, event): 214 self.text_area._text.mark_unset(ttkb__INSERT) 215 selection = None 216 if self.selection is not None: 217 self.normalize_selection() 218 selection = self.selection 219 else: 220 current_selection = self.text_area._text.index(ttkb__SEL) 221 current_selection_first = self.text_area._text.index(ttkb__SEL_FIRST) 222 current_selection_last = self.text_area._text.index(ttkb__SEL_LAST) 223 if current_selection_first and current_selection_last: 224 selection = (current_selection_first, current_selection_last) 225 226 if selection is not None: 227 self.text_area._text.clipboard_clear() 228 text_content = self.text_area._text.get(selection[0], selection[1]) 229 self.text_area._text.clipboard_append(text_content) 230 self.selection_last = None 231 self.selection = None 232 self.previously_was_mouse_b1_double = False 233 self.clear_selection()
def
put(self, text: str):
238 def put(self, text: str): 239 lines_num_before = self.index_to_line_number(self.text_area._text.index('end')) 240 view_fractions_before = self.text_area._text.yview() 241 line_before = int(ceil(lines_num_before * float(view_fractions_before[0]))) 242 tag_insert_before = self.text_area._text.index(ttkb__INSERT) 243 tag_current_before = self.text_area._text.index(ttkb__CURRENT) 244 245 if not self.append_data: 246 self.clear_context() 247 248 self.text_area._text.insert('end', text) 249 250 if self.auto_scroll: 251 self.text_area._text.yview(ttkb__END) 252 253 if not self.append_data: 254 self.text_area._text.tag_remove(ttkb__INSERT, '1.0', 'end') 255 self.text_area._text.tag_add(ttkb__INSERT, tag_insert_before) 256 self.text_area._text.tag_remove(ttkb__CURRENT, '1.0', 'end') 257 self.text_area._text.tag_add(ttkb__CURRENT, tag_current_before) 258 self.text_area._text.mark_unset(ttkb__INSERT) 259 self.text_area._text.mark_set(ttkb__INSERT, tag_insert_before) 260 self.apply_selection() 261 262 lines_num_after = self.index_to_line_number(self.text_area._text.index('end')) 263 view_fractions_after = self.text_area._text.yview() 264 line_after = int(ceil(lines_num_after * float(view_fractions_after[0]))) 265 266 line_before = line_before or 1 267 line_after = line_after or 1 268 if line_after != line_before: 269 movement = line_before - line_after 270 self.text_area._text.yview_scroll(movement, 'units')
def
yscroll(self, *args):
278 def yscroll(self, *args): 279 first, last = args 280 lines_num = self.index_to_line_number(self.text_area._text.index('end')) - 1 281 last_line_index = lines_num - 1 282 first_visible_line_index = int(ceil(lines_num * float(first))) 283 last_visible_line_index = int(ceil(lines_num * float(last))) - 1 284 285 if self.auto_scroll: 286 if (0 < first_visible_line_index) and (last_visible_line_index < last_line_index): 287 self.auto_scroll = False 288 else: 289 if (0 <= last_line_index) and (0 <= last_visible_line_index) and (0 < first_visible_line_index) and (last_visible_line_index >= first_visible_line_index) and (last_visible_line_index == last_line_index): 290 self.auto_scroll = True 291 292 self.text_area._vbar.set(*args)
def
start( self, wr: cengal.parallel_execution.coroutines.coro_standard_services.tkinter.versions.v_0.tkinter.TkObjWrapper):
Inherited Members
- tkinter.ttk.Widget
- identify
- instate
- state
- tkinter.BaseWidget
- widgetName
- destroy
- tkinter.Misc
- deletecommand
- tk_strictMotif
- tk_bisque
- tk_setPalette
- wait_variable
- waitvar
- wait_window
- wait_visibility
- setvar
- getvar
- getint
- getdouble
- getboolean
- focus_set
- focus
- focus_force
- focus_get
- focus_displayof
- focus_lastfor
- tk_focusFollowsMouse
- tk_focusNext
- tk_focusPrev
- after
- after_idle
- after_cancel
- bell
- clipboard_get
- clipboard_clear
- clipboard_append
- grab_current
- grab_release
- grab_set
- grab_set_global
- grab_status
- option_add
- option_clear
- option_get
- option_readfile
- selection_clear
- selection_get
- selection_handle
- selection_own
- selection_own_get
- send
- lower
- tkraise
- lift
- winfo_atom
- winfo_atomname
- winfo_cells
- winfo_children
- winfo_class
- winfo_colormapfull
- winfo_containing
- winfo_depth
- winfo_exists
- winfo_fpixels
- winfo_geometry
- winfo_height
- winfo_id
- winfo_interps
- winfo_ismapped
- winfo_manager
- winfo_name
- winfo_parent
- winfo_pathname
- winfo_pixels
- winfo_pointerx
- winfo_pointerxy
- winfo_pointery
- winfo_reqheight
- winfo_reqwidth
- winfo_rgb
- winfo_rootx
- winfo_rooty
- winfo_screen
- winfo_screencells
- winfo_screendepth
- winfo_screenheight
- winfo_screenmmheight
- winfo_screenmmwidth
- winfo_screenvisual
- winfo_screenwidth
- winfo_server
- winfo_toplevel
- winfo_viewable
- winfo_visual
- winfo_visualid
- winfo_visualsavailable
- winfo_vrootheight
- winfo_vrootwidth
- winfo_vrootx
- winfo_vrooty
- winfo_width
- winfo_x
- winfo_y
- update
- update_idletasks
- bind
- unbind
- bind_all
- unbind_all
- bind_class
- unbind_class
- mainloop
- quit
- nametowidget
- register
- cget
- keys
- pack_propagate
- propagate
- pack_slaves
- slaves
- place_slaves
- grid_anchor
- anchor
- grid_bbox
- bbox
- grid_columnconfigure
- columnconfigure
- grid_location
- grid_propagate
- grid_rowconfigure
- rowconfigure
- grid_size
- size
- grid_slaves
- event_add
- event_delete
- event_generate
- event_info
- image_names
- image_types
- tkinter.ttk.Frame
- configure
- config
- tkinter.Pack
- pack_configure
- pack_forget
- forget
- pack_info
- info
- pack
- tkinter.Place
- place_configure
- place_forget
- place_info
- place
- tkinter.Grid
- grid_configure
- grid_forget
- grid_remove
- grid_info
- grid
- location