Source code for qwt.text_engine

# -*- coding: utf-8 -*-
#
# Licensed under the terms of the Qwt License
# Copyright (c) 2002 Uwe Rathmann, for the original C++ code
# Copyright (c) 2015 Pierre Raybaut, for the Python translation/optimization
# (see LICENSE file for more details)

"""
Text engine
-----------

QwtTextEngine
~~~~~~~~~~~~~

.. autoclass:: QwtTextEngine
   :members:
   
QwtPlainTextEngine
~~~~~~~~~~~~~~~~~~

.. autoclass:: QwtPlainTextEngine
   :members:
   
QwtRichTextEngine
~~~~~~~~~~~~~~~~~

.. autoclass:: QwtRichTextEngine
   :members:
"""

from qwt.painter import QwtPainter

from qwt.qt.QtGui import (QTextDocument, QTextOption, QColor, QFontMetricsF,
                          QPixmap, QPainter, QFontMetrics)
from qwt.qt.QtCore import Qt, QRectF, QSizeF

import struct

QWIDGETSIZE_MAX = (1<<24)-1


def taggedRichText(text, flags):
    richText = text
    if flags & Qt.AlignJustify:
        richText = "<div align=\"justify\">" + richText + "</div>"
    elif flags & Qt.AlignRight:
        richText = "<div align=\"right\">" + richText + "</div>"
    elif flags & Qt.AlignHCenter:
        richText = "<div align=\"center\">" + richText + "</div>"
    return richText


class QwtRichTextDocument(QTextDocument):
    def __init__(self, text, flags, font):
        super(QwtRichTextDocument, self).__init__(None)
        self.setUndoRedoEnabled(False)
        self.setDefaultFont(font)
        self.setHtml(text)
        
        option = self.defaultTextOption()
        if flags & Qt.TextWordWrap:
            option.setWrapMode(QTextOption.WordWrap)
        else:
            option.setWrapMode(QTextOption.NoWrap)
        
        option.setAlignment(flags)
        self.setDefaultTextOption(option)
        
        root = self.rootFrame()
        fm = root.frameFormat()
        fm.setBorder(0)
        fm.setMargin(0)
        fm.setPadding(0)
        fm.setBottomMargin(0)
        fm.setLeftMargin(0)
        root.setFrameFormat(fm)

        self.adjustSize();


[docs]class QwtTextEngine(object): """ Abstract base class for rendering text strings A text engine is responsible for rendering texts for a specific text format. They are used by `QwtText` to render a text. `QwtPlainTextEngine` and `QwtRichTextEngine` are part of the `python-qwt` library. The implementation of `QwtMathMLTextEngine` uses code from the `Qt` solution package. Because of license implications it is built into a separate library. .. seealso:: :py:meth:`qwt.text.QwtText.setTextEngine()` """ def __init__(self): pass
[docs] def heightForWidth(self, font, flags, text, width): """ Find the height for a given width :param QFont font: Font of the text :param int flags: Bitwise OR of the flags used like in QPainter::drawText :param str text: Text to be rendered :param float width: Width :return: Calculated height """ pass
[docs] def textSize(self, font, flags, text): """ Returns the size, that is needed to render text :param QFont font: Font of the text :param int flags: Bitwise OR of the flags like in for QPainter::drawText :param str text: Text to be rendered :return: Calculated size """ pass
[docs] def mightRender(self, text): """ Test if a string can be rendered by this text engine :param str text: Text to be tested :return: True, if it can be rendered """ pass
[docs] def textMargins(self, font): """ Return margins around the texts The textSize might include margins around the text, like QFontMetrics::descent(). In situations where texts need to be aligned in detail, knowing these margins might improve the layout calculations. :param QFont font: Font of the text :return: tuple (left, right, top, bottom) representing margins """ pass
[docs] def draw(self, painter, rect, flags, text): """ Draw the text in a clipping rectangle :param QPainter painter: Painter :param QRectF rect: Clipping rectangle :param int flags: Bitwise OR of the flags like in for QPainter::drawText() :param str text: Text to be rendered """ pass
ASCENTCACHE = {}
[docs]class QwtPlainTextEngine(QwtTextEngine): """ A text engine for plain texts `QwtPlainTextEngine` renders texts using the basic `Qt` classes `QPainter` and `QFontMetrics`. """ def __init__(self): self.qrectf_max = QRectF(0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX) self._fm_cache = {} self._fm_cache_f = {} def fontmetrics(self, font): fid = font.toString() fm = self._fm_cache.get(fid) if fm is None: return self._fm_cache.setdefault(fid, QFontMetrics(font)) else: return fm def fontmetrics_f(self, font): fid = font.toString() fm = self._fm_cache_f.get(fid) if fm is None: return self._fm_cache_f.setdefault(fid, QFontMetricsF(font)) else: return fm
[docs] def heightForWidth(self, font, flags, text, width): """ Find the height for a given width :param QFont font: Font of the text :param int flags: Bitwise OR of the flags used like in QPainter::drawText :param str text: Text to be rendered :param float width: Width :return: Calculated height """ fm = self.fontmetrics_f(font) rect = fm.boundingRect(QRectF(0, 0, width, QWIDGETSIZE_MAX), flags, text) return rect.height()
[docs] def textSize(self, font, flags, text): """ Returns the size, that is needed to render text :param QFont font: Font of the text :param int flags: Bitwise OR of the flags like in for QPainter::drawText :param str text: Text to be rendered :return: Calculated size """ fm = self.fontmetrics_f(font) rect = fm.boundingRect(self.qrectf_max, flags, text) return rect.size()
def effectiveAscent(self, font): global ASCENTCACHE fontKey = font.key() ascent = ASCENTCACHE.get(fontKey) if ascent is not None: return ascent return ASCENTCACHE.setdefault(fontKey, self.findAscent(font)) def findAscent(self, font): dummy = "E" white = QColor(Qt.white) fm = self.fontmetrics(font) pm = QPixmap(fm.width(dummy), fm.height()) pm.fill(white) p = QPainter(pm) p.setFont(font) p.drawText(0, 0, pm.width(), pm.height(), 0, dummy) p.end() img = pm.toImage() w = pm.width() linebytes = w*4 for row in range(img.height()): line = img.scanLine(row).asstring(linebytes) for col in range(w): color = struct.unpack('I', line[col*4:(col+1)*4])[0] if color != white.rgb(): return fm.ascent()-row+1 return fm.ascent()
[docs] def textMargins(self, font): """ Return margins around the texts The textSize might include margins around the text, like QFontMetrics::descent(). In situations where texts need to be aligned in detail, knowing these margins might improve the layout calculations. :param QFont font: Font of the text :return: tuple (left, right, top, bottom) representing margins """ left = right = top = 0 fm = self.fontmetrics_f(font) top = fm.ascent() - self.effectiveAscent(font) bottom = fm.descent() return left, right, top, bottom
[docs] def draw(self, painter, rect, flags, text): """ Draw the text in a clipping rectangle :param QPainter painter: Painter :param QRectF rect: Clipping rectangle :param int flags: Bitwise OR of the flags like in for QPainter::drawText() :param str text: Text to be rendered """ QwtPainter.drawText(painter, rect, flags, text)
[docs] def mightRender(self, text): """ Test if a string can be rendered by this text engine :param str text: Text to be tested :return: True, if it can be rendered """ return True
[docs]class QwtRichTextEngine(QwtTextEngine): """ A text engine for `Qt` rich texts `QwtRichTextEngine` renders `Qt` rich texts using the classes of the Scribe framework of `Qt`. """ def __init__(self): pass
[docs] def heightForWidth(self, font, flags, text, width): """ Find the height for a given width :param QFont font: Font of the text :param int flags: Bitwise OR of the flags used like in QPainter::drawText :param str text: Text to be rendered :param float width: Width :return: Calculated height """ doc = QwtRichTextDocument(text, flags, font) doc.setPageSize(QSizeF(width, QWIDGETSIZE_MAX)) return doc.documentLayout().documentSize().height()
[docs] def textSize(self, font, flags, text): """ Returns the size, that is needed to render text :param QFont font: Font of the text :param int flags: Bitwise OR of the flags like in for QPainter::drawText :param str text: Text to be rendered :return: Calculated size """ doc = QwtRichTextDocument(text, flags, font) option = doc.defaultTextOption() if option.wrapMode() != QTextOption.NoWrap: option.setWrapMode(QTextOption.NoWrap) doc.setDefaultTextOption(option) doc.adjustSize() return doc.size()
[docs] def draw(self, painter, rect, flags, text): """ Draw the text in a clipping rectangle :param QPainter painter: Painter :param QRectF rect: Clipping rectangle :param int flags: Bitwise OR of the flags like in for QPainter::drawText() :param str text: Text to be rendered """ doc = QwtRichTextDocument(text, flags, painter.font()) QwtPainter.drawSimpleRichText(painter, rect, flags, doc)
def taggedText(self, text, flags): return self.taggedRichText(text,flags)
[docs] def mightRender(self, text): """ Test if a string can be rendered by this text engine :param str text: Text to be tested :return: True, if it can be rendered """ return Qt.mightBeRichText(text)
[docs] def textMargins(self, font): """ Return margins around the texts The textSize might include margins around the text, like QFontMetrics::descent(). In situations where texts need to be aligned in detail, knowing these margins might improve the layout calculations. :param QFont font: Font of the text :return: tuple (left, right, top, bottom) representing margins """ return 0, 0, 0, 0