Custom Qt QMenu

2019-04-23 05:22发布

Is there a way in Qt to add a layout or widgets to a QMenu to create a custom menu?

The example below (left) is what I have, and I'd like to aim for something similar to the mock-up on the right, by adding non-menu widgets. If it can't be done by QMenu, are there guides to produce similar results (perhaps by having a more standard widget act as a context menu) anywhere?

Custom QMenu

3条回答
叛逆
2楼-- · 2019-04-23 05:58

I wrote a script, you can try it.

but I am not subclass 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_())
查看更多
We Are One
3楼-- · 2019-04-23 06:02

To customize menu items you can use QWidgetAction class. But you want to customize menu to look like popup widget. So you may subclass QMenu and try to improve layout of menu for your needs (QMenu is QWidget). You questoin is not clear.

查看更多
欢心
4楼-- · 2019-04-23 06:18

Sure there is! In Qt, if there is a a will there is a way.

You will probably need to make your own class that uses QMenu and uses a member QListWidget.

You will then need to generate the layout and overload all the correct QLayout functions for size recalculation.

And then you'll need to use this layout (think QHBoxLayout) to display both a QMenu and a QListWidget side by side.

That should be enough to point you in the right direction.

EDIT:

As a commenter pointed out, you can't inherit two QObject things so I updated the answer accordingly.

查看更多
登录 后发表回答