@@ -385,7 +385,12 @@ def test_identify(self):
385385 # Identifying the element under a point requires the widget to be
386386 # mapped with a real size; the rest of the checks do not.
387387 if wait_until_mapped (self .entry ):
388- self .assertIn (self .entry .identify (5 , 5 ), self .IDENTIFY_AS )
388+ # Probe the centre of the widget: a fixed pixel such as (5, 5)
389+ # lands in the field at a normal scaling but in the surrounding
390+ # padding at a high DPI.
391+ x = self .entry .winfo_width () // 2
392+ y = self .entry .winfo_height () // 2
393+ self .assertIn (self .entry .identify (x , y ), self .IDENTIFY_AS )
389394 self .assertEqual (self .entry .identify (- 1 , - 1 ), "" )
390395
391396 self .assertRaises (tkinter .TclError , self .entry .identify , None , 5 )
@@ -1062,21 +1067,15 @@ def test_tab_identifiers(self):
10621067
10631068 self .nb .pack ()
10641069 self .nb .update ()
1065- if sys .platform == 'darwin' :
1066- tb_idx = "@20,5"
1067- else :
1068- tb_idx = "@5,5"
1069- self .assertEqual (self .nb .tab (tb_idx ), self .nb .tab ('current' ))
1070+ # Address a tab by a point taken from Tk's own geometry rather than a
1071+ # fixed pixel, which can land on a different tab or miss the tabs
1072+ # entirely (e.g. on Aqua the tabs are inset) as the display scaling
1073+ # changes.
1074+ x , y = self ._tab_point (self .nb .index ('current' ))
1075+ self .assertEqual (self .nb .tab (f'@{ x } ,{ y } ' ), self .nb .tab ('current' ))
10701076
1071- for i in range (5 , 100 , 5 ):
1072- try :
1073- if self .nb .tab ('@%d, 5' % i , text = None ) == 'a' :
1074- break
1075- except tkinter .TclError :
1076- pass
1077-
1078- else :
1079- self .fail ("Tab with text 'a' not found" )
1077+ x , y = self ._tab_point (self .nb .index (self .child1 ))
1078+ self .assertEqual (self .nb .tab (f'@{ x } ,{ y } ' , text = None ), 'a' )
10801079
10811080 def test_add_and_hidden (self ):
10821081 self .assertRaises (tkinter .TclError , self .nb .hide , - 1 )
@@ -1206,23 +1205,43 @@ def test_configure_tabs(self):
12061205
12071206 self .assertEqual (self .nb .tabs (), ())
12081207
1208+ def _tab_point (self , index ):
1209+ # Return a window coordinate that really lies on tab *index*, taken
1210+ # from Tk's own geometry (ttk::notebook index @x,y), so that it is
1211+ # correct regardless of the theme, platform and display scaling. A
1212+ # fixed pixel such as (5, 5) can land on a different tab element
1213+ # depending on the DPI, or miss the tabs entirely (e.g. on Aqua the
1214+ # tabs are inset), which would make a click there test nothing.
1215+ self .assertTrue (wait_until_mapped (self .nb , full_size = True ))
1216+ w , h = self .nb .winfo_width (), self .nb .winfo_height ()
1217+ # The selected pane fills the widget except for the tab strip, so skip
1218+ # that large tab-free region instead of querying every pixel in it.
1219+ pane = self .nb .nametowidget (self .nb .select ())
1220+ px , py = pane .winfo_x (), pane .winfo_y ()
1221+ pw , ph = pane .winfo_width (), pane .winfo_height ()
1222+ for y in range (h ):
1223+ inside_y = py <= y < py + ph
1224+ for x in range (w ):
1225+ if inside_y and px <= x < px + pw :
1226+ continue
1227+ try :
1228+ if self .nb .index (f'@{ x } ,{ y } ' ) == index :
1229+ return x , y
1230+ except TclError :
1231+ continue
1232+ self .fail (f'no point found on tab { index } ' )
1233+
12091234 def test_traversal (self ):
12101235 self .nb .pack ()
12111236 self .nb .update ()
12121237
1213- self .nb .select (0 )
1238+ # A mouse click selects the tab it lands on.
1239+ self .nb .select (1 )
1240+ self .assertEqual (self .nb .select (), str (self .child2 ))
1241+ simulate_mouse_click (self .nb , * self ._tab_point (0 ))
1242+ self .assertEqual (self .nb .select (), str (self .child1 ))
12141243
1215- if sys .platform == 'darwin' :
1216- focus_identify_as = ''
1217- elif sys .platform == 'win32' :
1218- focus_identify_as = 'focus'
1219- else :
1220- focus_identify_as = 'focus' if tk_version < (8 , 7 ) else 'padding'
1221- # identify() at (5, 5) needs the tab realized there; under focus
1222- # contention the mapped size can lag, so wait for the full size.
1223- if wait_until_mapped (self .nb , full_size = True ):
1224- self .assertEqual (self .nb .identify (5 , 5 ), focus_identify_as )
1225- simulate_mouse_click (self .nb , 5 , 5 )
1244+ # Control-Tab and Shift-Control-Tab traverse the tabs.
12261245 self .nb .focus_force ()
12271246 self .nb .event_generate ('<Control-Tab>' )
12281247 self .assertEqual (self .nb .select (), str (self .child2 ))
@@ -1237,21 +1256,25 @@ def test_traversal(self):
12371256 self .nb .tab (self .child2 , text = 'e' , underline = 0 )
12381257 self .nb .enable_traversal ()
12391258 self .nb .focus_force ()
1240- if wait_until_mapped (self .nb , full_size = True ):
1241- self .assertEqual (self .nb .identify (5 , 5 ), focus_identify_as )
1242- simulate_mouse_click (self .nb , 5 , 5 )
1243- # on macOS Emacs-style keyboard shortcuts are region-dependent;
1244- # let's use the regular arrow keys instead
1259+
1260+ # A click still selects the tab it lands on after enable_traversal().
1261+ self .nb .select (1 )
1262+ self .assertEqual (self .nb .select (), str (self .child2 ))
1263+ simulate_mouse_click (self .nb , * self ._tab_point (0 ))
1264+ self .assertEqual (self .nb .select (), str (self .child1 ))
1265+
1266+ # Mnemonics traverse the tabs (macOS uses region-dependent
1267+ # Emacs-style shortcuts, so use the regular arrow keys there).
12451268 if sys .platform == 'darwin' :
12461269 begin = '<Left>'
12471270 end = '<Right>'
12481271 else :
12491272 begin = '<Alt-a>'
12501273 end = '<Alt-e>'
1251- self .nb .event_generate (begin )
1252- self .assertEqual (self .nb .select (), str (self .child1 ))
12531274 self .nb .event_generate (end )
12541275 self .assertEqual (self .nb .select (), str (self .child2 ))
1276+ self .nb .event_generate (begin )
1277+ self .assertEqual (self .nb .select (), str (self .child1 ))
12551278
12561279
12571280@add_configure_tests (IntegerSizeTests , StandardTtkOptionsTests )
@@ -2062,14 +2085,17 @@ def test_tag_bind(self):
20622085 self .tv .pack ()
20632086 self .tv .update ()
20642087
2088+ # Find the y coordinate of each item. Scan the whole height of the
2089+ # widget rather than a fixed pixel range, since the row height grows
2090+ # with the display scaling.
20652091 pos_y = set ()
20662092 found = set ()
2067- for i in range (0 , 100 , 10 ):
2093+ for y in range (self . tv . winfo_height () ):
20682094 if len (found ) == 2 : # item1 and item2 already found
20692095 break
2070- item_id = self .tv .identify_row (i )
2096+ item_id = self .tv .identify_row (y )
20712097 if item_id and item_id not in found :
2072- pos_y .add (i )
2098+ pos_y .add (y )
20732099 found .add (item_id )
20742100
20752101 self .assertEqual (len (pos_y ), 2 ) # item1 and item2 y pos
0 commit comments