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}'
AggregatorAppendFormatter(initial_text: str)
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
initial_text: str
initiated: bool
def reset(self):
58    def reset(self):
59        self._items_num = 0
60        self.initiated = False
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
append_data: bool
updating_interval: float
aggregator_key: Hashable
data_formatter_func: Union[Callable, NoneType]
updates_num: int
max_len: int
text_area
auto_scroll: bool
last_yscroll: Tuple[str, str]
last_lines_num: int
last_yview: Tuple[str]
last_line: int
desired_line: int
selection_started
selection_first
selection_last
selection
previously_was_mouse_b1_double
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 clear_selection(self):
139    def clear_selection(self):
140        self.text_area._text.tag_remove(ttkb__SEL, '1.0', 'end')
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 apply_selection(self):
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)
def mouse_b1_down(self, event):
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()
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_motion(self, event):
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'
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 selection_changed(self, event):
235    def selection_changed(self, event):
236        return
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 index_to_line_number(self, index: str):
272    def index_to_line_number(self, index: str):
273        return int(index.split('.')[0])
def clear_context(self):
275    def clear_context(self):
276        self.text_area._text.delete('1.0', 'end')
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 yview(self, *args):
294    def yview(self, *args):
295        self.text_area._text.yview(*args)
def start( self, wr: cengal.parallel_execution.coroutines.coro_standard_services.tkinter.versions.v_0.tkinter.TkObjWrapper):
297    def start(self, wr: TkObjWrapper):
298        wr.put_coro(self._updater)
def stop(self):
300    def stop(self):
301        self._stop = True
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
bindtags
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