# ============================================================================
#
# Copyright (C) 2007-2013 Conceptive Engineering bvba. All rights reserved.
# www.conceptive.be / info@conceptive.be
#
# This file is part of the Camelot Library.
#
# This file may be used under the terms of the GNU General Public
# License version 2.0 as published by the Free Software Foundation
# and appearing in the file license.txt included in the packaging of
# this file. Please review this information to ensure GNU
# General Public Licensing requirements will be met.
#
# If you are unsure which license is appropriate for your use, please
# visit www.python-camelot.com or contact info@conceptive.be
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# For use of this library in commercial applications, please contact
# info@conceptive.be
#
# ============================================================================
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import Qt
from wideeditor import WideEditor
from customeditor import CustomEditor
from camelot.view.art import Icon
[docs]class CustomTextEdit(QtGui.QTextEdit):
"""
A TextEdit editor that sends editingFinished events
when the text was changed and focus is lost.
"""
editingFinished = QtCore.pyqtSignal()
receivedFocus = QtCore.pyqtSignal()
def __init__(self, parent):
super(CustomTextEdit, self).__init__(parent)
self._changed = False
self.setTabChangesFocus( True )
self.textChanged.connect( self._handle_text_changed )
def focusInEvent(self, event):
super(CustomTextEdit, self).focusInEvent( event )
self.receivedFocus.emit()
def focusOutEvent(self, event):
if self._changed:
self.editingFinished.emit()
super(CustomTextEdit, self).focusOutEvent( event )
def _handle_text_changed(self):
self._changed = True
def setTextChanged(self, state=True):
self._changed = state
def setHtml(self, html):
QtGui.QTextEdit.setHtml(self, html)
self._changed = False
class RichTextEditor(CustomEditor, WideEditor):
def __init__(self,
parent = None,
field_name = 'richtext',
**kwargs):
CustomEditor.__init__(self, parent)
self.setObjectName( field_name )
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setContentsMargins( 0, 0, 0, 0)
self.setSizePolicy( QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding )
self.textedit = CustomTextEdit(self)
self.initToolbar() # Has to be invoked before the connect's below.
self.toolbar.hide() # Should only be visible when textedit is focused.
self.textedit.editingFinished.connect(self.emit_editing_finished)
self.textedit.receivedFocus.connect(self.toolbar.show)
self.textedit.setAcceptRichText(True)
# Layout
self.layout.addWidget(self.toolbar)
self.layout.addWidget(self.textedit)
self.setLayout(self.layout)
# Format
self.textedit.setFontWeight(QtGui.QFont.Normal)
self.textedit.setFontItalic(False)
self.textedit.setFontUnderline(False)
#self.textedit.setFocus(Qt.OtherFocusReason)
self.update_alignment()
self.textedit.currentCharFormatChanged.connect(self.update_format)
self.textedit.cursorPositionChanged.connect(self.update_text)
@QtCore.pyqtSlot()
def emit_editing_finished(self):
if self.textedit._changed:
self.editingFinished.emit()
def set_editable(self, editable):
self.textedit.setReadOnly( not editable )
def set_field_attributes(self, editable=True, background_color=None, **kwargs):
self.set_editable(editable)
self.set_background_color(background_color)
def set_toolbar_hidden( self, hidden ):
"""Show or hide the toolbar, by default the toolbar is hidden until
the user starts editing.
:param hidden: `True` or `False`
"""
toolbar = self.findChild( QtGui.QToolBar )
if toolbar:
toolbar.setHidden( hidden )
def initToolbar(self):
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setObjectName( 'toolbar' )
self.toolbar.setOrientation(Qt.Horizontal)
self.toolbar.setContentsMargins(0, 0, 0, 0)
self.bold_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/format-text-bold.png').getQIcon()
self.bold_button.setIcon(icon)
self.bold_button.setAutoRaise(True)
self.bold_button.setCheckable(True)
self.bold_button.setFocusPolicy( Qt.ClickFocus )
self.bold_button.setMaximumSize(QtCore.QSize(20, 20))
self.bold_button.setShortcut(QtGui.QKeySequence('Ctrl+B'))
self.bold_button.setToolTip('Bold')
self.bold_button.clicked.connect(self.set_bold)
self.italic_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/format-text-italic.png').getQIcon()
self.italic_button.setIcon(icon)
self.italic_button.setAutoRaise(True)
self.italic_button.setCheckable(True)
self.italic_button.setFocusPolicy( Qt.ClickFocus )
self.italic_button.setMaximumSize(QtCore.QSize(20, 20))
self.italic_button.setShortcut(QtGui.QKeySequence('Ctrl+I'))
self.italic_button.setToolTip('Italic')
self.italic_button.clicked.connect(self.set_italic)
self.underline_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/format-text-underline.png').getQIcon()
self.underline_button.setIcon(icon)
self.underline_button.setAutoRaise(True)
self.underline_button.setCheckable(True)
self.underline_button.setFocusPolicy( Qt.ClickFocus )
self.underline_button.setMaximumSize(QtCore.QSize(20, 20))
self.underline_button.setShortcut(QtGui.QKeySequence('Ctrl+U'))
self.underline_button.setToolTip('Underline')
self.underline_button.clicked.connect(self.set_underline)
self.copy_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/edit-copy.png').getQIcon()
self.copy_button.setIcon(icon)
self.copy_button.setAutoRaise(True)
self.copy_button.setMaximumSize(QtCore.QSize(20, 20))
self.copy_button.setFocusPolicy( Qt.ClickFocus )
self.copy_button.setToolTip('Copy')
self.copy_button.clicked.connect(self.textedit.copy)
self.cut_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/edit-cut.png').getQIcon()
self.cut_button.setIcon(icon)
self.cut_button.setAutoRaise(True)
self.cut_button.setMaximumSize(QtCore.QSize(20, 20))
self.cut_button.setToolTip('Cut')
self.cut_button.clicked.connect(self.textedit.cut)
self.cut_button.setFocusPolicy( Qt.ClickFocus )
self.paste_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/edit-paste.png').getQIcon()
self.paste_button.setIcon(icon)
self.paste_button.setAutoRaise(True)
self.paste_button.setMaximumSize(QtCore.QSize(20, 20))
self.paste_button.setFocusPolicy( Qt.ClickFocus )
self.paste_button.setToolTip('Paste')
self.paste_button.clicked.connect(self.textedit.paste)
self.alignleft_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/format-justify-left.png').getQIcon()
self.alignleft_button.setIcon(icon)
self.alignleft_button.setAutoRaise(True)
self.alignleft_button.setCheckable(True)
self.alignleft_button.setMaximumSize(QtCore.QSize(20, 20))
self.alignleft_button.setFocusPolicy( Qt.ClickFocus )
self.alignleft_button.setToolTip('Align left')
self.alignleft_button.clicked.connect(self.set_alignleft)
self.aligncenter_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/format-justify-center.png').getQIcon()
self.aligncenter_button.setIcon(icon)
self.aligncenter_button.setAutoRaise(True)
self.aligncenter_button.setCheckable(True)
self.aligncenter_button.setMaximumSize(QtCore.QSize(20, 20))
self.aligncenter_button.setFocusPolicy( Qt.ClickFocus )
self.aligncenter_button.setToolTip('Align Center')
self.aligncenter_button.clicked.connect(self.set_aligncenter)
self.alignright_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/format-justify-right.png').getQIcon()
self.alignright_button.setIcon(icon)
self.alignright_button.setAutoRaise(True)
self.alignright_button.setCheckable(True)
self.alignright_button.setMaximumSize(QtCore.QSize(20, 20))
self.alignright_button.setFocusPolicy( Qt.ClickFocus )
self.alignright_button.setToolTip('Align Right')
self.alignright_button.clicked.connect(self.set_alignright)
self.zoomin_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/list-add.png').getQIcon()
self.zoomin_button.setIcon(icon)
self.zoomin_button.setAutoRaise(True)
self.zoomin_button.setCheckable(True)
self.zoomin_button.setMaximumSize(QtCore.QSize(20, 20))
self.zoomin_button.setFocusPolicy( Qt.ClickFocus )
self.zoomin_button.setToolTip('Zoom in')
self.zoomin_button.clicked.connect(self.zoomin)
self.zoomout_button = QtGui.QToolButton(self)
icon = Icon('tango/16x16/actions/list-remove.png').getQIcon()
self.zoomout_button.setIcon(icon)
self.zoomout_button.setAutoRaise(True)
self.zoomout_button.setCheckable(True)
self.zoomout_button.setMaximumSize(QtCore.QSize(20, 20))
self.zoomout_button.setFocusPolicy( Qt.ClickFocus )
self.zoomout_button.setToolTip('Zoom out')
self.zoomout_button.clicked.connect(self.zoomout)
self.color_button = QtGui.QToolButton(self)
self.color_button.setAutoRaise(True)
self.color_button.setMaximumSize(QtCore.QSize(20, 20))
self.color_button.setFocusPolicy( Qt.ClickFocus )
self.color_button.setToolTip('Color')
self.color_button.clicked.connect(self.set_color)
self.toolbar.addWidget(self.copy_button)
self.toolbar.addWidget(self.cut_button)
self.toolbar.addWidget(self.paste_button)
self.toolbar.addSeparator()
self.toolbar.addWidget(self.bold_button)
self.toolbar.addWidget(self.italic_button)
self.toolbar.addWidget(self.underline_button)
self.toolbar.addSeparator()
self.toolbar.addWidget(self.alignleft_button)
self.toolbar.addWidget(self.aligncenter_button)
self.toolbar.addWidget(self.alignright_button)
self.toolbar.addSeparator()
self.toolbar.addWidget(self.color_button)
self.toolbar.addSeparator()
self.toolbar.addWidget(self.zoomin_button)
self.toolbar.addWidget(self.zoomout_button)
#
# Button methods
#
def set_bold(self):
if self.bold_button.isChecked():
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setFontWeight(QtGui.QFont.Bold)
else:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setFontWeight(QtGui.QFont.Normal)
def set_italic(self, bool):
if bool:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setFontItalic(True)
else:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setFontItalic(False)
def set_underline(self, bool):
if bool:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setFontUnderline(True)
else:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setFontUnderline(False)
def zoomin( self ):
self.textedit.zoomIn()
self.textedit.setFocus(Qt.OtherFocusReason)
def zoomout( self ):
self.textedit.zoomOut()
self.textedit.setFocus(Qt.OtherFocusReason)
def set_alignleft(self, bool):
if bool:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setAlignment(Qt.AlignLeft)
self.update_alignment(Qt.AlignLeft)
def set_aligncenter(self, bool):
if bool:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setAlignment(Qt.AlignCenter)
self.update_alignment(Qt.AlignCenter)
def set_alignright(self, bool):
if bool:
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setAlignment(Qt.AlignRight)
self.update_alignment(Qt.AlignRight)
def update_alignment(self, al=None):
if al is None:
al = self.textedit.alignment()
if al == Qt.AlignLeft:
self.alignleft_button.setChecked(True)
self.aligncenter_button.setChecked(False)
self.alignright_button.setChecked(False)
elif al == Qt.AlignCenter:
self.aligncenter_button.setChecked(True)
self.alignleft_button.setChecked(False)
self.alignright_button.setChecked(False)
elif al == Qt.AlignRight:
self.alignright_button.setChecked(True)
self.alignleft_button.setChecked(False)
self.aligncenter_button.setChecked(False)
def set_color(self):
color = QtGui.QColorDialog.getColor(self.textedit.textColor())
if color.isValid():
self.textedit.setFocus(Qt.OtherFocusReason)
self.textedit.setTextColor(color)
pixmap = QtGui.QPixmap(16, 16)
pixmap.fill(color)
self.color_button.setIcon(QtGui.QIcon(pixmap))
def update_color(self):
color = self.textedit.textColor()
pixmap = QtGui.QPixmap(16, 16)
pixmap.fill(color)
self.color_button.setIcon(QtGui.QIcon(pixmap))
def update_format(self, format):
font = format.font()
self.bold_button.setChecked(font.bold())
self.italic_button.setChecked(font.italic())
self.underline_button.setChecked(font.underline())
self.update_alignment(self.textedit.alignment())
def update_text(self):
self.update_alignment()
self.update_color()
def get_value(self):
from xml.dom import minidom
tree = minidom.parseString(unicode(self.textedit.toHtml()).encode('utf-8'))
value = u''.join([node.toxml() for node in tree.getElementsByTagName('html')[0].getElementsByTagName('body')[0].childNodes])
return CustomEditor.get_value(self) or value
def set_document( self, document ):
"""
:param document: a :class:`QtGui.QTextDocument` object.
"""
self.textedit.setDocument( document )
def set_value( self, value ):
value = CustomEditor.set_value(self, value)
if value!=None:
if unicode(self.textedit.toHtml())!=value:
self.update_alignment()
self.textedit.setHtml(value)
self.update_color()
else:
self.textedit.clear()