第十八章 使用wxPython的其他功能

本章内容:

  • 放置对象到剪贴板上
  • 拖放
  • 传送和获取自定义对象
  • 使用wx.Timer设置定时的事件

  • 编写多线程的wxPython应用程序

放置对象到剪贴板上

wxPython中,剪贴板和拖放特性是紧密相关的。期间,内部窗口的通信是由使用wx.DataObject类或它的子类的一个实例作为中介的。wx.DataObject是一个特殊的数据对象,它包含描述输出数据格式的元数据。我们将从剪贴板入手,然后我们将讨论拖放的不同处理。

对于一个剪切和粘贴操作,有三个元素:

  • source(源)

  • clipboard(剪贴板)

  • target(目标)

如果source是在你的应用程序中,那么你的应用程序负责创建wx.DataObject的一个实例并把它放到剪贴板对象。通常source都是在你的应用程序的外部。

这里的clipboard是一个全局对象,它容纳数据并在必要时与操作系统的剪贴板交互。

target对象负责从剪贴板获取wx.DataObject并把它转换为对你的应用程序有用的那一类数据。

得到剪贴板中的数据

如果你想你的应用程序能够引起一个剪贴事件,也就是说你想能够将数据剪切或复制到剪贴板,把数据放置到一个wx.DataObject里面。wx.DataObject知道自己能够被读写何种格式的数据。这点是比较重要的,例如如果你当时正在写一个词处理程序并希望给用户在粘贴时选择无格式文本的粘贴或丰富文本格式的粘贴的情况。然而大多数时候,在你的剪贴板行为中不需要太强大或太灵活的性能。对于最常用的情况,wxPython提供了三个预定义的wx.DataObject的子类:纯文本,位图图像和文件名。

要传递纯文本,可以创建类wx.TextDataObject的一个实例,使用它如下的构造器:

wx.TextDataObject(text="")

参数text是你想传递到剪贴的文本。你可以使用Text(text)方法来设置该文本,你也可以使用GetText()方法来得到该文本,你还可以使用GetTextLength()方法来得到该文本的长度。

一旦你创建了这种数据对象后,接着你必须访问剪贴板。系统的剪贴板在wxPython中是一个全局性的对象,名为wx.TheClipboard。要使用它,可以使用它的Open()方法来打开它。如果该剪贴板被打开了则该方法返回True,否则返回False。如果该剪贴板正在被另一应用程序写入的话,该剪贴板的打开有可能会失败,因此在使用该剪贴板之前,你应该检查打开方法的返回值。当你使用完剪贴板之后,你应该调用它的 Close()方法来关闭它。打开剪贴板会阻塞其它的剪贴板用户的使用,因此剪贴板打开的时间应该尽可能的短。

处理剪贴板中的数据

一旦你有了打开的剪贴板,你就可以处理它所包含的数据对象。你可以使用SetData(data)来将你的对象放置到剪贴板上,其中参数data是一个wx.DataObject实例。你可以使用方法Clear()方法来清空剪贴板。如果你希望在你的应用程序结束后,剪贴板上的数据还存在,那么你必须调用方法Flush(),该方法命令系统维持你的数据。否则,该wxPython剪贴板对象在你的应用程序退出时会被清除。

下面是一段添加文本到剪贴板的代码:

text_data = wx.TextDataObject("hi there") 
if wx.TheClipboard.Open(): 
wx.TheClipboard.SetData(text_data) 
    wx.TheClipboard.Close()

获得剪贴板中的文本数据

从剪贴板中获得文本数据也是很简单的。一旦你打开了剪贴板,你就可以调用GetData(data)方法,其中参数datawx.DataObject的一些特定的子类的一个实例。如果剪贴板中的数据能够以与方法中的数据对象参数相一致的某种格式被输出的话,该方法的返回值则为True。这里,由于我们传递进的是一个wx.TextDataObject,那么返回值True就意味该剪贴板能够被转换到纯文本。下面是一段样例代码:

text_data = wx.TextDataObject() 
if wx.TheClipboard.Open(): 
success = wx.TheClipboard.GetData(text_data) 
wx.TheClipboard.Close() 
if success: 
return text_data.GetText()

注意,当你从剪贴板获取数据时,数据并不关心是哪个应用程序将它放置到剪贴板的。剪贴板中的数据本身被底层的操作系统所管理,wxPython的责任是确保格式的匹配及你能够得到你能够处理的数据格式。

实战剪贴板

在这一节,我们将显示一个简单的例子,它演示了如何与剪贴板交换数据。它是一个有着两个按钮的框架,它使用户能够复制和粘贴文本。当你运行这个例子时,结果将会如图18.1所示。

图18.1

例18.1是产生图18.1的代码。

例18.1 剪贴板交互示例

#-*- encoding:UTF-8 -*-
import wx

t1_text = """\
The whole contents of this control
will be placed in the system's
clipboard when you click the copy
button below.
"""

t2_text = """\
If the clipboard contains a text
data object then it will be placed
in this control when you click
the paste button below.  Try
copying to and pasting from
other applications too!
"""

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Clipboard",
                          size=(500,300))
        p = wx.Panel(self)

        # create the controls
        self.t1 = wx.TextCtrl(p, -1, t1_text,
                              style=wx.TE_MULTILINE|wx.HSCROLL)
        self.t2 = wx.TextCtrl(p, -1, t2_text,
                              style=wx.TE_MULTILINE|wx.HSCROLL)
        copy = wx.Button(p, -1, "Copy")
        paste = wx.Button(p, -1, "Paste")

        # setup the layout with sizers
        fgs = wx.FlexGridSizer(2, 2, 5, 5)
        fgs.AddGrowableRow(0)
        fgs.AddGrowableCol(0)
        fgs.AddGrowableCol(1)
        fgs.Add(self.t1, 0, wx.EXPAND)
        fgs.Add(self.t2, 0, wx.EXPAND)
        fgs.Add(copy, 0, wx.EXPAND)
        fgs.Add(paste, 0, wx.EXPAND)
        border = wx.BoxSizer()
        border.Add(fgs, 1, wx.EXPAND|wx.ALL, 5)
        p.SetSizer(border)

        # Bind events
        self.Bind(wx.EVT_BUTTON, self.OnDoCopy, copy)
        self.Bind(wx.EVT_BUTTON, self.OnDoPaste, paste)

    def OnDoCopy(self, evt):#Copy按钮的事件处理函数
        data = wx.TextDataObject()
        data.SetText(self.t1.GetValue())
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(data)#将数据放置到剪贴板上
            wx.TheClipboard.Close()
        else:
            wx.MessageBox("Unable to open the clipboard", "Error")

    def OnDoPaste(self, evt):#Paste按钮的事件处理函数
        success = False
        data = wx.TextDataObject()
        if wx.TheClipboard.Open():
            success = wx.TheClipboard.GetData(data)#从剪贴板得到数据
            wx.TheClipboard.Close()

        if success:
            self.t2.SetValue(data.GetText())#更新文本控件
        else:
            wx.MessageBox(
                "There is no data in the clipboard in the required format",
                "Error")

app = wx.PySimpleApp()
frm = MyFrame()
frm.Show()
app.MainLoop()

获得剪贴板中的文本数据

从剪贴板中获得文本数据也是很简单的。一旦你打开了剪贴板,你就可以调用GetData(data)方法,其中参数datawx.DataObject的一些特定的子类的一个实例。如果剪贴板中的数据能够以与方法中的数据对象参数相一致的某种格式被输出的话,该方法的返回值则为True。这里,由于我们传递进的是一个wx.TextDataObject,那么返回值True就意味该剪贴板能够被转换到纯文本。下面是一段样例代码:

text_data = wx.TextDataObject() 
if wx.TheClipboard.Open(): 
success = wx.TheClipboard.GetData(text_data) 
wx.TheClipboard.Close() 
if success: 
return text_data.GetText()

注意,当你从剪贴板获取数据时,数据并不关心是哪个应用程序将它放置到剪贴板的。剪贴板中的数据本身被底层的操作系统所管理,wxPython的责任是确保格式的匹配及你能够得到你能够处理的数据格式。

实战剪贴板

在这一节,我们将显示一个简单的例子,它演示了如何与剪贴板交换数据。它是一个有着两个按钮的框架,它使用户能够复制和粘贴文本。当你运行这个例子时,结果将会如图18.1所示。

图18.1

例18.1是产生图18.1的代码。

例18.1 剪贴板交互示例

#-*- encoding:UTF-8 -*-
import wx

t1_text = """\
The whole contents of this control
will be placed in the system's
clipboard when you click the copy
button below.
"""

t2_text = """\
If the clipboard contains a text
data object then it will be placed
in this control when you click
the paste button below.  Try
copying to and pasting from
other applications too!
"""

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Clipboard",
                          size=(500,300))
        p = wx.Panel(self)

        # create the controls
        self.t1 = wx.TextCtrl(p, -1, t1_text,
                              style=wx.TE_MULTILINE|wx.HSCROLL)
        self.t2 = wx.TextCtrl(p, -1, t2_text,
                              style=wx.TE_MULTILINE|wx.HSCROLL)
        copy = wx.Button(p, -1, "Copy")
        paste = wx.Button(p, -1, "Paste")

        # setup the layout with sizers
        fgs = wx.FlexGridSizer(2, 2, 5, 5)
        fgs.AddGrowableRow(0)
        fgs.AddGrowableCol(0)
        fgs.AddGrowableCol(1)
        fgs.Add(self.t1, 0, wx.EXPAND)
        fgs.Add(self.t2, 0, wx.EXPAND)
        fgs.Add(copy, 0, wx.EXPAND)
        fgs.Add(paste, 0, wx.EXPAND)
        border = wx.BoxSizer()
        border.Add(fgs, 1, wx.EXPAND|wx.ALL, 5)
        p.SetSizer(border)

        # Bind events
        self.Bind(wx.EVT_BUTTON, self.OnDoCopy, copy)
        self.Bind(wx.EVT_BUTTON, self.OnDoPaste, paste)

    def OnDoCopy(self, evt):#Copy按钮的事件处理函数
        data = wx.TextDataObject()
        data.SetText(self.t1.GetValue())
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(data)#将数据放置到剪贴板上
            wx.TheClipboard.Close()
        else:
            wx.MessageBox("Unable to open the clipboard", "Error")

    def OnDoPaste(self, evt):#Paste按钮的事件处理函数
        success = False
        data = wx.TextDataObject()
        if wx.TheClipboard.Open():
            success = wx.TheClipboard.GetData(data)#从剪贴板得到数据
            wx.TheClipboard.Close()

        if success:
            self.t2.SetValue(data.GetText())#更新文本控件
        else:
            wx.MessageBox(
                "There is no data in the clipboard in the required format",
                "Error")

app = wx.PySimpleApp()
frm = MyFrame()
frm.Show()
app.MainLoop()

在下一节中,我们将讨论如何传递其它格式的数据,如位图。

传递其它格式的数据

经由剪贴板交互位图几乎与传递文本相同。你所使用的相关的数据对象子类是wx.BitmapDataObject,其get方法和set方法分别是GetBitmap()SetBitmap(bitmap)。经由该数据对象与剪贴板交互的数据对象必须是wx.Bitmap类型的。

最后一个预定义的数据对象类型是wx.FileDataObject。通常该数据对象被用于拖放中(将在18.2节中讨论),例如当你将一个文件从你的资源管理器或查找窗口放置到你的应用程序上时。你可以使用该数据对象从剪贴板接受文件名数据,并且你可以使用方法GetFilenames()来从该数据对象获取文件名,该方法返回一个文件名的列表,列表中的每个文件名是已经被添加到剪贴板的文件名。你可以使用该数据对象的AddFile(file)方法来将数据放置到剪贴板上,该方法将一个文件名字符串添加到该数据对象。这里没有其它的方法用于直接处理列表,所以这就要靠你自己了。本章的稍后部份,我们将讨论如何经由剪贴板传送自定义对象,以及如何拖放对象。

拖放源

拖放是一个类似剪切和粘贴的功能。它是在你的应用程序的不同部分之间或两个不同的应用程序之间传送数据。由于管理数据和格式几乎是相同的,所以wxPython同样使用wx.DataObject族来确保对格式作恰当的处理。

拖放和剪切粘贴的最大不同是,剪切粘贴信赖于中介剪贴板的存在。因为是剪贴板管理数据,所以源程序&#x