Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 389e00f

Browse filesBrowse files
gh-151678: Add more tests for tkinter.dnd (GH-152362)
Cover the drag cursor, the Motion and ButtonRelease bindings, switching between targets, the target search up the master chain, dnd_accept() returning None, and restarting after a drag has finished. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 11b3943 commit 389e00f
Copy full SHA for 389e00f

1 file changed

+73Lines changed: 73 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎Lib/test/test_tkinter/test_dnd.py‎

Copy file name to clipboardExpand all lines: Lib/test/test_tkinter/test_dnd.py
+73Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ def setUp(self):
6060
self.source = Source(self.log)
6161
self.target = Target(self.canvas, self.log)
6262

63+
def tearDown(self):
64+
# Make sure no drag-and-drop is left active between tests: the
65+
# recursion guard is a name-mangled attribute on the root.
66+
try:
67+
del self.root._DndHandler__dnd
68+
except AttributeError:
69+
pass
70+
super().tearDown()
71+
6372
def test_drag_and_drop(self):
6473
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
6574
self.assertIsNotNone(handler)
@@ -93,6 +102,70 @@ def test_no_recursive_start(self):
93102
def test_high_button_number_ignored(self):
94103
self.assertIsNone(dnd.dnd_start(self.source, FakeEvent(self.canvas, num=6)))
95104

105+
def test_restart_after_finish(self):
106+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
107+
handler.cancel()
108+
# Once a drag has finished a new one can start.
109+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
110+
self.assertIsNotNone(handler)
111+
handler.cancel()
112+
113+
def test_drag_cursor(self):
114+
self.canvas['cursor'] = 'watch'
115+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
116+
# The drag cursor is shown while dragging, the original restored after.
117+
self.assertEqual(handler.save_cursor, 'watch')
118+
self.assertEqual(str(self.canvas['cursor']), 'hand2')
119+
handler.cancel()
120+
self.assertEqual(str(self.canvas['cursor']), 'watch')
121+
122+
def test_bindings_added_and_removed(self):
123+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
124+
self.assertIn('<Motion>', self.canvas.bind())
125+
self.assertIn('<B1-ButtonRelease-1>', self.canvas.bind())
126+
handler.cancel()
127+
self.assertNotIn('<Motion>', self.canvas.bind())
128+
self.assertNotIn('<B1-ButtonRelease-1>', self.canvas.bind())
129+
130+
def test_switch_target(self):
131+
log1, log2 = [], []
132+
w1, w2 = tkinter.Frame(self.root), tkinter.Frame(self.root)
133+
target1, target2 = Target(w1, log1), Target(w2, log2)
134+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
135+
self.canvas.winfo_containing = lambda x, y: w1
136+
handler.on_motion(FakeEvent(self.canvas)) # Enter target1.
137+
self.canvas.winfo_containing = lambda x, y: w2
138+
handler.on_motion(FakeEvent(self.canvas)) # Leave target1, enter target2.
139+
self.assertIs(handler.target, target2)
140+
self.assertEqual(log1, ['accept', 'enter', 'leave'])
141+
self.assertEqual(log2, ['accept', 'enter'])
142+
handler.cancel()
143+
144+
def test_target_in_ancestor(self):
145+
# The widget under the pointer has no dnd_accept, but an ancestor
146+
# does: the search walks up the master chain to find it.
147+
parent = tkinter.Frame(self.root)
148+
target = Target(parent, self.log)
149+
child = tkinter.Frame(parent)
150+
self.canvas.winfo_containing = lambda x, y: child
151+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
152+
handler.on_motion(FakeEvent(self.canvas))
153+
self.assertIs(handler.target, target)
154+
self.assertEqual(self.log, ['accept', 'enter'])
155+
handler.cancel()
156+
157+
def test_accept_returning_none_continues(self):
158+
# dnd_accept() returning None means "not me, keep looking up".
159+
parent = tkinter.Frame(self.root)
160+
target = Target(parent, self.log)
161+
child = tkinter.Frame(parent)
162+
child.dnd_accept = lambda source, event: None
163+
self.canvas.winfo_containing = lambda x, y: child
164+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
165+
handler.on_motion(FakeEvent(self.canvas))
166+
self.assertIs(handler.target, target)
167+
handler.cancel()
168+
96169

97170
if __name__ == "__main__":
98171
unittest.main()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.