自定义Qt QMenu

9
有没有一种方法可以在Qt中添加布局或小部件到QMenu中,以创建自定义菜单?
下面的示例(左侧)是我拥有的内容,我希望通过添加非菜单小部件来实现类似右侧草图的效果。如果无法使用QMenu完成此操作,是否有指南可以在任何地方生成类似的结果(可能是通过将更标准的小部件作为上下文菜单)?
3个回答

9
当然可以!在Qt中,只要有意愿就有办法。
您可能需要创建自己的类,使用QMenu并使用成员QListWidget
然后,您需要生成布局并重载所有正确的QLayout函数进行大小重新计算。
接下来,您需要使用此布局(考虑QHBoxLayout)将QMenuQListWidget并排显示。
这应该足以让您朝着正确的方向前进。
编辑:正如评论者指出的那样,您不能继承两个QObject东西,因此我更新了答案。

@Mat 如果你想花费几个小时的时间为这个答案编写代码,那就请便吧。我已经提供了足够的信息让OP开始着手了。 - Tyler Jandreau
1
没有人要求你编写代码,你可以从回答中删除所有元数据。请注意问题是“在Qt中是否有一种方法可以添加布局或小部件到Qmenu以创建自定义菜单?”,而不是“请为我编写执行此操作的代码”。如果答案是“不,你需要自己编写”,那么好吧,你的建议看起来很不错。(我不知道是否还有其他方法,但QWidgetAction可能可以利用一下。) - Mat
@Mat,我现在明白你的意思了。我只是想说“是的,有一种方法”,而不是写代码。谢谢你澄清。我从我的答案中删除了元数据。 - Tyler Jandreau
我还在适应“Qt方式”做事情,浪费了几个小时在“自定义”的东西上,才意识到有更简单的方法来做或者根本不需要这样做。你的答案非常接近我的原始计划,所以至少我现在知道我正在朝着正确的方向开始(也许是围绕Qt);我没有预料到要编写大小重新计算 - 这可能会使我严重偏离轨道。如果我遇到任何严重的问题,我会回来的。:D - Kver
QLayout 的东西是我认为最难的。你需要重载几个虚函数。如果这是我的钱,我不会这样做,因为这可能需要我8到12个小时,而我很懒。 - Tyler Jandreau

0

我写了一个脚本,你可以试试。

但是我没有子类化 QMenu。


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from PySide.QtGui import *
from PySide.QtCore import *


class MenuItem(QWidget):
    """docstring for MenuItem"""
    def __init__(self, text='test', icon=None, parent=None):
        super(MenuItem, self).__init__(parent)

        hbox = QHBoxLayout(self)
        # hbox.setContentsMargins(0, 0, 0, 0)
        label = QLabel(text)
        btn = QPushButton()
        if icon:
            btn.setIcon(icon)

        hbox.addWidget(label)
        hbox.addStretch()
        hbox.addWidget(btn)
        self.setMinimumWidth(parent.width())


class MyMenu(QWidget):
    """docstring for MyMenu"""
    def __init__(self, parent=None):
        super(MyMenu, self).__init__(parent)

        self.main_width = 200
        self.main_height = 150
        self.close_menu = False

        self.parent = parent
        self.setGeometry(0, 0, 200, 150)

        self.initUI()
        self.setWindowFlags(Qt.Popup)
        # self.setWindowModality(Qt.WindowModal)

    def initUI(self):
        main_frame = QWidget(self)
        main_v_layout = QVBoxLayout(main_frame)
        main_v_layout.setContentsMargins(0, 0, 0, 0)
        item_1 = MenuItem('item 1', parent=self)
        item_2 = MenuItem('item 2', parent=self)
        item_3 = MenuItem('item 3', parent=self)
        main_v_layout.addWidget(item_1)
        main_v_layout.addWidget(item_2)
        main_v_layout.addWidget(item_3)

    def animationShow(self):
        self.close_menu = False
        self.start_close_menu = True
        self.show()

        # PyQt4.QtCore.QRect(0, 0, 400, 23)
        rect = self.parent.rect()

        # PyQt4.QtCore.QPoint(199, 11)
        center_pos = rect.center()

        # PyQt4.QtCore.QPoint(654, 465)
        global_center_pos = self.parent.mapToGlobal(center_pos)

        height = rect.height()

        show_pos = QPoint(
            global_center_pos.x() - (self.width() / 2),
            global_center_pos.y() + height)
        # print show_pos

        self.move(show_pos)
        self.inAnimation(show_pos)

    def inAnimation(self, show_pos=None):
        start_height = QSize(self.main_width, 0)
        end_height = QSize(self.main_width, self.main_height)

        size_anim = QPropertyAnimation(self, 'size')
        size_anim.setStartValue(start_height)
        size_anim.setEndValue(end_height)
        size_anim.setDuration(160)
        size_anim.setEasingCurve(QEasingCurve.OutQuad)

        opacity_anim = QPropertyAnimation(self, 'windowOpacity')
        opacity_anim.setStartValue(0.0)
        opacity_anim.setEndValue(1.0)
        opacity_anim.setDuration(260)
        opacity_anim.setEasingCurve(QEasingCurve.OutQuad)

        self.in_anim_group = QParallelAnimationGroup()
        self.in_anim_group.addAnimation(size_anim)
        self.in_anim_group.addAnimation(opacity_anim)
        self.in_anim_group.start()

    def outAnimation(self):
        try:
            end_size = QSize(self.size().width(), 0)

            pos_anim = QPropertyAnimation(self, 'size')
            pos_anim.setEndValue(end_size)
            pos_anim.setDuration(200)
            pos_anim.setEasingCurve(QEasingCurve.InQuad)

            opacity_anim = QPropertyAnimation(self, 'windowOpacity')
            opacity_anim.setStartValue(1.0)
            opacity_anim.setEndValue(0.0)
            opacity_anim.setDuration(200)
            opacity_anim.setEasingCurve(QEasingCurve.InQuad)

            self.out_anim_group = QParallelAnimationGroup()
            self.out_anim_group.addAnimation(pos_anim)
            self.out_anim_group.addAnimation(opacity_anim)
            self.out_anim_group.finished.connect(self.closeMenu)
            self.out_anim_group.start()

        except RuntimeError as e:
            pass
        except Exception as e:
            print e

    def closeMenu(self):
        self.close_menu = True
        self.setVisible(False)

    def closeEvent(self, event):
        # super(MyMenu, self).closeEvent(event)
        if self.start_close_menu:
            self.outAnimation()
            self.start_close_menu = False

    def hideEvent(self, event):
        # print 'hideEvent', event
        super(MyMenu, self).hideEvent(event)

    def setVisible(self, visible):
        if self.close_menu:
            visible = False

        elif not visible:
            visible = True

        super(MyMenu, self).setVisible(visible)


class Win(QWidget):
    """docstring for Win"""
    def __init__(self):
        super(Win, self).__init__()

        vbox = QVBoxLayout(self)
        btn = QPushButton('call menu')
        vbox.addWidget(btn)

        self.menu = MyMenu(btn)
        btn.clicked.connect(self.menu.animationShow)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Win()
    win.show()
    sys.exit(app.exec_())

0

要自定义菜单项,您可以使用 QWidgetAction 类。但是您想要自定义菜单以看起来像弹出式小部件。因此,您可以子类化 QMenu 并尝试改进菜单的布局以满足您的需求(QMenuQWidget)。您的问题不太清楚。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接