python (3.12.0)

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