<p>在样式表中为顶级小部件</em>(有自己“窗口”的小部件)设置边框半径是不够的</p>
<p>虽然克里斯蒂安·卡彻提出的<a href="https://stackoverflow.com/a/65576117/2001654">solution</a>很好,但需要考虑两个重要因素:</p>
<ol>
<li>系统<em>必须</em>支持合成;虽然大多数现代操作系统都是如此,但至少在Linux上,即使是最新的系统也有可能选择不支持它(我在计算机上禁用了它);如果是这种情况,设置<code>WA_TranslucentBackground</code>属性将不起作用</李>
<li>在Linux上不应设置<code>FramelessWindowHint</code>,因为它可能会导致窗口管理器出现问题,因此只有在确保操作系统需要它(Windows)后才应设置它</李>
</ol>
<p>有鉴于此,每当不支持合成时,使用<a href="https://doc.qt.io/qt-5/qwidget.html#setMask-1" rel="nofollow noreferrer">^{<cd3>}</a>是正确的修复方法,这必须发生在<code>resizeEvent()</code>中。请注意,掩蔽是基于位图<em>的,抗锯齿是不受支持的,因此根据边界半径,圆形边界有时有点难看</p>
<p>此外,由于您需要自定义颜色,因此必须使用样式表,因为QMenu的自定义绘制很难实现</p>
<pre><code>class AddContextMenu(QtWidgets.QMenu):
def __init__(self, *args, **kwargs):
super(AddContextMenu, self).__init__()
self.setMinimumSize(150, 200)
self.radius = 4
self.setStyleSheet('''
QMenu {{
background: blue;
border: 2px solid red;
border-radius: {radius}px;
}}
QMenu::item {{
color: white;
}}
QMenu::item:selected {{
color: red;
}}
'''.format(radius=self.radius))
def resizeEvent(self, event):
path = QtGui.QPainterPath()
# the rectangle must be translated and adjusted by 1 pixel in order to
# correctly map the rounded shape
rect = QtCore.QRectF(self.rect()).adjusted(.5, .5, -1.5, -1.5)
path.addRoundedRect(rect, self.radius, self.radius)
# QRegion is bitmap based, so the returned QPolygonF (which uses float
# values must be transformed to an integer based QPolygon
region = QtGui.QRegion(path.toFillPolygon(QtGui.QTransform()).toPolygon())
self.setMask(region)
</code></pre>
<p>关于您的paintEvent实现的一些旁注,由于上述原因在本例中不是必需的,但仍然很重要(有些要点与已注释的代码部分有关,但您尝试了这些方面的事实值得一提):</p>
<ol>
<li>用于小部件的QPainter必须<strong>从不在<code>paintEvent()</code>外部实例化:像您那样在<code>__init__</code>中创建实例是一个严重错误,甚至可能导致崩溃。油漆工只能在收到油漆事件时创建,且不得重复使用。这显然使得将其设置为实例属性(<code>self.painter</code>)毫无用处,因为在绘制事件之后没有实际理由访问它</李>
<li>如果画笔宽度始终相同,那么只需在构造函数中设置它(<code>self.pen = QtGui.QPen(QtCore.Qt.red, 2)</code>),在paintEvent中连续设置它是无用的</李>
<li>QPen和QBrush可以直接接受Qt全局颜色,因此无需创建QBrush实例,因为画师将自动(内部和快速)设置它:<code>self.painter.setBrush(QtCore.Qt.blue)</code></李>
<li><code>self.update()</code>不应在paintEvent中调用<strong>(甚至不应<code>self.repaint()</code>)。结果是未定义的,可能是危险的</李>
<li>如果您使用QPaint进行一些手动绘制,然后调用super paintEvent,结果很可能是之前绘制的所有内容都将被隐藏;一般来说,基本实现应该首先被称为<em></em>,然后任何其他自定义绘制都应该在</em>之后进行<em>(在这种情况下,它显然不起作用,因为您将绘制一个填充的圆形矩形,使菜单项不可见)</李>
</ol>