MDI窗口和QSignalMapper基础知识

时间:2012-09-29 11:35:38

标签: c++ qt

首先,我为看起来很大的问题道歉,但事实并非如此。我正在阅读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的信号映射对象   连接的。信号映射器用于将信号源连接到   另一个信号的论点。在此示例中,菜单的操作   对应于窗口菜单中每个窗口的项目与   实际文档窗口。这些操作又连接到mapper。   当动作发出触发信号时,发送动作   已与相应文档的QWidget *相关联   窗口。此指针用作映射中的参数(QWidget *)   信号映射对象发出的信号。

我的问题:我仍然没有得到什么是信号映射器类,它是如何使用的以及它在上面的例子中做了什么功能?任何人都可以使用简单的条款解释上述段落吗?如果你能用简单的例子教我关于mapper类的基础知识,那真是太棒了吗?可能是外行人的任期?

PS :混淆是当我们有MDI窗口时,做菜单更改(虽然操作被禁用/启用)例如假设一个特定文档我们有菜单“文件/关闭”和其他文档我们有“文件/重新加载”?

2 个答案:

答案 0 :(得分:4)

QSignalMapper用于使用可选参数重新发射信号。换句话说(来自documentation):

  

该类收集一组无参数信号,并重新发射它们   与对象对应的整数,字符串或窗口小部件参数   发出信号。

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

  

假设我们要创建一个包含a的自定义窗口小部件   按钮组(如工具调色板)。一种方法是连接   每个按钮的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 &)));

假设我们有一个名为TestClass的测试类,那么可以使用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

Buttom系列是信号映射器非常适合捆绑多个信号,甚至可以在重新发出信号时设置参数。我希望对QSignalMapper的使用有一些直觉。


您的示例代码

解释(您的“段落”)指出所有操作都分别映射到特定的QWidget*。触发操作时,其相应的QWidget*将传递到QWorkspace::setActiveWindow(QWidget*)的广告符workspace,然后激活小部件。

另请注意,从操作到窗口小部件的映射必须在代码中的某处发生。我假设它可能在createActions()enableActions()中完成。

答案 1 :(得分: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比我快;)