Qt自定义菜单

 Qt开发过程中,弹出菜单时我们一般使用QMenu,但是QMenu都是一条项固定的格式,如查想要自己的设计界面就没法使用默认的Action项了,因此我们得用自定义的QMenu。

本篇介绍使用自定义的QMenu设计出UI。我们使用QWidget + QWidgetAction来实现。QWidgetAction继承自QAction,无法通过继承来实现一个界面,但它提供了setDefaultWidget来绑定一个界面,使用起来就更加方便了。

首先创建一个PlayWidget带UI的类,里面添加两个按钮,然后把这个类嵌进QWidgetAction中。代码如下:

#ifndef PLAYWIDGET_H
#define PLAYWIDGET_H

#include <QWidget>

namespace Ui {
class PlayWidget;
}

class PlayWidget : public QWidget
{
    Q_OBJECT

public:
    explicit PlayWidget(QWidget *parent = nullptr);
    ~PlayWidget();

private:
    Ui::PlayWidget *ui;
};

#endif // PLAYWIDGET_H

#include "playwidget.h"
#include "ui_playwidget.h"

PlayWidget::PlayWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::PlayWidget)
{
    ui->setupUi(this);
}

PlayWidget::~PlayWidget()
{
    delete ui;
}
void CustomMenu::initData()
{
    QString strLineEditStyle = QString("QLineEdit{background-color:#E9E9EA;color:#6D6E6B;border-radius:8px;}"
                                       "QLineEdit:hover{background-color:#E9E9EA;border-radius:8px;border:2px solid rgba(47, 137, 252, 1);}"
                                       "QLineEdit:focus{background-color:#E9E9EA;border-radius:8px;border:2px solid rgba(47, 137, 252, 1);}"
                                       "QLineEdit:disabled{background-color:#F0F0F0;border-radius:8px;}");
    QFont font = qApp->font();
    font.setPixelSize(12);


    QLineEdit *pLineEdit = new QLineEdit(this);
    pLineEdit->setFont(font);
    pLineEdit->setMaximumSize(QSize(86, 24));
    pLineEdit->setMinimumSize(QSize(86, 24));
    pLineEdit->setStyleSheet(strLineEditStyle);
    //播放项
    QWidgetAction *pWdtAction = new QWidgetAction(this);
    //播放项界面,继承自QWidget的类
    PlayWidget *playWdt = new PlayWidget(this);
    playWdt->setMinimumSize(QSize(200, 50));
    pWdtAction->setDefaultWidget(playWdt);

    //创建一个包含声音调整控件的界面项
    QWidgetAction *pVoice = new QWidgetAction(this);
    QWidget *pVoiceWdt = new QWidget(this);
    QSlider *pSlider = new QSlider(Qt::Horizontal);
    QHBoxLayout *layout = new QHBoxLayout(pVoiceWdt);
    layout->addWidget(pSlider);
    layout->addWidget(pLineEdit);
    pVoiceWdt->setLayout(layout);
    pVoice->setDefaultWidget(pVoiceWdt);
    //生成菜单栏
    if(m_menu == nullptr) {
        m_menu = new QMenu(this);
        m_menu->addAction(pWdtAction);
        m_menu->addSeparator();
        m_menu->addAction(pVoice);
        m_menu->addAction(QStringLiteral("显示歌词"));
        m_menu->addAction(QStringLiteral("锁定歌词"));

        m_menu->addSeparator();
        m_menu->addAction(QStringLiteral("选项设置"));
        m_menu->addSeparator();
        m_menu->addAction(QStringLiteral("登陆"));
        m_menu->addAction(QStringLiteral("退出"));
    }
}

void CustomMenu::initConnect()
{
    connect(ui->btnCreateMenu, SIGNAL(clicked()), this, SLOT(slotCreateMenu()));
}


void CustomMenu::slotCreateMenu()
{
    QPoint point = ui->btnCreateMenu->pos();
    point.setY(point.y() + 50);
    m_menu->popup(this->mapToGlobal(point));
    //ui->btnCreateMenu->setMenu(m_menu);

}

 运行效果

这是使用QMenu的方式,还有一个直接使用QWidget的方式,把属性设置为

setWindowFlags(Qt::FramelessWindowHint|Qt::Popup);

这样弹出菜单后,点击其他地方会自动关闭此Widget,达到与弹出菜单一样的效果。

完整代码如下:

#ifndef PROVINCEWIDGET_H
#define PROVINCEWIDGET_H

#include <QWidget>

class QLineEdit;
class QListView;

class ProvinceWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ProvinceWidget(QWidget *parent = nullptr);

    void initView();
    void initConnect();
    void setClearFocus();

signals:
    void signalChangeScale(int scale);

protected:
    void paintEvent(QPaintEvent *event) override;

public slots:
    void slotReturnPressed();
    void slotEditingFinished();
    void slotClicked(const QModelIndex &index);

private:
    QListView *m_listView;
    QLineEdit *m_lineEdit;


};

#endif // PROVINCEWIDGET_H

#include "provincewidget.h"
#include <QGraphicsDropShadowEffect>
#include <QPainter>
#include <QtMath>
#include <QStyleOption>
#include <QBitmap>
#include <QApplication>
#include <QStringListModel>
#include <QVBoxLayout>
#include <QDebug>
#include <QListView>
#include <QLineEdit>

ProvinceWidget::ProvinceWidget(QWidget *parent)
    : QWidget{parent}
    ,m_listView(new QListView(this))
    ,m_lineEdit(new QLineEdit(this))
{
    setWindowFlags(Qt::FramelessWindowHint|Qt::Popup);
    initView();
    initConnect();
}

void ProvinceWidget::initView()
{
    QString strLineEditStyle = QString("QLineEdit{background-color:#E9E9EA;color:#6D6E6B;padding-left:20px;border-radius:8px;}"
                                       "QLineEdit:hover{background-color:#E9E9EA;border-radius:8px;border:2px solid rgba(47, 137, 252, 1);}"
                                       "QLineEdit:focus{background-color:#E9E9EA;border-radius:8px;border:2px solid rgba(47, 137, 252, 1);}"
                                       "QLineEdit:disabled{background-color:#F0F0F0;border-radius:8px;}");
    QFont font = qApp->font();
    font.setPixelSize(14);
    m_lineEdit->setFont(font);
    m_lineEdit->setMaximumSize(QSize(108, 24));
    m_lineEdit->setMinimumSize(QSize(108, 24));
    m_lineEdit->setStyleSheet(strLineEditStyle);


    // 使用QListView显示一个简单的列表
    QStringListModel* model = new QStringListModel();
    model->setStringList({QStringLiteral("广东"), QStringLiteral("广西"), QStringLiteral("海南"),QStringLiteral("云南"), QStringLiteral("浙江"), QStringLiteral("江西"),QStringLiteral("北京"), QStringLiteral("黑龙江")});

    m_listView->setModel(model);
    //margin item离外边框的间距 padding 内容与item项的边框
    QString listViewStyle = QString("QListView{color:#6D6E6B; background-color:white;border:none;outline:none;}"
                                    "QListView::item{height:24px;background:gray;margin-top:3px; margin-right:10px;margin-bottom:4px; margin-left:10px;padding-left:20px;padding-right:10px;color:#333333;border:none;outline:none;}"
                                    "QListView::item:hover{background:#2F89FC;color:#ffffff;border-radius:4px;border:none;outline:none;}"
                                    "QListView::item:selected{background:white;color:#ff0000;border:none;outline:none;}"
                                    "QListView::item:selected:!active{background:white;color:#00ff00;border:none;outline:none;}"
                                    "QListView::item:selected:active{background:white;color:#0000ff;border:none;outline:none;}");

    QString listViewStyle2 = QString("QListView{color:#6D6E6B; background-color:white;border:none;outline:none;}"
                            "QListView::item{height:24px;background:white;margin-top:3px; margin-right:10px;margin-bottom:4px; margin-left:10px;padding-left:20px;padding-right:10px;color:#333333;border:none;outline:none;}"
                            "QListView::item:hover{background:#2F89FC;color:#ffffff;border-radius:4px;border:none;outline:none;}"
                            "QListView::item:selected{background:white;color:#ff0000;border:none;outline:none;}"
                            "QListView::item:selected:!active{background:white;color:#00ff00;border:none;outline:none;}"
                            "QListView::item:selected:active{background:white;color:#0000ff;border:none;outline:none;}");

    m_listView->setStyleSheet(listViewStyle2);

    QVBoxLayout *layout = new QVBoxLayout();
    layout->setContentsMargins(0, 1, 0, 0);
    layout->setSpacing(1);
    layout->addWidget(m_listView);
    layout->addWidget(m_lineEdit);
    setLayout(layout);
}

void ProvinceWidget::initConnect()
{
    connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
    connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(slotEditingFinished()));
    connect(m_listView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotClicked(const QModelIndex &)));
}

void ProvinceWidget::setClearFocus()
{
    m_listView->clearFocus();
    m_listView->clearSelection();
    m_lineEdit->clearFocus();
}


void ProvinceWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
//    QPainterPath path;
//    path.setFillRule(Qt::WindingFill);
//    path.addRoundedRect(5, 5, this->width() - 5 * 2, this->height() - 5 * 2, 3, 3);
//    painter.fillPath(path, QBrush(Qt::red));

    //绘制样式
    QStyleOption opt;
    opt.initFrom(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);//绘制样式
    QBitmap bmp(this->size());
    bmp.fill();
    QPainter painter(&bmp);
    painter.setPen(Qt::NoPen);
    painter.setBrush(Qt::black);
    painter.setRenderHint(QPainter::Antialiasing);
    //设置边框为圆角12px
    painter.drawRoundedRect(bmp.rect(), 8, 8);
    setMask(bmp);

    //再画颜色块
    QRect tmpRect = QRect(0, 0, this->width(), this->height());
    QBrush brush = QBrush(QColor("#ffffff"));
    p.setPen(Qt::NoPen);  //去掉边框线
    p.setBrush(brush);
    p.drawRect(tmpRect);



//    QColor color(Qt::gray);
//    for (int i = 0; i < 5; i++)
//    {
//        QPainterPath path;
//        path.setFillRule(Qt::WindingFill);
//        path.addRoundedRect(5 - i, 5 - i, this->width() - (5 - i) * 2, this->height() - (5 - i) * 2, 3 + i, 3 + i);
//        color.setAlpha(80 - qSqrt(i) * 40);
//        painter.setPen(color);
//        painter.drawPath(path);
//    }

}

void ProvinceWidget::slotReturnPressed()
{
    int scale = m_lineEdit->text().toInt();
    qDebug() << "slotReturnPressed===========================" << scale;
    emit signalChangeScale(scale);
}

void ProvinceWidget::slotEditingFinished()
{
    int scale = m_lineEdit->text().toInt();
    qDebug() << "slotEditingFinished===========================" << scale;
}

void ProvinceWidget::slotClicked(const QModelIndex &index)
{
    QString data = index.data().toString();
    int scale = data.left(data.size() - 1).toInt();
    qDebug() << "PopupScaleList::slotClicked==========data======" << data << data.size() << scale;
    emit signalChangeScale(scale);
}

#ifndef CUSTOMMENU_H
#define CUSTOMMENU_H

#include <QMainWindow>
#include "provincewidget.h"

class QMenu;

QT_BEGIN_NAMESPACE
namespace Ui { class CustomMenu; }
QT_END_NAMESPACE

class CustomMenu : public QMainWindow
{
    Q_OBJECT

public:
    CustomMenu(QWidget *parent = nullptr);
    ~CustomMenu();

    void initData();
    void initConnect();

public slots:
    void slotCreateMenu();
    void slotPopupMenu();
    void slotTriggered(QAction *action);

private:
    Ui::CustomMenu *ui;
    ProvinceWidget *provinceWidget{nullptr};
    QMenu *m_menu{nullptr};

};
#endif // CUSTOMMENU_H

#include "custommenu.h"
#include "ui_custommenu.h"
#include "playwidget.h"
#include <QWidgetAction>
#include <QMenu>
#include <QSlider>
#include <QHBoxLayout>
#include <QDebug>
#include <QLineEdit>

CustomMenu::CustomMenu(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::CustomMenu)
{
    ui->setupUi(this);

    initData();
    initConnect();
}

CustomMenu::~CustomMenu()
{
    delete ui;
}



void CustomMenu::initData()
{

    provinceWidget = new ProvinceWidget(this);
    provinceWidget->setObjectName(QString::fromUtf8("provinceWidget"));
    provinceWidget->setMinimumSize(QSize(108, 280));
    provinceWidget->setMaximumSize(QSize(108, 280));

    QString strLineEditStyle = QString("QLineEdit{background-color:#E9E9EA;color:#6D6E6B;border-radius:8px;}"
                                       "QLineEdit:hover{background-color:#E9E9EA;border-radius:8px;border:2px solid rgba(47, 137, 252, 1);}"
                                       "QLineEdit:focus{background-color:#E9E9EA;border-radius:8px;border:2px solid rgba(47, 137, 252, 1);}"
                                       "QLineEdit:disabled{background-color:#F0F0F0;border-radius:8px;}");
    QFont font = qApp->font();
    font.setPixelSize(12);


    QLineEdit *pLineEdit = new QLineEdit(this);
    pLineEdit->setFont(font);
    pLineEdit->setMaximumSize(QSize(86, 24));
    pLineEdit->setMinimumSize(QSize(86, 24));
    pLineEdit->setStyleSheet(strLineEditStyle);
    //播放项
    QWidgetAction *pWdtAction = new QWidgetAction(this);
    //播放项界面,继承自QWidget的类
    PlayWidget *playWdt = new PlayWidget(this);
    playWdt->setMinimumSize(QSize(200, 50));
    pWdtAction->setDefaultWidget(playWdt);

    //创建一个包含声音调整控件的界面项
    QWidgetAction *pVoice = new QWidgetAction(this);
    QWidget *pVoiceWdt = new QWidget(this);
    QSlider *pSlider = new QSlider(Qt::Horizontal);
    QHBoxLayout *layout = new QHBoxLayout(pVoiceWdt);
    layout->addWidget(pSlider);
    layout->addWidget(pLineEdit);
    pVoiceWdt->setLayout(layout);
    pVoice->setDefaultWidget(pVoiceWdt);
    //生成菜单栏
    if(m_menu == nullptr) {
        m_menu = new QMenu(this);
        m_menu->addAction(pWdtAction);
        m_menu->addSeparator();
        m_menu->addAction(pVoice);
        m_menu->addAction(QStringLiteral("显示歌词"));
        m_menu->addAction(QStringLiteral("锁定歌词"));

        m_menu->addSeparator();
        m_menu->addAction(QStringLiteral("选项设置"));
        m_menu->addSeparator();
        m_menu->addAction(QStringLiteral("登陆"));
        m_menu->addAction(QStringLiteral("退出"));
    }
}

void CustomMenu::initConnect()
{
    connect(ui->btnCreateMenu, SIGNAL(clicked()), this, SLOT(slotCreateMenu()));
    connect(ui->btnPopupMenu, SIGNAL(clicked()), this, SLOT(slotPopupMenu()));
    connect(m_menu, SIGNAL(triggered(QAction *)), this, SLOT(slotTriggered(QAction *)));
}


void CustomMenu::slotCreateMenu()
{
    QPoint point = ui->btnCreateMenu->pos();
    point.setY(point.y() + 50);
    m_menu->popup(this->mapToGlobal(point));
    //ui->btnCreateMenu->setMenu(m_menu);

}

void CustomMenu::slotPopupMenu()
{
    QPoint point = ui->btnPopupMenu->pos();
    point.setY(point.y() + 50);
    QPoint mapPoint = mapToGlobal(point);
    provinceWidget->setClearFocus();
    provinceWidget->move(mapPoint);
    provinceWidget->show();
}

void CustomMenu::slotTriggered(QAction *action)
{
    qDebug() << "slotCreateMenu====================" << action->text();
}

#include "custommenu.h"

#include <QApplication>
#include <QFont>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QFont defaultFont = qApp->font();
    defaultFont.setFamily("Microsoft YaHei");
    qApp->setFont(defaultFont);
    CustomMenu w;
    w.show();
    return a.exec();
}

运行效果:

 

参考:

https://www.cnblogs.com/lingluotianya/p/3789245.html
https://blog.csdn.net/yyz_1987/article/details/130986313

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值