Search code examples
pythonpyqt5borderpyside2qtextedit

QTextEdit corners do not follow the border boundaries assigned in style sheet


How to correct sharp corners of a QTextEdit widget applied in style sheet?
Corners of the QTextEdit being inside the border's boundaries is the purpose.

enter image description here

# importing libraries
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
import sys

class Scene(QGraphicsScene):
    def __init__(self, _scene_width, _scene_height):
        super(Scene, self).__init__()
        self.setSceneRect(0, 0, _scene_width, _scene_height)
        map_center = _scene_width / 2
        te = QTextEdit()
        te.setStyleSheet(" border-radius: 30px;background-clip: border; background-color: rgb(220, 220,220); color: rgb(113, 113, 113); font: 14pt Vazir; border:4px solid; border-color: rgb(255,95,95);")
        te.insertPlainText("Test Text")
        te.setGeometry(map_center, map_center, 100, 100)
        self.addWidget(te)


class View(QGraphicsView):
    def __init__(self, _scene_width, _scene_height):
        super(View, self).__init__()
        self.scale_factor = 1
        self.scene_width = _scene_width
        self.scene_height = _scene_height
        scene = Scene(_scene_width, _scene_height)
        self.setScene(scene)
        self.showMaximized()

def main():
    app = QApplication(sys.argv)
    view = View(1000, 1000)
    view.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Result of the code

enter image description here


Solution

  • Adding widgets to a graphics scene makes things a bit more complicated.

    The problem is that widgets without any parent are treated as top level widgets ("windows") are: even if you set a rounded border around them, the widget will still have a rectangular shape, and that shape will always be drawn using the background role palette of the widget, including what's outside the rounded edges.

    Since you are using stylesheets, one thing that can be done is to set the background palette role to transparent (this works since you're specifying the background in the stylesheet and that background will be clipped to the border):

            palette = te.palette()
            palette.setBrush(palette.Window, QBrush(Qt.transparent))
            te.setPalette(palette)
    

    Note that this might not be enough, as some styles (notably, the Oxygen style) add a default background alpha gradient to the palette while painting the widget, and there's almost nothing you can do about that, at least in this situations.

    In that case, as with top level windows, you need to set a mask on the widget. Consider that masks are bit maps, meaning that no aliasing is applied: the pixels are drawn or they are not.

            mask = QPainterPath()
            # the rectangle needs a 1 pixel adjustment to ensure that the aliasing is
            # correctly drawn, with the drawback of showing partial background pixels
            mask.addRoundedRect(QRectF(te.rect()).adjusted(-1, -1, 1, 1), 30, 30)
            te.setMask(QRegion(mask.toFillPolygon(QTransform()).toPolygon()))
    

    Finally, some important considerations:

    • stylesheets should be always used with care, as they should usually be considered for special "exceptions" only, as their behavior might be erratic;
    • using large values for border radius is not suggested, as it might clip other contents (most importantly, the scroll bars);
    • some styles apply the stylesheet parameters in different ways, especially with QAbstractScrollArea subclasses (like QTextEdit); for example, with Oxygen style, i get the rounded border inside the text area contents, while I don't get it in the Windows style;
    • it's usually good practice to use specific syntax in stylesheets, meaning that you should specify the widget class (which is important for widgets containing subwidgets like QTextEdit) and provide correct indentation:
            te.setStyleSheet('''
                QTextEdit {
                    border-radius: 30px;
                    background-clip: border;
                    background-color: rgb(220, 220,220);
                    color: rgb(113, 113, 113);
                    font: 14pt Vazir;
                    border:4px solid;
                    border-color: rgb(255,95,95);
                }
            ''')