简体   繁体   中英

How to make label text outlined AND fit the size of the label

How to make label text to be outlined (for better visibility on transparent Widget) and to be fit into label (as per function setWordWrap (True)? There are examples of how to do one or another, but never both.

Basic example of static label text on transparent widget is:

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class Application(QWidget):
    def __init__(self):
        super().__init__()
        screen_size = QWidget.screen(self).size()
        screen_width = screen_size.width()
        screen_height = screen_size.height()
        widget_width = screen_width * 0.30


        self.setGeometry(100, 100, widget_width, screen_height)
        self.move(screen_width - widget_width, 0)
        self.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
                            | Qt.CustomizeWindowHint | Qt.Window)

        l1 = QLabel(self)
        l1.setWordWrap(True)
        l1.setGeometry(0, 0, widget_width, screen_height)
        l1.setFont(QFont('Arial', 22))
        l1.setStyleSheet('color:rgb(0,255,0)')
        l1.setText('Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello')


def render_the_app():
    app = QApplication(sys.argv)
    window = Application()
    window.setAttribute(Qt.WA_TranslucentBackground)
    window.show()


    app.exec_()


render_the_app()

This code example will render green color text that is "Hello Hello..." and renders as: wrap text example on transparent widget:
在透明小部件上包装文本示例

Using a painter path is a fine solution, but it doesn't allow finer control on the contents, especially considering that a text-based widget should also provide correct size hints and, possibly, allow word wrapping.

On the other hand, QTextDocument , which is used by any QWidget subclass that uses text (sometimes publicly, like QTextEdit, others internally, like QLabel), provides the outline feature, so a possible solution is to create a class that partially mimics a QLabel, providing correct size hints and word wrapping.

class Test(QtWidgets.QWidget):
    _outlinePen = _alignment = None
    def __init__(self, text='', parent=None, **kwargs):
        super().__init__(parent)
        self.doc = QtGui.QTextDocument()
        self._reference = self.doc.clone()
        self.doc.contentsChanged.connect(self._rebuildReference)

        self._text = text
        self._outlineColor = QtGui.QColor(kwargs.get('outlineColor'))
        self._outlineWidth = kwargs.get('outlineWidth', 0)

        if text:
            self.doc.setPlainText(text)
            self.updateContents()

    def _rebuildReference(self):
        self._reference = self.doc.clone()

    def updateContents(self):
        cursor = QtGui.QTextCursor(self.doc)
        cursor.select(cursor.Document)
        blockFmt = cursor.blockFormat()
        blockFmt.setAlignment(self.alignment())
        cursor.setBlockFormat(blockFmt)
        charFmt = cursor.charFormat()
        charFmt.setFont(self.font())

        outlinePen = self.outlinePen()
        if outlinePen:
            charFmt.setTextOutline(outlinePen)

        cursor.setCharFormat(charFmt)
        self.updateGeometry()
        self.adjustSize()

    def text(self):
        return self._text

    def setText(self, text):
        if self._text == text:
            return
        self.doc.setPlainText(text)
        self.updateContents()

    def outlinePen(self):
        if isinstance(self._outlineColor, QtGui.QColor) and self._outlineColor.isValid():
            pen = QtGui.QPen(self._outlineColor)
            pen.setWidthF(self._outlineWidth or self.fontMetrics().lineWidth() * .25)
            return pen

    def setOutlinePen(self, pen):
        self._outlineColor = pen.brush().color()
        self._outlineWidth = pen.widthF()
        self.updateContents()

    def outlineColor(self):
        return self._outlineColor

    def setOutlineColor(self, color):
        if self._outlineColor != color:
            self._outlineColor = color
            self.updateContents()

    def outlineWidth(self):
        return self._outlineWidth

    def setOutlineWidth(self, width):
        if self._outlineWidth != width:
            self._outlineWidth = width
            self.updateContents()

    def alignment(self):
        alignment = self._alignment
        if not isinstance(alignment, (QtCore.Qt.Alignment, QtCore.Qt.AlignmentFlag)):
            alignment = QtCore.Qt.AlignTop
            if self.layoutDirection() == QtCore.Qt.LeftToRight:
                alignment |= QtCore.Qt.AlignLeft
            else:
                alignment |= QtCore.Qt.AlignRight
        return alignment

    def setAlignment(self, alignment):
        if self._alignment != alignment:
            self._alignment = alignment
            self.updateContents()

    def changeEvent(self, event):
        if event.type() == event.FontChange:
            self.updateContents()

    def sizeHint(self):
        if self.isVisible():
            doc = self._reference
            doc.setTextWidth(self.width())
        else:
            doc = self.doc
        return doc.size().toSize()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        self._reference.setTextWidth(self.width())
        self._reference.drawContents(qp, QtCore.QRectF(self.rect()))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM