self.source = Source(self.log)
self.target = Target(self.canvas, self.log)
+ def tearDown(self):
+ # Make sure no drag-and-drop is left active between tests: the
+ # recursion guard is a name-mangled attribute on the root.
+ try:
+ del self.root._DndHandler__dnd
+ except AttributeError:
+ pass
+ super().tearDown()
+
def test_drag_and_drop(self):
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
self.assertIsNotNone(handler)
def test_high_button_number_ignored(self):
self.assertIsNone(dnd.dnd_start(self.source, FakeEvent(self.canvas, num=6)))
+ def test_restart_after_finish(self):
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ handler.cancel()
+ # Once a drag has finished a new one can start.
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ self.assertIsNotNone(handler)
+ handler.cancel()
+
+ def test_drag_cursor(self):
+ self.canvas['cursor'] = 'watch'
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ # The drag cursor is shown while dragging, the original restored after.
+ self.assertEqual(handler.save_cursor, 'watch')
+ self.assertEqual(str(self.canvas['cursor']), 'hand2')
+ handler.cancel()
+ self.assertEqual(str(self.canvas['cursor']), 'watch')
+
+ def test_bindings_added_and_removed(self):
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ self.assertIn('<Motion>', self.canvas.bind())
+ self.assertIn('<B1-ButtonRelease-1>', self.canvas.bind())
+ handler.cancel()
+ self.assertNotIn('<Motion>', self.canvas.bind())
+ self.assertNotIn('<B1-ButtonRelease-1>', self.canvas.bind())
+
+ def test_switch_target(self):
+ log1, log2 = [], []
+ w1, w2 = tkinter.Frame(self.root), tkinter.Frame(self.root)
+ target1, target2 = Target(w1, log1), Target(w2, log2)
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ self.canvas.winfo_containing = lambda x, y: w1
+ handler.on_motion(FakeEvent(self.canvas)) # Enter target1.
+ self.canvas.winfo_containing = lambda x, y: w2
+ handler.on_motion(FakeEvent(self.canvas)) # Leave target1, enter target2.
+ self.assertIs(handler.target, target2)
+ self.assertEqual(log1, ['accept', 'enter', 'leave'])
+ self.assertEqual(log2, ['accept', 'enter'])
+ handler.cancel()
+
+ def test_target_in_ancestor(self):
+ # The widget under the pointer has no dnd_accept, but an ancestor
+ # does: the search walks up the master chain to find it.
+ parent = tkinter.Frame(self.root)
+ target = Target(parent, self.log)
+ child = tkinter.Frame(parent)
+ self.canvas.winfo_containing = lambda x, y: child
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ handler.on_motion(FakeEvent(self.canvas))
+ self.assertIs(handler.target, target)
+ self.assertEqual(self.log, ['accept', 'enter'])
+ handler.cancel()
+
+ def test_accept_returning_none_continues(self):
+ # dnd_accept() returning None means "not me, keep looking up".
+ parent = tkinter.Frame(self.root)
+ target = Target(parent, self.log)
+ child = tkinter.Frame(parent)
+ child.dnd_accept = lambda source, event: None
+ self.canvas.winfo_containing = lambda x, y: child
+ handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
+ handler.on_motion(FakeEvent(self.canvas))
+ self.assertIs(handler.target, target)
+ handler.cancel()
+
if __name__ == "__main__":
unittest.main()