Fill QMenu with an enum

Deadly 提交于 2019-12-25 16:54:11

问题


I have a few structs with enum and std::string and I used to fill QComboBoxes with them using a for() loop. Something like this:

struct Struct
{
    enum Enum
    {
        ENTRY1,
        ENTRY2,
        ...
        ENTRY_ALL
    };
    std::string names[ENTRY_ALL] {...};
};
for(int i=0; i<Struct::ENTRY_ALL; ++i)
    comboBox->insertItem(i, names[i].c_str());

It worked, but given recent advances I need more entries, more structs, more combo boxes, and it gets ugly, so I decided I'd switch to a QMenu.

Looking at the documentation and the examples, I see I have to provide a connect() for each entry, a QAction for each entry, etc, and if my enums are large, this gets to be a problem, so I was wondering if there is a way to automate this process.

My question mainly comes from how come I only need one connect() for each QComboBox and how do QComboBoxes know that, if I fill them up, which entry does what?


If it helps, this is some bogus code, from here and there, of what I had:

// tank class, header:
class Class
{
private:
    // main entries
    struct MainStruct
    {
        enum Enum {MAIN1, MAIN2, MAIN3, MAIN_ALL};
        std::string names[MAIN_ALL] {"Main 1", "Main 2", "Main 3"};
    };
    // secondary entries
    struct SubStruct
    {
        enum Enum {SUB1, SUB2, SUB3, SUB_ALL};
        std::string names[SUB_ALL] {"Sub 1", "Sub 2", "Sub 3"};
    };
    // family of entries for SUB2
    struct FamStruct
    {
        enum Enum {FAM1, FAM2, FAM3, FAM_ALL};
        std::string names[FAM_ALL] {"Fam 1", "Fam 2", "Fam 3"};
    }
public:
    MainStruct mainEntries;
    SubStruct subEntries;
    FamStruct famEntries;
};

// inside the main window, there was a QComboBox doing this:
// header
QComboBox *cbMain, *cbSub, *cbFam;
// source
Class cls {Class()};
cbMain = new QComboBox; // same for cbSub, cbFam
// retranslateUi() (manually edited, not automatic), called each time the menu was clicked
void MainApp::retranslateUi()
{
    cbMain.clear();
    for(unsigned char i=0; i<cls.mainEntries.MAIN_ALL; ++i)
    {
        cbMain->insertItem(i, tr(cls.mainEntries.names[i].c_str()) );
        cbMain->setItemData(i, tr(cls.mainEntries.tooltips[i].c_str()), Qt::ToolTipRole);
    }
    // same for cbSub, cbFam
}
// a function that did what was supposed to do when a combo box entry was clicked
void MainApp::func() { /* execution with variables changed by the combo */ }
/* There were additional functions taking care of the rest.
 * Whenever an entry from the combo was clicked, somehow it "knew" that the entry was
 * changed and func(), when executed, it ran with the variables changed by combo.
 */
void MainApp::updateSub()
{
    switch(cbMain->currentIndex())
    {
    case cls.mainEntries.MAIN1: /*subentry stuff*/ break;
    case cls.mainEntries.MAIN2: /*subentry stuff*/ break;
    case cls.mainEntries.MAIN3: /*subentry stuff*/ break;
    }
}
void MainApp::updateFam()
{
    switch(cbSub->currentIndex())
    {
    case cls.subEntries.SUB1: /*fam entry stuff*/ break;
    case cls.subEntries.SUB2: /*fam entry stuff*/ break;
    case cls.subEntries.SUB3: /*fam entry stuff*/ break;
    }
}
/* The connect() were attached to specific combos. When an entry in cbMain was
 * clicked, cbSub and cbFam were properly updated and func() was run with
 * the proper values, when cbSub was clicked, cbFam was updated and func()
 * was run accordingly. Similar to an instant refresh.
 */
connect(cbMain, SIGNAL(activated(int)), this, SLOT(retranslateUi()));
connect(cbMain, SIGNAL(activated(int)), this, SLOT(updateSub()));
connect(cbMain, SIGNAL(activated(int)), this, SLOT(func()));
connect(cbSub, SIGNAL(activated(int)), this, SLOT(updateFam()));
connect(cbSub, SIGNAL(activated(int)), this, SLOT(func()));
connect(cbFam, SIGNAL(activated(int)), this, SLOT(func()));

Whenever I had to add new families of entries for the submenus, or even main menus with subentries, things would complicate, so I think a menu like this:

Menu:   Submenu:                AnotherSubmenu:
menu 1  submenu 1               altsub 1
menu 2  submenu 2 > family 1    altsub 2
menu 3  submenu 3   family 2    ...
                    family 3

would work better and unclutter things. The entries in Menu call the appropiate Submenu, which has its own Family, wherever they are, Submenu may call yet another level of AnotherSubmenu, etc.


回答1:


You could connect the triggered signal of all the QAction to a same slot and be able to have the object with sender(), and convert it to the QAction object type with qobject_cast<>.

Also if you want to use an index we could use the setObjectName() and objectName()

void {your Widget}::createAction()
    QMenu *menu = new QMenu(this);
    for(int i=0; i < 10; ++i){
        QAction *action = new QAction(QString("action %1").arg(i));
        action->setObjectName(QString("%1").arg(i));
        connect(action, &QAction::triggered, this, &{your Widget}::onTriggeredAction);
        menu->addAction(action);
    }
}

void {your Widget}::onTriggeredAction()
{
    QAction *obj = qobject_cast<QAction *>(sender());
    int index = obj->objectName().toInt();
    qDebug()<<index;
}


来源:https://stackoverflow.com/questions/43684721/fill-qmenu-with-an-enum

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!