原文: http://zetcode.com/wxpython/draganddrop/

在计算机图形用户界面中,拖放是单击虚拟对象并将其拖动到其他位置或另一个虚拟对象上的动作(或支持以下动作)。 通常,它可用于调用多种动作,或在两个抽象对象之间创建各种类型的关联。

拖放操作使您可以直观地完成复杂的事情。

在拖放操作中,我们将一些数据从数据源拖动到数据目标。 所以我们必须有:

  • 一些数据
  • 数据来源
  • 数据目标

在 wxPython 中,我们有两个预定义的数据目标:wx.TextDropTargetwx.FileDropTarget

wx.TextDropTarget

wx.TextDropTarget是用于处理文本数据的预定义放置目标。

dragdrop_text.py

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. ZetCode wxPython tutorial
  5. In this example, we drag and drop text data.
  6. author: Jan Bodnar
  7. website: www.zetcode.com
  8. last modified: May 2018
  9. """
  10. from pathlib import Path
  11. import os
  12. import wx
  13. class MyTextDropTarget(wx.TextDropTarget):
  14. def __init__(self, object):
  15. wx.TextDropTarget.__init__(self)
  16. self.object = object
  17. def OnDropText(self, x, y, data):
  18. self.object.InsertItem(0, data)
  19. return True
  20. class Example(wx.Frame):
  21. def __init__(self, *args, **kw):
  22. super(Example, self).__init__(*args, **kw)
  23. self.InitUI()
  24. def InitUI(self):
  25. splitter1 = wx.SplitterWindow(self, style=wx.SP_3D)
  26. splitter2 = wx.SplitterWindow(splitter1, style=wx.SP_3D)
  27. home_dir = str(Path.home())
  28. self.dirWid = wx.GenericDirCtrl(splitter1, dir=home_dir,
  29. style=wx.DIRCTRL_DIR_ONLY)
  30. self.lc1 = wx.ListCtrl(splitter2, style=wx.LC_LIST)
  31. self.lc2 = wx.ListCtrl(splitter2, style=wx.LC_LIST)
  32. dt = MyTextDropTarget(self.lc2)
  33. self.lc2.SetDropTarget(dt)
  34. self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDragInit, id=self.lc1.GetId())
  35. tree = self.dirWid.GetTreeCtrl()
  36. splitter2.SplitHorizontally(self.lc1, self.lc2, 150)
  37. splitter1.SplitVertically(self.dirWid, splitter2, 200)
  38. self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelect, id=tree.GetId())
  39. self.OnSelect(0)
  40. self.SetTitle('Drag and drop text')
  41. self.Centre()
  42. def OnSelect(self, event):
  43. list = os.listdir(self.dirWid.GetPath())
  44. self.lc1.ClearAll()
  45. self.lc2.ClearAll()
  46. for i in range(len(list)):
  47. if list[i][0] != '.':
  48. self.lc1.InsertItem(0, list[i])
  49. def OnDragInit(self, event):
  50. text = self.lc1.GetItemText(event.GetIndex())
  51. tdo = wx.TextDataObject(text)
  52. tds = wx.DropSource(self.lc1)
  53. tds.SetData(tdo)
  54. tds.DoDragDrop(True)
  55. def main():
  56. app = wx.App()
  57. ex = Example(None)
  58. ex.Show()
  59. app.MainLoop()
  60. if __name__ == '__main__':
  61. main()

在示例中,我们在wx.GenericDirCtrl中显示了一个文件系统。 所选目录的内容显示在右上方的列表控件中。 可以将文件名拖放到右下角的列表控件中。

  1. def OnDropText(self, x, y, data):
  2. self.object.InsertItem(0, data)
  3. return True

当我们将文本数据放到目标上时,该数据将通过InsertItem()方法插入到列表控件中。

  1. dt = MyTextDropTarget(self.lc2)
  2. self.lc2.SetDropTarget(dt)

创建放置目标。 我们使用SetDropTarget()方法将放置目标设置为第二个列表控件。

  1. self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDragInit, id=self.lc1.GetId())

当拖动操作开始时,将调用OnDragInit()方法。

  1. def OnDragInit(self, event):
  2. text = self.lc1.GetItemText(event.GetIndex())
  3. tdo = wx.TextDataObject(text)
  4. tds = wx.DropSource(self.lc1)
  5. ...

OnDragInit()方法中,我们创建一个wx.TextDataObject,其中包含我们的文本数据。 从第一个列表控件创建放置源。

  1. tds.SetData(tdo)
  2. tds.DoDragDrop(True)

我们使用SetData()将数据设置到放置源,并使用DoDragDrop()启动拖放操作。

wx.FileDropTarget

wx.FileDropTarget是接受文件的放置目标,这些文件是从文件管理器中拖动的。

dragdrop_file.py

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. ZetCode wxPython tutorial
  5. In this example, we drag and drop files.
  6. author: Jan Bodnar
  7. website: www.zetcode.com
  8. last modified: May 2018
  9. """
  10. import wx
  11. class FileDrop(wx.FileDropTarget):
  12. def __init__(self, window):
  13. wx.FileDropTarget.__init__(self)
  14. self.window = window
  15. def OnDropFiles(self, x, y, filenames):
  16. for name in filenames:
  17. try:
  18. file = open(name, 'r')
  19. text = file.read()
  20. self.window.WriteText(text)
  21. except IOError as error:
  22. msg = "Error opening file\n {}".format(str(error))
  23. dlg = wx.MessageDialog(None, msg)
  24. dlg.ShowModal()
  25. return False
  26. except UnicodeDecodeError as error:
  27. msg = "Cannot open non ascii files\n {}".format(str(error))
  28. dlg = wx.MessageDialog(None, msg)
  29. dlg.ShowModal()
  30. return False
  31. finally:
  32. file.close()
  33. return True
  34. class Example(wx.Frame):
  35. def __init__(self, *args, **kw):
  36. super(Example, self).__init__(*args, **kw)
  37. self.InitUI()
  38. def InitUI(self):
  39. self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE)
  40. dt = FileDrop(self.text)
  41. self.text.SetDropTarget(dt)
  42. self.SetTitle('File drag and drop')
  43. self.Centre()
  44. def main():
  45. app = wx.App()
  46. ex = Example(None)
  47. ex.Show()
  48. app.MainLoop()
  49. if __name__ == '__main__':
  50. main()

该示例创建一个简单的wx.TextCtrl。 我们可以将文本文件从文件管理器拖到控件中。

  1. def OnDropFiles(self, x, y, filenames):
  2. for name in filenames:
  3. ...

我们可以一次拖放多个文件。

  1. try:
  2. file = open(name, 'r')
  3. text = file.read()
  4. self.window.WriteText(text)

我们以只读模式打开文件,获取其内容,然后将内容写入文本控件窗口。

  1. except IOError as error:
  2. msg = "Error opening file\n {}".format(str(error))
  3. dlg = wx.MessageDialog(None, msg)
  4. dlg.ShowModal()
  5. return False

如果出现输入/输出错误,我们将显示一个消息对话框并终止操作。

  1. self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE)
  2. dt = FileDrop(self.text)
  3. self.text.SetDropTarget(dt)

wx.TextCtrl是放置目标。

在本章中,我们使用了 wxPython 中的拖放操作。