Inconsistent borders when drawing a custom translucent widget using QPainterPath
Unsolved
General and Desktop
-
Hi,
I am trying to draw a widget with a triangular tip at the bottom with the help of this guide. The shape is drawn as expected but trying to add a border to it makes it sag from the bottom:This is my first attempt at making a custom shaped widget and would appreciate if someone could help me in figuring this out :)
Please note that the code is in python but I do not mind looking at C++ sources and documentation.import typing as _t from Qt import ( QtCore as _QtCore, QtGui as _QtGui, QtWidgets as _QtWidgets, ) class _TipTriangleDimensions(_t.NamedTuple): height: float = 15.0 base: float = 20.0 class _TipTriangleCords(_t.NamedTuple): origin: _QtCore.QPointF tip: _QtCore.QPointF base: _QtCore.QPointF class QBalloonTip(_QtWidgets.QWidget): def __init__( self, message: str, parent: _t.Optional[_QtWidgets.QWidget] = None ) -> None: super().__init__( parent=parent, f=_QtCore.Qt.FramelessWindowHint, ) self.setAttribute(_QtCore.Qt.WA_TranslucentBackground) main_layout = _QtWidgets.QVBoxLayout() self._setup_ui(main_layout) self._message_label.setText(message) self._mask_path: _QtGui.QPainterPath = self._get_mask_shape_path( _TipTriangleDimensions() ) def paintEvent(self, event): painter = _QtGui.QPainter(self) painter.setRenderHint(_QtGui.QPainter.RenderHint.Antialiasing) painter.setBrush(_QtGui.QBrush(_QtCore.Qt.white)) border_pen = _QtGui.QPen() border_pen.setWidthF(6.0) border_pen.setColor(_QtCore.Qt.red) painter.setPen(border_pen) painter.drawPath(self._mask_path) def _get_mask_shape_path(self, triangle_d: _TipTriangleDimensions) -> _QtGui.QPainterPath: self.setContentsMargins(0, 0, 0, int(triangle_d.height)) self.updateGeometry() self_sz = _QtCore.QSizeF(self.sizeHint()) triangle_cords = self._get_triangle_shape_points(self_sz, triangle_d) origin = _QtCore.QPointF(0.0, 0.0) right = _QtCore.QPointF(self_sz.width(), triangle_cords.base.y()) top = _QtCore.QPointF(self_sz.width(), origin.y()) path = _QtGui.QPainterPath() path.moveTo(origin) path.lineTo(triangle_cords.tip) path.lineTo(triangle_cords.base) path.lineTo(right) path.lineTo(top) path.lineTo(origin) return path @staticmethod def _get_triangle_shape_points( total_size: _QtCore.QSizeF, dimensions: _TipTriangleDimensions ) -> _TipTriangleCords: origin = _QtCore.QPointF(0.5, total_size.height() - dimensions.height) tip = _QtCore.QPointF(0.0, total_size.height()) base = _QtCore.QPointF(dimensions.base, origin.y()) return _TipTriangleCords(origin, tip, base) def _setup_ui(self, main_layout: _QtWidgets.QVBoxLayout): self._message_label = _QtWidgets.QLabel(parent=self) self._message_label.setFixedWidth(200) self._message_label.setFixedHeight(100) self._message_label.setWordWrap(True) main_layout.addWidget(self._message_label) main_layout.addWidget(_QtWidgets.QPushButton("Test")) self.setLayout(main_layout) if __name__ == "__main__": app = _QtWidgets.QApplication([]) widget = QBalloonTip("test message") widget.show() app.exec_()