Source code for camelot.view.controls.editors.richtexteditor

#  ============================================================================
#
#  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()