Qt的MDI中多个子窗口响应一个菜单事件的优雅实现(动态slot)

问题:

用过MFC的人都知道,MDI中,某个菜单或者按钮,在视图中可以添加响应函数,在文档中也可以添加响应函数,在框架中也可以添加它的响应函数,优先级分别是视图、文档、框架,而且MFC自动将消息发给当前激活的视图或者文档。

解决思路:

在Qt中,菜单/按钮的响应是通过信号/槽来实现的,在MDI中,多个视图经常需要响应同一个菜单,且是激活的视图去响应,这有两种处理方法,一 是,由主窗口接收事件再转调用当前激活窗口,二是,通过disconnect/connect动态修改信号槽。第一种方法,当这样的菜单事件比较多时,比 较繁琐;如果多个视图分属不同子类,只要其中一个子类实现了一个处理函数,其他子类都需要添加这样的处理函数(即使啥也不干),另外,在主窗口转调用时, 还需要做类型转换(或者通过虚函数),总而言之,比较复杂。


这里,我按照第二种方法,通过少量代码实现了动态修改信号槽。思路是:

1、自定义一个结构,将connect信息保存起来;

2、当MDI的子窗口焦点切换时,将connect信息中的receiver全部换成当前窗口,并重新connect;


该方法只需要在mainwindow中做这些工作即可,比较简洁优雅。

关键代码粘贴如下

class MainWindow : public QMainWindow
{

    //......

    void dynamic_connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method);
    void set_dynamic_connect_receiver(const QObject * receiver);

    //......

    struct MdiConnectInfo
    {
        MdiConnectInfo(const QObject * sender1, const char * signal1, const QObject * receiver1, const char * method1)
        {
            sender = sender1;
            strcpy(signal,signal1);
            receiver = receiver1;
            strcpy(method, method1);
        }
        const QObject * sender;
        char signal[100];
        const QObject * receiver;
        char method[100];
    };

    std::vector<MdiConnectInfo> m_mdiConnects;

};

MainWindow::MainWindow()
{
   //......
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),
            this, SLOT(onMdiActived()));
   //......
}


void MainWindow::dynamic_connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method)
{
    m_mdiConnects.push_back(MdiConnectInfo(sender, signal, receiver, method));
}

void MainWindow::set_dynamic_connect_receiver(const QObject * receiver)
{
    for (int i = 0; i < m_mdiConnects.size(); i++)
    {
        MdiConnectInfo item = m_mdiConnects[i];
        disconnect(item.sender, item.signal, item.receiver, item.method);
    }
    for (int i = 0; i < m_mdiConnects.size(); i++)
    {
        MdiConnectInfo item = m_mdiConnects[i];
        connect(item.sender, item.signal, receiver, item.method);
        m_mdiConnects[i].receiver = receiver;
    }
}


void MainWindow::onMdiActived()
{
    QWidget* child = activeMdiChild();
    if (child)
    {
        set_dynamic_connect_receiver(child);
    }
}


void MainWindow::createActions()
{

   //......

    zoomInAct = new QAction(QIcon(":/images/zoomIn.png"), tr("Zoom In"), this);
    zoomInAct->setStatusTip(tr("Zoom in view"));
    dynamic_connect(zoomInAct, SIGNAL(triggered()), this, SLOT(zoomInView()));

    zoomOutAct = new QAction(QIcon(":/images/zoomOut.png"), tr("Zoom Out"), this);
    zoomOutAct->setStatusTip(tr("Zoom out view"));
    dynamic_connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOutView()));

    zoomFitAct = new QAction(QIcon(":/images/zoomFit.png"), tr("Zoom Fit"), this);
    zoomFitAct->setStatusTip(tr("Zoom fit view"));
    dynamic_connect(zoomFitAct, SIGNAL(triggered()), this, SLOT(zoomFitView()));

    zoomPanAct = new QAction(QIcon(":/images/zoomPan.png"), tr("Pan Zoom"), this);
    zoomPanAct->setStatusTip(tr("Pan zoom view"));
    dynamic_connect(zoomPanAct, SIGNAL(triggered()), this, SLOT(zoomPanView()));

   //......

}

 

 

作一下解释:

1、通过MdiConnectInfo结构和m_mdiConnects变量,将connect信息保存起来;具体是通过函数dynamic_connect来完成的;

2、当MDI的子窗口焦点切换时,其响应函数onMdiActived()中,将connect信息中的receiver全部换掉,具体是通过 set_dynamic_connect_receiver来完成的;


上述代码运行正常。

 

posted @ 2016-04-22 10:11  pmc1  阅读(2001)  评论(2编辑  收藏  举报