python (3.11.7)

(root)/
lib/
python3.11/
tkinter/
test/
widget_tests.py
       1  # Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py
       2  
       3  import unittest
       4  import tkinter
       5  from tkinter.test.support import (AbstractTkTest, tk_version,
       6                                    pixels_conv, tcl_obj_eq)
       7  import test.support
       8  
       9  
      10  _sentinel = object()
      11  
      12  class ESC[4;38;5;81mAbstractWidgetTest(ESC[4;38;5;149mAbstractTkTest):
      13      _conv_pixels = round
      14      _conv_pad_pixels = None
      15      _stringify = False
      16  
      17      @property
      18      def scaling(self):
      19          try:
      20              return self._scaling
      21          except AttributeError:
      22              self._scaling = float(self.root.call('tk', 'scaling'))
      23              return self._scaling
      24  
      25      def _str(self, value):
      26          if not self._stringify and self.wantobjects and tk_version >= (8, 6):
      27              return value
      28          if isinstance(value, tuple):
      29              return ' '.join(map(self._str, value))
      30          return str(value)
      31  
      32      def assertEqual2(self, actual, expected, msg=None, eq=object.__eq__):
      33          if eq(actual, expected):
      34              return
      35          self.assertEqual(actual, expected, msg)
      36  
      37      def checkParam(self, widget, name, value, *, expected=_sentinel,
      38                     conv=False, eq=None):
      39          widget[name] = value
      40          if expected is _sentinel:
      41              expected = value
      42          if conv:
      43              expected = conv(expected)
      44          if self._stringify or not self.wantobjects:
      45              if isinstance(expected, tuple):
      46                  expected = tkinter._join(expected)
      47              else:
      48                  expected = str(expected)
      49          if eq is None:
      50              eq = tcl_obj_eq
      51          self.assertEqual2(widget[name], expected, eq=eq)
      52          self.assertEqual2(widget.cget(name), expected, eq=eq)
      53          t = widget.configure(name)
      54          self.assertEqual(len(t), 5)
      55          self.assertEqual2(t[4], expected, eq=eq)
      56  
      57      def checkInvalidParam(self, widget, name, value, errmsg=None):
      58          orig = widget[name]
      59          if errmsg is not None:
      60              errmsg = errmsg.format(value)
      61          with self.assertRaises(tkinter.TclError) as cm:
      62              widget[name] = value
      63          if errmsg is not None:
      64              self.assertEqual(str(cm.exception), errmsg)
      65          self.assertEqual(widget[name], orig)
      66          with self.assertRaises(tkinter.TclError) as cm:
      67              widget.configure({name: value})
      68          if errmsg is not None:
      69              self.assertEqual(str(cm.exception), errmsg)
      70          self.assertEqual(widget[name], orig)
      71  
      72      def checkParams(self, widget, name, *values, **kwargs):
      73          for value in values:
      74              self.checkParam(widget, name, value, **kwargs)
      75  
      76      def checkIntegerParam(self, widget, name, *values, **kwargs):
      77          self.checkParams(widget, name, *values, **kwargs)
      78          self.checkInvalidParam(widget, name, '',
      79                  errmsg='expected integer but got ""')
      80          self.checkInvalidParam(widget, name, '10p',
      81                  errmsg='expected integer but got "10p"')
      82          self.checkInvalidParam(widget, name, 3.2,
      83                  errmsg='expected integer but got "3.2"')
      84  
      85      def checkFloatParam(self, widget, name, *values, conv=float, **kwargs):
      86          for value in values:
      87              self.checkParam(widget, name, value, conv=conv, **kwargs)
      88          self.checkInvalidParam(widget, name, '',
      89                  errmsg='expected floating-point number but got ""')
      90          self.checkInvalidParam(widget, name, 'spam',
      91                  errmsg='expected floating-point number but got "spam"')
      92  
      93      def checkBooleanParam(self, widget, name):
      94          for value in (False, 0, 'false', 'no', 'off'):
      95              self.checkParam(widget, name, value, expected=0)
      96          for value in (True, 1, 'true', 'yes', 'on'):
      97              self.checkParam(widget, name, value, expected=1)
      98          self.checkInvalidParam(widget, name, '',
      99                  errmsg='expected boolean value but got ""')
     100          self.checkInvalidParam(widget, name, 'spam',
     101                  errmsg='expected boolean value but got "spam"')
     102  
     103      def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs):
     104          self.checkParams(widget, name,
     105                           '#ff0000', '#00ff00', '#0000ff', '#123456',
     106                           'red', 'green', 'blue', 'white', 'black', 'grey',
     107                           **kwargs)
     108          self.checkInvalidParam(widget, name, 'spam',
     109                  errmsg='unknown color name "spam"')
     110  
     111      def checkCursorParam(self, widget, name, **kwargs):
     112          self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs)
     113          self.checkParam(widget, name, 'none')
     114          self.checkInvalidParam(widget, name, 'spam',
     115                  errmsg='bad cursor spec "spam"')
     116  
     117      def checkCommandParam(self, widget, name):
     118          def command(*args):
     119              pass
     120          widget[name] = command
     121          self.assertTrue(widget[name])
     122          self.checkParams(widget, name, '')
     123  
     124      def checkEnumParam(self, widget, name, *values, errmsg=None, **kwargs):
     125          self.checkParams(widget, name, *values, **kwargs)
     126          if errmsg is None:
     127              errmsg2 = ' %s "{}": must be %s%s or %s' % (
     128                      name,
     129                      ', '.join(values[:-1]),
     130                      ',' if len(values) > 2 else '',
     131                      values[-1])
     132              self.checkInvalidParam(widget, name, '',
     133                                     errmsg='ambiguous' + errmsg2)
     134              errmsg = 'bad' + errmsg2
     135          self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg)
     136  
     137      def checkPixelsParam(self, widget, name, *values,
     138                           conv=None, **kwargs):
     139          if conv is None:
     140              conv = self._conv_pixels
     141          for value in values:
     142              expected = _sentinel
     143              conv1 = conv
     144              if isinstance(value, str):
     145                  if conv1 and conv1 is not str:
     146                      expected = pixels_conv(value) * self.scaling
     147                      conv1 = round
     148              self.checkParam(widget, name, value, expected=expected,
     149                              conv=conv1, **kwargs)
     150          self.checkInvalidParam(widget, name, '6x',
     151                  errmsg='bad screen distance "6x"')
     152          self.checkInvalidParam(widget, name, 'spam',
     153                  errmsg='bad screen distance "spam"')
     154  
     155      def checkReliefParam(self, widget, name):
     156          self.checkParams(widget, name,
     157                           'flat', 'groove', 'raised', 'ridge', 'solid', 'sunken')
     158          errmsg='bad relief "spam": must be '\
     159                 'flat, groove, raised, ridge, solid, or sunken'
     160          if tk_version < (8, 6):
     161              errmsg = None
     162          self.checkInvalidParam(widget, name, 'spam',
     163                  errmsg=errmsg)
     164  
     165      def checkImageParam(self, widget, name):
     166          image = tkinter.PhotoImage(master=self.root, name='image1')
     167          self.checkParam(widget, name, image, conv=str)
     168          self.checkInvalidParam(widget, name, 'spam',
     169                  errmsg='image "spam" doesn\'t exist')
     170          widget[name] = ''
     171  
     172      def checkVariableParam(self, widget, name, var):
     173          self.checkParam(widget, name, var, conv=str)
     174  
     175      def assertIsBoundingBox(self, bbox):
     176          self.assertIsNotNone(bbox)
     177          self.assertIsInstance(bbox, tuple)
     178          if len(bbox) != 4:
     179              self.fail('Invalid bounding box: %r' % (bbox,))
     180          for item in bbox:
     181              if not isinstance(item, int):
     182                  self.fail('Invalid bounding box: %r' % (bbox,))
     183                  break
     184  
     185  
     186      def test_keys(self):
     187          widget = self.create()
     188          keys = widget.keys()
     189          self.assertEqual(sorted(keys), sorted(widget.configure()))
     190          for k in keys:
     191              widget[k]
     192          # Test if OPTIONS contains all keys
     193          if test.support.verbose:
     194              aliases = {
     195                  'bd': 'borderwidth',
     196                  'bg': 'background',
     197                  'fg': 'foreground',
     198                  'invcmd': 'invalidcommand',
     199                  'vcmd': 'validatecommand',
     200              }
     201              keys = set(keys)
     202              expected = set(self.OPTIONS)
     203              for k in sorted(keys - expected):
     204                  if not (k in aliases and
     205                          aliases[k] in keys and
     206                          aliases[k] in expected):
     207                      print('%s.OPTIONS doesn\'t contain "%s"' %
     208                            (self.__class__.__name__, k))
     209  
     210  
     211  class ESC[4;38;5;81mStandardOptionsTests:
     212      STANDARD_OPTIONS = (
     213          'activebackground', 'activeborderwidth', 'activeforeground', 'anchor',
     214          'background', 'bitmap', 'borderwidth', 'compound', 'cursor',
     215          'disabledforeground', 'exportselection', 'font', 'foreground',
     216          'highlightbackground', 'highlightcolor', 'highlightthickness',
     217          'image', 'insertbackground', 'insertborderwidth',
     218          'insertofftime', 'insertontime', 'insertwidth',
     219          'jump', 'justify', 'orient', 'padx', 'pady', 'relief',
     220          'repeatdelay', 'repeatinterval',
     221          'selectbackground', 'selectborderwidth', 'selectforeground',
     222          'setgrid', 'takefocus', 'text', 'textvariable', 'troughcolor',
     223          'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand',
     224      )
     225  
     226      def test_configure_activebackground(self):
     227          widget = self.create()
     228          self.checkColorParam(widget, 'activebackground')
     229  
     230      def test_configure_activeborderwidth(self):
     231          widget = self.create()
     232          self.checkPixelsParam(widget, 'activeborderwidth',
     233                                0, 1.3, 2.9, 6, -2, '10p')
     234  
     235      def test_configure_activeforeground(self):
     236          widget = self.create()
     237          self.checkColorParam(widget, 'activeforeground')
     238  
     239      def test_configure_anchor(self):
     240          widget = self.create()
     241          self.checkEnumParam(widget, 'anchor',
     242                  'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
     243  
     244      def test_configure_background(self):
     245          widget = self.create()
     246          self.checkColorParam(widget, 'background')
     247          if 'bg' in self.OPTIONS:
     248              self.checkColorParam(widget, 'bg')
     249  
     250      def test_configure_bitmap(self):
     251          widget = self.create()
     252          self.checkParam(widget, 'bitmap', 'questhead')
     253          self.checkParam(widget, 'bitmap', 'gray50')
     254          filename = test.support.findfile('python.xbm', subdir='imghdrdata')
     255          self.checkParam(widget, 'bitmap', '@' + filename)
     256          # Cocoa Tk widgets don't detect invalid -bitmap values
     257          # See https://core.tcl.tk/tk/info/31cd33dbf0
     258          if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and
     259                  'AppKit' in self.root.winfo_server()):
     260              self.checkInvalidParam(widget, 'bitmap', 'spam',
     261                      errmsg='bitmap "spam" not defined')
     262  
     263      def test_configure_borderwidth(self):
     264          widget = self.create()
     265          self.checkPixelsParam(widget, 'borderwidth',
     266                                0, 1.3, 2.6, 6, -2, '10p')
     267          if 'bd' in self.OPTIONS:
     268              self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p')
     269  
     270      def test_configure_compound(self):
     271          widget = self.create()
     272          self.checkEnumParam(widget, 'compound',
     273                  'bottom', 'center', 'left', 'none', 'right', 'top')
     274  
     275      def test_configure_cursor(self):
     276          widget = self.create()
     277          self.checkCursorParam(widget, 'cursor')
     278  
     279      def test_configure_disabledforeground(self):
     280          widget = self.create()
     281          self.checkColorParam(widget, 'disabledforeground')
     282  
     283      def test_configure_exportselection(self):
     284          widget = self.create()
     285          self.checkBooleanParam(widget, 'exportselection')
     286  
     287      def test_configure_font(self):
     288          widget = self.create()
     289          self.checkParam(widget, 'font',
     290                          '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
     291          self.checkInvalidParam(widget, 'font', '',
     292                                 errmsg='font "" doesn\'t exist')
     293  
     294      def test_configure_foreground(self):
     295          widget = self.create()
     296          self.checkColorParam(widget, 'foreground')
     297          if 'fg' in self.OPTIONS:
     298              self.checkColorParam(widget, 'fg')
     299  
     300      def test_configure_highlightbackground(self):
     301          widget = self.create()
     302          self.checkColorParam(widget, 'highlightbackground')
     303  
     304      def test_configure_highlightcolor(self):
     305          widget = self.create()
     306          self.checkColorParam(widget, 'highlightcolor')
     307  
     308      def test_configure_highlightthickness(self):
     309          widget = self.create()
     310          self.checkPixelsParam(widget, 'highlightthickness',
     311                                0, 1.3, 2.6, 6, '10p')
     312          self.checkParam(widget, 'highlightthickness', -2, expected=0,
     313                          conv=self._conv_pixels)
     314  
     315      def test_configure_image(self):
     316          widget = self.create()
     317          self.checkImageParam(widget, 'image')
     318  
     319      def test_configure_insertbackground(self):
     320          widget = self.create()
     321          self.checkColorParam(widget, 'insertbackground')
     322  
     323      def test_configure_insertborderwidth(self):
     324          widget = self.create()
     325          self.checkPixelsParam(widget, 'insertborderwidth',
     326                                0, 1.3, 2.6, 6, -2, '10p')
     327  
     328      def test_configure_insertofftime(self):
     329          widget = self.create()
     330          self.checkIntegerParam(widget, 'insertofftime', 100)
     331  
     332      def test_configure_insertontime(self):
     333          widget = self.create()
     334          self.checkIntegerParam(widget, 'insertontime', 100)
     335  
     336      def test_configure_insertwidth(self):
     337          widget = self.create()
     338          self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p')
     339  
     340      def test_configure_jump(self):
     341          widget = self.create()
     342          self.checkBooleanParam(widget, 'jump')
     343  
     344      def test_configure_justify(self):
     345          widget = self.create()
     346          self.checkEnumParam(widget, 'justify', 'left', 'right', 'center',
     347                  errmsg='bad justification "{}": must be '
     348                         'left, right, or center')
     349          self.checkInvalidParam(widget, 'justify', '',
     350                  errmsg='ambiguous justification "": must be '
     351                         'left, right, or center')
     352  
     353      def test_configure_orient(self):
     354          widget = self.create()
     355          self.assertEqual(str(widget['orient']), self.default_orient)
     356          self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical')
     357  
     358      def test_configure_padx(self):
     359          widget = self.create()
     360          self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m',
     361                                conv=self._conv_pad_pixels)
     362  
     363      def test_configure_pady(self):
     364          widget = self.create()
     365          self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m',
     366                                conv=self._conv_pad_pixels)
     367  
     368      def test_configure_relief(self):
     369          widget = self.create()
     370          self.checkReliefParam(widget, 'relief')
     371  
     372      def test_configure_repeatdelay(self):
     373          widget = self.create()
     374          self.checkIntegerParam(widget, 'repeatdelay', -500, 500)
     375  
     376      def test_configure_repeatinterval(self):
     377          widget = self.create()
     378          self.checkIntegerParam(widget, 'repeatinterval', -500, 500)
     379  
     380      def test_configure_selectbackground(self):
     381          widget = self.create()
     382          self.checkColorParam(widget, 'selectbackground')
     383  
     384      def test_configure_selectborderwidth(self):
     385          widget = self.create()
     386          self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p')
     387  
     388      def test_configure_selectforeground(self):
     389          widget = self.create()
     390          self.checkColorParam(widget, 'selectforeground')
     391  
     392      def test_configure_setgrid(self):
     393          widget = self.create()
     394          self.checkBooleanParam(widget, 'setgrid')
     395  
     396      def test_configure_state(self):
     397          widget = self.create()
     398          self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal')
     399  
     400      def test_configure_takefocus(self):
     401          widget = self.create()
     402          self.checkParams(widget, 'takefocus', '0', '1', '')
     403  
     404      def test_configure_text(self):
     405          widget = self.create()
     406          self.checkParams(widget, 'text', '', 'any string')
     407  
     408      def test_configure_textvariable(self):
     409          widget = self.create()
     410          var = tkinter.StringVar(self.root)
     411          self.checkVariableParam(widget, 'textvariable', var)
     412  
     413      def test_configure_troughcolor(self):
     414          widget = self.create()
     415          self.checkColorParam(widget, 'troughcolor')
     416  
     417      def test_configure_underline(self):
     418          widget = self.create()
     419          self.checkIntegerParam(widget, 'underline', 0, 1, 10)
     420  
     421      def test_configure_wraplength(self):
     422          widget = self.create()
     423          self.checkPixelsParam(widget, 'wraplength', 100)
     424  
     425      def test_configure_xscrollcommand(self):
     426          widget = self.create()
     427          self.checkCommandParam(widget, 'xscrollcommand')
     428  
     429      def test_configure_yscrollcommand(self):
     430          widget = self.create()
     431          self.checkCommandParam(widget, 'yscrollcommand')
     432  
     433      # non-standard but common options
     434  
     435      def test_configure_command(self):
     436          widget = self.create()
     437          self.checkCommandParam(widget, 'command')
     438  
     439      def test_configure_indicatoron(self):
     440          widget = self.create()
     441          self.checkBooleanParam(widget, 'indicatoron')
     442  
     443      def test_configure_offrelief(self):
     444          widget = self.create()
     445          self.checkReliefParam(widget, 'offrelief')
     446  
     447      def test_configure_overrelief(self):
     448          widget = self.create()
     449          self.checkReliefParam(widget, 'overrelief')
     450  
     451      def test_configure_selectcolor(self):
     452          widget = self.create()
     453          self.checkColorParam(widget, 'selectcolor')
     454  
     455      def test_configure_selectimage(self):
     456          widget = self.create()
     457          self.checkImageParam(widget, 'selectimage')
     458  
     459      def test_configure_tristateimage(self):
     460          widget = self.create()
     461          self.checkImageParam(widget, 'tristateimage')
     462  
     463      def test_configure_tristatevalue(self):
     464          widget = self.create()
     465          self.checkParam(widget, 'tristatevalue', 'unknowable')
     466  
     467      def test_configure_variable(self):
     468          widget = self.create()
     469          var = tkinter.DoubleVar(self.root)
     470          self.checkVariableParam(widget, 'variable', var)
     471  
     472  
     473  class ESC[4;38;5;81mIntegerSizeTests:
     474      def test_configure_height(self):
     475          widget = self.create()
     476          self.checkIntegerParam(widget, 'height', 100, -100, 0)
     477  
     478      def test_configure_width(self):
     479          widget = self.create()
     480          self.checkIntegerParam(widget, 'width', 402, -402, 0)
     481  
     482  
     483  class ESC[4;38;5;81mPixelSizeTests:
     484      def test_configure_height(self):
     485          widget = self.create()
     486          self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c')
     487  
     488      def test_configure_width(self):
     489          widget = self.create()
     490          self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i')
     491  
     492  
     493  def add_standard_options(*source_classes):
     494      # This decorator adds test_configure_xxx methods from source classes for
     495      # every xxx option in the OPTIONS class attribute if they are not defined
     496      # explicitly.
     497      def decorator(cls):
     498          for option in cls.OPTIONS:
     499              methodname = 'test_configure_' + option
     500              if not hasattr(cls, methodname):
     501                  for source_class in source_classes:
     502                      if hasattr(source_class, methodname):
     503                          setattr(cls, methodname,
     504                                  getattr(source_class, methodname))
     505                          break
     506                  else:
     507                      def test(self, option=option):
     508                          widget = self.create()
     509                          widget[option]
     510                          raise AssertionError('Option "%s" is not tested in %s' %
     511                                               (option, cls.__name__))
     512                      test.__name__ = methodname
     513                      setattr(cls, methodname, test)
     514          return cls
     515      return decorator
     516  
     517  def setUpModule():
     518      if test.support.verbose:
     519          tcl = tkinter.Tcl()
     520          print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True)