Introduction
One of the most simple forms of interacting with other applications is by the use of the Clipboard. On the one hand you can get text or other types of data from it, on the other hand you are able to write to it, to make data accessible from within other applications.
Basics
The contents of the Clipboard is given by a reference to an object that provides functions for representing itself in different data types. E.g. a HTML content in the Clipboard usually is able to show as plain text or RTF---or HTML, of course.
wxPython handles the Clipboard in the wx.TheClipboard class.
General steps
Your first step is to test whether the clipboard is available to be opened, just in case some other application has a lock on it, already.
if not wx.TheClipboard.IsOpened()
Next, we acquire control of the Clipboard in order to prevent other applications from using it, while your application is using it.
wx.TheClipboard.Open()
Next, you create a DataObject of the type you wish to get from the Clipboard, e.g. a wx.TextDataObject
do = wx.TextDataObject()
Now you can try to get the Clipboard's secret.
success = wx.TheClipboard.GetData(do)
The return value of GetData is True if you were succesful, False in case of bad luck.
Don't forget to close it again.
wx.TheClipboard.Close()
That's all, folks! You just read the Clipboard!
Example
This sample application displays continually the Clipboard's contents if it is representable as text and a message else.
import wx class MyFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent) self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.HSCROLL) self.Paste() def Paste(self): if not wx.TheClipboard.IsOpened(): # may crash, otherwise do = wx.TextDataObject() wx.TheClipboard.Open() success = wx.TheClipboard.GetData(do) wx.TheClipboard.Close() if success: self.text.SetValue(do.GetText()) else: self.text.SetValue("There is no data in the clipboard in the required format") app = wx.App(False) f = MyFrame(None) f.Show() app.MainLoop()
Writing
Writing to the Clipboard is nearly as easy:
clipdata = wx.TextDataObject() clipdata.SetText("Hi folks!") wx.TheClipboard.Open() wx.TheClipboard.SetData(clipdata) wx.TheClipboard.Close()
Attention: Don't delete the written object (clipdata in this case)---it now belongs to wx.TheClipboard and will be discarded by it when necessary.
Handling the Clipboard using wx.EVT_IDLE
It can be expensive to keep checking the clipboard in your EVT_IDLE handler, in order to enable/disable a paste menu/toolbar. It can also lead to segmentation faults. We prevent this by keeping a counter variable, that is incremented every time the ID_PASTE idle event is triggered. Then, the clipboard is only checked every time the counter == 5. This means, that although we've specified to update the UI idle event every 75ms, the clipboard is checked every 450ms. This may lead to a small delay between valid data being ready to paste and the button/menu being enabled/disabled.
To test the sample, copy and paste some text or bitmap data, the toolbar button will enable. If you now copy and paste a file, then the toolbar will disable.
1 #!/usr/bin/env python
2 import wx
3
4 class MyFrame(wx.Frame):
5 def __init__(self, parent):
6 wx.Frame.__init__(self, parent, title="Paste Button Demo")
7 self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.HSCROLL)
8 self.count = 4 # gets incremented
9
10 menu = wx.MenuBar()
11 edit = wx.Menu()
12 paste = edit.Append(wx.ID_PASTE, "&Paste", "Paste from the clipboard")
13 menu.Append(edit, "&Edit")
14 self.SetMenuBar(menu)
15
16 self.toolbar = self.CreateToolBar()
17 bmp = wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR)
18 self.toolbar.AddSimpleTool(wx.ID_PASTE, bmp)
19 self.toolbar.Realize()
20
21 self.Bind(wx.EVT_IDLE, self.update_ui)
22 self.Bind(wx.EVT_UPDATE_UI, self.update_ui, id=wx.ID_PASTE)
23
24 wx.UpdateUIEvent.SetUpdateInterval(75)
25 self.UpdateWindowUI()
26
27
28
29 def update_ui(self, event):
30 if event.GetId() == wx.ID_PASTE: # check this less frequently, possibly expensive
31 self.count += 1
32 if self.count < 5:
33 return
34
35 if not wx.TheClipboard.IsOpened():
36 self.count = 0
37 wx.TheClipboard.Open()
38 success = wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_BITMAP))
39 success2 = wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT))
40 wx.TheClipboard.Close()
41 if success or success2:
42 self.text.SetValue("You can paste!")
43 event.Enable(True)
44
45 else:
46 event.Enable(False)
47 self.text.SetValue("You can't paste. :(")
48
49 app = wx.App(False)
50 f = MyFrame(None)
51 f.Show()
52 app.MainLoop()
How to Use the Clipboard Using the "With" Statement
The wx.Clipboard supports using context managers, which means you can wrap your calls to it using Python's with statement. Here is a simple example:
import wx ######################################################################## class ClipboardPanel(wx.Panel): """""" #---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" wx.Panel.__init__(self, parent) lbl = wx.StaticText(self, label="Enter text to copy to clipboard:") self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE) copyBtn = wx.Button(self, label="Copy") copyBtn.Bind(wx.EVT_BUTTON, self.onCopy) copyFlushBtn = wx.Button(self, label="Copy and Flush") copyFlushBtn.Bind(wx.EVT_BUTTON, self.onCopyAndFlush) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(lbl, 0, wx.ALL, 5) sizer.Add(self.text, 1, wx.EXPAND) sizer.Add(copyBtn, 0, wx.ALL|wx.CENTER, 5) sizer.Add(copyFlushBtn, 0, wx.ALL|wx.CENTER, 5) self.SetSizer(sizer) #---------------------------------------------------------------------- def onCopy(self, event): """""" self.dataObj = wx.TextDataObject() self.dataObj.SetText(self.text.GetValue()) try: with wx.Clipboard.Get() as clipboard: clipboard.SetData(self.dataObj) except TypeError: wx.MessageBox("Unable to open the clipboard", "Error") #---------------------------------------------------------------------- def onCopyAndFlush(self, event): """""" self.dataObj = wx.TextDataObject() self.dataObj.SetText(self.text.GetValue()) try: with wx.Clipboard.Get() as clipboard: clipboard.SetData(self.dataObj) clipboard.Flush() except TypeError: wx.MessageBox("Unable to open the clipboard", "Error") ######################################################################## class ClipboardFrame(wx.Frame): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" wx.Frame.__init__(self, None, title="Clipboard Tutorial") panel = ClipboardPanel(self) self.Show() if __name__ == "__main__": app = wx.App(False) frame = ClipboardFrame() app.MainLoop()