3

In my menu, I am setting data to the menu actions. How can I extract that data in my slot? Or even better, instead of connecting a slot, can I also connect a member function that is able to extract the action data (like in the 1st connect)? The action data is meant to identify each action. As a sidenode, I am not sure if I can use several menu action entries on only one openNote-action.

void Traymenu::createMainContextMenu() {
      QAction *actionNewNote = m_mainContextMenu.addAction("Neue Notiz");

      actionNewNote->setIcon(QIcon("C:\\new.ico"));
      actionNewNote->setIconVisibleInMenu(true);

      QObject::connect(actionNewNote,&QAction::triggered,this,&Traymenu::newNote);

      QString menuEntryName;
      QAction *openNote;
      QVariant noteID;
      for (int i = 0; i<m_noteList.count(); i++) {
        std::string noteTitle = m_noteList[i].data()->getTitle();
        menuEntryName = QString::fromStdString(noteTitle);

        openNote = m_mainContextMenu.addAction(menuEntryName);
        connect(openNote,SIGNAL(triggered()),this,SLOT(s_showNote()));

        noteID.setValue(m_noteList[i].data()->getID());
        openNote->setData(noteID);
    }
    m_mainIcon.setContextMenu(&m_mainContextMenu);
}

And the slot:

void Traymenu::s_showNote() {
    QObject* obj = sender();
    //int noteID = data.toInt();
    //Search all notes in noteList for that ID and show it
}
Matthew
  • 2,759
  • 18
  • 28
user2366975
  • 4,350
  • 9
  • 47
  • 87

2 Answers2

3

Using QObject::sender()

You can use QObject::sender() to get the signal's sender, followed by qobject_cast to cast the sender pointer to the right type.

void Traymenu::s_showNote()
{
    QAction* act = qobject_cast<QAction *>(sender());
    if (act != 0)
    {
        QVariant data = act->data();
        int noteID = data.toInt();
        showNote(noteID); // isolate showNote logic from "get my ID" stuff
     }
}

void Traymenu::showNote(int noteID)
{
    // Do the real work here, now that you have the ID ...
}

As the Qt documentation warns, "This function violates the object-oriented principle of modularity." It's still a fairly safe and standard practice, though — just one with some shortcomings. In particular, note that you're committing to having a s_showNote method that only works when it's accessed as a slot (otherwise sender is 0).

Using QSignalMapper

Alternatively, you can use the QSignalMapper class to return a pointer to teh item or to associate a unique identifier (int or QString) with each item.

Something like this:

void Traymenu::createMainContextMenu() 
{
  signalMapper = new QSignalMapper(this); // (or initialize elsewhere)

  // ... (create your newNote here same as before) ...

  QString menuEntryName;
  QAction *openNote;
  int noteID;
  for (int i = 0; i<m_noteList.count(); i++) {
    std::string noteTitle = m_noteList[i].data()->getTitle();
    menuEntryName = QString::fromStdString(noteTitle);

    openNote = m_mainContextMenu.addAction(menuEntryName);

    noteID = m_noteList[i].data()->getID();
    openNote->setData(QVariant(noteID)); // (if you still need data in the QActions)

    signalMapper->setMapping(openNote, noteID);
  }
  connect(signalMapper, SIGNAL(mapped(int)),
          this, SLOT(showNote(int)));

  m_mainIcon.setContextMenu(&m_mainContextMenu);
}

void Traymenu::showNote(int noteID) {
    // Now you have the ID ...
}

This pattern has the benefit of isolating all the ugly "Wait, how do I get my identifier?" stuff in one spot, instead of having both the initialization code and the slot function having code for associating actions and IDs.

Community
  • 1
  • 1
Alex P
  • 1,559
  • 11
  • 23
0

I would write it like:

void Traymenu::s_showNote() {
    QObject* obj = sender();
    QAction *action = qobject_cast<QAction *>(obj);
    int id = action->data().toInt();

    for (int i = 0; i < m_noteList.count(); i++) {
        if (m_noteList[i].data()->getID() == id) {
            [..]
        }
    }
}
vahancho
  • 20,808
  • 3
  • 47
  • 55