pyqt 中 QMenu 的圆角

问题描述 投票:0回答:3

我正在尝试覆盖

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)
python pyqt pyqt5
3个回答
3
投票

在样式表中为顶级小部件(具有自己的“窗口”的小部件)设置边框半径是不够的。

虽然 Christian Karcher 提出的解决方案很好,但需要考虑两个重要因素:

  1. 系统必须支持合成;虽然对于大多数现代操作系统来说都是如此,但至少在 Linux 上,即使是最新的系统也有可能选择支持它(我在我的计算机上禁用了);如果是这种情况,设置
    WA_TranslucentBackground
    属性将不起作用。
  2. FramelessWindowHint
    不应该在 Linux 上设置,因为它可能会导致窗口管理器出现问题,因此只有在确保操作系统需要它(Windows)后才应设置。
  3. 有鉴于此,只要不支持合成,使用

setMask()

 就是正确的修复方法,并且这必须在 
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 必须
    never
  1. paintEvent() 之外实例化:像您那样在
    __init__
    中创建实例是一个严重的错误,甚至可能导致崩溃。画家只能在收到paintEvent时创建,并且“永远”不会被重用。这显然使得将其设置为实例属性 (
    self.painter
    ) 毫无用处,因为在绘制事件之后没有实际理由访问它。 如果画笔宽度始终相同,那么只需在构造函数中设置即可(
    self.pen = QtGui.QPen(QtCore.Qt.red, 2)
    ),在paintEvent中不断设置是没有用的。
  2. QPen 和 QBrush 可以直接接受 Qt 全局颜色,因此无需创建 QBrush 实例,因为画家会自动(内部且快速)设置它:
    self.painter.setBrush(QtCore.Qt.blue)
  3. self.update()
    不应该
  4. never
  5. 在 PaintEvent 中调用(甚至
    self.repaint()
    也不应该)。结果是不确定的并且可能是危险的。 如果您使用 QPainter 进行一些手动绘制,然后
    then
    调用超级 PaintEvent,结果很可能是之前绘制的所有内容都将被隐藏;作为一般规则,基本实现应该称为
  6. first
  7. ,然后任何其他自定义绘制都应该发生after(在这种情况下,它显然不起作用,因为您将绘制一个填充的圆角矩形,使菜单物品不可见)。
  8. 我无法评论 PaintEvent 功能,但可以使用样式表实现圆角。必须修改一些 qmenu 属性才能禁用背景中的默认矩形,这会给您带来不需要的结果。


0
投票
QWidget

实现了圆角菜单。您可以在

https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/menu/demo.py
下载代码。

    

© www.soinside.com 2019 - 2024. All rights reserved.