class ReadOnlyEditor(tkScrolledText): """Implement a read only mode text editor class with scroll bar. Done by replacing the bindings for the insert and delete events. From: http://stackoverflow.com/questions/3842155/is-there-a-way-to-make-the-tkinter-text-widget-read-only """ def __init__(self, *args, **kwargs): """Init the class and set the insert and delete event bindings.""" super().__init__(*args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break") def clear(self): """Clear the contents of the text area.""" self.delete(1.0, "end") def replace(self, new_content): """Remove all editor content and inserts the new content. :param new_content: String to insert. """ self.clear() self.insert(1.0, new_content)
class ReadOnlyText(tk.Text): def __init__(self, *args, **kwargs): tk.Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break")
class ReadOnlyText(Text): def __init__(self, *args, **kwargs): Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = \ self.redirector.register("insert", lambda *args, **kw: "break") self.delete = \ self.redirector.register("delete", lambda *args, **kw: "break")
class ReadOnlyEntry(Entry): def __init__(self, *args, **kwargs): Entry.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register( "insert", lambda *args, **kw: "break") self.delete = self.redirector.register( "delete", lambda *args, **kw: "break")
class ReadOnlyText(tkinter.Text): def __init__(self, *args, **kwargs): from idlelib.WidgetRedirector import WidgetRedirector tkinter.Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = \ self.redirector.register("insert", lambda *args, **kw: "break") self.delete = \ self.redirector.register("delete", lambda *args, **kw: "break")
class ReadOnlyText(tk.Text): def __init__(self, *args, **kwargs): kwargs['relief'] = tk.FLAT kwargs['insertwidth'] = 0 kwargs['highlightthickness'] = 0 tk.Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = \ self.redirector.register("insert", lambda *args, **kw: "break") self.delete = \ self.redirector.register("delete", lambda *args, **kw: "break")
class ReadOnlyText(Tkinter.Text): """Sets up a read-only chat box that can be added to with the Text.insert method, but cannot be modified by the user because all the input is rerouted""" def __init__(self, *args, **kwargs): Tkinter.Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register( "insert", lambda *args, **kw: "break") self.delete = self.redirector.register( "delete", lambda *args, **kw: "break")
class ReadOnlyText(Text) : def __init__(self, *args, **kwdargs) : Text.__init__(self, *args, **kwdargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args,**kw: "break") self.delete = self.redirector.register("delete", lambda *args,**kw: "break") self.replace = self.redirector.register("replace", lambda *args,**kw: "break") def setText(self, text) : self.delete(1.0, END) self.insert(END, text)
class readText(tk.Text): def __init__(self, *args, **kwargs): tk.Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break") self.tag_config("bgGREEN", background="green", foreground="black") self.tag_config("bgRED", background="red", foreground="black") self.tag_config("fgGREEN", background="black", foreground="green") self.tag_config("fgRED", background="black", foreground="red")
class TextWithInsertChangeCallback(tk.Text): def __init__(self, on_insert_change, *args, **kwargs): tk.Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break") self.callback = on_insert_change private_callback = self.register(self._callback) self.tk.eval(''' proc widget_proxy {actual_widget callback args} { # this prevents recursion if the widget is called # during the callback set flag ::dont_recurse(actual_widget) # call the real tk widget with the real args set result [uplevel [linsert $args 0 $actual_widget]] # call the callback and ignore errors, but only # do so on inserts, deletes, and changes in the # mark. Otherwise we'll call the callback way too # often. if {! [info exists $flag]} { if {([lindex $args 0] in {insert replace delete}) || ([lrange $args 0 2] == {mark set insert})} { # the flag makes sure that whatever happens in the # callback doesn't cause the callbacks to be called again. set $flag 1 catch {$callback $result {*}$args } callback_result unset -nocomplain $flag } } # return the result from the real widget command return $result } ''') self.tk.eval(''' rename {widget} _{widget} interp alias {{}} ::{widget} {{}} widget_proxy _{widget} {callback} '''.format(widget=str(self), callback=private_callback)) def _callback(self, result, *args): self.callback(result, *args)
class GUIText(Text): """ TODO: * READONLY state support for config, configure, _configure e.t.c. * returning READONLY by config, configure, _configure e.t.c. * switching back to NORMAL, DISABLED """ def __init__(self, **kw): read_only = False try: state = kw["state"] except: pass else: if state == READONLY: read_only = True kw["state"] = NORMAL Text.__init__(self, **kw) self.redirector = WidgetRedirector(self) if read_only: self.__read_only = True """ Note that software editing is still possible by calling those "insert" and "delete". """ self.insert = self.redirector.register("insert", _break) self.delete = self.redirector.register("delete", _break)
class ReadOnlyText(ScrolledText): def __init__(self, *args, **kwargs): ScrolledText.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break") self.config(wrap=tk.WORD)
class ReadOnlyText(Text): def __init__(self, master=None, cnf={}, **kw): super().__init__(master, cnf, **kw) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break")
class ReadOnlyText(Text): """ Prevent keys from being shown in the text area. We want to control what is actualxpyl presented to the user. """ def __init__(self, *args, **kwargs): """ Trashes every attempt to modify the text area coming from user input. """ Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = \ self.redirector.register("insert", lambda *args, **kw: "break") self.delete = \ self.redirector.register("delete", lambda *args, **kw: "break")
class Percolator: def __init__(self, text): self.text = text self.redir = WidgetRedirector(text) self.top = self.bottom = Delegator(text) self.bottom.insert = self.redir.register('insert', self.insert) self.bottom.delete = self.redir.register('delete', self.delete) self.filters = [] def close(self): while self.top is not self.bottom: self.removefilter(self.top) self.top = None self.bottom.setdelegate(None) self.bottom = None self.redir.close() self.redir = None self.text = None return def insert(self, index, chars, tags = None): self.top.insert(index, chars, tags) def delete(self, index1, index2 = None): self.top.delete(index1, index2) def insertfilter(self, filter): raise isinstance(filter, Delegator) or AssertionError raise filter.delegate is None or AssertionError filter.setdelegate(self.top) self.top = filter return def removefilter(self, filter): if not isinstance(filter, Delegator): raise AssertionError raise filter.delegate is not None or AssertionError f = self.top self.top = f is filter and filter.delegate filter.setdelegate(None) else: while not (f.delegate is not filter and f is not self.bottom): raise AssertionError f.resetcache() f = f.delegate f.setdelegate(filter.delegate) filter.setdelegate(None) return
class Percolator: def __init__(self, text): # XXX would be nice to inherit from Delegator self.text = text self.redir = WidgetRedirector(text) self.top = self.bottom = Delegator(text) self.bottom.insert = self.redir.register("insert", self.insert) self.bottom.delete = self.redir.register("delete", self.delete) self.filters = [] def close(self): while self.top is not self.bottom: self.removefilter(self.top) self.top = None self.bottom.setdelegate(None) self.bottom = None self.redir.close() self.redir = None self.text = None def insert(self, index, chars, tags=None): # Could go away if inheriting from Delegator self.top.insert(index, chars, tags) def delete(self, index1, index2=None): # Could go away if inheriting from Delegator self.top.delete(index1, index2) def insertfilter(self, filter): # Perhaps rename to pushfilter()? assert isinstance(filter, Delegator) assert filter.delegate is None filter.setdelegate(self.top) self.top = filter def removefilter(self, filter): # XXX Perhaps should only support popfilter()? assert isinstance(filter, Delegator) assert filter.delegate is not None f = self.top if f is filter: self.top = filter.delegate filter.setdelegate(None) else: while f.delegate is not filter: assert f is not self.bottom f.resetcache() f = f.delegate f.setdelegate(filter.delegate) filter.setdelegate(None)
class ReadOnlyText(Text): """A Text widget that redirects the insert and delete handlers so that they are no-ops. This effectively makes the widget readonly with respect to keyboard input handlers. Adapted from http://tkinter.unpythonic.net/wiki/ReadOnlyText, which is itself adapting a solution described here: http://wiki.tcl.tk/1152 """ def __init__(self, *args, **kwargs): Text.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", tk_break) self.delete = self.redirector.register("delete", tk_break)
class ThreadSafeConsole(ScrolledText): """A thread-safe, read-only, scrolling text widget.""" def __init__(self, *args, **kwargs): tk.Text.__init__(self, *args, **kwargs) self.tag_config("ok", foreground="forest green") self.tag_config("error", foreground="red") self.redir = WidgetRedirector(self) self.insert = self.redir.register("insert", lambda *args, **kw: "break") self.delete = self.redir.register("delete", lambda *args, **kw: "break") self.queue = queue.Queue() self.update() def write(self, line): # pragma: no cover self.queue.put(line) def clear(self): # pragma: no cover self.queue.put(None) def update(self): # pragma: no cover try: while True: line = self.queue.get_nowait() if line is None: self.delete(1.0, tk.END) else: parts = line.split("...") self.insert(tk.END, str(parts[0])) if len(parts) > 1: suffix = str(parts[1]) self.insert(tk.END, "...") if suffix.endswith("OK"): suffix = suffix.replace("OK", "") self.insert(tk.END, suffix) self.insert(tk.END, "OK", ("ok")) elif suffix.startswith("ERROR"): suffix = suffix.replace("ERROR", "") self.insert(tk.END, "ERROR", ("error")) self.insert(tk.END, suffix) else: self.insert(tk.END, suffix) self.insert(tk.END, "\n") self.see(tk.END) self.update_idletasks() except queue.Empty: pass self.after(100, self.update)
class Percolator: def __init__(self, text): self.text = text self.redir = WidgetRedirector(text) self.top = self.bottom = Delegator(text) self.bottom.insert = self.redir.register('insert', self.insert) self.bottom.delete = self.redir.register('delete', self.delete) self.filters = [] def close(self): while self.top is not self.bottom: self.removefilter(self.top) self.top = None self.bottom.setdelegate(None) self.bottom = None self.redir.close() self.redir = None self.text = None return def insert(self, index, chars, tags=None): self.top.insert(index, chars, tags) def delete(self, index1, index2=None): self.top.delete(index1, index2) def insertfilter(self, filter): filter.setdelegate(self.top) self.top = filter def removefilter(self, filter): f = self.top if f is filter: self.top = filter.delegate filter.setdelegate(None) else: while f.delegate is not filter: f.resetcache() f = f.delegate f.setdelegate(filter.delegate) filter.setdelegate(None) return
class ScrolledTextReadOnly(ScrolledText): def __init__(self, *args, **kwargs): ScrolledText.__init__(self, *args, **kwargs) _rc_menu_install(self) # overwrite default class binding so we don't need to return "break" self.bind_class("Text", "<Control-a>", self.event_select_all) self.bind("<Button-3><ButtonRelease-3>", self.show_menu) # Make the widget ready only self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break") def event_select_all(self, *args): self.focus_force() self.tag_add("sel","1.0","end") def show_menu(self, e): self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)
class PRReadonlyText(PRText): def __init__(self, *args, **kwargs): PRText.__init__(self, *args, **kwargs) self._insert = self.insert self._delete = self.delete self._redirector = WidgetRedirector(self) self.readonly = True def _setup_menu(self, master, editable=False): super(PRReadonlyText, self)._setup_menu(master, editable=False) def clear(self): self.readonly = False self._delete(1.0, tk.END) self.readonly = True @property def readonly(self): return self._readonly @readonly.setter def readonly(self, value): _break_func = lambda *args, **kw: 'break' if value: self.insert = self._redirector.register('insert', _break_func) self.delete = self._redirector.register('delete', _break_func) else: self.insert = self._redirector.unregister('insert') self.delete = self._redirector.unregister('delete') self._readonly = value @property def text(self): return self.get(0.0, tk.END) @text.setter def text(self, value): self.readonly = False self._delete(0.0, tk.END) self._insert(0.0, value) self.readonly = True
class ReadOnlyScrolledText(scrt.ScrolledText): def __init__(self, *args, **kwargs): scrt.ScrolledText.__init__(self, *args, **kwargs) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break")
class AnsiColorText(Text): """ class to convert text with ansi color codes to text with tkinter color tags for now we ignore all but the simplest color directives see http://www.termsys.demon.co.uk/vtansi.htm for a list of other directives it has not been thoroughly tested, but it works well enough for demonstration purposes """ foreground_colors = { 'bright' : { '30' : 'Black', '31' : 'Red', '32' : 'Green', '33' : 'Brown', '34' : 'Blue', '35' : 'Purple', '36' : 'Cyan', '37' : 'White' }, 'dim' : { '30' : 'DarkGray', '31' : 'LightRed', '32' : 'LightGreen', '33' : 'Yellow', '34' : 'LightBlue', '35' : 'Magenta', '36' : 'Pink', '37' : 'White' } } background_colors= { 'bright' : { '40' : 'Black', '41' : 'Red', '42' : 'Green', '43' : 'Brown', '44' : 'Blue', '45' : 'Purple', '46' : 'Cyan', '47' : 'White' }, 'dim' : { '40' : 'DarkGray', '41' : 'LightRed', '42' : 'LightGreen', '43' : 'Yellow', '44' : 'LightBlue', '45' : 'Magenta', '46' : 'Pink', '47' : 'White' } } # define some regexes which will come in handy in filtering # out the ansi color codes color_pat = re.compile('\x01?\x1b\[([\d+;]*?)m\x02?') inner_color_pat = re.compile("^(\d+;?)+$") def __init__(self, parent): """ initialize our specialized tkinter Text widget """ Text.__init__(self, parent) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", lambda *args, **kw: "break") self.delete = self.redirector.register("delete", lambda *args, **kw: "break") self.known_tags = set([]) # register a default color tag self.register_tag("30", "Black", "White") self.reset_to_default_attribs() def reset_to_default_attribs(self): self.tag = '30' self.bright = 'bright' self.foregroundcolor = 'Black' self.backgroundcolor = 'White' def colored_write(self, txt, color): self.tag = str(30+color) txt += '\n' self.color_set(self.tag) self.insert(END,txt,self.tag) def register_tag(self, txt, foreground, background): """ register a tag with name txt and with given foreground and background color """ self.tag_config(txt, foreground=foreground, background=background) self.known_tags.add(txt) def color_set(self, tag): if tag not in self.known_tags: # if tag not yet registered, # extract the foreground and background color # and ignore the other things parts = tag.split(";") for part in parts: if part in AnsiColorText.foreground_colors[self.bright]: self.foregroundcolor = AnsiColorText.foreground_colors[self.bright][part] elif part in AnsiColorText.background_colors[self.bright]: self.backgroundcolor = AnsiColorText.background_colors[self.bright][part] else: for ch in part: if ch == '0' : # reset all attributes self.reset_to_default_attribs() if ch == '1' : # define bright colors self.bright = 'bright' if ch == '2' : # define dim colors self.bright = 'dim' self.register_tag(tag, foreground=self.foregroundcolor, background=self.backgroundcolor) # remember that we switched to this tag self.tag = tag def write(self, text, is_editable=False): """ add text to the text widget """ # first split the text at color codes, stripping stuff like the <ESC> # and \[ characters and keeping only the inner "0;23"-like codes segments = AnsiColorText.color_pat.split(text) if segments: for text in segments: # a segment can be regular text, or it can be a color pattern if AnsiColorText.inner_color_pat.match(text): # if it's a color pattern, check if we already have # registered a tag for it color_set(text) elif text == '': # reset tag to black self.tag = '30' # black else: # no color pattern, insert text with the currently selected # tag self.insert(END,text,self.tag)
class WidgetRedirectorTest(unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') cls.root = Tk() cls.text = Text(cls.root) @classmethod def tearDownClass(cls): del cls.text cls.root.destroy() del cls.root def setUp(self): self.redir = WidgetRedirector(self.text) self.func = Func() self.orig_insert = self.redir.register('insert', self.func) self.text.insert('insert', 'asdf') # leaves self.text empty def tearDown(self): self.text.delete('1.0', 'end') self.redir.close() def test_repr(self): # partly for 100% coverage self.assertIn('Redirector', repr(self.redir)) self.assertIn('Original', repr(self.orig_insert)) def test_register(self): self.assertEqual(self.text.get('1.0', 'end'), '\n') self.assertEqual(self.func.args, ('insert', 'asdf')) self.assertIn('insert', self.redir._operations) self.assertIn('insert', self.text.__dict__) self.assertEqual(self.text.insert, self.func) def test_original_command(self): self.assertEqual(self.orig_insert.operation, 'insert') self.assertEqual(self.orig_insert.tk_call, self.text.tk.call) self.orig_insert('insert', 'asdf') self.assertEqual(self.text.get('1.0', 'end'), 'asdf\n') def test_unregister(self): self.assertIsNone(self.redir.unregister('invalid operation name')) self.assertEqual(self.redir.unregister('insert'), self.func) self.assertNotIn('insert', self.redir._operations) self.assertNotIn('insert', self.text.__dict__) def test_unregister_no_attribute(self): del self.text.insert self.assertEqual(self.redir.unregister('insert'), self.func) def test_dispatch_intercept(self): self.func.__init__(True) self.assertTrue(self.redir.dispatch('insert', False)) self.assertFalse(self.func.args[0]) def test_dispatch_bypass(self): self.orig_insert('insert', 'asdf') # tk.call returns '' where Python would return None self.assertEqual(self.redir.dispatch('delete', '1.0', 'end'), '') self.assertEqual(self.text.get('1.0', 'end'), '\n') def test_dispatch_error(self): self.func.__init__(TclError()) self.assertEqual(self.redir.dispatch('insert', False), '') self.assertEqual(self.redir.dispatch('invalid'), '') def test_command_dispatch(self): # Test that .__init__ causes redirection of tk calls # through redir.dispatch self.root.call(self.text._w, 'insert', 'hello') self.assertEqual(self.func.args, ('hello', )) self.assertEqual(self.text.get('1.0', 'end'), '\n') # Ensure that called through redir .dispatch and not through # self.text.insert by having mock raise TclError. self.func.__init__(TclError()) self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '')
def test_close(self): redir = WidgetRedirector(self.text) redir.register('insert', Func) redir.close() self.assertEqual(redir._operations, {}) self.assertFalse(hasattr(self.text, 'widget'))
class WidgetRedirectorTest(unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') cls.root = Tk() cls.text = Text(cls.root) @classmethod def tearDownClass(cls): del cls.text cls.root.update_idletasks() cls.root.destroy() del cls.root def setUp(self): self.redir = WidgetRedirector(self.text) self.func = Func() self.orig_insert = self.redir.register('insert', self.func) self.text.insert('insert', 'asdf') # leaves self.text empty def tearDown(self): self.text.delete('1.0', 'end') self.redir.close() def test_repr(self): # partly for 100% coverage self.assertIn('Redirector', repr(self.redir)) self.assertIn('Original', repr(self.orig_insert)) def test_register(self): self.assertEqual(self.text.get('1.0', 'end'), '\n') self.assertEqual(self.func.args, ('insert', 'asdf')) self.assertIn('insert', self.redir._operations) self.assertIn('insert', self.text.__dict__) self.assertEqual(self.text.insert, self.func) def test_original_command(self): self.assertEqual(self.orig_insert.operation, 'insert') self.assertEqual(self.orig_insert.tk_call, self.text.tk.call) self.orig_insert('insert', 'asdf') self.assertEqual(self.text.get('1.0', 'end'), 'asdf\n') def test_unregister(self): self.assertIsNone(self.redir.unregister('invalid operation name')) self.assertEqual(self.redir.unregister('insert'), self.func) self.assertNotIn('insert', self.redir._operations) self.assertNotIn('insert', self.text.__dict__) def test_unregister_no_attribute(self): del self.text.insert self.assertEqual(self.redir.unregister('insert'), self.func) def test_dispatch_intercept(self): self.func.__init__(True) self.assertTrue(self.redir.dispatch('insert', False)) self.assertFalse(self.func.args[0]) def test_dispatch_bypass(self): self.orig_insert('insert', 'asdf') # tk.call returns '' where Python would return None self.assertEqual(self.redir.dispatch('delete', '1.0', 'end'), '') self.assertEqual(self.text.get('1.0', 'end'), '\n') def test_dispatch_error(self): self.func.__init__(TclError()) self.assertEqual(self.redir.dispatch('insert', False), '') self.assertEqual(self.redir.dispatch('invalid'), '') def test_command_dispatch(self): # Test that .__init__ causes redirection of tk calls # through redir.dispatch self.root.call(self.text._w, 'insert', 'hello') self.assertEqual(self.func.args, ('hello',)) self.assertEqual(self.text.get('1.0', 'end'), '\n') # Ensure that called through redir .dispatch and not through # self.text.insert by having mock raise TclError. self.func.__init__(TclError()) self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '')
class TextWindow(tk.Text): def __init__(self, parent): tk.Text.__init__(self, parent) self.redirector = WidgetRedirector(self) self.insert = self.redirector.register("insert", self.myinsert) self.delete = self.redirector.register("delete", self.mydelete) self.parent = parent self.config(undo=False) self.load_cache() self.recv() self.save_cache() def myinsert(self, *args): _from = self.index('insert') self.output( source='myinsert', message=args[1], _from=_from, _to=None, _type=args[0], _order=None, ) def mydelete(self, *args): selected_indexes = self.get_selected_indexes() if selected_indexes is not None: _from, _to = selected_indexes else: _from = self.index('insert -1 chars') _to = self.index('insert') self.output( source='mydelete', message=None, _from=_from, _to=_to, _type='delete', _order=None, ) def get_selected_indexes(self): try: _from = self.index('sel.first') _to = self.index('sel.last') selected_indexes = (_from, _to) except tk.TclError: selected_indexes = None if _from == '' or _to == '': selected_indexes = None return selected_indexes def log(self, source=None, message=None, *args, **kwargs): '''Unified logging for all methods.''' log_message = '{} @ {}: "{}"'.format(now(), source, message) if args: log_message = '{} args:{}'.format(log_message, args) if kwargs: log_message = '{} kwargs:{}'.format(log_message, kwargs) print(log_message) def output(self, source, message, _from=None, _to=None, _type=None, _order=None): '''Stub for the unified output method.''' out = { 'source': source, 'message': message, '_from': _from, '_to': _to, '_type': _type, '_order': _order, } #self.log(**out) self.parent.send_queue.put(out) def recv(self): try: while True: message = self.parent.recv_queue.get_nowait() _from = message.get('_from') _to = message.get('_to') message_text = message.get('message') _type = message.get('_type') _order = message.get('_order') self.log('recv', repr(message_text), _from=_from, _to=_to, _type=_type, _order=_order) if _type == 'insert': self.insert(_from, message_text) elif _type == 'delete': self.delete(_from, _to) elif _type == 'sync_request': self.send_all_text() elif _type == 'sync_response': self.receive_all_text(message_text) elif _type == 'connection' and message_text == 'lost': self.parent.enable_buttons() elif _type == 'authentication' and message_text == 'denied': self.parent.enable_buttons() else: self.log('recv', 'Could not handle message type.', _type=_type) except queue.Empty: pass self.parent.after(10, self.recv) def send_all_text(self): all_text = self.get('1.0', 'end-1c') self.output( source='send_whole_text', message=all_text, _from=None, _to=None, _type='sync_response', _order=None, ) def receive_all_text(self, all_text): self.delete('1.0', 'end-1c') self.insert('1.0', all_text) def save_cache(self): all_text = self.get('1.0', 'end-1c') with open('cache.txt', 'w') as text_file: print(all_text, file=text_file) self.parent.after(1000, self.save_cache) def load_cache(self): with open('cache.txt', 'r') as text_file: cached_text = text_file.read() cached_text = cached_text.rstrip('\n') # save_cache() adds a newline to the cached text self.insert('1.0', cached_text)