1

首先,我为看起来很大的问题道歉,但事实并非如此。我正在阅读《qt 开发基础》一书,在阅读第四章时,作者通过展示以下示例讲述了 MDI 窗口的基础知识:

MdiWindow::MdiWindow( QWidget *parent ) : QMainWindow( parent ) {
  setWindowTitle( tr( "MDI" ) );
  QWorkspace* workspace = new QWorkspace;
  setCentralWidget( workspace );
  connect( workspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(enableActions()));
  QSignalMapper* mapper = new QSignalMapper( this );

  //my problem is in this line
  connect( mapper, SIGNAL(mapped(QWidget*)), workspace, SLOT(setActiveWindow(QWidget*)) );

  createActions();
  createMenus();
  createToolbars();
  statusBar()->showMessage( tr("Done") );
  enableActions();
}

他的这段解释完全让我无法理解(是我还是其他人也有理解问题?):

接下来,创建并连接一个名为 QSignalMapper 的信号映射对象。信号映射器用于将信号源与另一个信号的参数联系起来。在本例中,Window 菜单中每个窗口对应的菜单项的操作与实际的文档窗口相关联。这些动作又连接到映射器。当动作发出触发信号时,发送动作已经与对应文档窗口的QWidget*相关联。该指针用作信号映射对象发出的 mapped(QWidget*) 信号中的参数。

我的问题:我仍然不明白什么是信号映射器类,它是如何使用的以及在上面的示例中它的功能是什么?任何人都可以用简单的术语解释上述段落吗?如果你能用简单的例子教我关于映射器类的基础知识,那会很棒吗?可能用外行的话?

PS:一个困惑是当我们有 MDI 窗口时,菜单是否更改(尽管操作被禁用/启用)例如假设对于一个特定文档我们有菜单“文件/关闭”而对于其他文档我们有“文件/重映射器”?

4

2 回答 2

4

QSignalMapper用于重新发出带有可选参数的信号。换句话说(来自文档):

此类收集一组无参数信号,并使用与发送信号的对象相对应的整数、字符串或小部件参数重新发出它们。

一个很好的例子(也来自文档 - 看看它)设置如下:

假设我们要创建一个包含一组按钮的自定义小部件(如工具面板)。一种方法是将每个按钮的 clicked() 信号连接到其自己的自定义插槽;但在本例中,我们希望将所有按钮连接到单个插槽,并通过单击的按钮对插槽进行参数化。

所以想象一下,你有许多按钮封装在一个类中,比如说ButtonWidget,带有一个自定义信号void clicked(const QString &text)。这是定义:

class ButtonWidget : public QWidget {
  Q_OBJECT

public:
  ButtonWidget(QStringList texts, QWidget *parent = 0);

signals:
  void clicked(const QString &text);

private:
  QSignalMapper *signalMapper;
};

然后可以像下面这样定义构造函数:

ButtonWidget::ButtonWidget(QStringList texts, QWidget *parent)
  : QWidget(parent)
{
  signalMapper = new QSignalMapper(this);

  QGridLayout *gridLayout = new QGridLayout;
  for (int i = 0; i < texts.size(); ++i) {
    QPushButton *button = new QPushButton(texts[i]);
    connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
    signalMapper->setMapping(button, texts[i]);
    gridLayout->addWidget(button, i / 3, i % 3);
  }

  connect(signalMapper, SIGNAL(mapped(const QString &)),
          this, SIGNAL(clicked(const QString &)));

  setLayout(gridLayout);
}

那么这里会发生什么?我们构建了一个网格布局和我们的按钮类型QPushButton。这些clicked()中的每一个的信号都连接到信号映射器。

使用的力量之一QSignalMapper是您可以将参数传递给重新发出的信号。在我们的示例中,每个按钮都应该发出不同的文本(由于我们信号的定义),所以我们使用setMapping()方法设置它。

现在剩下要做的就是将信号映射器映射到我们类的信号:

connect(signalMapper, SIGNAL(mapped(const QString &)),
        this, SIGNAL(clicked(const QString &)));

假设我们有一个名为TestClassthen的测试类,ButtonWidget可以这样使用:

TestClass::TestClass() {
  widget = new ButtonWidget(QStringList() << "Foo" << "Bar");
  connect(widget, SIGNAL(clicked(const QString &)),
          this, SLOT(onButtonClicked(const QString &)));
}

void TestClass::onButtonClicked(const QString &btnText) {
  if (btnText == "Foo") {
    // Do stuff.
  }
  else {
    // Or something else.   
  }
}

通过以这种方式使用信号映射器,您不必声明和管理所有按钮及其单击的信号,只需一个信号 pr。ButtonWidget.

最重要的是,信号映射器非常适合捆绑多个信号,您甚至可以在重新发射信号时设置参数。我希望对QSignalMapper.


您的示例代码

解释(您的“para”)指出所有操作都单独映射到特定的QWidget*. 当触发一个动作时,其各自QWidget*将被传递到 的插槽QWorkspace::setActiveWindow(QWidget*)workspace这反过来又激活了小部件。

另请注意,从操作到小部件的映射必须发生在代码中的某个位置。我认为它已经完成createActions()enableActions()可能完成。

于 2012-09-29T15:54:39.220 回答
1

QSignalMapper允许您在需要时向信号添加一些信息这个对象内部有一个像QMap<QObject*,QVariant>. 然后将一个对象连接到它,当调用槽时,它会重新发出具有相关值的信号。

工作流程:

mySignalMapper:
    [ obj1 -> 42       ]
    [ obj2 -> "obiwan" ]
    [ obj3 -> myWidget ]

connect(obj1,mySignal,mySignalMapper,SLOT(map())); // idem for obj2 and obj3

(obj2 emits "mySignal")
-> (mySignalMapper::map slot is called)
-> (sender() == obj2, associated data = "obiwan")
-> (mySignalMapper emits mapped("obiwan"))

我打算添加一个更详细的示例,但 Morten Kristensen 比我快;)

于 2012-09-29T16:00:00.493 回答