(root)/
Python-3.11.7/
Lib/
tkinter/
test/
test_ttk/
test_style.py
       1  import unittest
       2  import sys
       3  import tkinter
       4  from tkinter import ttk
       5  from tkinter import TclError
       6  from test import support
       7  from test.support import requires
       8  from tkinter.test.support import AbstractTkTest, get_tk_patchlevel
       9  
      10  requires('gui')
      11  
      12  CLASS_NAMES = [
      13      '.', 'ComboboxPopdownFrame', 'Heading',
      14      'Horizontal.TProgressbar', 'Horizontal.TScale', 'Item', 'Sash',
      15      'TButton', 'TCheckbutton', 'TCombobox', 'TEntry',
      16      'TLabelframe', 'TLabelframe.Label', 'TMenubutton',
      17      'TNotebook', 'TNotebook.Tab', 'Toolbutton', 'TProgressbar',
      18      'TRadiobutton', 'Treeview', 'TScale', 'TScrollbar', 'TSpinbox',
      19      'Vertical.TProgressbar', 'Vertical.TScale'
      20  ]
      21  
      22  class ESC[4;38;5;81mStyleTest(ESC[4;38;5;149mAbstractTkTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      23  
      24      def setUp(self):
      25          super().setUp()
      26          self.style = ttk.Style(self.root)
      27  
      28  
      29      def test_configure(self):
      30          style = self.style
      31          style.configure('TButton', background='yellow')
      32          self.assertEqual(style.configure('TButton', 'background'),
      33              'yellow')
      34          self.assertIsInstance(style.configure('TButton'), dict)
      35  
      36  
      37      def test_map(self):
      38          style = self.style
      39  
      40          # Single state
      41          for states in ['active'], [('active',)]:
      42              with self.subTest(states=states):
      43                  style.map('TButton', background=[(*states, 'white')])
      44                  expected = [('active', 'white')]
      45                  self.assertEqual(style.map('TButton', 'background'), expected)
      46                  m = style.map('TButton')
      47                  self.assertIsInstance(m, dict)
      48                  self.assertEqual(m['background'], expected)
      49  
      50          # Multiple states
      51          for states in ['pressed', '!disabled'], ['pressed !disabled'], [('pressed', '!disabled')]:
      52              with self.subTest(states=states):
      53                  style.map('TButton', background=[(*states, 'black')])
      54                  expected = [('pressed', '!disabled', 'black')]
      55                  self.assertEqual(style.map('TButton', 'background'), expected)
      56                  m = style.map('TButton')
      57                  self.assertIsInstance(m, dict)
      58                  self.assertEqual(m['background'], expected)
      59  
      60          # Default state
      61          for states in [], [''], [()]:
      62              with self.subTest(states=states):
      63                  style.map('TButton', background=[(*states, 'grey')])
      64                  expected = [('grey',)]
      65                  self.assertEqual(style.map('TButton', 'background'), expected)
      66                  m = style.map('TButton')
      67                  self.assertIsInstance(m, dict)
      68                  self.assertEqual(m['background'], expected)
      69  
      70  
      71      def test_lookup(self):
      72          style = self.style
      73          style.configure('TButton', background='yellow')
      74          style.map('TButton', background=[('active', 'background', 'blue')])
      75  
      76          self.assertEqual(style.lookup('TButton', 'background'), 'yellow')
      77          self.assertEqual(style.lookup('TButton', 'background',
      78              ['active', 'background']), 'blue')
      79          self.assertEqual(style.lookup('TButton', 'optionnotdefined',
      80              default='iknewit'), 'iknewit')
      81  
      82  
      83      def test_layout(self):
      84          style = self.style
      85          self.assertRaises(tkinter.TclError, style.layout, 'NotALayout')
      86          tv_style = style.layout('Treeview')
      87  
      88          # "erase" Treeview layout
      89          style.layout('Treeview', '')
      90          self.assertEqual(style.layout('Treeview'),
      91              [('null', {'sticky': 'nswe'})]
      92          )
      93  
      94          # restore layout
      95          style.layout('Treeview', tv_style)
      96          self.assertEqual(style.layout('Treeview'), tv_style)
      97  
      98          # should return a list
      99          self.assertIsInstance(style.layout('TButton'), list)
     100  
     101          # correct layout, but "option" doesn't exist as option
     102          self.assertRaises(tkinter.TclError, style.layout, 'Treeview',
     103              [('name', {'option': 'inexistent'})])
     104  
     105  
     106      def test_theme_use(self):
     107          self.assertRaises(tkinter.TclError, self.style.theme_use,
     108              'nonexistingname')
     109  
     110          curr_theme = self.style.theme_use()
     111          new_theme = None
     112          for theme in self.style.theme_names():
     113              if theme != curr_theme:
     114                  new_theme = theme
     115                  self.style.theme_use(theme)
     116                  break
     117          else:
     118              # just one theme available, can't go on with tests
     119              return
     120  
     121          self.assertFalse(curr_theme == new_theme)
     122          self.assertFalse(new_theme != self.style.theme_use())
     123  
     124          self.style.theme_use(curr_theme)
     125  
     126      def test_configure_custom_copy(self):
     127          style = self.style
     128  
     129          curr_theme = self.style.theme_use()
     130          self.addCleanup(self.style.theme_use, curr_theme)
     131          for theme in self.style.theme_names():
     132              self.style.theme_use(theme)
     133              for name in CLASS_NAMES:
     134                  default = style.configure(name)
     135                  if not default:
     136                      continue
     137                  with self.subTest(theme=theme, name=name):
     138                      if support.verbose >= 2:
     139                          print('configure', theme, name, default)
     140                      if (theme in ('vista', 'xpnative')
     141                              and sys.getwindowsversion()[:2] == (6, 1)):
     142                          # Fails on the Windows 7 buildbot
     143                          continue
     144                      newname = f'C.{name}'
     145                      self.assertEqual(style.configure(newname), None)
     146                      style.configure(newname, **default)
     147                      self.assertEqual(style.configure(newname), default)
     148                      for key, value in default.items():
     149                          self.assertEqual(style.configure(newname, key), value)
     150  
     151  
     152      def test_map_custom_copy(self):
     153          style = self.style
     154  
     155          curr_theme = self.style.theme_use()
     156          self.addCleanup(self.style.theme_use, curr_theme)
     157          for theme in self.style.theme_names():
     158              self.style.theme_use(theme)
     159              for name in CLASS_NAMES:
     160                  default = style.map(name)
     161                  if not default:
     162                      continue
     163                  with self.subTest(theme=theme, name=name):
     164                      if support.verbose >= 2:
     165                          print('map', theme, name, default)
     166                      if (theme in ('vista', 'xpnative')
     167                              and sys.getwindowsversion()[:2] == (6, 1)):
     168                          # Fails on the Windows 7 buildbot
     169                          continue
     170                      newname = f'C.{name}'
     171                      self.assertEqual(style.map(newname), {})
     172                      style.map(newname, **default)
     173                      if theme == 'alt' and name == '.' and get_tk_patchlevel(self.root) < (8, 6, 1):
     174                          default['embossed'] = [('disabled', '1')]
     175                      self.assertEqual(style.map(newname), default)
     176                      for key, value in default.items():
     177                          self.assertEqual(style.map(newname, key), value)
     178  
     179      def test_element_options(self):
     180          style = self.style
     181          element_names = style.element_names()
     182          self.assertNotIsInstance(element_names, str)
     183          for name in element_names:
     184              self.assertIsInstance(name, str)
     185              element_options = style.element_options(name)
     186              self.assertNotIsInstance(element_options, str)
     187              for optname in element_options:
     188                  self.assertIsInstance(optname, str)
     189  
     190      def test_element_create_errors(self):
     191          style = self.style
     192          with self.assertRaises(TypeError):
     193              style.element_create('plain.newelem')
     194          with self.assertRaisesRegex(TclError, 'No such element type spam'):
     195              style.element_create('plain.newelem', 'spam')
     196  
     197      def test_element_create_from(self):
     198          style = self.style
     199          style.element_create('plain.background', 'from', 'default')
     200          self.assertIn('plain.background', style.element_names())
     201          style.element_create('plain.arrow', 'from', 'default', 'rightarrow')
     202          self.assertIn('plain.arrow', style.element_names())
     203  
     204      def test_element_create_from_errors(self):
     205          style = self.style
     206          with self.assertRaises(IndexError):
     207              style.element_create('plain.newelem', 'from')
     208          with self.assertRaisesRegex(TclError, 'theme "spam" doesn\'t exist'):
     209              style.element_create('plain.newelem', 'from', 'spam')
     210  
     211      def test_element_create_image(self):
     212          style = self.style
     213          image = tkinter.PhotoImage(master=self.root, width=12, height=10)
     214          style.element_create('block', 'image', image)
     215          self.assertIn('block', style.element_names())
     216  
     217          style.layout('TestLabel1', [('block', {'sticky': 'news'})])
     218          a = ttk.Label(self.root, style='TestLabel1')
     219          a.pack(expand=True, fill='both')
     220          self.assertEqual(a.winfo_reqwidth(), 12)
     221          self.assertEqual(a.winfo_reqheight(), 10)
     222  
     223          imgfile = support.findfile('python.xbm', subdir='imghdrdata')
     224          img1 = tkinter.BitmapImage(master=self.root, file=imgfile,
     225                                     foreground='yellow', background='blue')
     226          img2 = tkinter.BitmapImage(master=self.root, file=imgfile,
     227                                     foreground='blue', background='yellow')
     228          img3 = tkinter.BitmapImage(master=self.root, file=imgfile,
     229                                     foreground='white', background='black')
     230          style.element_create('Button.button', 'image',
     231                               img1, ('pressed', img2), ('active', img3),
     232                               border=(2, 4), sticky='we')
     233          self.assertIn('Button.button', style.element_names())
     234  
     235          style.layout('Button', [('Button.button', {'sticky': 'news'})])
     236          b = ttk.Button(self.root, style='Button')
     237          b.pack(expand=True, fill='both')
     238          self.assertEqual(b.winfo_reqwidth(), 16)
     239          self.assertEqual(b.winfo_reqheight(), 16)
     240  
     241      def test_element_create_image_errors(self):
     242          style = self.style
     243          image = tkinter.PhotoImage(master=self.root, width=10, height=10)
     244          with self.assertRaises(IndexError):
     245              style.element_create('block2', 'image')
     246          with self.assertRaises(TypeError):
     247              style.element_create('block2', 'image', image, 1)
     248          with self.assertRaises(ValueError):
     249              style.element_create('block2', 'image', image, ())
     250          with self.assertRaisesRegex(TclError, 'Invalid state name'):
     251              style.element_create('block2', 'image', image, ('spam', image))
     252          with self.assertRaisesRegex(TclError, 'Invalid state name'):
     253              style.element_create('block2', 'image', image, (1, image))
     254          with self.assertRaises(TypeError):
     255              style.element_create('block2', 'image', image, ('pressed', 1, image))
     256          with self.assertRaises(TypeError):
     257              style.element_create('block2', 'image', image, (1, 'selected', image))
     258          with self.assertRaisesRegex(TclError, 'bad option'):
     259              style.element_create('block2', 'image', image, spam=1)
     260  
     261      def test_theme_create(self):
     262          style = self.style
     263          curr_theme = style.theme_use()
     264          curr_layout = style.layout('TLabel')
     265          style.theme_create('testtheme1')
     266          self.assertIn('testtheme1', style.theme_names())
     267  
     268          style.theme_create('testtheme2', settings={
     269              'elem' : {'element create': ['from', 'default'],},
     270              'TLabel' : {
     271                  'configure': {'padding': 10},
     272                  'layout': [('elem', {'sticky': 'we'})],
     273              },
     274          })
     275          self.assertIn('testtheme2', style.theme_names())
     276  
     277          style.theme_create('testtheme3', 'testtheme2')
     278          self.assertIn('testtheme3', style.theme_names())
     279  
     280          style.theme_use('testtheme1')
     281          self.assertEqual(style.element_names(), ())
     282          self.assertEqual(style.layout('TLabel'), curr_layout)
     283  
     284          style.theme_use('testtheme2')
     285          self.assertEqual(style.element_names(), ('elem',))
     286          self.assertEqual(style.lookup('TLabel', 'padding'), '10')
     287          self.assertEqual(style.layout('TLabel'), [('elem', {'sticky': 'we'})])
     288  
     289          style.theme_use('testtheme3')
     290          self.assertEqual(style.element_names(), ())
     291          self.assertEqual(style.lookup('TLabel', 'padding'), '')
     292          self.assertEqual(style.layout('TLabel'), [('elem', {'sticky': 'we'})])
     293  
     294          style.theme_use(curr_theme)
     295  
     296      def test_theme_create_image(self):
     297          style = self.style
     298          curr_theme = style.theme_use()
     299          image = tkinter.PhotoImage(master=self.root, width=10, height=10)
     300          new_theme = 'testtheme4'
     301          style.theme_create(new_theme, settings={
     302              'block' : {
     303                  'element create': ['image', image, {'width': 120, 'height': 100}],
     304              },
     305              'TestWidget.block2' : {
     306                  'element create': ['image', image],
     307              },
     308              'TestWidget' : {
     309                  'configure': {
     310                      'anchor': 'left',
     311                      'padding': (3, 0, 0, 2),
     312                      'foreground': 'yellow',
     313                  },
     314                  'map': {
     315                      'foreground': [
     316                          ('pressed', 'red'),
     317                          ('active', 'disabled', 'blue'),
     318                      ],
     319                  },
     320                  'layout': [
     321                      ('TestWidget.block', {'sticky': 'we', 'side': 'left'}),
     322                      ('TestWidget.border', {
     323                          'sticky': 'nsw',
     324                          'border': 1,
     325                          'children': [
     326                              ('TestWidget.block2', {'sticky': 'nswe'})
     327                          ]
     328                      })
     329                  ],
     330              },
     331          })
     332  
     333          style.theme_use(new_theme)
     334          self.assertIn('block', style.element_names())
     335          self.assertEqual(style.lookup('TestWidget', 'anchor'), 'left')
     336          self.assertEqual(style.lookup('TestWidget', 'padding'), '3 0 0 2')
     337          self.assertEqual(style.lookup('TestWidget', 'foreground'), 'yellow')
     338          self.assertEqual(style.lookup('TestWidget', 'foreground',
     339                                        ['active']), 'yellow')
     340          self.assertEqual(style.lookup('TestWidget', 'foreground',
     341                                        ['active', 'pressed']), 'red')
     342          self.assertEqual(style.lookup('TestWidget', 'foreground',
     343                                        ['active', 'disabled']), 'blue')
     344          self.assertEqual(style.layout('TestWidget'),
     345              [
     346                  ('TestWidget.block', {'side': 'left', 'sticky': 'we'}),
     347                  ('TestWidget.border', {
     348                      'sticky': 'nsw',
     349                      'border': '1',
     350                      'children': [('TestWidget.block2', {'sticky': 'nswe'})]
     351                  })
     352              ])
     353  
     354          b = ttk.Label(self.root, style='TestWidget')
     355          b.pack(expand=True, fill='both')
     356          self.assertEqual(b.winfo_reqwidth(), 134)
     357          self.assertEqual(b.winfo_reqheight(), 100)
     358  
     359          style.theme_use(curr_theme)
     360  
     361  
     362  if __name__ == "__main__":
     363      unittest.main()