I am trying to create a label with text outline. I just want a simple white text with black outline. I first tried to do it in css like this label.setStyleSheet("color:white; outline:2px black;")
but outline didn’t do anything.
I did lots of searching and found the way to do it with qpainter path. But the problem is that the text is always cut off.
According to the function the text is supposed to be started from the bottom left but it starts too low and left. I know I can find a point by trial error so it doesn’t gets cut off- you can -20 to the height and it will be fine enough for this one. But it will only fix this specific text! It wont be the same for any label with a different size or text or font.
I will put the minimal code example here
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QWidget, QLabel, QMainWindow
class MainLabel(QLabel):
def __init__(self, text):
super(MainLabel, self).__init__(text)
def paintEvent(self, event):
qp = QtGui.QPainter()
qp.begin(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing)
font=QtGui.QFont()
font.setPointSize(70)
painterPath = QtGui.QPainterPath()
#how to get the right positioning for addText
painterPath.addText(0, self.height(), font,self.text())#HERE
qp.strokePath(painterPath, QtGui.QPen(QtGui.QColor(0,0,0), 6))
qp.fillPath(painterPath, QtGui.QColor(255,255,255))
qp.end()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.centralWidget=QWidget(self)
self.setCentralWidget(self.centralWidget)
self.lay = QtWidgets.QVBoxLayout()
self.centralWidget.setLayout(self.lay)
self.label = MainLabel("text gets cut off")
self.label.setStyleSheet("font-size:70pt;color:white; outline:2px black;")
self.lay.addWidget(self.label)
self.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
sys.exit(app.exec_())
It is such a common thing that I see literally everywhere but yet there is no simple function for this in PYQT without causing more issue? So the normal Qlabel will deal with the positioning automatically, but if you want text outline you have to give that up!
So I am asking how to find the correct positioning like a normal Qlabel if this is the only way to have text outline, otherwise if there is some other way that is better please tell me.
Just because there is no convenient function or stylesheet property does not mean there is no consistent solution!
There are a number of properties to consider to set the baseline position of the text: the geometry of the QLabel, boundingRect of the text, alignment, indent, font metrics. The outlined text is going to be larger overall than regular text of the same point size, so the
sizeHint
andminimumSizeHint
are reimplemented to account for it. The docs explain how indent is calculated and used with alignment. The text and character geometry, ascent, descent, and bearings are obtained from QFontMetrics. With this information a position forQPainterPath.addText
can be determined that will emulate QLabel.You can set the
OutlinedLabel
fill and outline color withsetBrush
andsetPen
. The default is white text with a black outline. The outline thickness is based on the point size of the font, the default ratio is 1/25 (i.e. a 25pt font will have a 1px thick outline). UsesetOutlineThickness
to change it. If you want a fixed outline not based on the point size (e.g. 3px), callsetScaledOutlineMode(False)
andsetOutlineThickness(3)
.This class only supports single line, plain text strings with left/right/top/bottom/center alignment. If you want other QLabel features like hyperlinks, word wrap, elided text, etc. those will need to be implemented too. But chances are you wouldn’t use text outline in those cases anyway.
Here is an example to show that it will work for a variety of labels:
Now Qt actually comes in clutch because you can get so much more out of this than just solid color text and outlines with all the QBrush/QPen options:
Note that I’ve chosen to treat OutlinedLabel like a QGraphicsItem with the
setBrush
/setPen
methods. If you want to use style sheets for the text color fill the path withqp.fillPath(path, self.palette().text())
Another option instead of calling
QPainter.strokePath
and thenQPainter.fillPath
is to generate a fillable outline of the text path with QPainterPathStroker, but I’ve noticed it’s slower. I would only use it to adjust the clarity of very small text by setting a larger width to the stroker than the pen. To try it replace the last 5 lines inpaintEvent
with: