| import unittest | |
| import tkinter | |
| from tkinter import TclError | |
| from test.support import requires | |
| from test.test_tkinter.support import setUpModule # noqa: F401 | |
| from test.test_tkinter.support import AbstractTkTest, requires_tk | |
| requires('gui') | |
| class TextTest(AbstractTkTest, unittest.TestCase): | |
| def setUp(self): | |
| super().setUp() | |
| self.text = tkinter.Text(self.root) | |
| self.text.pack() | |
| def test_debug(self): | |
| text = self.text | |
| olddebug = text.debug() | |
| try: | |
| text.debug(False) | |
| self.assertIs(text.debug(), False) | |
| text.debug(True) | |
| self.assertIs(text.debug(), True) | |
| finally: | |
| text.debug(olddebug) | |
| self.assertEqual(text.debug(), olddebug) | |
| def test_sync(self): | |
| text = self.text | |
| # sync() returns None and brings line metrics up to date. | |
| self.assertIsNone(text.sync()) | |
| self.assertIs(text.pendingsync(), False) | |
| # sync(command) schedules a one-shot callback. | |
| events = [] | |
| text.sync(command=lambda: events.append('synced')) | |
| text.update() | |
| self.assertEqual(events, ['synced']) | |
| def test_index(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum\ndolor sit amet') | |
| self.assertEqual(text.index('1.0'), '1.0') | |
| self.assertEqual(text.index('1.end'), '1.11') | |
| self.assertEqual(text.index('end'), '3.0') | |
| self.assertEqual(text.index('2.5'), '2.5') | |
| # Index past the end of a line is clamped to its end. | |
| self.assertEqual(text.index('1.100'), '1.11') | |
| self.assertRaises(TclError, text.index, 'invalid') | |
| self.assertRaises(TypeError, text.index) | |
| self.assertRaises(TypeError, text.index, '1.0', '2.5') | |
| def test_compare(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum\ndolor sit amet') | |
| self.assertIs(text.compare('1.0', '<', '2.0'), True) | |
| self.assertIs(text.compare('2.0', '<', '1.0'), False) | |
| self.assertIs(text.compare('1.5', '==', '1.5'), True) | |
| self.assertIs(text.compare('1.5', '!=', '1.5'), False) | |
| self.assertIs(text.compare('2.0', '>=', '2.0'), True) | |
| self.assertIs(text.compare('1.0', '<=', 'end'), True) | |
| self.assertRaises(TclError, text.compare, '1.0', 'invalid', '2.0') | |
| self.assertRaises(TypeError, text.compare, '1.0', '<') | |
| self.assertRaises(TypeError, text.compare, '1.0', '<', '2.0', '3.0') | |
| def test_insert_get_delete(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| self.assertEqual(text.get('1.0', 'end'), 'Lorem ipsum\n') | |
| self.assertEqual(text.get('1.0', '1.5'), 'Lorem') | |
| self.assertEqual(text.get('1.6'), 'i') # single character | |
| # insert before an existing index | |
| text.insert('1.0', '*** ') | |
| self.assertEqual(text.get('1.0', 'end'), '*** Lorem ipsum\n') | |
| text.delete('1.0', '1.4') | |
| self.assertEqual(text.get('1.0', 'end'), 'Lorem ipsum\n') | |
| text.delete('1.5') # delete a single character | |
| self.assertEqual(text.get('1.0', 'end'), 'Loremipsum\n') | |
| self.assertRaises(TypeError, text.get) | |
| self.assertRaises(TypeError, text.get, '1.0', '1.5', 'end') | |
| self.assertRaises(TypeError, text.delete, '1.0', '1.5', 'end') | |
| def test_insert_with_tags(self): | |
| text = self.text | |
| text.insert('1.0', 'hello', 'a', ' ', ('a', 'b'), 'world', 'b') | |
| self.assertEqual(text.get('1.0', 'end'), 'hello world\n') | |
| self.assertEqual([str(i) for i in text.tag_ranges('a')], | |
| ['1.0', '1.6']) | |
| self.assertEqual([str(i) for i in text.tag_ranges('b')], | |
| ['1.5', '1.11']) | |
| def test_replace(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| text.replace('1.0', '1.5', 'Hello') | |
| self.assertEqual(text.get('1.0', 'end'), 'Hello ipsum\n') | |
| text.replace('1.6', 'end - 1 char', 'world') | |
| self.assertEqual(text.get('1.0', 'end'), 'Hello world\n') | |
| self.assertRaises(TclError, text.replace, 'invalid', '1.5', 'x') | |
| # The first index must not be after the second. | |
| self.assertRaises(TclError, text.replace, '1.5', '1.0', 'x') | |
| self.assertRaises(TypeError, text.replace, '1.0', '1.5') | |
| def test_mark(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum\ndolor sit amet') | |
| # 'insert' and 'current' marks always exist. | |
| self.assertIn('insert', text.mark_names()) | |
| self.assertIn('current', text.mark_names()) | |
| text.mark_set('here', '1.5') | |
| self.assertIn('here', text.mark_names()) | |
| self.assertEqual(text.index('here'), '1.5') | |
| text.mark_set('here', '2.3') | |
| self.assertEqual(text.index('here'), '2.3') | |
| text.mark_unset('here') | |
| self.assertNotIn('here', text.mark_names()) | |
| # Unsetting a non-existent mark is not an error. | |
| text.mark_unset('nonexistent') | |
| self.assertRaises(TclError, text.mark_set, 'm', 'invalid') | |
| self.assertRaises(TypeError, text.mark_set, 'm') | |
| self.assertRaises(TypeError, text.mark_set, 'm', '1.0', '2.0') | |
| def test_mark_gravity(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| text.mark_set('here', '1.5') | |
| # The default gravity is right. | |
| self.assertEqual(text.mark_gravity('here'), 'right') | |
| text.mark_gravity('here', 'left') | |
| self.assertEqual(text.mark_gravity('here'), 'left') | |
| # With left gravity the mark stays before inserted text. | |
| text.insert('here', 'XXX') | |
| self.assertEqual(text.index('here'), '1.5') | |
| # With right gravity the mark moves after inserted text. | |
| text.mark_gravity('here', 'right') | |
| text.insert('here', 'YYY') | |
| self.assertEqual(text.index('here'), '1.8') | |
| self.assertRaises(TclError, text.mark_gravity, 'nonexistent') | |
| self.assertRaises(TclError, text.mark_gravity, 'here', 'invalid') | |
| self.assertRaises(TypeError, text.mark_gravity) | |
| self.assertRaises(TypeError, text.mark_gravity, 'here', 'left', 'extra') | |
| def test_mark_next_previous(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| # Keep the builtin 'insert' and 'current' marks at 1.0 so they do | |
| # not interfere with the queries below. | |
| text.mark_set('insert', '1.0') | |
| text.mark_set('current', '1.0') | |
| text.mark_set('m1', '1.3') | |
| text.mark_set('m2', '1.7') | |
| # mark_next finds a mark at a position at or after the index, | |
| # mark_previous finds one at a position strictly before it. | |
| self.assertEqual(text.mark_next('1.1'), 'm1') | |
| self.assertEqual(text.mark_next('1.3'), 'm1') | |
| self.assertEqual(text.mark_next('1.4'), 'm2') | |
| self.assertIsNone(text.mark_next('1.8')) | |
| self.assertEqual(text.mark_previous('1.4'), 'm1') | |
| self.assertEqual(text.mark_previous('1.7'), 'm1') | |
| self.assertEqual(text.mark_previous('end'), 'm2') | |
| self.assertIsNone(text.mark_previous('1.0')) | |
| self.assertRaises(TclError, text.mark_next, 'invalid') | |
| self.assertRaises(TclError, text.mark_previous, 'invalid') | |
| self.assertRaises(TypeError, text.mark_next) | |
| self.assertRaises(TypeError, text.mark_previous) | |
| self.assertRaises(TypeError, text.mark_next, '1.0', '2.0') | |
| self.assertRaises(TypeError, text.mark_previous, '1.0', '2.0') | |
| def test_tag_add_remove_ranges(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum\ndolor sit amet') | |
| self.assertEqual(text.tag_ranges('sel'), ()) | |
| text.tag_add('big', '1.0', '1.5') | |
| self.assertEqual([str(i) for i in text.tag_ranges('big')], | |
| ['1.0', '1.5']) | |
| # Add a second, disjoint range. | |
| text.tag_add('big', '2.0', '2.5') | |
| self.assertEqual([str(i) for i in text.tag_ranges('big')], | |
| ['1.0', '1.5', '2.0', '2.5']) | |
| text.tag_remove('big', '1.0', '1.3') | |
| self.assertEqual([str(i) for i in text.tag_ranges('big')], | |
| ['1.3', '1.5', '2.0', '2.5']) | |
| # tag_ranges of an undefined tag is empty, not an error. | |
| self.assertEqual(text.tag_ranges('nonexistent'), ()) | |
| self.assertRaises(TclError, text.tag_add, 'big', 'invalid') | |
| self.assertRaises(TypeError, text.tag_add, 'big') | |
| self.assertRaises(TclError, text.tag_remove, 'big', 'invalid') | |
| self.assertRaises(TypeError, text.tag_remove, 'big', '1.0', '2.0', '3.0') | |
| self.assertRaises(TypeError, text.tag_ranges, 'big', 'extra') | |
| def test_tag_names(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| text.tag_add('a', '1.0', '1.5') | |
| text.tag_add('b', '1.3', '1.8') | |
| # 'sel' always exists; order reflects priority (creation order). | |
| self.assertEqual(set(text.tag_names()), {'sel', 'a', 'b'}) | |
| # Tags applied to the character at the given index. | |
| self.assertEqual(set(text.tag_names('1.4')), {'a', 'b'}) | |
| self.assertEqual(set(text.tag_names('1.0')), {'a'}) | |
| self.assertEqual(set(text.tag_names('1.10')), set()) | |
| self.assertRaises(TclError, text.tag_names, 'invalid') | |
| self.assertRaises(TypeError, text.tag_names, '1.0', '2.0') | |
| def test_tag_nextrange_prevrange(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum dolor') | |
| text.tag_add('a', '1.0', '1.5') | |
| text.tag_add('a', '1.12', '1.17') | |
| self.assertEqual([str(i) for i in text.tag_nextrange('a', '1.0')], | |
| ['1.0', '1.5']) | |
| self.assertEqual([str(i) for i in text.tag_nextrange('a', '1.5')], | |
| ['1.12', '1.17']) | |
| self.assertEqual(text.tag_nextrange('a', '1.17'), ()) | |
| self.assertEqual([str(i) for i in text.tag_prevrange('a', 'end')], | |
| ['1.12', '1.17']) | |
| self.assertEqual([str(i) for i in text.tag_prevrange('a', '1.12')], | |
| ['1.0', '1.5']) | |
| self.assertEqual(text.tag_prevrange('a', '1.0'), ()) | |
| # An undefined tag has no ranges, but the index must be valid. | |
| self.assertEqual(text.tag_nextrange('nonexistent', '1.0'), ()) | |
| self.assertRaises(TclError, text.tag_nextrange, 'a', 'invalid') | |
| self.assertRaises(TclError, text.tag_prevrange, 'a', 'invalid') | |
| self.assertRaises(TypeError, text.tag_nextrange, 'a') | |
| self.assertRaises(TypeError, text.tag_prevrange, 'a') | |
| self.assertRaises(TypeError, text.tag_nextrange, 'a', '1.0', '2.0', '3.0') | |
| self.assertRaises(TypeError, text.tag_prevrange, 'a', '1.0', '2.0', '3.0') | |
| def test_tag_delete(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| text.tag_add('a', '1.0', '1.5') | |
| text.tag_add('b', '1.6', '1.11') | |
| self.assertEqual(set(text.tag_names()), {'sel', 'a', 'b'}) | |
| text.tag_delete('a', 'b') | |
| self.assertEqual(set(text.tag_names()), {'sel'}) | |
| self.assertEqual(text.tag_ranges('a'), ()) | |
| def test_tag_raise_lower(self): | |
| text = self.text | |
| text.tag_configure('a', foreground='red') | |
| text.tag_configure('b', foreground='green') | |
| text.tag_configure('c', foreground='blue') | |
| # Creation order is lowest-to-highest priority; tag_names lists | |
| # them in increasing priority order. | |
| self.assertEqual(text.tag_names(), ('sel', 'a', 'b', 'c')) | |
| text.tag_raise('a') | |
| self.assertEqual(text.tag_names(), ('sel', 'b', 'c', 'a')) | |
| text.tag_lower('a') | |
| self.assertEqual(text.tag_names(), ('a', 'sel', 'b', 'c')) | |
| text.tag_raise('a', 'b') | |
| self.assertEqual(text.tag_names(), ('sel', 'b', 'a', 'c')) | |
| text.tag_lower('c', 'b') | |
| self.assertEqual(text.tag_names(), ('sel', 'c', 'b', 'a')) | |
| self.assertRaises(TclError, text.tag_raise, 'nonexistent') | |
| self.assertRaises(TclError, text.tag_lower, 'nonexistent') | |
| self.assertRaises(TclError, text.tag_raise, 'a', 'nonexistent') | |
| self.assertRaises(TypeError, text.tag_raise) | |
| self.assertRaises(TypeError, text.tag_raise, 'a', 'b', 'c') | |
| self.assertRaises(TypeError, text.tag_lower, 'a', 'b', 'c') | |
| def test_tag_cget_configure(self): | |
| text = self.text | |
| text.tag_configure('a', foreground='red', underline=True) | |
| self.assertEqual(str(text.tag_cget('a', 'foreground')), 'red') | |
| self.assertIn(text.tag_cget('a', 'underline'), (1, '1', True)) | |
| # tag_cget normalizes the option name (no leading hyphen needed). | |
| self.assertEqual(str(text.tag_cget('a', '-foreground')), 'red') | |
| # configure() query returns the full option spec. | |
| self.assertEqual(text.tag_configure('a', 'foreground')[-1], 'red') | |
| text.tag_configure('a', foreground='blue') | |
| self.assertEqual(str(text.tag_cget('a', 'foreground')), 'blue') | |
| self.assertRaises(TclError, text.tag_cget, 'nonexistent', | |
| 'foreground') | |
| self.assertRaises(TypeError, text.tag_cget, 'a') | |
| self.assertRaises(TypeError, text.tag_cget, 'a', 'foreground', 'extra') | |
| def test_edit_modified(self): | |
| text = self.text | |
| self.assertIs(text.edit_modified(), False) | |
| text.insert('1.0', 'spam') | |
| self.assertIs(text.edit_modified(), True) | |
| text.edit_modified(False) | |
| self.assertIs(text.edit_modified(), False) | |
| text.edit_modified(True) | |
| self.assertIs(text.edit_modified(), True) | |
| def test_edit_undo_redo(self): | |
| text = self.text | |
| text.configure(undo=True) | |
| text.insert('1.0', 'spam') | |
| text.edit_separator() | |
| text.insert('end', ' eggs') | |
| self.assertEqual(text.get('1.0', 'end'), 'spam eggs\n') | |
| text.edit_undo() | |
| self.assertEqual(text.get('1.0', 'end'), 'spam\n') | |
| text.edit_redo() | |
| self.assertEqual(text.get('1.0', 'end'), 'spam eggs\n') | |
| # An empty undo stack raises. | |
| text.edit_reset() | |
| self.assertRaises(TclError, text.edit_undo) | |
| def test_edit_canundo_canredo(self): | |
| text = self.text | |
| text.configure(undo=True) | |
| self.assertIs(text.edit_canundo(), False) | |
| self.assertIs(text.edit_canredo(), False) | |
| text.insert('1.0', 'spam') | |
| self.assertIs(text.edit_canundo(), True) | |
| self.assertIs(text.edit_canredo(), False) | |
| text.edit_undo() | |
| self.assertIs(text.edit_canundo(), False) | |
| self.assertIs(text.edit_canredo(), True) | |
| text.edit_redo() | |
| self.assertIs(text.edit_canundo(), True) | |
| self.assertIs(text.edit_canredo(), False) | |
| text.edit_reset() | |
| self.assertIs(text.edit_canundo(), False) | |
| self.assertIs(text.edit_canredo(), False) | |
| def test_dump(self): | |
| text = self.text | |
| text.insert('1.0', 'hello') | |
| text.tag_add('a', '1.0', '1.3') | |
| text.mark_set('here', '1.2') | |
| dump = text.dump('1.0', 'end') | |
| self.assertIsInstance(dump, list) | |
| for item in dump: | |
| self.assertIsInstance(item, tuple) | |
| self.assertEqual(len(item), 3) | |
| kinds = {item[0] for item in dump} | |
| self.assertIn('text', kinds) | |
| self.assertIn('tagon', kinds) | |
| self.assertIn('mark', kinds) | |
| # Filtering by kind. | |
| text_only = text.dump('1.0', 'end', text=True) | |
| self.assertEqual({item[0] for item in text_only}, {'text'}) | |
| self.assertEqual(''.join(item[1] for item in text_only), 'hello\n') | |
| # The command callback receives each triple and suppresses the result. | |
| collected = [] | |
| result = text.dump('1.0', 'end', text=True, | |
| command=lambda *a: collected.append(a)) | |
| self.assertIsNone(result) | |
| self.assertEqual(''.join(key_value_index[1] | |
| for key_value_index in collected), 'hello\n') | |
| self.assertRaises(TclError, text.dump, 'invalid') | |
| self.assertRaises(TypeError, text.dump) | |
| self.assertRaises(TypeError, text.dump, '1.0', 'end', None, 'extra') | |
| def test_image(self): | |
| text = self.text | |
| image = tkinter.PhotoImage(master=self.root, width=10, height=10) | |
| text.insert('1.0', 'AB') | |
| name = text.image_create('1.1', image=image) | |
| self.assertIsInstance(name, str) | |
| self.assertIn(name, self.root.splitlist(text.image_names())) | |
| self.assertEqual(str(text.image_cget(name, 'image')), str(image)) | |
| text.image_configure(name, align='top') | |
| self.assertEqual(str(text.image_cget(name, 'align')), 'top') | |
| # An embedded image occupies a single index position. | |
| self.assertEqual(text.index('end - 1 char'), '1.3') | |
| # The image name can be used as an index; it is matched as a whole. | |
| self.assertEqual(text.index(name), '1.1') | |
| # Either a name or an image is required, and the index must be valid. | |
| self.assertRaises(TclError, text.image_create, '1.0') | |
| self.assertRaises(TclError, text.image_create, 'invalid', | |
| image=image) | |
| self.assertRaises(TypeError, text.image_cget, name) | |
| self.assertRaises(TypeError, text.image_cget, name, 'image', 'extra') | |
| def test_window(self): | |
| text = self.text | |
| button = tkinter.Button(text, text='ok') | |
| text.insert('1.0', 'AB') | |
| text.window_create('1.1', window=button) | |
| self.assertEqual(self.root.splitlist(text.window_names()), | |
| (str(button),)) | |
| self.assertEqual(text.window_cget('1.1', 'window'), str(button)) | |
| # The window can be addressed by its name where an index is expected; | |
| # the name is matched as a whole rather than parsed (gh-143070). | |
| self.assertEqual(text.index(str(button)), '1.1') | |
| self.assertEqual(text.window_cget(str(button), 'window'), str(button)) | |
| text.window_configure('1.1', padx=5) | |
| self.assertEqual(text.window_cget('1.1', 'padx'), 5) | |
| # No embedded window at this index, and the index must be valid. | |
| self.assertRaises(TclError, text.window_cget, '1.0', 'window') | |
| self.assertRaises(TclError, text.window_cget, 'invalid', 'window') | |
| self.assertRaises(TypeError, text.window_cget, '1.1') | |
| self.assertRaises(TypeError, text.window_cget, '1.1', 'window', 'extra') | |
| button.destroy() | |
| def test_tag_configure(self): | |
| text = self.text | |
| tag = 'a' | |
| getint = text.tk.getint | |
| getboolean = text.tk.getboolean | |
| # Color options. | |
| for opt in ('foreground', 'background'): | |
| text.tag_configure(tag, **{opt: '#ff0000'}) | |
| self.assertEqual(str(text.tag_cget(tag, opt)), '#ff0000') | |
| # Stipple (bitmap) options. | |
| for opt in ('bgstipple', 'fgstipple'): | |
| text.tag_configure(tag, **{opt: 'gray50'}) | |
| self.assertEqual(str(text.tag_cget(tag, opt)), 'gray50') | |
| # Enumerated options. | |
| for opt, value in (('justify', 'center'), ('wrap', 'word'), | |
| ('relief', 'raised'), ('tabstyle', 'wordprocessor'), | |
| ('offset', '5')): | |
| text.tag_configure(tag, **{opt: value}) | |
| self.assertEqual(str(text.tag_cget(tag, 'justify')), 'center') | |
| self.assertEqual(str(text.tag_cget(tag, 'wrap')), 'word') | |
| self.assertEqual(str(text.tag_cget(tag, 'relief')), 'raised') | |
| self.assertEqual(str(text.tag_cget(tag, 'tabstyle')), 'wordprocessor') | |
| # Boolean options. | |
| for opt in ('underline', 'overstrike', 'elide'): | |
| text.tag_configure(tag, **{opt: True}) | |
| self.assertIs(getboolean(text.tag_cget(tag, opt)), True) | |
| text.tag_configure(tag, **{opt: False}) | |
| self.assertIs(getboolean(text.tag_cget(tag, opt)), False) | |
| # Screen-distance (pixel) options. | |
| for opt in ('borderwidth', 'lmargin1', 'lmargin2', 'rmargin', | |
| 'spacing1', 'spacing2', 'spacing3'): | |
| text.tag_configure(tag, **{opt: 7}) | |
| self.assertEqual(getint(text.tag_cget(tag, opt)), 7) | |
| # Other options. | |
| text.tag_configure(tag, font='Helvetica 12') | |
| self.assertEqual(str(text.tag_cget(tag, 'font')), 'Helvetica 12') | |
| text.tag_configure(tag, tabs=(10.2, '1i')) | |
| self.assertEqual([str(x) for x in text.tag_ranges('sel')], []) | |
| self.assertRaises(TclError, text.tag_cget, tag, 'spam') | |
| @requires_tk(8, 6, 6) | |
| def test_tag_configure_colors(self): | |
| # Tag color options added in Tk 8.6.6. | |
| text = self.text | |
| tag = 'a' | |
| for opt in ('selectforeground', 'selectbackground', | |
| 'lmargincolor', 'rmargincolor', | |
| 'underlinefg', 'overstrikefg'): | |
| text.tag_configure(tag, **{opt: '#00ff00'}) | |
| self.assertEqual(str(text.tag_cget(tag, opt)), '#00ff00') | |
| def test_tag_configure_query(self): | |
| text = self.text | |
| tag = 'a' | |
| # Querying all options returns a dict keyed by option name. | |
| cnf = text.tag_configure(tag) | |
| self.assertIsInstance(cnf, dict) | |
| self.assertIn('foreground', cnf) | |
| # The value is the full 5-tuple option specification. | |
| self.assertEqual(len(cnf['foreground']), 5) | |
| # Querying a single option returns its specification. | |
| spec = text.tag_configure(tag, 'foreground') | |
| self.assertEqual(spec[0], 'foreground') | |
| self.assertEqual(spec[-1], '') # unset by default | |
| # Setting via keyword arguments and via a dict are equivalent. | |
| text.tag_configure(tag, foreground='red') | |
| self.assertEqual(str(text.tag_configure(tag, 'foreground')[-1]), 'red') | |
| text.tag_configure(tag, {'foreground': 'blue'}) | |
| self.assertEqual(str(text.tag_cget(tag, 'foreground')), 'blue') | |
| # tag_config is an alias of tag_configure. | |
| self.assertEqual(text.tag_config, text.tag_configure) | |
| def test_image_configure(self): | |
| text = self.text | |
| image = tkinter.PhotoImage(master=self.root, width=10, height=10) | |
| text.insert('1.0', 'AB') | |
| name = text.image_create('1.1', image=image, name='img') | |
| self.assertEqual(name, 'img') | |
| self.assertEqual(str(text.image_cget(name, 'name')), 'img') | |
| self.assertEqual(str(text.image_cget(name, 'image')), str(image)) | |
| for value in ('top', 'center', 'bottom', 'baseline'): | |
| text.image_configure(name, align=value) | |
| self.assertEqual(text.image_cget(name, 'align'), value) | |
| text.image_configure(name, padx=3, pady=4) | |
| self.assertEqual(text.image_cget(name, 'padx'), | |
| 3 if self.wantobjects else '3') | |
| self.assertEqual(text.image_cget(name, 'pady'), | |
| 4 if self.wantobjects else '4') | |
| # Querying returns the full option set. | |
| cnf = text.image_configure(name) | |
| self.assertIsInstance(cnf, dict) | |
| self.assertIn('align', cnf) | |
| self.assertRaises(TclError, text.image_cget, name, 'spam') | |
| def test_window_configure(self): | |
| text = self.text | |
| button = tkinter.Button(text, text='ok') | |
| text.insert('1.0', 'AB') | |
| text.window_create('1.1', window=button) | |
| self.assertEqual(text.window_cget('1.1', 'window'), str(button)) | |
| for value in ('top', 'center', 'bottom', 'baseline'): | |
| text.window_configure('1.1', align=value) | |
| self.assertEqual(text.window_cget('1.1', 'align'), value) | |
| text.window_configure('1.1', padx=3, pady=4) | |
| self.assertEqual(text.window_cget('1.1', 'padx'), | |
| 3 if self.wantobjects else '3') | |
| self.assertEqual(text.window_cget('1.1', 'pady'), | |
| 4 if self.wantobjects else '4') | |
| text.window_configure('1.1', stretch=True) | |
| self.assertIs(text.tk.getboolean(text.window_cget('1.1', 'stretch')), | |
| True) | |
| cnf = text.window_configure('1.1') | |
| self.assertIsInstance(cnf, dict) | |
| self.assertIn('stretch', cnf) | |
| self.assertRaises(TclError, text.window_cget, '1.1', 'spam') | |
| self.assertEqual(text.window_config, text.window_configure) | |
| button.destroy() | |
| def test_peer(self): | |
| text = self.text | |
| text.insert('1.0', 'Lorem ipsum') | |
| self.assertEqual(text.peer_names(), ()) | |
| text.peer_create('.peer1') | |
| names = self.root.splitlist(text.tk.call('winfo', 'children', '.')) | |
| self.assertIn('.peer1', [str(n) for n in names]) | |
| self.assertEqual([str(p) for p in text.peer_names()], ['.peer1']) | |
| # Peers share content. | |
| self.assertEqual(text.tk.call('.peer1', 'get', '1.0', 'end'), | |
| 'Lorem ipsum\n') | |
| text.tk.call('destroy', '.peer1') | |
| def test_bbox(self): | |
| text = self.text | |
| text.insert('1.0', 'hello') | |
| text.update() | |
| bbox = text.bbox('1.0') | |
| self.assertIsInstance(bbox, tuple) | |
| self.assertEqual(len(bbox), 4) | |
| for v in bbox: | |
| self.assertIsInstance(v, int) | |
| # A character that is not displayed has no bounding box. | |
| self.assertIsNone(text.bbox('end')) | |
| self.assertRaises(TclError, text.bbox, 'invalid') | |
| self.assertRaises(TypeError, text.bbox) | |
| self.assertRaises(TypeError, text.bbox, '1.0', '2.0') | |
| def test_dlineinfo(self): | |
| text = self.text | |
| text.insert('1.0', 'hello\nworld') | |
| text.update() | |
| info = text.dlineinfo('1.0') | |
| self.assertIsInstance(info, tuple) | |
| self.assertEqual(len(info), 5) | |
| for v in info: | |
| self.assertIsInstance(v, int) | |
| self.assertRaises(TclError, text.dlineinfo, 'invalid') | |
| self.assertRaises(TypeError, text.dlineinfo) | |
| self.assertRaises(TypeError, text.dlineinfo, '1.0', '2.0') | |
| def test_see(self): | |
| text = self.text | |
| text.insert('1.0', '\n'.join('line %d' % i for i in range(200))) | |
| text.update() | |
| # Initially the last line is not visible. | |
| self.assertIsNone(text.bbox('200.0')) | |
| text.see('200.0') | |
| text.update() | |
| self.assertIsNotNone(text.bbox('200.0')) | |
| self.assertRaises(TclError, text.see, 'invalid') | |
| self.assertRaises(TypeError, text.see) | |
| self.assertRaises(TypeError, text.see, '1.0', '2.0') | |
| # yview_pickplace is a deprecated way to make an index visible. | |
| text.yview_pickplace('1.0') | |
| text.update() | |
| self.assertIsNotNone(text.bbox('1.0')) | |
| def test_search(self): | |
| text = self.text | |
| # pattern and index are obligatory arguments. | |
| self.assertRaises(TclError, text.search, None, '1.0') | |
| self.assertRaises(TclError, text.search, 'a', None) | |
| self.assertRaises(TclError, text.search, None, None) | |
| # Invalid text index. | |
| self.assertRaises(TclError, text.search, '', 0) | |
| self.assertRaises(TclError, text.search, '', '') | |
| self.assertRaises(TclError, text.search, '', 'invalid') | |
| self.assertRaises(TclError, text.search, '', '1.0', 0) | |
| self.assertRaises(TclError, text.search, '', '1.0', '') | |
| self.assertRaises(TclError, text.search, '', '1.0', 'invalid') | |
| text.insert('1.0', | |
| 'This is a test. This is only a test.\n' | |
| 'Another line.\n' | |
| 'Yet another line.\n' | |
| '64-bit') | |
| self.assertEqual(text.search('test', '1.0'), '1.10') | |
| self.assertEqual(text.search('test', '1.0', 'end'), '1.10') | |
| self.assertEqual(text.search('test', '1.0', '1.10'), '') | |
| self.assertEqual(text.search('test', '1.11'), '1.31') | |
| self.assertEqual(text.search('test', '1.32', 'end'), '') | |
| self.assertEqual(text.search('test', '1.32'), '1.10') | |
| self.assertEqual(text.search('', '1.0'), '1.0') # empty pattern | |
| self.assertEqual(text.search('nonexistent', '1.0'), '') | |
| self.assertEqual(text.search('-bit', '1.0'), '4.2') # starts with a hyphen | |
| self.assertEqual(text.search('line', '3.0'), '3.12') | |
| self.assertEqual(text.search('line', '3.0', forwards=True), '3.12') | |
| self.assertEqual(text.search('line', '3.0', backwards=True), '2.8') | |
| self.assertEqual(text.search('line', '3.0', forwards=True, backwards=True), '2.8') | |
| self.assertEqual(text.search('t.', '1.0'), '1.13') | |
| self.assertEqual(text.search('t.', '1.0', exact=True), '1.13') | |
| self.assertEqual(text.search('t.', '1.0', regexp=True), '1.10') | |
| self.assertEqual(text.search('t.', '1.0', exact=True, regexp=True), '1.10') | |
| self.assertEqual(text.search('TEST', '1.0'), '') | |
| self.assertEqual(text.search('TEST', '1.0', nocase=True), '1.10') | |
| self.assertEqual(text.search('.*line', '1.0', regexp=True), '2.0') | |
| self.assertEqual(text.search('.*line', '1.0', regexp=True, nolinestop=True), '1.0') | |
| self.assertEqual(text.search('test', '1.0', '1.13'), '1.10') | |
| self.assertEqual(text.search('test', '1.0', '1.13', strictlimits=True), '') | |
| self.assertEqual(text.search('test', '1.0', '1.14', strictlimits=True), '1.10') | |
| var = tkinter.Variable(self.root) | |
| self.assertEqual(text.search('test', '1.0', count=var), '1.10') | |
| self.assertEqual(var.get(), 4 if self.wantobjects else '4') | |
| # TODO: Add test for elide=True | |
| def test_search_all(self): | |
| text = self.text | |
| # pattern and index are obligatory arguments. | |
| self.assertRaises(TclError, text.search_all, None, '1.0') | |
| self.assertRaises(TclError, text.search_all, 'a', None) | |
| self.assertRaises(TclError, text.search_all, None, None) | |
| # Keyword-only arguments | |
| self.assertRaises(TypeError, text.search_all, 'a', '1.0', 'end', None) | |
| # Invalid text index. | |
| self.assertRaises(TclError, text.search_all, '', 0) | |
| self.assertRaises(TclError, text.search_all, '', '') | |
| self.assertRaises(TclError, text.search_all, '', 'invalid') | |
| self.assertRaises(TclError, text.search_all, '', '1.0', 0) | |
| self.assertRaises(TclError, text.search_all, '', '1.0', '') | |
| self.assertRaises(TclError, text.search_all, '', '1.0', 'invalid') | |
| def eq(res, expected): | |
| self.assertIsInstance(res, tuple) | |
| self.assertEqual([str(i) for i in res], expected) | |
| text.insert('1.0', 'ababa\naba\n64-bit') | |
| eq(text.search_all('aba', '1.0'), ['1.0', '2.0']) | |
| eq(text.search_all('aba', '1.0', 'end'), ['1.0', '2.0']) | |
| eq(text.search_all('aba', '1.1', 'end'), ['1.2', '2.0']) | |
| eq(text.search_all('aba', '1.1'), ['1.2', '2.0', '1.0']) | |
| res = text.search_all('', '1.0') # empty pattern | |
| eq(res[:5], ['1.0', '1.1', '1.2', '1.3', '1.4']) | |
| eq(res[-5:], ['3.2', '3.3', '3.4', '3.5', '3.6']) | |
| eq(text.search_all('nonexistent', '1.0'), []) | |
| eq(text.search_all('-bit', '1.0'), ['3.2']) # starts with a hyphen | |
| eq(text.search_all('aba', '1.0', 'end', forwards=True), ['1.0', '2.0']) | |
| eq(text.search_all('aba', 'end', '1.0', backwards=True), ['2.0', '1.2']) | |
| eq(text.search_all('aba', '1.0', overlap=True), ['1.0', '1.2', '2.0']) | |
| eq(text.search_all('aba', 'end', '1.0', overlap=True, backwards=True), ['2.0', '1.2', '1.0']) | |
| eq(text.search_all('aba', '1.0', exact=True), ['1.0', '2.0']) | |
| eq(text.search_all('a.a', '1.0', exact=True), []) | |
| eq(text.search_all('a.a', '1.0', regexp=True), ['1.0', '2.0']) | |
| eq(text.search_all('ABA', '1.0'), []) | |
| eq(text.search_all('ABA', '1.0', nocase=True), ['1.0', '2.0']) | |
| eq(text.search_all('a.a', '1.0', regexp=True), ['1.0', '2.0']) | |
| eq(text.search_all('a.a', '1.0', regexp=True, nolinestop=True), ['1.0', '1.4']) | |
| eq(text.search_all('aba', '1.0', '2.2'), ['1.0', '2.0']) | |
| eq(text.search_all('aba', '1.0', '2.2', strictlimits=True), ['1.0']) | |
| eq(text.search_all('aba', '1.0', '2.3', strictlimits=True), ['1.0', '2.0']) | |
| var = tkinter.Variable(self.root) | |
| eq(text.search_all('aba', '1.0', count=var), ['1.0', '2.0']) | |
| self.assertEqual(var.get(), (3, 3) if self.wantobjects else '3 3') | |
| # TODO: Add test for elide=True | |
| def test_count(self): | |
| text = self.text | |
| text.insert('1.0', | |
| 'Lorem ipsum dolor sit amet,\n' | |
| 'consectetur adipiscing elit,\n' | |
| 'sed do eiusmod tempor incididunt\n' | |
| 'ut labore et dolore magna aliqua.') | |
| options = ('chars', 'indices', 'lines', | |
| 'displaychars', 'displayindices', 'displaylines', | |
| 'xpixels', 'ypixels') | |
| self.assertEqual(len(text.count('1.0', 'end', *options, return_ints=True)), 8) | |
| self.assertEqual(len(text.count('1.0', 'end', *options)), 8) | |
| self.assertEqual(text.count('1.0', 'end', 'chars', 'lines', return_ints=True), | |
| (124, 4)) | |
| self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3)) | |
| self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines', return_ints=True), | |
| (-92, -3)) | |
| self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3)) | |
| self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines', return_ints=True), | |
| (0, 0)) | |
| self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0)) | |
| self.assertEqual(text.count('1.0', 'end', 'lines', return_ints=True), 4) | |
| self.assertEqual(text.count('1.0', 'end', 'lines'), (4,)) | |
| self.assertEqual(text.count('end', '1.0', 'lines', return_ints=True), -4) | |
| self.assertEqual(text.count('end', '1.0', 'lines'), (-4,)) | |
| self.assertEqual(text.count('1.3', '1.5', 'lines', return_ints=True), 0) | |
| self.assertEqual(text.count('1.3', '1.5', 'lines'), None) | |
| self.assertEqual(text.count('1.3', '1.3', 'lines', return_ints=True), 0) | |
| self.assertEqual(text.count('1.3', '1.3', 'lines'), None) | |
| # Count 'indices' by default. | |
| self.assertEqual(text.count('1.0', 'end', return_ints=True), 124) | |
| self.assertEqual(text.count('1.0', 'end'), (124,)) | |
| self.assertEqual(text.count('1.0', 'end', 'indices', return_ints=True), 124) | |
| self.assertEqual(text.count('1.0', 'end', 'indices'), (124,)) | |
| self.assertRaises(TclError, text.count, '1.0', 'end', 'spam') | |
| self.assertRaises(TclError, text.count, '1.0', 'end', '-lines') | |
| # The ypixels counts depend on the widget geometry. | |
| text.update() | |
| # Indices on the same display line have zero vertical distance. | |
| self.assertEqual(text.count('1.3', '1.5', 'ypixels', return_ints=True), 0) | |
| self.assertIsNone(text.count('1.3', '1.5', 'ypixels')) | |
| self.assertIsInstance(text.count('1.3', '2.5', 'ypixels', return_ints=True), int) | |
| self.assertIsInstance(text.count('1.3', '2.5', 'ypixels'), tuple) | |
| self.assertIsInstance(text.count('1.3', '2.5', 'update', 'ypixels', return_ints=True), int) | |
| self.assertIsInstance(text.count('1.3', '2.5', 'update', 'ypixels'), int) | |
| self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels', return_ints=True), 0) | |
| self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), None) | |
| self.assertEqual(text.count('1.3', '1.5', 'update', 'indices', return_ints=True), 2) | |
| self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2) | |
| self.assertEqual(text.count('1.3', '1.3', 'update', 'indices', return_ints=True), 0) | |
| self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), None) | |
| self.assertEqual(text.count('1.3', '1.5', 'update', return_ints=True), 2) | |
| self.assertEqual(text.count('1.3', '1.5', 'update'), (2,)) | |
| self.assertEqual(text.count('1.3', '1.3', 'update', return_ints=True), 0) | |
| self.assertEqual(text.count('1.3', '1.3', 'update'), None) | |
| if __name__ == "__main__": | |
| unittest.main() |