I would need some of your expertise concerning GUI and more precisely PyQt4.
Context
I am currently designing a GUI with PyQt. It was previously done with wxPython, but I was kind of force to migrate to Qt due to internal issue.
At some point, I needed to display traditional RTF content, ie including hidden tags such as {\rtf1\ansi\ansicpg1252\deff0\deflang1036\deflangfe1036{\fonttbl{\f0\fswiss\fprq2\fcharset0 Calibri;}{\f1\froman\fprq2\fcharset2 Symbol;}}
, \tx360
or \par
and so on.
If I'm not mistaking, QTextEdit from PyQt can't "interpret" this RTF and will just display the whole string. But neither did wxPython and I had found a workaround provided by the wxPython community, which was to copy the string to windows clipboard and then paste it in the wanted text widget.
Thus, I had this piece of code:
class rtfClip():
def __init__(self):
self.CF_RTF = win32clipboard.RegisterClipboardFormat("Rich Text Format")
# 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
# 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
# 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
And then I just had to paste my RTF string in the associated widget:
rtf = copy_to_clipboard.rtfClip()
rtf.saveClipboard() # Save the current user's clipboard
rtf.setClipboard(my_rtf_string_full_of_rtf_tags) # Put our RTF on the clipboard
preview_dlg = preview_rtf_text(None)
preview_dlg.preview_rtf_ctrl.SetEditable(True)
preview_dlg.preview_rtf_ctrl.Paste() # Paste in into the textbox
rtf.restoreClipboard() # Restore the user's clipboard
preview_dlg.ShowModal()
preview_dlg.Destroy()
(preview_rtf_text being a class with only a TextCtrl named preview_rtf_ctrl)
Problem
My problem is that for any reason I can't manage to get this solution working with PyQt.
I have attempted designing a very similar solution with
rtf = copy_to_clipboard.rtfClip()
rtf.saveClipboard() # Save the current user's clipboard
rtf.setClipboard(rtf_content) # Put our RTF on the clipboard
#
rtf_preview_dlg = AEM_RTF_preview(self)
rtf_preview_dlg.rtf_preview_ctl.setReadOnly(False)
rtf_preview_dlg.rtf_preview_ctl.setAcceptRichText(True)
cursor = QtGui.QTextCursor(rtf_preview_dlg.rtf_preview_ctl.document())
cursor.setPosition(0)
rtf_preview_dlg.rtf_preview_ctl.setTextCursor(cursor)
rtf_preview_dlg.rtf_preview_ctl.paste()
rtf.restoreClipboard() # Restore the user's clipboard
rtf_preview_dlg.rtf_preview_ctl.setReadOnly(True)
rtf_preview_dlg.exec_()
But for any reason this won't work: nothing is pasted to the QTextEdit (rtf_preview_ctl).
I saw on some topics that PyQt had its own clipboard, but how would I make him "take" the content from the windows one? Is it even a solution?
Sorry for the very long question, I hope some of you may have an idea, since it would be an important feature of the GUI.
EDIT : There might be other solution for my need, my dream would just be to display formatted microsoft RTF content, one way or another.
wx.TE_RICH2
to the TextCtrl test I made. The reason it works is that most widgets wx uses are abstractions to the platform's native widgets, so you're able to paste rtf contents only because the native widget API that Windows exposes allows you to do that: in fact, with the same code I'm able to paste RTF content if I run it under Wine, but not on Linux. The only solution is to find a valid rtf to x(ht)ml converter (there are many, but most of them have limitations) or make your own parser, as Qt has its own native widgets and never uses the platform ones. – musicamante