## page was renamed from Display RTF using the Clipboard
## page was renamed from ClipboardRtfDisplay
== Introduction ==
There may be times in life when you would like to display Rtf text in a text control.  You already have the Rtf code; you just need a way to get it to appear as formatted text in the text box.  Until wxPython gets it's own Rtf display control, there's no great way of doing this aside from creating your own Rtf text object.  The method of displaying the formatted text described here involves using the Windows clipboard to do all the work for you.

''Note that this solution is platform specific: WINDOWS ONLY.''

== Objects Involved ==
 * {{{(wx)win32clipboard}}}
 * {{{(wx)random}}}
 * {{{(wx)time}}}
== Process Overview ==
Basically, this code registers/uses the "Rich Text Format" clipboard format and writes the rich text into the clipboard, allowing you to paste it into a text control as RTF.  Because it's being saved in the RTF part of the clipboard, Windows will do all the display work upon paste.

The built-in wxClipboard would only allow us to paste into the RTF clipboard format if we created our own RTF text object.  Here, we will talk with the clipboard a little more directly to get things done.

== Special Concerns ==
This approach is rather hackish for showing Rtf and probably has some issues. The main concern is that the user's existing clipboard data will be erased through this process.  Included, though, are methods thats save the user's existing clipboard initially and restore them in the end.  This clipboard backup doesn't seem to work copied files, image data, etc...only text-based things (HTML,RTF,Unicode,text).

**Also, justification information isn't pasted.

== Code to Write RTF to the Clipboard ==
Do the imports.

{{{
#!python
import wx
import random
import time
import win32clipboard
}}}
Register the RTF format with the clipboard.

{{{
#!python
def __init__(self):
    self.CF_RTF = win32clipboard.RegisterClipboardFormat("Rich Text Format")
}}}
Put stuff on the board.

{{{
#!python
# Puts 'toPaste' on the clipboard
def setClipboard(self,toPaste):
    cbOpened = False
    # Wait for board availability, then do operations
    while not cbOpened:
        try:
            win32clipboard.OpenClipboard(0)
            cbOpened = True
            win32clipboard.EmptyClipboard() # need to empty, or prev data will stay
            win32clipboard.SetClipboardData(self.CF_RTF, toPaste)
            win32clipboard.CloseClipboard()
        except Exception, err:
            # If access is denied, that means that the clipboard is in use.
            # Keep trying until it's available.
            if err[0] == 5:  #Access Denied
                pass
                #print 'waiting on clipboard...'
                # wait on clipboard because something else has it. we're waiting a
                # random amount of time before we try again so we don't collide again
                time.sleep( random.random()/50 )
            elif err[0] == 1418:  #doesn't have board open
                pass
            elif err[0] == 0:  #open failure
                pass
            else:
                print 'ERROR in Clipboard section of readcomments: %s' % err
                pass
}}}
What's happening is that it tries to open the clipboard, but it may be in use, so we keep trying until it's available.  The board is then emptied, data is written, and the board closed.

When board is inaccessible, we wait a small random amount of time before trying again so that if the program that was originally holding onto the board and not allowing us access tries to connect back, there will less of a chance of another 'collision'. (Think networking)  This is mainly necessary if you have two programs running simultaneously that are trying to access the clipboard repeatedly and very frequently.

=== Code to Save Clipboard ===
This saves the user's data. The CF_HDROP format (#15) can't just be straight copied, so we give up and move on.

{{{
#!python
# Save the user's existing clipboard data, if possible. It is unable to save
# copied files, image data, etc; text, HTML, RTF, etc are preserved just fine
def saveClipboard(self):
    cbOpened = False
    while not cbOpened:
        try:
            win32clipboard.OpenClipboard(0)
            cbOpened = True

            self.cbSaved = {}
            rval = win32clipboard.EnumClipboardFormats( 0 )
            while rval != 0:
                #print "Retrieving CB format %d" % rval
                dat = win32clipboard.GetClipboardData( rval )
                if rval == 15:  #CF_HDROP
                    #this'll error, so just give up
                    self.cbSaved = {}
                    win32clipboard.EmptyClipboard()
                    break
                else:
                    self.cbSaved[ rval ] = win32clipboard.GetClipboardData( rval )
                rval = win32clipboard.EnumClipboardFormats( rval )
            win32clipboard.CloseClipboard()
        except Exception, err:
            if err[0] == 5:  #Access Denied
                #print 'waiting on clipboard...'
                time.sleep( random.random()/50 )
                pass
            elif err[0]== 6:
                #print 'clipboard type error, aborting...'
                win32clipboard.CloseClipboard()
                break
            elif err[0] == 1418:  #doesn't have board open
                cbOpened = False
            elif err[0] == 0:  #open failure
                cbOpened = False
            else:
                print 'Error while saving clipboard: %s' % err
                pass
}}}
=== Code to Restore Clipboard ===
{{{
#!python
# Restore the user's clipboard, if possible
def restoreClipboard(self):
    cbOpened = False

    # don't wait for the CB if we don't have to
    if len(self.cbSaved) > 0:
        #open clipboard
        while not cbOpened:
            try:
                win32clipboard.OpenClipboard(0)
                win32clipboard.EmptyClipboard()
                cbOpened = True
            except Exception, err:
                if err[0] == 5:  #Access Denied
                    #print 'waiting on clipboard...'
                    time.sleep( random.random()/50 )
                    pass
                elif err[0] == 1418:  #doesn't have board open
                    cbOpened = False
                elif err[0] == 0:  #open failure
                    cbOpened = False
                else:
                    print 'Error with clipboard restoration: %s' % err
                    pass
        #replace items
        try:
            for item in self.cbSaved:
                data = self.cbSaved.get(item)
                # windows appends NULL to most clipboard items, so strip off the NULL
                if data[-1] == '\0':
                    data = data[:-1]
                win32clipboard.SetClipboardData( item, data )
        except Exception, err:
            #print 'ERR: %s' % err
            win32clipboard.EmptyClipboard()
        try:
            win32clipboard.CloseClipboard()
        except:
            pass
}}}
=== Test Program ===
I put these methods into a module called 'rtfClip'.  Here is a test program that shows the functionality of this process.

{{{
#!python
import wx
import rtfClip

class App(wx.PySimpleApp):
    def OnInit(self):
        frame = wx.Frame(parent=None,size=(640,480),title='Test RTF')
        tbox = wx.TextCtrl(frame,-1,size=(100,100),
                           style=wx.TE_MULTILINE | wx.TE_RICH2 | wx.EXPAND)
        frame.Show()

        sampleData = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033" + \
                     "{\\fonttbl{\\f0\\fswiss\\fcharset0 Arial;}" + \
                     "{\\f1\\fmodern\\fprq1\\fcharset0 Courier New;}" + \
                     "{\\f2\\fdecor\\fprq2\\fcharset0 Broadway;}}\n" + \
                     "{\\colortbl ;\\red0\\green0\\blue255;\\red128\\green0\\blue128;" + \
                     "\\red255\\green0\\blue255;}\n" + \
                     "\\viewkind4\\uc1\\pard\\f0\\fs24 This sample \\b TEXT \\b0 shows how" + \
                     "\\ul\\i RTF \\ulnone\\i0 data\\fs20\\par\n" + \
                     "\\f1\\fs22 can be displayed in a \\cf1 wxTextCtrl \\cf0 text box" + \
                     "by first copying the data\\f0\\fs20\\par\n" + \
                     "\\f2\\fs32 to the \\cf2 clipboard\\cf0 , then \\cf3 pasting \\cf0 it" + \
                     "into the box.\\f0\\fs20\\par\n}"

        rtf = rtfClip.rtfClip()
        rtf.saveClipboard() #Save the current user's clipboard
        rtf.setClipboard(sampleData)  #Put our RTF on the clipboard
        tbox.Paste()  #Paste in into the textbox
        rtf.restoreClipboard()  #Restore the user's clipboard
        return True

if __name__ == '__main__':
    app = App()
    app.MainLoop()
}}}

So that's that. Please let me know if you have a better way of displaying RTF as well as backing up/restoring the clipboard--one that will work in ''all'' cases. --JonathanRice

=== Comments ===