(root)/
Python-3.12.0/
Lib/
test/
test_ttk/
test_extensions.py
       1  import sys
       2  import unittest
       3  import tkinter
       4  from tkinter import ttk
       5  from test.support import requires, gc_collect
       6  from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
       7  
       8  requires('gui')
       9  
      10  class ESC[4;38;5;81mLabeledScaleTest(ESC[4;38;5;149mAbstractTkTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      11  
      12      def tearDown(self):
      13          self.root.update_idletasks()
      14          super().tearDown()
      15  
      16      def test_widget_destroy(self):
      17          # automatically created variable
      18          x = ttk.LabeledScale(self.root)
      19          var = x._variable._name
      20          x.destroy()
      21          gc_collect()  # For PyPy or other GCs.
      22          self.assertRaises(tkinter.TclError, x.tk.globalgetvar, var)
      23  
      24          # manually created variable
      25          myvar = tkinter.DoubleVar(self.root)
      26          name = myvar._name
      27          x = ttk.LabeledScale(self.root, variable=myvar)
      28          x.destroy()
      29          if self.wantobjects:
      30              self.assertEqual(x.tk.globalgetvar(name), myvar.get())
      31          else:
      32              self.assertEqual(float(x.tk.globalgetvar(name)), myvar.get())
      33          del myvar
      34          gc_collect()  # For PyPy or other GCs.
      35          self.assertRaises(tkinter.TclError, x.tk.globalgetvar, name)
      36  
      37          # checking that the tracing callback is properly removed
      38          myvar = tkinter.IntVar(self.root)
      39          # LabeledScale will start tracing myvar
      40          x = ttk.LabeledScale(self.root, variable=myvar)
      41          x.destroy()
      42          # Unless the tracing callback was removed, creating a new
      43          # LabeledScale with the same var will cause an error now. This
      44          # happens because the variable will be set to (possibly) a new
      45          # value which causes the tracing callback to be called and then
      46          # it tries calling instance attributes not yet defined.
      47          ttk.LabeledScale(self.root, variable=myvar)
      48          if hasattr(sys, 'last_exc'):
      49              self.assertNotEqual(type(sys.last_exc), tkinter.TclError)
      50          elif hasattr(sys, 'last_type'):
      51              self.assertNotEqual(sys.last_type, tkinter.TclError)
      52  
      53      def test_initialization(self):
      54          # master passing
      55          master = tkinter.Frame(self.root)
      56          x = ttk.LabeledScale(master)
      57          self.assertEqual(x.master, master)
      58          x.destroy()
      59  
      60          # variable initialization/passing
      61          passed_expected = (('0', 0), (0, 0), (10, 10),
      62              (-1, -1), (sys.maxsize + 1, sys.maxsize + 1),
      63              (2.5, 2), ('2.5', 2))
      64          for pair in passed_expected:
      65              x = ttk.LabeledScale(self.root, from_=pair[0])
      66              self.assertEqual(x.value, pair[1])
      67              x.destroy()
      68          x = ttk.LabeledScale(self.root, from_=None)
      69          self.assertRaises((ValueError, tkinter.TclError), x._variable.get)
      70          x.destroy()
      71          # variable should have its default value set to the from_ value
      72          myvar = tkinter.DoubleVar(self.root, value=20)
      73          x = ttk.LabeledScale(self.root, variable=myvar)
      74          self.assertEqual(x.value, 0)
      75          x.destroy()
      76          # check that it is really using a DoubleVar
      77          x = ttk.LabeledScale(self.root, variable=myvar, from_=0.5)
      78          self.assertEqual(x.value, 0.5)
      79          self.assertEqual(x._variable._name, myvar._name)
      80          x.destroy()
      81  
      82          # widget positionment
      83          def check_positions(scale, scale_pos, label, label_pos):
      84              self.assertEqual(scale.pack_info()['side'], scale_pos)
      85              self.assertEqual(label.place_info()['anchor'], label_pos)
      86          x = ttk.LabeledScale(self.root, compound='top')
      87          check_positions(x.scale, 'bottom', x.label, 'n')
      88          x.destroy()
      89          x = ttk.LabeledScale(self.root, compound='bottom')
      90          check_positions(x.scale, 'top', x.label, 's')
      91          x.destroy()
      92          # invert default positions
      93          x = ttk.LabeledScale(self.root, compound='unknown')
      94          check_positions(x.scale, 'top', x.label, 's')
      95          x.destroy()
      96          x = ttk.LabeledScale(self.root) # take default positions
      97          check_positions(x.scale, 'bottom', x.label, 'n')
      98          x.destroy()
      99  
     100          # extra, and invalid, kwargs
     101          self.assertRaises(tkinter.TclError, ttk.LabeledScale, master, a='b')
     102  
     103  
     104      def test_horizontal_range(self):
     105          lscale = ttk.LabeledScale(self.root, from_=0, to=10)
     106          lscale.pack()
     107          lscale.update()
     108  
     109          linfo_1 = lscale.label.place_info()
     110          prev_xcoord = lscale.scale.coords()[0]
     111          self.assertEqual(prev_xcoord, int(linfo_1['x']))
     112          # change range to: from -5 to 5. This should change the x coord of
     113          # the scale widget, since 0 is at the middle of the new
     114          # range.
     115          lscale.scale.configure(from_=-5, to=5)
     116          # The following update is needed since the test doesn't use mainloop,
     117          # at the same time this shouldn't affect test outcome
     118          lscale.update()
     119          curr_xcoord = lscale.scale.coords()[0]
     120          self.assertNotEqual(prev_xcoord, curr_xcoord)
     121          # the label widget should have been repositioned too
     122          linfo_2 = lscale.label.place_info()
     123          self.assertEqual(lscale.label['text'], 0 if self.wantobjects else '0')
     124          self.assertEqual(curr_xcoord, int(linfo_2['x']))
     125          # change the range back
     126          lscale.scale.configure(from_=0, to=10)
     127          self.assertNotEqual(prev_xcoord, curr_xcoord)
     128          self.assertEqual(prev_xcoord, int(linfo_1['x']))
     129  
     130          lscale.destroy()
     131  
     132  
     133      def test_variable_change(self):
     134          x = ttk.LabeledScale(self.root)
     135          x.pack()
     136          x.update()
     137  
     138          curr_xcoord = x.scale.coords()[0]
     139          newval = x.value + 1
     140          x.value = newval
     141          # The following update is needed since the test doesn't use mainloop,
     142          # at the same time this shouldn't affect test outcome
     143          x.update()
     144          self.assertEqual(x.value, newval)
     145          self.assertEqual(x.label['text'],
     146                           newval if self.wantobjects else str(newval))
     147          self.assertEqual(float(x.scale.get()), newval)
     148          self.assertGreater(x.scale.coords()[0], curr_xcoord)
     149          self.assertEqual(x.scale.coords()[0],
     150              int(x.label.place_info()['x']))
     151  
     152          # value outside range
     153          if self.wantobjects:
     154              conv = lambda x: x
     155          else:
     156              conv = int
     157          x.value = conv(x.scale['to']) + 1 # no changes shouldn't happen
     158          x.update()
     159          self.assertEqual(x.value, newval)
     160          self.assertEqual(conv(x.label['text']), newval)
     161          self.assertEqual(float(x.scale.get()), newval)
     162          self.assertEqual(x.scale.coords()[0],
     163              int(x.label.place_info()['x']))
     164  
     165          # non-integer value
     166          x.value = newval = newval + 1.5
     167          x.update()
     168          self.assertEqual(x.value, int(newval))
     169          self.assertEqual(conv(x.label['text']), int(newval))
     170          self.assertEqual(float(x.scale.get()), newval)
     171  
     172          x.destroy()
     173  
     174  
     175      def test_resize(self):
     176          x = ttk.LabeledScale(self.root)
     177          x.pack(expand=True, fill='both')
     178          gc_collect()  # For PyPy or other GCs.
     179          x.update()
     180  
     181          width, height = x.master.winfo_width(), x.master.winfo_height()
     182          width_new, height_new = width * 2, height * 2
     183  
     184          x.value = 3
     185          x.update()
     186          x.master.wm_geometry("%dx%d" % (width_new, height_new))
     187          self.assertEqual(int(x.label.place_info()['x']),
     188              x.scale.coords()[0])
     189  
     190          # Reset geometry
     191          x.master.wm_geometry("%dx%d" % (width, height))
     192          x.destroy()
     193  
     194  
     195  class ESC[4;38;5;81mOptionMenuTest(ESC[4;38;5;149mAbstractTkTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     196  
     197      def setUp(self):
     198          super().setUp()
     199          self.textvar = tkinter.StringVar(self.root)
     200  
     201      def tearDown(self):
     202          del self.textvar
     203          super().tearDown()
     204  
     205  
     206      def test_widget_destroy(self):
     207          var = tkinter.StringVar(self.root)
     208          optmenu = ttk.OptionMenu(self.root, var)
     209          name = var._name
     210          optmenu.update_idletasks()
     211          optmenu.destroy()
     212          self.assertEqual(optmenu.tk.globalgetvar(name), var.get())
     213          del var
     214          gc_collect()  # For PyPy or other GCs.
     215          self.assertRaises(tkinter.TclError, optmenu.tk.globalgetvar, name)
     216  
     217  
     218      def test_initialization(self):
     219          self.assertRaises(tkinter.TclError,
     220              ttk.OptionMenu, self.root, self.textvar, invalid='thing')
     221  
     222          optmenu = ttk.OptionMenu(self.root, self.textvar, 'b', 'a', 'b')
     223          self.assertEqual(optmenu._variable.get(), 'b')
     224  
     225          self.assertTrue(optmenu['menu'])
     226          self.assertTrue(optmenu['textvariable'])
     227  
     228          optmenu.destroy()
     229  
     230  
     231      def test_menu(self):
     232          items = ('a', 'b', 'c')
     233          default = 'a'
     234          optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
     235          found_default = False
     236          for i in range(len(items)):
     237              value = optmenu['menu'].entrycget(i, 'value')
     238              self.assertEqual(value, items[i])
     239              if value == default:
     240                  found_default = True
     241          self.assertTrue(found_default)
     242          optmenu.destroy()
     243  
     244          # default shouldn't be in menu if it is not part of values
     245          default = 'd'
     246          optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
     247          curr = None
     248          i = 0
     249          while True:
     250              last, curr = curr, optmenu['menu'].entryconfigure(i, 'value')
     251              if last == curr:
     252                  # no more menu entries
     253                  break
     254              self.assertNotEqual(curr, default)
     255              i += 1
     256          self.assertEqual(i, len(items))
     257  
     258          # check that variable is updated correctly
     259          optmenu.pack()
     260          gc_collect()  # For PyPy or other GCs.
     261          optmenu['menu'].invoke(0)
     262          self.assertEqual(optmenu._variable.get(), items[0])
     263  
     264          # changing to an invalid index shouldn't change the variable
     265          self.assertRaises(tkinter.TclError, optmenu['menu'].invoke, -1)
     266          self.assertEqual(optmenu._variable.get(), items[0])
     267  
     268          optmenu.destroy()
     269  
     270          # specifying a callback
     271          success = []
     272          def cb_test(item):
     273              self.assertEqual(item, items[1])
     274              success.append(True)
     275          optmenu = ttk.OptionMenu(self.root, self.textvar, 'a', command=cb_test,
     276              *items)
     277          optmenu['menu'].invoke(1)
     278          if not success:
     279              self.fail("Menu callback not invoked")
     280  
     281          optmenu.destroy()
     282  
     283      def test_unique_radiobuttons(self):
     284          # check that radiobuttons are unique across instances (bpo25684)
     285          items = ('a', 'b', 'c')
     286          default = 'a'
     287          optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
     288          textvar2 = tkinter.StringVar(self.root)
     289          optmenu2 = ttk.OptionMenu(self.root, textvar2, default, *items)
     290          optmenu.pack()
     291          optmenu2.pack()
     292          optmenu['menu'].invoke(1)
     293          optmenu2['menu'].invoke(2)
     294          optmenu_stringvar_name = optmenu['menu'].entrycget(0, 'variable')
     295          optmenu2_stringvar_name = optmenu2['menu'].entrycget(0, 'variable')
     296          self.assertNotEqual(optmenu_stringvar_name,
     297                              optmenu2_stringvar_name)
     298          self.assertEqual(self.root.tk.globalgetvar(optmenu_stringvar_name),
     299                           items[1])
     300          self.assertEqual(self.root.tk.globalgetvar(optmenu2_stringvar_name),
     301                           items[2])
     302  
     303          optmenu.destroy()
     304          optmenu2.destroy()
     305  
     306      def test_trace_variable(self):
     307          # prior to bpo45160, tracing a variable would cause the callback to be made twice
     308          success = []
     309          items = ('a', 'b', 'c')
     310          textvar = tkinter.StringVar(self.root)
     311          def cb_test(*args):
     312              success.append(textvar.get())
     313          optmenu = ttk.OptionMenu(self.root, textvar, "a", *items)
     314          optmenu.pack()
     315          cb_name = textvar.trace_add("write", cb_test)
     316          optmenu['menu'].invoke(1)
     317          self.assertEqual(success, ['b'])
     318          self.assertEqual(textvar.get(), 'b')
     319          textvar.trace_remove("write", cb_name)
     320          optmenu.destroy()
     321  
     322  
     323  class ESC[4;38;5;81mDefaultRootTest(ESC[4;38;5;149mAbstractDefaultRootTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     324  
     325      def test_labeledscale(self):
     326          self._test_widget(ttk.LabeledScale)
     327  
     328  
     329  if __name__ == "__main__":
     330      unittest.main()