test_widgets.py 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891
  1. import unittest
  2. import tkinter
  3. from tkinter import ttk, TclError
  4. from test.support import requires
  5. import sys
  6. from tkinter.test.test_ttk.test_functions import MockTclObj
  7. from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
  8. simulate_mouse_click)
  9. from tkinter.test.widget_tests import (add_standard_options, noconv,
  10. AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
  11. setUpModule)
  12. requires('gui')
  13. class StandardTtkOptionsTests(StandardOptionsTests):
  14. def test_class(self):
  15. widget = self.create()
  16. self.assertEqual(widget['class'], '')
  17. errmsg='attempt to change read-only option'
  18. if get_tk_patchlevel() < (8, 6, 0, 'beta', 3):
  19. errmsg='Attempt to change read-only option'
  20. self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg)
  21. widget2 = self.create(class_='Foo')
  22. self.assertEqual(widget2['class'], 'Foo')
  23. def test_padding(self):
  24. widget = self.create()
  25. self.checkParam(widget, 'padding', 0, expected=('0',))
  26. self.checkParam(widget, 'padding', 5, expected=('5',))
  27. self.checkParam(widget, 'padding', (5, 6), expected=('5', '6'))
  28. self.checkParam(widget, 'padding', (5, 6, 7),
  29. expected=('5', '6', '7'))
  30. self.checkParam(widget, 'padding', (5, 6, 7, 8),
  31. expected=('5', '6', '7', '8'))
  32. self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p'))
  33. self.checkParam(widget, 'padding', (), expected='')
  34. def test_style(self):
  35. widget = self.create()
  36. self.assertEqual(widget['style'], '')
  37. errmsg = 'Layout Foo not found'
  38. if hasattr(self, 'default_orient'):
  39. errmsg = ('Layout %s.Foo not found' %
  40. getattr(self, 'default_orient').title())
  41. self.checkInvalidParam(widget, 'style', 'Foo',
  42. errmsg=errmsg)
  43. widget2 = self.create(class_='Foo')
  44. self.assertEqual(widget2['class'], 'Foo')
  45. # XXX
  46. pass
  47. class WidgetTest(AbstractTkTest, unittest.TestCase):
  48. """Tests methods available in every ttk widget."""
  49. def setUp(self):
  50. super().setUp()
  51. self.widget = ttk.Button(self.root, width=0, text="Text")
  52. self.widget.pack()
  53. self.widget.wait_visibility()
  54. def test_identify(self):
  55. self.widget.update_idletasks()
  56. self.assertEqual(self.widget.identify(
  57. int(self.widget.winfo_width() / 2),
  58. int(self.widget.winfo_height() / 2)
  59. ), "label")
  60. self.assertEqual(self.widget.identify(-1, -1), "")
  61. self.assertRaises(tkinter.TclError, self.widget.identify, None, 5)
  62. self.assertRaises(tkinter.TclError, self.widget.identify, 5, None)
  63. self.assertRaises(tkinter.TclError, self.widget.identify, 5, '')
  64. def test_widget_state(self):
  65. # XXX not sure about the portability of all these tests
  66. self.assertEqual(self.widget.state(), ())
  67. self.assertEqual(self.widget.instate(['!disabled']), True)
  68. # changing from !disabled to disabled
  69. self.assertEqual(self.widget.state(['disabled']), ('!disabled', ))
  70. # no state change
  71. self.assertEqual(self.widget.state(['disabled']), ())
  72. # change back to !disable but also active
  73. self.assertEqual(self.widget.state(['!disabled', 'active']),
  74. ('!active', 'disabled'))
  75. # no state changes, again
  76. self.assertEqual(self.widget.state(['!disabled', 'active']), ())
  77. self.assertEqual(self.widget.state(['active', '!disabled']), ())
  78. def test_cb(arg1, **kw):
  79. return arg1, kw
  80. self.assertEqual(self.widget.instate(['!disabled'],
  81. test_cb, "hi", **{"msg": "there"}),
  82. ('hi', {'msg': 'there'}))
  83. # attempt to set invalid statespec
  84. currstate = self.widget.state()
  85. self.assertRaises(tkinter.TclError, self.widget.instate,
  86. ['badstate'])
  87. self.assertRaises(tkinter.TclError, self.widget.instate,
  88. ['disabled', 'badstate'])
  89. # verify that widget didn't change its state
  90. self.assertEqual(currstate, self.widget.state())
  91. # ensuring that passing None as state doesn't modify current state
  92. self.widget.state(['active', '!disabled'])
  93. self.assertEqual(self.widget.state(), ('active', ))
  94. class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
  95. _conv_pixels = noconv
  96. @add_standard_options(StandardTtkOptionsTests)
  97. class FrameTest(AbstractToplevelTest, unittest.TestCase):
  98. OPTIONS = (
  99. 'borderwidth', 'class', 'cursor', 'height',
  100. 'padding', 'relief', 'style', 'takefocus',
  101. 'width',
  102. )
  103. def create(self, **kwargs):
  104. return ttk.Frame(self.root, **kwargs)
  105. @add_standard_options(StandardTtkOptionsTests)
  106. class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
  107. OPTIONS = (
  108. 'borderwidth', 'class', 'cursor', 'height',
  109. 'labelanchor', 'labelwidget',
  110. 'padding', 'relief', 'style', 'takefocus',
  111. 'text', 'underline', 'width',
  112. )
  113. def create(self, **kwargs):
  114. return ttk.LabelFrame(self.root, **kwargs)
  115. def test_labelanchor(self):
  116. widget = self.create()
  117. self.checkEnumParam(widget, 'labelanchor',
  118. 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws',
  119. errmsg='Bad label anchor specification {}')
  120. self.checkInvalidParam(widget, 'labelanchor', 'center')
  121. def test_labelwidget(self):
  122. widget = self.create()
  123. label = ttk.Label(self.root, text='Mupp', name='foo')
  124. self.checkParam(widget, 'labelwidget', label, expected='.foo')
  125. label.destroy()
  126. class AbstractLabelTest(AbstractWidgetTest):
  127. def checkImageParam(self, widget, name):
  128. image = tkinter.PhotoImage(master=self.root, name='image1')
  129. image2 = tkinter.PhotoImage(master=self.root, name='image2')
  130. self.checkParam(widget, name, image, expected=('image1',))
  131. self.checkParam(widget, name, 'image1', expected=('image1',))
  132. self.checkParam(widget, name, (image,), expected=('image1',))
  133. self.checkParam(widget, name, (image, 'active', image2),
  134. expected=('image1', 'active', 'image2'))
  135. self.checkParam(widget, name, 'image1 active image2',
  136. expected=('image1', 'active', 'image2'))
  137. self.checkInvalidParam(widget, name, 'spam',
  138. errmsg='image "spam" doesn\'t exist')
  139. def test_compound(self):
  140. widget = self.create()
  141. self.checkEnumParam(widget, 'compound',
  142. 'none', 'text', 'image', 'center',
  143. 'top', 'bottom', 'left', 'right')
  144. def test_state(self):
  145. widget = self.create()
  146. self.checkParams(widget, 'state', 'active', 'disabled', 'normal')
  147. def test_width(self):
  148. widget = self.create()
  149. self.checkParams(widget, 'width', 402, -402, 0)
  150. @add_standard_options(StandardTtkOptionsTests)
  151. class LabelTest(AbstractLabelTest, unittest.TestCase):
  152. OPTIONS = (
  153. 'anchor', 'background', 'borderwidth',
  154. 'class', 'compound', 'cursor', 'font', 'foreground',
  155. 'image', 'justify', 'padding', 'relief', 'state', 'style',
  156. 'takefocus', 'text', 'textvariable',
  157. 'underline', 'width', 'wraplength',
  158. )
  159. _conv_pixels = noconv
  160. def create(self, **kwargs):
  161. return ttk.Label(self.root, **kwargs)
  162. def test_font(self):
  163. widget = self.create()
  164. self.checkParam(widget, 'font',
  165. '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
  166. @add_standard_options(StandardTtkOptionsTests)
  167. class ButtonTest(AbstractLabelTest, unittest.TestCase):
  168. OPTIONS = (
  169. 'class', 'command', 'compound', 'cursor', 'default',
  170. 'image', 'padding', 'state', 'style',
  171. 'takefocus', 'text', 'textvariable',
  172. 'underline', 'width',
  173. )
  174. def create(self, **kwargs):
  175. return ttk.Button(self.root, **kwargs)
  176. def test_default(self):
  177. widget = self.create()
  178. self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled')
  179. def test_invoke(self):
  180. success = []
  181. btn = ttk.Button(self.root, command=lambda: success.append(1))
  182. btn.invoke()
  183. self.assertTrue(success)
  184. @add_standard_options(StandardTtkOptionsTests)
  185. class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
  186. OPTIONS = (
  187. 'class', 'command', 'compound', 'cursor',
  188. 'image',
  189. 'offvalue', 'onvalue',
  190. 'padding', 'state', 'style',
  191. 'takefocus', 'text', 'textvariable',
  192. 'underline', 'variable', 'width',
  193. )
  194. def create(self, **kwargs):
  195. return ttk.Checkbutton(self.root, **kwargs)
  196. def test_offvalue(self):
  197. widget = self.create()
  198. self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
  199. def test_onvalue(self):
  200. widget = self.create()
  201. self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
  202. def test_invoke(self):
  203. success = []
  204. def cb_test():
  205. success.append(1)
  206. return "cb test called"
  207. cbtn = ttk.Checkbutton(self.root, command=cb_test)
  208. # the variable automatically created by ttk.Checkbutton is actually
  209. # undefined till we invoke the Checkbutton
  210. self.assertEqual(cbtn.state(), ('alternate', ))
  211. self.assertRaises(tkinter.TclError, cbtn.tk.globalgetvar,
  212. cbtn['variable'])
  213. res = cbtn.invoke()
  214. self.assertEqual(res, "cb test called")
  215. self.assertEqual(cbtn['onvalue'],
  216. cbtn.tk.globalgetvar(cbtn['variable']))
  217. self.assertTrue(success)
  218. cbtn['command'] = ''
  219. res = cbtn.invoke()
  220. self.assertFalse(str(res))
  221. self.assertLessEqual(len(success), 1)
  222. self.assertEqual(cbtn['offvalue'],
  223. cbtn.tk.globalgetvar(cbtn['variable']))
  224. @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
  225. class EntryTest(AbstractWidgetTest, unittest.TestCase):
  226. OPTIONS = (
  227. 'background', 'class', 'cursor',
  228. 'exportselection', 'font', 'foreground',
  229. 'invalidcommand', 'justify',
  230. 'show', 'state', 'style', 'takefocus', 'textvariable',
  231. 'validate', 'validatecommand', 'width', 'xscrollcommand',
  232. )
  233. def setUp(self):
  234. super().setUp()
  235. self.entry = self.create()
  236. def create(self, **kwargs):
  237. return ttk.Entry(self.root, **kwargs)
  238. def test_invalidcommand(self):
  239. widget = self.create()
  240. self.checkCommandParam(widget, 'invalidcommand')
  241. def test_show(self):
  242. widget = self.create()
  243. self.checkParam(widget, 'show', '*')
  244. self.checkParam(widget, 'show', '')
  245. self.checkParam(widget, 'show', ' ')
  246. def test_state(self):
  247. widget = self.create()
  248. self.checkParams(widget, 'state',
  249. 'disabled', 'normal', 'readonly')
  250. def test_validate(self):
  251. widget = self.create()
  252. self.checkEnumParam(widget, 'validate',
  253. 'all', 'key', 'focus', 'focusin', 'focusout', 'none')
  254. def test_validatecommand(self):
  255. widget = self.create()
  256. self.checkCommandParam(widget, 'validatecommand')
  257. def test_bbox(self):
  258. self.assertIsBoundingBox(self.entry.bbox(0))
  259. self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex')
  260. self.assertRaises(tkinter.TclError, self.entry.bbox, None)
  261. def test_identify(self):
  262. self.entry.pack()
  263. self.entry.wait_visibility()
  264. self.entry.update_idletasks()
  265. # bpo-27313: macOS Cocoa widget differs from X, allow either
  266. if sys.platform == 'darwin':
  267. self.assertIn(self.entry.identify(5, 5),
  268. ("textarea", "Combobox.button") )
  269. else:
  270. self.assertEqual(self.entry.identify(5, 5), "textarea")
  271. self.assertEqual(self.entry.identify(-1, -1), "")
  272. self.assertRaises(tkinter.TclError, self.entry.identify, None, 5)
  273. self.assertRaises(tkinter.TclError, self.entry.identify, 5, None)
  274. self.assertRaises(tkinter.TclError, self.entry.identify, 5, '')
  275. def test_validation_options(self):
  276. success = []
  277. test_invalid = lambda: success.append(True)
  278. self.entry['validate'] = 'none'
  279. self.entry['validatecommand'] = lambda: False
  280. self.entry['invalidcommand'] = test_invalid
  281. self.entry.validate()
  282. self.assertTrue(success)
  283. self.entry['invalidcommand'] = ''
  284. self.entry.validate()
  285. self.assertEqual(len(success), 1)
  286. self.entry['invalidcommand'] = test_invalid
  287. self.entry['validatecommand'] = lambda: True
  288. self.entry.validate()
  289. self.assertEqual(len(success), 1)
  290. self.entry['validatecommand'] = ''
  291. self.entry.validate()
  292. self.assertEqual(len(success), 1)
  293. self.entry['validatecommand'] = True
  294. self.assertRaises(tkinter.TclError, self.entry.validate)
  295. def test_validation(self):
  296. validation = []
  297. def validate(to_insert):
  298. if not 'a' <= to_insert.lower() <= 'z':
  299. validation.append(False)
  300. return False
  301. validation.append(True)
  302. return True
  303. self.entry['validate'] = 'key'
  304. self.entry['validatecommand'] = self.entry.register(validate), '%S'
  305. self.entry.insert('end', 1)
  306. self.entry.insert('end', 'a')
  307. self.assertEqual(validation, [False, True])
  308. self.assertEqual(self.entry.get(), 'a')
  309. def test_revalidation(self):
  310. def validate(content):
  311. for letter in content:
  312. if not 'a' <= letter.lower() <= 'z':
  313. return False
  314. return True
  315. self.entry['validatecommand'] = self.entry.register(validate), '%P'
  316. self.entry.insert('end', 'avocado')
  317. self.assertEqual(self.entry.validate(), True)
  318. self.assertEqual(self.entry.state(), ())
  319. self.entry.delete(0, 'end')
  320. self.assertEqual(self.entry.get(), '')
  321. self.entry.insert('end', 'a1b')
  322. self.assertEqual(self.entry.validate(), False)
  323. self.assertEqual(self.entry.state(), ('invalid', ))
  324. self.entry.delete(1)
  325. self.assertEqual(self.entry.validate(), True)
  326. self.assertEqual(self.entry.state(), ())
  327. @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
  328. class ComboboxTest(EntryTest, unittest.TestCase):
  329. OPTIONS = (
  330. 'background', 'class', 'cursor', 'exportselection',
  331. 'font', 'foreground', 'height', 'invalidcommand',
  332. 'justify', 'postcommand', 'show', 'state', 'style',
  333. 'takefocus', 'textvariable',
  334. 'validate', 'validatecommand', 'values',
  335. 'width', 'xscrollcommand',
  336. )
  337. def setUp(self):
  338. super().setUp()
  339. self.combo = self.create()
  340. def create(self, **kwargs):
  341. return ttk.Combobox(self.root, **kwargs)
  342. def test_height(self):
  343. widget = self.create()
  344. self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i')
  345. def _show_drop_down_listbox(self):
  346. width = self.combo.winfo_width()
  347. self.combo.event_generate('<ButtonPress-1>', x=width - 5, y=5)
  348. self.combo.event_generate('<ButtonRelease-1>', x=width - 5, y=5)
  349. self.combo.update_idletasks()
  350. def test_virtual_event(self):
  351. success = []
  352. self.combo['values'] = [1]
  353. self.combo.bind('<<ComboboxSelected>>',
  354. lambda evt: success.append(True))
  355. self.combo.pack()
  356. self.combo.wait_visibility()
  357. height = self.combo.winfo_height()
  358. self._show_drop_down_listbox()
  359. self.combo.update()
  360. self.combo.event_generate('<Return>')
  361. self.combo.update()
  362. self.assertTrue(success)
  363. def test_postcommand(self):
  364. success = []
  365. self.combo['postcommand'] = lambda: success.append(True)
  366. self.combo.pack()
  367. self.combo.wait_visibility()
  368. self._show_drop_down_listbox()
  369. self.assertTrue(success)
  370. # testing postcommand removal
  371. self.combo['postcommand'] = ''
  372. self._show_drop_down_listbox()
  373. self.assertEqual(len(success), 1)
  374. def test_values(self):
  375. def check_get_current(getval, currval):
  376. self.assertEqual(self.combo.get(), getval)
  377. self.assertEqual(self.combo.current(), currval)
  378. self.assertEqual(self.combo['values'],
  379. () if tcl_version < (8, 5) else '')
  380. check_get_current('', -1)
  381. self.checkParam(self.combo, 'values', 'mon tue wed thur',
  382. expected=('mon', 'tue', 'wed', 'thur'))
  383. self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur'))
  384. self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string'))
  385. self.checkParam(self.combo, 'values', '')
  386. self.combo['values'] = ['a', 1, 'c']
  387. self.combo.set('c')
  388. check_get_current('c', 2)
  389. self.combo.current(0)
  390. check_get_current('a', 0)
  391. self.combo.set('d')
  392. check_get_current('d', -1)
  393. # testing values with empty string
  394. self.combo.set('')
  395. self.combo['values'] = (1, 2, '', 3)
  396. check_get_current('', 2)
  397. # testing values with empty string set through configure
  398. self.combo.configure(values=[1, '', 2])
  399. self.assertEqual(self.combo['values'],
  400. ('1', '', '2') if self.wantobjects else
  401. '1 {} 2')
  402. # testing values with spaces
  403. self.combo['values'] = ['a b', 'a\tb', 'a\nb']
  404. self.assertEqual(self.combo['values'],
  405. ('a b', 'a\tb', 'a\nb') if self.wantobjects else
  406. '{a b} {a\tb} {a\nb}')
  407. # testing values with special characters
  408. self.combo['values'] = [r'a\tb', '"a"', '} {']
  409. self.assertEqual(self.combo['values'],
  410. (r'a\tb', '"a"', '} {') if self.wantobjects else
  411. r'a\\tb {"a"} \}\ \{')
  412. # out of range
  413. self.assertRaises(tkinter.TclError, self.combo.current,
  414. len(self.combo['values']))
  415. # it expects an integer (or something that can be converted to int)
  416. self.assertRaises(tkinter.TclError, self.combo.current, '')
  417. # testing creating combobox with empty string in values
  418. combo2 = ttk.Combobox(self.root, values=[1, 2, ''])
  419. self.assertEqual(combo2['values'],
  420. ('1', '2', '') if self.wantobjects else '1 2 {}')
  421. combo2.destroy()
  422. @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
  423. class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
  424. OPTIONS = (
  425. 'class', 'cursor', 'height',
  426. 'orient', 'style', 'takefocus', 'width',
  427. )
  428. def setUp(self):
  429. super().setUp()
  430. self.paned = self.create()
  431. def create(self, **kwargs):
  432. return ttk.PanedWindow(self.root, **kwargs)
  433. def test_orient(self):
  434. widget = self.create()
  435. self.assertEqual(str(widget['orient']), 'vertical')
  436. errmsg='attempt to change read-only option'
  437. if get_tk_patchlevel() < (8, 6, 0, 'beta', 3):
  438. errmsg='Attempt to change read-only option'
  439. self.checkInvalidParam(widget, 'orient', 'horizontal',
  440. errmsg=errmsg)
  441. widget2 = self.create(orient='horizontal')
  442. self.assertEqual(str(widget2['orient']), 'horizontal')
  443. def test_add(self):
  444. # attempt to add a child that is not a direct child of the paned window
  445. label = ttk.Label(self.paned)
  446. child = ttk.Label(label)
  447. self.assertRaises(tkinter.TclError, self.paned.add, child)
  448. label.destroy()
  449. child.destroy()
  450. # another attempt
  451. label = ttk.Label(self.root)
  452. child = ttk.Label(label)
  453. self.assertRaises(tkinter.TclError, self.paned.add, child)
  454. child.destroy()
  455. label.destroy()
  456. good_child = ttk.Label(self.root)
  457. self.paned.add(good_child)
  458. # re-adding a child is not accepted
  459. self.assertRaises(tkinter.TclError, self.paned.add, good_child)
  460. other_child = ttk.Label(self.paned)
  461. self.paned.add(other_child)
  462. self.assertEqual(self.paned.pane(0), self.paned.pane(1))
  463. self.assertRaises(tkinter.TclError, self.paned.pane, 2)
  464. good_child.destroy()
  465. other_child.destroy()
  466. self.assertRaises(tkinter.TclError, self.paned.pane, 0)
  467. def test_forget(self):
  468. self.assertRaises(tkinter.TclError, self.paned.forget, None)
  469. self.assertRaises(tkinter.TclError, self.paned.forget, 0)
  470. self.paned.add(ttk.Label(self.root))
  471. self.paned.forget(0)
  472. self.assertRaises(tkinter.TclError, self.paned.forget, 0)
  473. def test_insert(self):
  474. self.assertRaises(tkinter.TclError, self.paned.insert, None, 0)
  475. self.assertRaises(tkinter.TclError, self.paned.insert, 0, None)
  476. self.assertRaises(tkinter.TclError, self.paned.insert, 0, 0)
  477. child = ttk.Label(self.root)
  478. child2 = ttk.Label(self.root)
  479. child3 = ttk.Label(self.root)
  480. self.assertRaises(tkinter.TclError, self.paned.insert, 0, child)
  481. self.paned.insert('end', child2)
  482. self.paned.insert(0, child)
  483. self.assertEqual(self.paned.panes(), (str(child), str(child2)))
  484. self.paned.insert(0, child2)
  485. self.assertEqual(self.paned.panes(), (str(child2), str(child)))
  486. self.paned.insert('end', child3)
  487. self.assertEqual(self.paned.panes(),
  488. (str(child2), str(child), str(child3)))
  489. # reinserting a child should move it to its current position
  490. panes = self.paned.panes()
  491. self.paned.insert('end', child3)
  492. self.assertEqual(panes, self.paned.panes())
  493. # moving child3 to child2 position should result in child2 ending up
  494. # in previous child position and child ending up in previous child3
  495. # position
  496. self.paned.insert(child2, child3)
  497. self.assertEqual(self.paned.panes(),
  498. (str(child3), str(child2), str(child)))
  499. def test_pane(self):
  500. self.assertRaises(tkinter.TclError, self.paned.pane, 0)
  501. child = ttk.Label(self.root)
  502. self.paned.add(child)
  503. self.assertIsInstance(self.paned.pane(0), dict)
  504. self.assertEqual(self.paned.pane(0, weight=None),
  505. 0 if self.wantobjects else '0')
  506. # newer form for querying a single option
  507. self.assertEqual(self.paned.pane(0, 'weight'),
  508. 0 if self.wantobjects else '0')
  509. self.assertEqual(self.paned.pane(0), self.paned.pane(str(child)))
  510. self.assertRaises(tkinter.TclError, self.paned.pane, 0,
  511. badoption='somevalue')
  512. def test_sashpos(self):
  513. self.assertRaises(tkinter.TclError, self.paned.sashpos, None)
  514. self.assertRaises(tkinter.TclError, self.paned.sashpos, '')
  515. self.assertRaises(tkinter.TclError, self.paned.sashpos, 0)
  516. child = ttk.Label(self.paned, text='a')
  517. self.paned.add(child, weight=1)
  518. self.assertRaises(tkinter.TclError, self.paned.sashpos, 0)
  519. child2 = ttk.Label(self.paned, text='b')
  520. self.paned.add(child2)
  521. self.assertRaises(tkinter.TclError, self.paned.sashpos, 1)
  522. self.paned.pack(expand=True, fill='both')
  523. self.paned.wait_visibility()
  524. curr_pos = self.paned.sashpos(0)
  525. self.paned.sashpos(0, 1000)
  526. self.assertNotEqual(curr_pos, self.paned.sashpos(0))
  527. self.assertIsInstance(self.paned.sashpos(0), int)
  528. @add_standard_options(StandardTtkOptionsTests)
  529. class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
  530. OPTIONS = (
  531. 'class', 'command', 'compound', 'cursor',
  532. 'image',
  533. 'padding', 'state', 'style',
  534. 'takefocus', 'text', 'textvariable',
  535. 'underline', 'value', 'variable', 'width',
  536. )
  537. def create(self, **kwargs):
  538. return ttk.Radiobutton(self.root, **kwargs)
  539. def test_value(self):
  540. widget = self.create()
  541. self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
  542. def test_invoke(self):
  543. success = []
  544. def cb_test():
  545. success.append(1)
  546. return "cb test called"
  547. myvar = tkinter.IntVar(self.root)
  548. cbtn = ttk.Radiobutton(self.root, command=cb_test,
  549. variable=myvar, value=0)
  550. cbtn2 = ttk.Radiobutton(self.root, command=cb_test,
  551. variable=myvar, value=1)
  552. if self.wantobjects:
  553. conv = lambda x: x
  554. else:
  555. conv = int
  556. res = cbtn.invoke()
  557. self.assertEqual(res, "cb test called")
  558. self.assertEqual(conv(cbtn['value']), myvar.get())
  559. self.assertEqual(myvar.get(),
  560. conv(cbtn.tk.globalgetvar(cbtn['variable'])))
  561. self.assertTrue(success)
  562. cbtn2['command'] = ''
  563. res = cbtn2.invoke()
  564. self.assertEqual(str(res), '')
  565. self.assertLessEqual(len(success), 1)
  566. self.assertEqual(conv(cbtn2['value']), myvar.get())
  567. self.assertEqual(myvar.get(),
  568. conv(cbtn.tk.globalgetvar(cbtn['variable'])))
  569. self.assertEqual(str(cbtn['variable']), str(cbtn2['variable']))
  570. class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
  571. OPTIONS = (
  572. 'class', 'compound', 'cursor', 'direction',
  573. 'image', 'menu', 'padding', 'state', 'style',
  574. 'takefocus', 'text', 'textvariable',
  575. 'underline', 'width',
  576. )
  577. def create(self, **kwargs):
  578. return ttk.Menubutton(self.root, **kwargs)
  579. def test_direction(self):
  580. widget = self.create()
  581. self.checkEnumParam(widget, 'direction',
  582. 'above', 'below', 'left', 'right', 'flush')
  583. def test_menu(self):
  584. widget = self.create()
  585. menu = tkinter.Menu(widget, name='menu')
  586. self.checkParam(widget, 'menu', menu, conv=str)
  587. menu.destroy()
  588. @add_standard_options(StandardTtkOptionsTests)
  589. class ScaleTest(AbstractWidgetTest, unittest.TestCase):
  590. OPTIONS = (
  591. 'class', 'command', 'cursor', 'from', 'length',
  592. 'orient', 'style', 'takefocus', 'to', 'value', 'variable',
  593. )
  594. _conv_pixels = noconv
  595. default_orient = 'horizontal'
  596. def setUp(self):
  597. super().setUp()
  598. self.scale = self.create()
  599. self.scale.pack()
  600. self.scale.update()
  601. def create(self, **kwargs):
  602. return ttk.Scale(self.root, **kwargs)
  603. def test_from(self):
  604. widget = self.create()
  605. self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False)
  606. def test_length(self):
  607. widget = self.create()
  608. self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
  609. def test_to(self):
  610. widget = self.create()
  611. self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False)
  612. def test_value(self):
  613. widget = self.create()
  614. self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False)
  615. def test_custom_event(self):
  616. failure = [1, 1, 1] # will need to be empty
  617. funcid = self.scale.bind('<<RangeChanged>>', lambda evt: failure.pop())
  618. self.scale['from'] = 10
  619. self.scale['from_'] = 10
  620. self.scale['to'] = 3
  621. self.assertFalse(failure)
  622. failure = [1, 1, 1]
  623. self.scale.configure(from_=2, to=5)
  624. self.scale.configure(from_=0, to=-2)
  625. self.scale.configure(to=10)
  626. self.assertFalse(failure)
  627. def test_get(self):
  628. if self.wantobjects:
  629. conv = lambda x: x
  630. else:
  631. conv = float
  632. scale_width = self.scale.winfo_width()
  633. self.assertEqual(self.scale.get(scale_width, 0), self.scale['to'])
  634. self.assertEqual(conv(self.scale.get(0, 0)), conv(self.scale['from']))
  635. self.assertEqual(self.scale.get(), self.scale['value'])
  636. self.scale['value'] = 30
  637. self.assertEqual(self.scale.get(), self.scale['value'])
  638. self.assertRaises(tkinter.TclError, self.scale.get, '', 0)
  639. self.assertRaises(tkinter.TclError, self.scale.get, 0, '')
  640. def test_set(self):
  641. if self.wantobjects:
  642. conv = lambda x: x
  643. else:
  644. conv = float
  645. # set restricts the max/min values according to the current range
  646. max = conv(self.scale['to'])
  647. new_max = max + 10
  648. self.scale.set(new_max)
  649. self.assertEqual(conv(self.scale.get()), max)
  650. min = conv(self.scale['from'])
  651. self.scale.set(min - 1)
  652. self.assertEqual(conv(self.scale.get()), min)
  653. # changing directly the variable doesn't impose this limitation tho
  654. var = tkinter.DoubleVar(self.root)
  655. self.scale['variable'] = var
  656. var.set(max + 5)
  657. self.assertEqual(conv(self.scale.get()), var.get())
  658. self.assertEqual(conv(self.scale.get()), max + 5)
  659. del var
  660. # the same happens with the value option
  661. self.scale['value'] = max + 10
  662. self.assertEqual(conv(self.scale.get()), max + 10)
  663. self.assertEqual(conv(self.scale.get()), conv(self.scale['value']))
  664. # nevertheless, note that the max/min values we can get specifying
  665. # x, y coords are the ones according to the current range
  666. self.assertEqual(conv(self.scale.get(0, 0)), min)
  667. self.assertEqual(conv(self.scale.get(self.scale.winfo_width(), 0)), max)
  668. self.assertRaises(tkinter.TclError, self.scale.set, None)
  669. @add_standard_options(StandardTtkOptionsTests)
  670. class ProgressbarTest(AbstractWidgetTest, unittest.TestCase):
  671. OPTIONS = (
  672. 'class', 'cursor', 'orient', 'length',
  673. 'mode', 'maximum', 'phase',
  674. 'style', 'takefocus', 'value', 'variable',
  675. )
  676. _conv_pixels = noconv
  677. default_orient = 'horizontal'
  678. def create(self, **kwargs):
  679. return ttk.Progressbar(self.root, **kwargs)
  680. def test_length(self):
  681. widget = self.create()
  682. self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i')
  683. def test_maximum(self):
  684. widget = self.create()
  685. self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False)
  686. def test_mode(self):
  687. widget = self.create()
  688. self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate')
  689. def test_phase(self):
  690. # XXX
  691. pass
  692. def test_value(self):
  693. widget = self.create()
  694. self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10,
  695. conv=False)
  696. @unittest.skipIf(sys.platform == 'darwin',
  697. 'ttk.Scrollbar is special on MacOSX')
  698. @add_standard_options(StandardTtkOptionsTests)
  699. class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
  700. OPTIONS = (
  701. 'class', 'command', 'cursor', 'orient', 'style', 'takefocus',
  702. )
  703. default_orient = 'vertical'
  704. def create(self, **kwargs):
  705. return ttk.Scrollbar(self.root, **kwargs)
  706. @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
  707. class NotebookTest(AbstractWidgetTest, unittest.TestCase):
  708. OPTIONS = (
  709. 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width',
  710. )
  711. def setUp(self):
  712. super().setUp()
  713. self.nb = self.create(padding=0)
  714. self.child1 = ttk.Label(self.root)
  715. self.child2 = ttk.Label(self.root)
  716. self.nb.add(self.child1, text='a')
  717. self.nb.add(self.child2, text='b')
  718. def create(self, **kwargs):
  719. return ttk.Notebook(self.root, **kwargs)
  720. def test_tab_identifiers(self):
  721. self.nb.forget(0)
  722. self.nb.hide(self.child2)
  723. self.assertRaises(tkinter.TclError, self.nb.tab, self.child1)
  724. self.assertEqual(self.nb.index('end'), 1)
  725. self.nb.add(self.child2)
  726. self.assertEqual(self.nb.index('end'), 1)
  727. self.nb.select(self.child2)
  728. self.assertTrue(self.nb.tab('current'))
  729. self.nb.add(self.child1, text='a')
  730. self.nb.pack()
  731. self.nb.wait_visibility()
  732. if sys.platform == 'darwin':
  733. tb_idx = "@20,5"
  734. else:
  735. tb_idx = "@5,5"
  736. self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current'))
  737. for i in range(5, 100, 5):
  738. try:
  739. if self.nb.tab('@%d, 5' % i, text=None) == 'a':
  740. break
  741. except tkinter.TclError:
  742. pass
  743. else:
  744. self.fail("Tab with text 'a' not found")
  745. def test_add_and_hidden(self):
  746. self.assertRaises(tkinter.TclError, self.nb.hide, -1)
  747. self.assertRaises(tkinter.TclError, self.nb.hide, 'hi')
  748. self.assertRaises(tkinter.TclError, self.nb.hide, None)
  749. self.assertRaises(tkinter.TclError, self.nb.add, None)
  750. self.assertRaises(tkinter.TclError, self.nb.add, ttk.Label(self.root),
  751. unknown='option')
  752. tabs = self.nb.tabs()
  753. self.nb.hide(self.child1)
  754. self.nb.add(self.child1)
  755. self.assertEqual(self.nb.tabs(), tabs)
  756. child = ttk.Label(self.root)
  757. self.nb.add(child, text='c')
  758. tabs = self.nb.tabs()
  759. curr = self.nb.index('current')
  760. # verify that the tab gets readded at its previous position
  761. child2_index = self.nb.index(self.child2)
  762. self.nb.hide(self.child2)
  763. self.nb.add(self.child2)
  764. self.assertEqual(self.nb.tabs(), tabs)
  765. self.assertEqual(self.nb.index(self.child2), child2_index)
  766. self.assertEqual(str(self.child2), self.nb.tabs()[child2_index])
  767. # but the tab next to it (not hidden) is the one selected now
  768. self.assertEqual(self.nb.index('current'), curr + 1)
  769. def test_forget(self):
  770. self.assertRaises(tkinter.TclError, self.nb.forget, -1)
  771. self.assertRaises(tkinter.TclError, self.nb.forget, 'hi')
  772. self.assertRaises(tkinter.TclError, self.nb.forget, None)
  773. tabs = self.nb.tabs()
  774. child1_index = self.nb.index(self.child1)
  775. self.nb.forget(self.child1)
  776. self.assertNotIn(str(self.child1), self.nb.tabs())
  777. self.assertEqual(len(tabs) - 1, len(self.nb.tabs()))
  778. self.nb.add(self.child1)
  779. self.assertEqual(self.nb.index(self.child1), 1)
  780. self.assertNotEqual(child1_index, self.nb.index(self.child1))
  781. def test_index(self):
  782. self.assertRaises(tkinter.TclError, self.nb.index, -1)
  783. self.assertRaises(tkinter.TclError, self.nb.index, None)
  784. self.assertIsInstance(self.nb.index('end'), int)
  785. self.assertEqual(self.nb.index(self.child1), 0)
  786. self.assertEqual(self.nb.index(self.child2), 1)
  787. self.assertEqual(self.nb.index('end'), 2)
  788. def test_insert(self):
  789. # moving tabs
  790. tabs = self.nb.tabs()
  791. self.nb.insert(1, tabs[0])
  792. self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0]))
  793. self.nb.insert(self.child1, self.child2)
  794. self.assertEqual(self.nb.tabs(), tabs)
  795. self.nb.insert('end', self.child1)
  796. self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0]))
  797. self.nb.insert('end', 0)
  798. self.assertEqual(self.nb.tabs(), tabs)
  799. # bad moves
  800. self.assertRaises(tkinter.TclError, self.nb.insert, 2, tabs[0])
  801. self.assertRaises(tkinter.TclError, self.nb.insert, -1, tabs[0])
  802. # new tab
  803. child3 = ttk.Label(self.root)
  804. self.nb.insert(1, child3)
  805. self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1]))
  806. self.nb.forget(child3)
  807. self.assertEqual(self.nb.tabs(), tabs)
  808. self.nb.insert(self.child1, child3)
  809. self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs)
  810. self.nb.forget(child3)
  811. self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3)
  812. self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3)
  813. # bad inserts
  814. self.assertRaises(tkinter.TclError, self.nb.insert, 'end', None)
  815. self.assertRaises(tkinter.TclError, self.nb.insert, None, 0)
  816. self.assertRaises(tkinter.TclError, self.nb.insert, None, None)
  817. def test_select(self):
  818. self.nb.pack()
  819. self.nb.wait_visibility()
  820. success = []
  821. tab_changed = []
  822. self.child1.bind('<Unmap>', lambda evt: success.append(True))
  823. self.nb.bind('<<NotebookTabChanged>>',
  824. lambda evt: tab_changed.append(True))
  825. self.assertEqual(self.nb.select(), str(self.child1))
  826. self.nb.select(self.child2)
  827. self.assertTrue(success)
  828. self.assertEqual(self.nb.select(), str(self.child2))
  829. self.nb.update()
  830. self.assertTrue(tab_changed)
  831. def test_tab(self):
  832. self.assertRaises(tkinter.TclError, self.nb.tab, -1)
  833. self.assertRaises(tkinter.TclError, self.nb.tab, 'notab')
  834. self.assertRaises(tkinter.TclError, self.nb.tab, None)
  835. self.assertIsInstance(self.nb.tab(self.child1), dict)
  836. self.assertEqual(self.nb.tab(self.child1, text=None), 'a')
  837. # newer form for querying a single option
  838. self.assertEqual(self.nb.tab(self.child1, 'text'), 'a')
  839. self.nb.tab(self.child1, text='abc')
  840. self.assertEqual(self.nb.tab(self.child1, text=None), 'abc')
  841. self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc')
  842. def test_tabs(self):
  843. self.assertEqual(len(self.nb.tabs()), 2)
  844. self.nb.forget(self.child1)
  845. self.nb.forget(self.child2)
  846. self.assertEqual(self.nb.tabs(), ())
  847. def test_traversal(self):
  848. self.nb.pack()
  849. self.nb.wait_visibility()
  850. self.nb.select(0)
  851. simulate_mouse_click(self.nb, 5, 5)
  852. self.nb.focus_force()
  853. self.nb.event_generate('<Control-Tab>')
  854. self.assertEqual(self.nb.select(), str(self.child2))
  855. self.nb.focus_force()
  856. self.nb.event_generate('<Shift-Control-Tab>')
  857. self.assertEqual(self.nb.select(), str(self.child1))
  858. self.nb.focus_force()
  859. self.nb.event_generate('<Shift-Control-Tab>')
  860. self.assertEqual(self.nb.select(), str(self.child2))
  861. self.nb.tab(self.child1, text='a', underline=0)
  862. self.nb.enable_traversal()
  863. self.nb.focus_force()
  864. simulate_mouse_click(self.nb, 5, 5)
  865. if sys.platform == 'darwin':
  866. self.nb.event_generate('<Option-a>')
  867. else:
  868. self.nb.event_generate('<Alt-a>')
  869. self.assertEqual(self.nb.select(), str(self.child1))
  870. @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
  871. class SpinboxTest(EntryTest, unittest.TestCase):
  872. OPTIONS = (
  873. 'background', 'class', 'command', 'cursor', 'exportselection',
  874. 'font', 'foreground', 'format', 'from', 'increment',
  875. 'invalidcommand', 'justify', 'show', 'state', 'style',
  876. 'takefocus', 'textvariable', 'to', 'validate', 'validatecommand',
  877. 'values', 'width', 'wrap', 'xscrollcommand',
  878. )
  879. def setUp(self):
  880. super().setUp()
  881. self.spin = self.create()
  882. self.spin.pack()
  883. def create(self, **kwargs):
  884. return ttk.Spinbox(self.root, **kwargs)
  885. def _click_increment_arrow(self):
  886. width = self.spin.winfo_width()
  887. height = self.spin.winfo_height()
  888. x = width - 5
  889. y = height//2 - 5
  890. self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
  891. self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
  892. self.spin.update_idletasks()
  893. def _click_decrement_arrow(self):
  894. width = self.spin.winfo_width()
  895. height = self.spin.winfo_height()
  896. x = width - 5
  897. y = height//2 + 4
  898. self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
  899. self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
  900. self.spin.update_idletasks()
  901. def test_command(self):
  902. success = []
  903. self.spin['command'] = lambda: success.append(True)
  904. self.spin.update()
  905. self._click_increment_arrow()
  906. self.spin.update()
  907. self.assertTrue(success)
  908. self._click_decrement_arrow()
  909. self.assertEqual(len(success), 2)
  910. # testing postcommand removal
  911. self.spin['command'] = ''
  912. self.spin.update_idletasks()
  913. self._click_increment_arrow()
  914. self._click_decrement_arrow()
  915. self.spin.update()
  916. self.assertEqual(len(success), 2)
  917. def test_to(self):
  918. self.spin['from'] = 0
  919. self.spin['to'] = 5
  920. self.spin.set(4)
  921. self.spin.update()
  922. self._click_increment_arrow() # 5
  923. self.assertEqual(self.spin.get(), '5')
  924. self._click_increment_arrow() # 5
  925. self.assertEqual(self.spin.get(), '5')
  926. def test_from(self):
  927. self.spin['from'] = 1
  928. self.spin['to'] = 10
  929. self.spin.set(2)
  930. self.spin.update()
  931. self._click_decrement_arrow() # 1
  932. self.assertEqual(self.spin.get(), '1')
  933. self._click_decrement_arrow() # 1
  934. self.assertEqual(self.spin.get(), '1')
  935. def test_increment(self):
  936. self.spin['from'] = 0
  937. self.spin['to'] = 10
  938. self.spin['increment'] = 4
  939. self.spin.set(1)
  940. self.spin.update()
  941. self._click_increment_arrow() # 5
  942. self.assertEqual(self.spin.get(), '5')
  943. self.spin['increment'] = 2
  944. self.spin.update()
  945. self._click_decrement_arrow() # 3
  946. self.assertEqual(self.spin.get(), '3')
  947. def test_format(self):
  948. self.spin.set(1)
  949. self.spin['format'] = '%10.3f'
  950. self.spin.update()
  951. self._click_increment_arrow()
  952. value = self.spin.get()
  953. self.assertEqual(len(value), 10)
  954. self.assertEqual(value.index('.'), 6)
  955. self.spin['format'] = ''
  956. self.spin.update()
  957. self._click_increment_arrow()
  958. value = self.spin.get()
  959. self.assertTrue('.' not in value)
  960. self.assertEqual(len(value), 1)
  961. def test_wrap(self):
  962. self.spin['to'] = 10
  963. self.spin['from'] = 1
  964. self.spin.set(1)
  965. self.spin['wrap'] = True
  966. self.spin.update()
  967. self._click_decrement_arrow()
  968. self.assertEqual(self.spin.get(), '10')
  969. self._click_increment_arrow()
  970. self.assertEqual(self.spin.get(), '1')
  971. self.spin['wrap'] = False
  972. self.spin.update()
  973. self._click_decrement_arrow()
  974. self.assertEqual(self.spin.get(), '1')
  975. def test_values(self):
  976. self.assertEqual(self.spin['values'],
  977. () if tcl_version < (8, 5) else '')
  978. self.checkParam(self.spin, 'values', 'mon tue wed thur',
  979. expected=('mon', 'tue', 'wed', 'thur'))
  980. self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur'))
  981. self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string'))
  982. self.checkParam(self.spin, 'values', '')
  983. self.spin['values'] = ['a', 1, 'c']
  984. # test incrementing / decrementing values
  985. self.spin.set('a')
  986. self.spin.update()
  987. self._click_increment_arrow()
  988. self.assertEqual(self.spin.get(), '1')
  989. self._click_decrement_arrow()
  990. self.assertEqual(self.spin.get(), 'a')
  991. # testing values with empty string set through configure
  992. self.spin.configure(values=[1, '', 2])
  993. self.assertEqual(self.spin['values'],
  994. ('1', '', '2') if self.wantobjects else
  995. '1 {} 2')
  996. # testing values with spaces
  997. self.spin['values'] = ['a b', 'a\tb', 'a\nb']
  998. self.assertEqual(self.spin['values'],
  999. ('a b', 'a\tb', 'a\nb') if self.wantobjects else
  1000. '{a b} {a\tb} {a\nb}')
  1001. # testing values with special characters
  1002. self.spin['values'] = [r'a\tb', '"a"', '} {']
  1003. self.assertEqual(self.spin['values'],
  1004. (r'a\tb', '"a"', '} {') if self.wantobjects else
  1005. r'a\\tb {"a"} \}\ \{')
  1006. # testing creating spinbox with empty string in values
  1007. spin2 = ttk.Spinbox(self.root, values=[1, 2, ''])
  1008. self.assertEqual(spin2['values'],
  1009. ('1', '2', '') if self.wantobjects else '1 2 {}')
  1010. spin2.destroy()
  1011. @add_standard_options(StandardTtkOptionsTests)
  1012. class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
  1013. OPTIONS = (
  1014. 'class', 'columns', 'cursor', 'displaycolumns',
  1015. 'height', 'padding', 'selectmode', 'show',
  1016. 'style', 'takefocus', 'xscrollcommand', 'yscrollcommand',
  1017. )
  1018. def setUp(self):
  1019. super().setUp()
  1020. self.tv = self.create(padding=0)
  1021. def create(self, **kwargs):
  1022. return ttk.Treeview(self.root, **kwargs)
  1023. def test_columns(self):
  1024. widget = self.create()
  1025. self.checkParam(widget, 'columns', 'a b c',
  1026. expected=('a', 'b', 'c'))
  1027. self.checkParam(widget, 'columns', ('a', 'b', 'c'))
  1028. self.checkParam(widget, 'columns', '')
  1029. def test_displaycolumns(self):
  1030. widget = self.create()
  1031. widget['columns'] = ('a', 'b', 'c')
  1032. self.checkParam(widget, 'displaycolumns', 'b a c',
  1033. expected=('b', 'a', 'c'))
  1034. self.checkParam(widget, 'displaycolumns', ('b', 'a', 'c'))
  1035. self.checkParam(widget, 'displaycolumns', '#all',
  1036. expected=('#all',))
  1037. self.checkParam(widget, 'displaycolumns', (2, 1, 0))
  1038. self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'),
  1039. errmsg='Invalid column index d')
  1040. self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3),
  1041. errmsg='Column index 3 out of bounds')
  1042. self.checkInvalidParam(widget, 'displaycolumns', (1, -2),
  1043. errmsg='Column index -2 out of bounds')
  1044. def test_height(self):
  1045. widget = self.create()
  1046. self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False)
  1047. self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv)
  1048. def test_selectmode(self):
  1049. widget = self.create()
  1050. self.checkEnumParam(widget, 'selectmode',
  1051. 'none', 'browse', 'extended')
  1052. def test_show(self):
  1053. widget = self.create()
  1054. self.checkParam(widget, 'show', 'tree headings',
  1055. expected=('tree', 'headings'))
  1056. self.checkParam(widget, 'show', ('tree', 'headings'))
  1057. self.checkParam(widget, 'show', ('headings', 'tree'))
  1058. self.checkParam(widget, 'show', 'tree', expected=('tree',))
  1059. self.checkParam(widget, 'show', 'headings', expected=('headings',))
  1060. def test_bbox(self):
  1061. self.tv.pack()
  1062. self.assertEqual(self.tv.bbox(''), '')
  1063. self.tv.wait_visibility()
  1064. self.tv.update()
  1065. item_id = self.tv.insert('', 'end')
  1066. children = self.tv.get_children()
  1067. self.assertTrue(children)
  1068. bbox = self.tv.bbox(children[0])
  1069. self.assertIsBoundingBox(bbox)
  1070. # compare width in bboxes
  1071. self.tv['columns'] = ['test']
  1072. self.tv.column('test', width=50)
  1073. bbox_column0 = self.tv.bbox(children[0], 0)
  1074. root_width = self.tv.column('#0', width=None)
  1075. if not self.wantobjects:
  1076. root_width = int(root_width)
  1077. self.assertEqual(bbox_column0[0], bbox[0] + root_width)
  1078. # verify that bbox of a closed item is the empty string
  1079. child1 = self.tv.insert(item_id, 'end')
  1080. self.assertEqual(self.tv.bbox(child1), '')
  1081. def test_children(self):
  1082. # no children yet, should get an empty tuple
  1083. self.assertEqual(self.tv.get_children(), ())
  1084. item_id = self.tv.insert('', 'end')
  1085. self.assertIsInstance(self.tv.get_children(), tuple)
  1086. self.assertEqual(self.tv.get_children()[0], item_id)
  1087. # add item_id and child3 as children of child2
  1088. child2 = self.tv.insert('', 'end')
  1089. child3 = self.tv.insert('', 'end')
  1090. self.tv.set_children(child2, item_id, child3)
  1091. self.assertEqual(self.tv.get_children(child2), (item_id, child3))
  1092. # child3 has child2 as parent, thus trying to set child2 as a children
  1093. # of child3 should result in an error
  1094. self.assertRaises(tkinter.TclError,
  1095. self.tv.set_children, child3, child2)
  1096. # remove child2 children
  1097. self.tv.set_children(child2)
  1098. self.assertEqual(self.tv.get_children(child2), ())
  1099. # remove root's children
  1100. self.tv.set_children('')
  1101. self.assertEqual(self.tv.get_children(), ())
  1102. def test_column(self):
  1103. # return a dict with all options/values
  1104. self.assertIsInstance(self.tv.column('#0'), dict)
  1105. # return a single value of the given option
  1106. if self.wantobjects:
  1107. self.assertIsInstance(self.tv.column('#0', width=None), int)
  1108. # set a new value for an option
  1109. self.tv.column('#0', width=10)
  1110. # testing new way to get option value
  1111. self.assertEqual(self.tv.column('#0', 'width'),
  1112. 10 if self.wantobjects else '10')
  1113. self.assertEqual(self.tv.column('#0', width=None),
  1114. 10 if self.wantobjects else '10')
  1115. # check read-only option
  1116. self.assertRaises(tkinter.TclError, self.tv.column, '#0', id='X')
  1117. self.assertRaises(tkinter.TclError, self.tv.column, 'invalid')
  1118. invalid_kws = [
  1119. {'unknown_option': 'some value'}, {'stretch': 'wrong'},
  1120. {'anchor': 'wrong'}, {'width': 'wrong'}, {'minwidth': 'wrong'}
  1121. ]
  1122. for kw in invalid_kws:
  1123. self.assertRaises(tkinter.TclError, self.tv.column, '#0',
  1124. **kw)
  1125. def test_delete(self):
  1126. self.assertRaises(tkinter.TclError, self.tv.delete, '#0')
  1127. item_id = self.tv.insert('', 'end')
  1128. item2 = self.tv.insert(item_id, 'end')
  1129. self.assertEqual(self.tv.get_children(), (item_id, ))
  1130. self.assertEqual(self.tv.get_children(item_id), (item2, ))
  1131. self.tv.delete(item_id)
  1132. self.assertFalse(self.tv.get_children())
  1133. # reattach should fail
  1134. self.assertRaises(tkinter.TclError,
  1135. self.tv.reattach, item_id, '', 'end')
  1136. # test multiple item delete
  1137. item1 = self.tv.insert('', 'end')
  1138. item2 = self.tv.insert('', 'end')
  1139. self.assertEqual(self.tv.get_children(), (item1, item2))
  1140. self.tv.delete(item1, item2)
  1141. self.assertFalse(self.tv.get_children())
  1142. def test_detach_reattach(self):
  1143. item_id = self.tv.insert('', 'end')
  1144. item2 = self.tv.insert(item_id, 'end')
  1145. # calling detach without items is valid, although it does nothing
  1146. prev = self.tv.get_children()
  1147. self.tv.detach() # this should do nothing
  1148. self.assertEqual(prev, self.tv.get_children())
  1149. self.assertEqual(self.tv.get_children(), (item_id, ))
  1150. self.assertEqual(self.tv.get_children(item_id), (item2, ))
  1151. # detach item with children
  1152. self.tv.detach(item_id)
  1153. self.assertFalse(self.tv.get_children())
  1154. # reattach item with children
  1155. self.tv.reattach(item_id, '', 'end')
  1156. self.assertEqual(self.tv.get_children(), (item_id, ))
  1157. self.assertEqual(self.tv.get_children(item_id), (item2, ))
  1158. # move a children to the root
  1159. self.tv.move(item2, '', 'end')
  1160. self.assertEqual(self.tv.get_children(), (item_id, item2))
  1161. self.assertEqual(self.tv.get_children(item_id), ())
  1162. # bad values
  1163. self.assertRaises(tkinter.TclError,
  1164. self.tv.reattach, 'nonexistent', '', 'end')
  1165. self.assertRaises(tkinter.TclError,
  1166. self.tv.detach, 'nonexistent')
  1167. self.assertRaises(tkinter.TclError,
  1168. self.tv.reattach, item2, 'otherparent', 'end')
  1169. self.assertRaises(tkinter.TclError,
  1170. self.tv.reattach, item2, '', 'invalid')
  1171. # multiple detach
  1172. self.tv.detach(item_id, item2)
  1173. self.assertEqual(self.tv.get_children(), ())
  1174. self.assertEqual(self.tv.get_children(item_id), ())
  1175. def test_exists(self):
  1176. self.assertEqual(self.tv.exists('something'), False)
  1177. self.assertEqual(self.tv.exists(''), True)
  1178. self.assertEqual(self.tv.exists({}), False)
  1179. # the following will make a tk.call equivalent to
  1180. # tk.call(treeview, "exists") which should result in an error
  1181. # in the tcl interpreter since tk requires an item.
  1182. self.assertRaises(tkinter.TclError, self.tv.exists, None)
  1183. def test_focus(self):
  1184. # nothing is focused right now
  1185. self.assertEqual(self.tv.focus(), '')
  1186. item1 = self.tv.insert('', 'end')
  1187. self.tv.focus(item1)
  1188. self.assertEqual(self.tv.focus(), item1)
  1189. self.tv.delete(item1)
  1190. self.assertEqual(self.tv.focus(), '')
  1191. # try focusing inexistent item
  1192. self.assertRaises(tkinter.TclError, self.tv.focus, 'hi')
  1193. def test_heading(self):
  1194. # check a dict is returned
  1195. self.assertIsInstance(self.tv.heading('#0'), dict)
  1196. # check a value is returned
  1197. self.tv.heading('#0', text='hi')
  1198. self.assertEqual(self.tv.heading('#0', 'text'), 'hi')
  1199. self.assertEqual(self.tv.heading('#0', text=None), 'hi')
  1200. # invalid option
  1201. self.assertRaises(tkinter.TclError, self.tv.heading, '#0',
  1202. background=None)
  1203. # invalid value
  1204. self.assertRaises(tkinter.TclError, self.tv.heading, '#0',
  1205. anchor=1)
  1206. def test_heading_callback(self):
  1207. def simulate_heading_click(x, y):
  1208. simulate_mouse_click(self.tv, x, y)
  1209. self.tv.update()
  1210. success = [] # no success for now
  1211. self.tv.pack()
  1212. self.tv.wait_visibility()
  1213. self.tv.heading('#0', command=lambda: success.append(True))
  1214. self.tv.column('#0', width=100)
  1215. self.tv.update()
  1216. # assuming that the coords (5, 5) fall into heading #0
  1217. simulate_heading_click(5, 5)
  1218. if not success:
  1219. self.fail("The command associated to the treeview heading wasn't "
  1220. "invoked.")
  1221. success = []
  1222. commands = self.tv.master._tclCommands
  1223. self.tv.heading('#0', command=str(self.tv.heading('#0', command=None)))
  1224. self.assertEqual(commands, self.tv.master._tclCommands)
  1225. simulate_heading_click(5, 5)
  1226. if not success:
  1227. self.fail("The command associated to the treeview heading wasn't "
  1228. "invoked.")
  1229. # XXX The following raises an error in a tcl interpreter, but not in
  1230. # Python
  1231. #self.tv.heading('#0', command='I dont exist')
  1232. #simulate_heading_click(5, 5)
  1233. def test_index(self):
  1234. # item 'what' doesn't exist
  1235. self.assertRaises(tkinter.TclError, self.tv.index, 'what')
  1236. self.assertEqual(self.tv.index(''), 0)
  1237. item1 = self.tv.insert('', 'end')
  1238. item2 = self.tv.insert('', 'end')
  1239. c1 = self.tv.insert(item1, 'end')
  1240. c2 = self.tv.insert(item1, 'end')
  1241. self.assertEqual(self.tv.index(item1), 0)
  1242. self.assertEqual(self.tv.index(c1), 0)
  1243. self.assertEqual(self.tv.index(c2), 1)
  1244. self.assertEqual(self.tv.index(item2), 1)
  1245. self.tv.move(item2, '', 0)
  1246. self.assertEqual(self.tv.index(item2), 0)
  1247. self.assertEqual(self.tv.index(item1), 1)
  1248. # check that index still works even after its parent and siblings
  1249. # have been detached
  1250. self.tv.detach(item1)
  1251. self.assertEqual(self.tv.index(c2), 1)
  1252. self.tv.detach(c1)
  1253. self.assertEqual(self.tv.index(c2), 0)
  1254. # but it fails after item has been deleted
  1255. self.tv.delete(item1)
  1256. self.assertRaises(tkinter.TclError, self.tv.index, c2)
  1257. def test_insert_item(self):
  1258. # parent 'none' doesn't exist
  1259. self.assertRaises(tkinter.TclError, self.tv.insert, 'none', 'end')
  1260. # open values
  1261. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
  1262. open='')
  1263. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
  1264. open='please')
  1265. self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=True)))
  1266. self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=False)))
  1267. # invalid index
  1268. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'middle')
  1269. # trying to duplicate item id is invalid
  1270. itemid = self.tv.insert('', 'end', 'first-item')
  1271. self.assertEqual(itemid, 'first-item')
  1272. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
  1273. 'first-item')
  1274. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
  1275. MockTclObj('first-item'))
  1276. # unicode values
  1277. value = '\xe1ba'
  1278. item = self.tv.insert('', 'end', values=(value, ))
  1279. self.assertEqual(self.tv.item(item, 'values'),
  1280. (value,) if self.wantobjects else value)
  1281. self.assertEqual(self.tv.item(item, values=None),
  1282. (value,) if self.wantobjects else value)
  1283. self.tv.item(item, values=self.root.splitlist(self.tv.item(item, values=None)))
  1284. self.assertEqual(self.tv.item(item, values=None),
  1285. (value,) if self.wantobjects else value)
  1286. self.assertIsInstance(self.tv.item(item), dict)
  1287. # erase item values
  1288. self.tv.item(item, values='')
  1289. self.assertFalse(self.tv.item(item, values=None))
  1290. # item tags
  1291. item = self.tv.insert('', 'end', tags=[1, 2, value])
  1292. self.assertEqual(self.tv.item(item, tags=None),
  1293. ('1', '2', value) if self.wantobjects else
  1294. '1 2 %s' % value)
  1295. self.tv.item(item, tags=[])
  1296. self.assertFalse(self.tv.item(item, tags=None))
  1297. self.tv.item(item, tags=(1, 2))
  1298. self.assertEqual(self.tv.item(item, tags=None),
  1299. ('1', '2') if self.wantobjects else '1 2')
  1300. # values with spaces
  1301. item = self.tv.insert('', 'end', values=('a b c',
  1302. '%s %s' % (value, value)))
  1303. self.assertEqual(self.tv.item(item, values=None),
  1304. ('a b c', '%s %s' % (value, value)) if self.wantobjects else
  1305. '{a b c} {%s %s}' % (value, value))
  1306. # text
  1307. self.assertEqual(self.tv.item(
  1308. self.tv.insert('', 'end', text="Label here"), text=None),
  1309. "Label here")
  1310. self.assertEqual(self.tv.item(
  1311. self.tv.insert('', 'end', text=value), text=None),
  1312. value)
  1313. # test for values which are not None
  1314. itemid = self.tv.insert('', 'end', 0)
  1315. self.assertEqual(itemid, '0')
  1316. itemid = self.tv.insert('', 'end', 0.0)
  1317. self.assertEqual(itemid, '0.0')
  1318. # this is because False resolves to 0 and element with 0 iid is already present
  1319. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', False)
  1320. self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', '')
  1321. def test_selection(self):
  1322. self.assertRaises(TypeError, self.tv.selection, 'spam')
  1323. # item 'none' doesn't exist
  1324. self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none')
  1325. self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none')
  1326. self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none')
  1327. self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none')
  1328. item1 = self.tv.insert('', 'end')
  1329. item2 = self.tv.insert('', 'end')
  1330. c1 = self.tv.insert(item1, 'end')
  1331. c2 = self.tv.insert(item1, 'end')
  1332. c3 = self.tv.insert(item1, 'end')
  1333. self.assertEqual(self.tv.selection(), ())
  1334. self.tv.selection_set(c1, item2)
  1335. self.assertEqual(self.tv.selection(), (c1, item2))
  1336. self.tv.selection_set(c2)
  1337. self.assertEqual(self.tv.selection(), (c2,))
  1338. self.tv.selection_add(c1, item2)
  1339. self.assertEqual(self.tv.selection(), (c1, c2, item2))
  1340. self.tv.selection_add(item1)
  1341. self.assertEqual(self.tv.selection(), (item1, c1, c2, item2))
  1342. self.tv.selection_add()
  1343. self.assertEqual(self.tv.selection(), (item1, c1, c2, item2))
  1344. self.tv.selection_remove(item1, c3)
  1345. self.assertEqual(self.tv.selection(), (c1, c2, item2))
  1346. self.tv.selection_remove(c2)
  1347. self.assertEqual(self.tv.selection(), (c1, item2))
  1348. self.tv.selection_remove()
  1349. self.assertEqual(self.tv.selection(), (c1, item2))
  1350. self.tv.selection_toggle(c1, c3)
  1351. self.assertEqual(self.tv.selection(), (c3, item2))
  1352. self.tv.selection_toggle(item2)
  1353. self.assertEqual(self.tv.selection(), (c3,))
  1354. self.tv.selection_toggle()
  1355. self.assertEqual(self.tv.selection(), (c3,))
  1356. self.tv.insert('', 'end', id='with spaces')
  1357. self.tv.selection_set('with spaces')
  1358. self.assertEqual(self.tv.selection(), ('with spaces',))
  1359. self.tv.insert('', 'end', id='{brace')
  1360. self.tv.selection_set('{brace')
  1361. self.assertEqual(self.tv.selection(), ('{brace',))
  1362. self.tv.insert('', 'end', id='unicode\u20ac')
  1363. self.tv.selection_set('unicode\u20ac')
  1364. self.assertEqual(self.tv.selection(), ('unicode\u20ac',))
  1365. self.tv.insert('', 'end', id=b'bytes\xe2\x82\xac')
  1366. self.tv.selection_set(b'bytes\xe2\x82\xac')
  1367. self.assertEqual(self.tv.selection(), ('bytes\xe2\x82\xac',))
  1368. self.tv.selection_set()
  1369. self.assertEqual(self.tv.selection(), ())
  1370. # Old interface
  1371. self.tv.selection_set((c1, item2))
  1372. self.assertEqual(self.tv.selection(), (c1, item2))
  1373. self.tv.selection_add((c1, item1))
  1374. self.assertEqual(self.tv.selection(), (item1, c1, item2))
  1375. self.tv.selection_remove((item1, c3))
  1376. self.assertEqual(self.tv.selection(), (c1, item2))
  1377. self.tv.selection_toggle((c1, c3))
  1378. self.assertEqual(self.tv.selection(), (c3, item2))
  1379. if sys.version_info >= (3, 8):
  1380. import warnings
  1381. warnings.warn(
  1382. 'Deprecated API of Treeview.selection() should be removed')
  1383. self.tv.selection_set()
  1384. self.assertEqual(self.tv.selection(), ())
  1385. with self.assertWarns(DeprecationWarning):
  1386. self.tv.selection('set', (c1, item2))
  1387. self.assertEqual(self.tv.selection(), (c1, item2))
  1388. with self.assertWarns(DeprecationWarning):
  1389. self.tv.selection('add', (c1, item1))
  1390. self.assertEqual(self.tv.selection(), (item1, c1, item2))
  1391. with self.assertWarns(DeprecationWarning):
  1392. self.tv.selection('remove', (item1, c3))
  1393. self.assertEqual(self.tv.selection(), (c1, item2))
  1394. with self.assertWarns(DeprecationWarning):
  1395. self.tv.selection('toggle', (c1, c3))
  1396. self.assertEqual(self.tv.selection(), (c3, item2))
  1397. with self.assertWarns(DeprecationWarning):
  1398. selection = self.tv.selection(None)
  1399. self.assertEqual(selection, (c3, item2))
  1400. def test_set(self):
  1401. self.tv['columns'] = ['A', 'B']
  1402. item = self.tv.insert('', 'end', values=['a', 'b'])
  1403. self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'})
  1404. self.tv.set(item, 'B', 'a')
  1405. self.assertEqual(self.tv.item(item, values=None),
  1406. ('a', 'a') if self.wantobjects else 'a a')
  1407. self.tv['columns'] = ['B']
  1408. self.assertEqual(self.tv.set(item), {'B': 'a'})
  1409. self.tv.set(item, 'B', 'b')
  1410. self.assertEqual(self.tv.set(item, column='B'), 'b')
  1411. self.assertEqual(self.tv.item(item, values=None),
  1412. ('b', 'a') if self.wantobjects else 'b a')
  1413. self.tv.set(item, 'B', 123)
  1414. self.assertEqual(self.tv.set(item, 'B'),
  1415. 123 if self.wantobjects else '123')
  1416. self.assertEqual(self.tv.item(item, values=None),
  1417. (123, 'a') if self.wantobjects else '123 a')
  1418. self.assertEqual(self.tv.set(item),
  1419. {'B': 123} if self.wantobjects else {'B': '123'})
  1420. # inexistent column
  1421. self.assertRaises(tkinter.TclError, self.tv.set, item, 'A')
  1422. self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b')
  1423. # inexistent item
  1424. self.assertRaises(tkinter.TclError, self.tv.set, 'notme')
  1425. def test_tag_bind(self):
  1426. events = []
  1427. item1 = self.tv.insert('', 'end', tags=['call'])
  1428. item2 = self.tv.insert('', 'end', tags=['call'])
  1429. self.tv.tag_bind('call', '<ButtonPress-1>',
  1430. lambda evt: events.append(1))
  1431. self.tv.tag_bind('call', '<ButtonRelease-1>',
  1432. lambda evt: events.append(2))
  1433. self.tv.pack()
  1434. self.tv.wait_visibility()
  1435. self.tv.update()
  1436. pos_y = set()
  1437. found = set()
  1438. for i in range(0, 100, 10):
  1439. if len(found) == 2: # item1 and item2 already found
  1440. break
  1441. item_id = self.tv.identify_row(i)
  1442. if item_id and item_id not in found:
  1443. pos_y.add(i)
  1444. found.add(item_id)
  1445. self.assertEqual(len(pos_y), 2) # item1 and item2 y pos
  1446. for y in pos_y:
  1447. simulate_mouse_click(self.tv, 0, y)
  1448. # by now there should be 4 things in the events list, since each
  1449. # item had a bind for two events that were simulated above
  1450. self.assertEqual(len(events), 4)
  1451. for evt in zip(events[::2], events[1::2]):
  1452. self.assertEqual(evt, (1, 2))
  1453. def test_tag_configure(self):
  1454. # Just testing parameter passing for now
  1455. self.assertRaises(TypeError, self.tv.tag_configure)
  1456. self.assertRaises(tkinter.TclError, self.tv.tag_configure,
  1457. 'test', sky='blue')
  1458. self.tv.tag_configure('test', foreground='blue')
  1459. self.assertEqual(str(self.tv.tag_configure('test', 'foreground')),
  1460. 'blue')
  1461. self.assertEqual(str(self.tv.tag_configure('test', foreground=None)),
  1462. 'blue')
  1463. self.assertIsInstance(self.tv.tag_configure('test'), dict)
  1464. def test_tag_has(self):
  1465. item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1'])
  1466. item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2'])
  1467. self.assertRaises(TypeError, self.tv.tag_has)
  1468. self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing')
  1469. self.assertTrue(self.tv.tag_has('tag1', item1))
  1470. self.assertFalse(self.tv.tag_has('tag1', item2))
  1471. self.assertFalse(self.tv.tag_has('tag2', item1))
  1472. self.assertTrue(self.tv.tag_has('tag2', item2))
  1473. self.assertFalse(self.tv.tag_has('tag3', item1))
  1474. self.assertFalse(self.tv.tag_has('tag3', item2))
  1475. self.assertEqual(self.tv.tag_has('tag1'), (item1,))
  1476. self.assertEqual(self.tv.tag_has('tag2'), (item2,))
  1477. self.assertEqual(self.tv.tag_has('tag3'), ())
  1478. @add_standard_options(StandardTtkOptionsTests)
  1479. class SeparatorTest(AbstractWidgetTest, unittest.TestCase):
  1480. OPTIONS = (
  1481. 'class', 'cursor', 'orient', 'style', 'takefocus',
  1482. # 'state'?
  1483. )
  1484. default_orient = 'horizontal'
  1485. def create(self, **kwargs):
  1486. return ttk.Separator(self.root, **kwargs)
  1487. @add_standard_options(StandardTtkOptionsTests)
  1488. class SizegripTest(AbstractWidgetTest, unittest.TestCase):
  1489. OPTIONS = (
  1490. 'class', 'cursor', 'style', 'takefocus',
  1491. # 'state'?
  1492. )
  1493. def create(self, **kwargs):
  1494. return ttk.Sizegrip(self.root, **kwargs)
  1495. tests_gui = (
  1496. ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest,
  1497. FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
  1498. NotebookTest, PanedWindowTest, ProgressbarTest,
  1499. RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
  1500. SizegripTest, SpinboxTest, TreeviewTest, WidgetTest,
  1501. )
  1502. if __name__ == "__main__":
  1503. unittest.main()