我正在尝试覆盖
paintEvent()
的 QMenu
使其具有圆角。
上下文菜单应该看起来像这样。
这是我尝试过的代码,但什么也没出现:
from PyQt5 import QtWidgets, QtGui, QtCore
import sys
class Example(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Context menu')
self.show()
def contextMenuEvent(self, event):
cmenu = AddContextMenu(self)
newAct = cmenu.addAction("New")
openAct = cmenu.addAction("Open")
quitAct = cmenu.addAction("Quit")
action = cmenu.exec_(self.mapToGlobal(event.pos()))
class AddContextMenu(QtWidgets.QMenu):
def __init__(self, *args, **kwargs):
super(AddContextMenu, self).__init__()
self.painter = QtGui.QPainter(self)
self.setMinimumSize(150, 200)
self.pen = QtGui.QPen(QtCore.Qt.red)
#self.setStyleSheet('color:white; background:gray; border-radius:4px; border:2px solid white;')
def paintEvent(self, event) -> None:
self.pen.setWidth(2)
self.painter.setPen(self.pen)
self.painter.setBrush(QtGui.QBrush(QtCore.Qt.blue))
self.painter.drawRoundedRect(10, 10, 100, 100, 4.0, 4.0)
self.update()
#self.repaint()
#super(AddContextMenu, self).paintEvent(event)
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
注意:设置样式表对我不起作用:
这是我使用样式表时得到的结果它不是完全圆角的。
这是@musicamante建议后的paintEvent(仅供他/她检查)
def paintEvent(self, event) -> None:
painter = QtGui.QPainter(self)
#self.pen.setColor(QtCore.Qt.white)
#painter.setFont(QtGui.QFont("times", 22))
#painter.setPen(self.pen)
#painter.drawText(QtCore.QPointF(0, 0), 'Hello')
self.pen.setColor(QtCore.Qt.red)
painter.setPen(self.pen)
painter.setBrush(QtCore.Qt.gray)
painter.drawRoundedRect(self.rect(), 20.0, 20.0)
在 init() 中
self.pen = QtGui.QPen(QtCore.Qt.red)
self.pen.setWidth(2)
在样式表中为顶级小部件(具有自己的“窗口”的小部件)设置边框半径是不够的。
虽然 Christian Karcher 提出的解决方案很好,但需要考虑两个重要因素:
WA_TranslucentBackground
属性将不起作用。FramelessWindowHint
不应该在 Linux 上设置,因为它可能会导致窗口管理器出现问题,因此只有在确保操作系统需要它(Windows)后才应设置。
就是正确的修复方法,并且这必须在
resizeEvent()
内进行。请注意,遮罩是基于位图的,并且不支持抗锯齿,因此圆形边框有时会有点难看,具体取决于边框半径。 此外,由于您想要自定义颜色,因此必须使用样式表,因为 QMenu 的自定义绘画真的
很难实现。
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)
关于您的paintEvent实现的一些旁注,由于上述原因在这种特定情况下是不必要的,但仍然很重要(有些点与已注释的代码部分相关,但您尝试过
它们的事实值得一提方面):
用于小部件的 QPainter 必须paintEvent()
之外实例化:像您那样在 __init__
中创建实例是一个严重的错误,甚至可能导致崩溃。画家只能在收到paintEvent时创建,并且“永远”不会被重用。这显然使得将其设置为实例属性 (self.painter
) 毫无用处,因为在绘制事件之后没有实际理由访问它。
如果画笔宽度始终相同,那么只需在构造函数中设置即可(self.pen = QtGui.QPen(QtCore.Qt.red, 2)
),在paintEvent中不断设置是没有用的。self.painter.setBrush(QtCore.Qt.blue)
。self.update()
不应该 self.repaint()
也不应该)。结果是不确定的并且可能是危险的。
如果您使用 QPainter 进行一些手动绘制,然后 then调用超级 PaintEvent,结果很可能是之前绘制的所有内容都将被隐藏;作为一般规则,基本实现应该称为