# wxPython 中的拖放

> 原文： [http://zetcode.com/wxpython/draganddrop/](http://zetcode.com/wxpython/draganddrop/)

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

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

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

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

在 wxPython 中，我们有两个预定义的数据目标：`wx.TextDropTarget`和`wx.FileDropTarget`。

## `wx.TextDropTarget`

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

`dragdrop_text.py`

```py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
ZetCode wxPython tutorial

In this example, we drag and drop text data.

author: Jan Bodnar
website: www.zetcode.com
last modified: May 2018
"""

from pathlib import Path
import os
import wx

class MyTextDropTarget(wx.TextDropTarget):

    def __init__(self, object):

        wx.TextDropTarget.__init__(self)
        self.object = object

    def OnDropText(self, x, y, data):

        self.object.InsertItem(0, data)
        return True

class Example(wx.Frame):

    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)

        self.InitUI()

    def InitUI(self):

        splitter1 = wx.SplitterWindow(self, style=wx.SP_3D)
        splitter2 = wx.SplitterWindow(splitter1, style=wx.SP_3D)

        home_dir = str(Path.home())

        self.dirWid = wx.GenericDirCtrl(splitter1, dir=home_dir, 
                style=wx.DIRCTRL_DIR_ONLY)

        self.lc1 = wx.ListCtrl(splitter2, style=wx.LC_LIST)
        self.lc2 = wx.ListCtrl(splitter2, style=wx.LC_LIST)

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

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

        tree = self.dirWid.GetTreeCtrl()

        splitter2.SplitHorizontally(self.lc1, self.lc2, 150)
        splitter1.SplitVertically(self.dirWid, splitter2, 200)

        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelect, id=tree.GetId())

        self.OnSelect(0)

        self.SetTitle('Drag and drop text')
        self.Centre()

    def OnSelect(self, event):

        list = os.listdir(self.dirWid.GetPath())

        self.lc1.ClearAll()
        self.lc2.ClearAll()

        for i in range(len(list)):

            if list[i][0] != '.':
                self.lc1.InsertItem(0, list[i])

    def OnDragInit(self, event):

        text = self.lc1.GetItemText(event.GetIndex())
        tdo = wx.TextDataObject(text)
        tds = wx.DropSource(self.lc1)

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

def main():

    app = wx.App()
    ex = Example(None)
    ex.Show()
    app.MainLoop()

if __name__ == '__main__':
    main()

```

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

```py
def OnDropText(self, x, y, data):

    self.object.InsertItem(0, data)
    return True    

```

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

```py
dt = MyTextDropTarget(self.lc2)
self.lc2.SetDropTarget(dt)  

```

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

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

```

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

```py
def OnDragInit(self, event):

    text = self.lc1.GetItemText(event.GetIndex())
    tdo = wx.TextDataObject(text)
    tds = wx.DropSource(self.lc1)
    ...

```

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

```py
tds.SetData(tdo)
tds.DoDragDrop(True)

```

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

## `wx.FileDropTarget`

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

`dragdrop_file.py`

```py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
ZetCode wxPython tutorial

In this example, we drag and drop files.

author: Jan Bodnar
website: www.zetcode.com
last modified: May 2018
"""

import wx

class FileDrop(wx.FileDropTarget):

    def __init__(self, window):

        wx.FileDropTarget.__init__(self)
        self.window = window

    def OnDropFiles(self, x, y, filenames):

        for name in filenames:

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

            except IOError as error:

                msg = "Error opening file\n {}".format(str(error))
                dlg = wx.MessageDialog(None, msg)
                dlg.ShowModal()

                return False

            except UnicodeDecodeError as error:

                msg = "Cannot open non ascii files\n {}".format(str(error))
                dlg = wx.MessageDialog(None, msg)
                dlg.ShowModal()

                return False

            finally:

                file.close()

        return True

class Example(wx.Frame):

    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)

        self.InitUI()

    def InitUI(self):

        self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE)
        dt = FileDrop(self.text)

        self.text.SetDropTarget(dt)

        self.SetTitle('File drag and drop')
        self.Centre()

def main():

    app = wx.App()
    ex = Example(None)
    ex.Show()
    app.MainLoop()

if __name__ == '__main__':
    main()

```

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

```py
def OnDropFiles(self, x, y, filenames):

    for name in filenames:
    ...

```

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

```py
try:
    file = open(name, 'r')
    text = file.read()
    self.window.WriteText(text)

```

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

```py
except IOError as error:

    msg = "Error opening file\n {}".format(str(error))
    dlg = wx.MessageDialog(None, msg)
    dlg.ShowModal()

    return False

```

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

```py
self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE)
dt = FileDrop(self.text)

self.text.SetDropTarget(dt)

```

`wx.TextCtrl`是放置目标。

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