同一个按钮调用了多少不同的插槽?

How different slots are being called by the same button?

两天来,我一直在努力理解为什么我的应用程序中的同一个按钮会调用不同的插槽。我在谷歌上搜索了很多关于这个主题的内容,但主要是 I found 是关于一个独特的 SLOT 被同一个按钮多次调用,解决了在连接调用中添加第五个参数 Qt::UniqueConnection 即:

connect(obj, SIGNAL(signal()), obj2, SLOT(slot()), Qt::UniqueConnection);

继续,我的问题是同一个按钮在不应该调用不同的槽。我创建了一个通用代码来设置我所有的按钮:

void KHUB::btSetupInt(QPushButton **button, const QString name, int posX, int posY, int width, int height, void (KHUB::*fptr)(int parameter), int value, int handler) {
  *button = new QPushButton(name, this);
  (*button)->setGeometry(QRect(QPoint(posX, posY), QSize(width, height)));

  signalMapper->setMapping(*button, value);
  connect(*button, SIGNAL(clicked()), signalMapper, SLOT(map()));
  //qDebug() << "My value: " + QString::number(value);

  switch (handler) {
    case (int) ButtonHandler::hl_Register:
      connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleRegister(int)), Qt::UniqueConnection);
      break;
    case (int)ButtonHandler::hl_UpVote:
      connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleUpVote(int)), Qt::UniqueConnection);
      break;
    case (int) ButtonHandler::hl_OpenUrl:
      //connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleUrl(int)), Qt::UniqueConnection);
      break;
    case (int)ButtonHandler::hl_DownVote:
      connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleDownVote(int)), Qt::UniqueConnection);
      break;
    case (int) ButtonHandler::hl_DisposeBrowser:
      connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleDispose(int)), Qt::UniqueConnection);
      break;
    }
}

其中有我的 signalMapper,它在我的头文件中声明。此按钮设置由以下人员调用:

void KHUB::handleSearch() {
  //DO THE MATH
  for (int pos = 0; pos < localUrl.size(); pos++){
    QLabel *link = new QLabel();
    link->setText("<a href=\"" + localUrl.at(pos) + "\">" + localUrl.at(pos) + "</a>");
    link->setTextFormat(Qt::RichText);
    link->setTextInteractionFlags(Qt::TextBrowserInteraction);
    link->setOpenExternalLinks(true);

    QPushButton *upArrow = new QPushButton();
    btSetupInt(&upArrow, "Up Vote", 225, 300, 100, 25, &KHUB::handleUpVote, pos*147, (int)ButtonHandler::hl_UpVote);

    QPushButton *open = new QPushButton("Open");
    //btSetupInt(&open, "Open", 225, 300, 100, 25, &KHUB::handleUrl, pos, (int) ButtonHandler::hl_OpenUrl);

    QPushButton *downArrow = new QPushButton();
    btSetupInt(&downArrow, "Down Vote", 225, 300, 100, 25, &KHUB::handleDownVote, pos*163, (int)ButtonHandler::hl_DownVote);

    QLabel *separator = new QLabel();
    QLabel *guider = new QLabel("<----------------------------------------------------------------------------------------------------------------------------------------------------->");

    gridLayout->addWidget(link, componentsPos, 0, 1, -1);
    gridLayout->addWidget(guider, componentsPos, 1, 1, -1);
    gridLayout->addWidget(upArrow, componentsPos - 1, 2, 1, 1, Qt::AlignBottom);
    gridLayout->addWidget(open, componentsPos, 2, 1, 1);
    gridLayout->addWidget(downArrow, componentsPos + 1, 2, 1, 1, Qt::AlignTop);
    gridLayout->addWidget(separator, componentsPos + 2, 1, 1, 1);

    componentsPos = componentsPos + 5;
  }
  //KEEP DOING THE MATH
}

注释行用于调试目的。无论如何,当我只有 open QPushButton 时,这段代码完全可以工作。当添加 upVotedownVote 时,我希望程序能够复制其逻辑成功,相反,现在当我点击 Up voting ou Down voting 时,程序会触发两个插槽来处理 Up 和 Down votin 以及 "Open" 如果没有注释。处理赞成票和反对票的代码:

void KHUB::handleUpVote(int reference) {
    qDebug() << "This is my reference for upvoting: " + QString::number(reference/147);
}

void KHUB::handleDownVote(int reference) { 
    qDebug() << "This is my reference for DOWNVOTING: " + QString::number(reference/163);
}

根据 Qt QSignalMapper documentation(页面末尾的主题 "Advanced Signals and Slots Usage"),我所做的一切都符合预期。你一定也注意到我乘以 posbtSetupInt 由 147 和 163 调用(产生一个 "random" 号码)因为 every button must have its only integer ID [参见 setMapping for integers void QSignalMapper::setMapping(QObject * sender, int id)].

几天前,我对其中三个(按钮)使用了相同的位置 (pos),无论相乘与否,产生的结果是否相等。 这引出了我的第一个问题: 这个ID是每个按钮的唯一标识还是按钮可以传递的唯一参数?我考虑到我上面给出的上下文,仍然没有得到这个

这是我的 UI:

这是我的输出点击(随机 link 选择)在 us.battle.net/wow/en/blog/19913791/patch-623-preview-10-14-2015 上投票:

"This is my reference for upvoting: 3"
"This is my reference for DOWNVOTING: 3"

这是我的输出点击(随机 link 选择)在 http://www.swtor.com/holonet/companions/blizz 投反对票:

"This is my reference for upvoting: 6"
"This is my reference for DOWNVOTING: 6"

我使用断点来查看 btSetupInt 处的 handler 开关是否被正确调用并且确实如此。即使不使用我的成员函数 btSetupInt 来调用连接,我仍然会得到同样的错误结果。 最后,我的第二个主要问题在给定的上下文中,同一个按钮怎么可能调用不同的插槽?我在这里错过了什么?

因为,您要多次连接信号映射器,即针对每个按钮实例。您正在创建一个按钮,然后根据 handler 连接到不同的插槽。但是信号映射器的 mapped(int) 信号连接到多个插槽。希望你明白我在这里所说的。

您的代码的另一个问题是内存泄漏。

QPushButton *upArrow = new QPushButton();
btSetupInt(&upArrow, "Up Vote", 225, 300, 100, 25, &KHUB::handleUpVote, pos*147, (int)ButtonHandler::hl_UpVote);

btSetupInt() 正在创建新按钮。再次.

*button = new QPushButton(name, this);

这是未经测试的代码。检查是否有效。

// custom button claas
class VotingButton : public QPushButton
{
    Q_OBJECT

public:
    VotingButton(const QString& url, int voteType, QObject* prnt=0)
        : QPushButton(prnt)
          m_url(url),
          m_voteType(voteType)
    {}

    QString getUrl() const { return m_url; }
    int getVoteType() const { return m_voteType; } 

private:
    QString     m_url;
    int         m_voteType;
}

在你的KHUBclass

connect(signalMapper, SIGNAL(mapped(QObject*)), this, SLOT(handleButtonPress(QObject*)));

void KHUB::btSetupInt(QPushButton* button, const QString& name, int posX, int posY, int width, int height, const QString& url, int handler) {
    button = new VotingButton(url, handler, this);
    button->setText(name);
    button->setGeometry(QRect(QPoint(posX, posY), QSize(width, height)));
    signalMapper->setMapping(button, this);
    connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
}

void KHUB::handleSearch() {
  //DO THE MATH
  for (int pos = 0; pos < localUrl.size(); pos++){
    QLabel *link = new QLabel();
    link->setText("<a href=\"" + localUrl.at(pos) + "\">" + localUrl.at(pos) + "</a>");
    link->setTextFormat(Qt::RichText);
    link->setTextInteractionFlags(Qt::TextBrowserInteraction);
    link->setOpenExternalLinks(true);

    QPushButton *upArrow;
    btSetupInt(upArrow, "Up Vote", 225, 300, 100, 25, localUrl.at(i), (int)ButtonHandler::hl_UpVote);

    QPushButton *open;
    btSetupInt(open, "Open", 225, 300, 100, 25, localUrl.at(i), (int)ButtonHandler::hl_OpenUrl);    

    QPushButton *downArrow;
    btSetupInt(downArrow, "Down Vote", 225, 300, 100, 25, localUrl.at(i), (int)ButtonHandler::hl_DownVote);

    QLabel *separator = new QLabel();
    QLabel *guider = new QLabel("<----------------------------------------------------------------------------------------------------------------------------------------------------->");

    gridLayout->addWidget(link, componentsPos, 0, 1, -1);
    gridLayout->addWidget(guider, componentsPos, 1, 1, -1);
    gridLayout->addWidget(upArrow, componentsPos - 1, 2, 1, 1, Qt::AlignBottom);
    gridLayout->addWidget(open, componentsPos, 2, 1, 1);
    gridLayout->addWidget(downArrow, componentsPos + 1, 2, 1, 1, Qt::AlignTop);
    gridLayout->addWidget(separator, componentsPos + 2, 1, 1, 1);

    componentsPos = componentsPos + 5;
  }
  //KEEP DOING THE MATH
}

void KHUB::handleButtonPress(QObject* obj)
{
    VotingButton* voteButton = static_cast<VotingButton*>(obj);
    if (voteButton) { // now you have voteType and the url assigned to the button
        switch (voteButton->getVoteType())
        {
            case ...
        }
    }
}

据我根据 ramtheconqueror 的回答了解到,您不能使用同一张地图连接不同的插槽。在我的代码中,我使用 signalMapper 连接到不同的处理程序(插槽),为了解决这个问题,我只是在 btSetupInt 处添加了一个 QSignalMapper 参数。我为每种按钮类型创建了三个不同的地图,以提供我想连接插槽的地图。

我的代码现在看起来像这样:

void KHUB::btSetupInt(QPushButton **button, const QString name, int posX, int posY, int width, int height, void (KHUB::*fptr)(int parameter), QSignalMapper* map, int value, int handler) {
  *button = new QPushButton(name, this);
  (*button)->setGeometry(QRect(QPoint(posX, posY), QSize(width, height)));

  map->setMapping(*button, value);
  connect(*button, SIGNAL(clicked()), map, SLOT(map()));
  //qDebug() << "My value: " + QString::number(value);

  switch (handler) {
    case (int) ButtonHandler::hl_Register:
      connect(map, SIGNAL(mapped(int)), this, SLOT(handleRegister(int)), Qt::UniqueConnection);
      break;
    case (int)ButtonHandler::hl_UpVote:
      connect(map, SIGNAL(mapped(int)), this, SLOT(handleUpVote(int)), Qt::UniqueConnection);
      break;
    case (int) ButtonHandler::hl_OpenUrl:
      //connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleUrl(int)), Qt::UniqueConnection);
      break;
    case (int)ButtonHandler::hl_DownVote:
      connect(map, SIGNAL(mapped(int)), this, SLOT(handleDownVote(int)), Qt::UniqueConnection);
      break;
    case (int) ButtonHandler::hl_DisposeBrowser:
      connect(map, SIGNAL(mapped(int)), this, SLOT(handleDispose(int)), Qt::UniqueConnection);
      break;
    }
}

  void KHUB::handleSearch() {
  //DO THE MATH
  upVoteMap = new QSignalMapper(this);
  downVoteMap = new QSignalMapper(this);
  openMap = new QSignalMapper(this);

   for (int pos = 0; pos < localUrl.size(); pos++){
    QLabel *link = new QLabel();
    link->setText("<a href=\"" + localUrl.at(pos) + "\">" + localUrl.at(pos) + "</a>");
    link->setTextFormat(Qt::RichText);
    link->setTextInteractionFlags(Qt::TextBrowserInteraction);
    link->setOpenExternalLinks(true);

    QPushButton *upArrow;
    btSetupInt(&upArrow, "Up Vote", 225, 300, 100, 25, &KHUB::handleUpVote, upVoteMap, pos, (int)ButtonHandler::hl_UpVote);

    QPushButton *open;
    btSetupInt(&open, "Open", 225, 300, 100, 25, &KHUB::handleUrl, openMap, pos, (int) ButtonHandler::hl_OpenUrl);

    QPushButton *downArrow;
    btSetupInt(&downArrow, "Down Vote", 225, 300, 100, 25, &KHUB::handleDownVote, downVoteMap, pos, (int)ButtonHandler::hl_DownVote);

    QLabel *separator = new QLabel();
    QLabel *guider = new QLabel("<----------------------------------------------------------------------------------------------------------------------------------------------------->");

    gridLayout->addWidget(link, componentsPos, 0, 1, -1);
    gridLayout->addWidget(guider, componentsPos, 1, 1, -1);
    gridLayout->addWidget(upArrow, componentsPos - 1, 2, 1, 1, Qt::AlignBottom);
    gridLayout->addWidget(open, componentsPos, 2, 1, 1);
    gridLayout->addWidget(downArrow, componentsPos + 1, 2, 1, 1, Qt::AlignTop);
    gridLayout->addWidget(separator, componentsPos + 2, 1, 1, 1);

    componentsPos = componentsPos + 5;
  }
  //KEEP DOING THE MATH
}

我相信 ramtheconqueror 的解决方案会奏效,并且可以肯定它比我的更出色,尽管它不适用于我的情况,因为我正在写论文并证明我的 POV 我需要创建数量有限的 类 是预先建立的。

无论如何,我会假设他的回答是正确的,因为没有他我不会实现我自己的答案。

不幸的是,我仍然没有第一个问题的答案,但总的来说,我希望这对以后的人有所帮助。