mirror of
https://github.com/python/cpython.git
synced 2026-06-23 03:23:38 -04:00
88e5d8a7b7
Wrap the Tk text widget "sync" and "pendingsync" subcommands, which control and report the synchronization of the displayed view with the underlying text when line heights have not yet been computed.
783 lines
34 KiB
Python
783 lines
34 KiB
Python
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(0)
|
|
self.assertEqual(text.debug(), 0)
|
|
text.debug(1)
|
|
self.assertEqual(text.debug(), 1)
|
|
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.assertEqual(text.edit_modified(), 0)
|
|
text.insert('1.0', 'spam')
|
|
self.assertEqual(text.edit_modified(), 1)
|
|
text.edit_modified(False)
|
|
self.assertEqual(text.edit_modified(), 0)
|
|
text.edit_modified(True)
|
|
self.assertEqual(text.edit_modified(), 1)
|
|
|
|
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')
|
|
|
|
# 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))
|
|
|
|
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(str(text.image_cget(name, 'align')), value)
|
|
text.image_configure(name, padx=3, pady=4)
|
|
self.assertEqual(text.tk.getint(text.image_cget(name, 'padx')), 3)
|
|
self.assertEqual(text.tk.getint(text.image_cget(name, 'pady')), 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(str(text.window_cget('1.1', 'align')), value)
|
|
text.window_configure('1.1', padx=3, pady=4)
|
|
self.assertEqual(text.tk.getint(text.window_cget('1.1', 'padx')), 3)
|
|
self.assertEqual(text.tk.getint(text.window_cget('1.1', 'pady')), 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')
|
|
|
|
self.assertIsInstance(text.count('1.3', '1.5', 'ypixels', return_ints=True), int)
|
|
self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), tuple)
|
|
self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels', return_ints=True), int)
|
|
self.assertIsInstance(text.count('1.3', '1.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()
|