在 C++ 中使用 g_signal_connect() 进行动态统一快速列表的正确方法是什么?

hyt*_*omo 6 gtk qt dbus c++ application-development

我想让我的应用程序使用动态统一快速列表。为了构建我的应用程序,我使用 C++ 和 QtCreator IDE。当触发菜单操作时,我希望能够访问我的MainWindow类的非静态函数,以便能够更新可以从“正常”MainWindow 函数内部访问的图形用户界面。

所以,我正在像这样建立我的快速列表(mainwindow.cpp):

void MainWindow::enable_unity_quicklist(){
    Unity_Menu = dbusmenu_menuitem_new();

    dbusmenu_menuitem_property_set_bool (Unity_Menu, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);

    Unity_Stop = dbusmenu_menuitem_new();
    dbusmenu_menuitem_property_set(Unity_Stop, DBUSMENU_MENUITEM_PROP_LABEL, "Stop");

    dbusmenu_menuitem_child_append (Unity_Menu, Unity_Stop);

    g_signal_connect (Unity_Stop, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(&fake_callback), (gpointer)this);

    if(!unity_entry)
        unity_entry = unity_launcher_entry_get_for_desktop_id("myapp.desktop");

    unity_launcher_entry_set_quicklist(unity_entry, Unity_Menu);

    dbusmenu_menuitem_property_set_bool(Unity_Menu, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
    dbusmenu_menuitem_property_set_bool(Unity_Stop, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
}

void MainWindow::fake_callback(gpointer data){
    MainWindow* m = (MainWindow*)data;
    m->on_stopButton_clicked();
}

void MainWindow::on_stopButton_clicked(){
   //stopping the process...
}
Run Code Online (Sandbox Code Playgroud)

主窗口.h:

private slots:
   void enable_unity_quicklist();
   void on_stopButton_clicked();
public slots:
   static void fake_callback(gpointer data);
Run Code Online (Sandbox Code Playgroud)

此建议取自http://old.nabble.com/Using-g_signal_connect-in-class-td18461823.html

在我从 Unity Quicklist 中选择“停止”操作后,程序立即崩溃。调试程序表明我无法在不崩溃的情况下访问 on_stopButton_clicked() 中与 MainWindow 相关的任何内容。例如,它在执行此检查时崩溃(这是此函数中的前 2 行代码):

if (!ui->stopButton->isEnabled())
        return;
Run Code Online (Sandbox Code Playgroud)

我还测试了我在互联网上找到的许多其他东西,但都没有奏效。一个有趣的解决方案是使用 gtkmm ( http://developer.gnome.org/gtkmm-tutorial/stable/sec-connecting-signal-handlers.html.en ) 但我根本不使用 GTK 应用程序 (我仅在 Qt 中工作),我不知道这是否适合我的场合。

Nat*_*man 3

编辑:好的,经过大量的调查(其中涉及运行valgrind),我终于设法弄清楚这里发生了什么。您的回调签名不正确。

静态方法的签名应该是:

void MainWindow::fake_callback(DbusmenuMenuitem *, guint, gpointer data)
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

请注意添加DbusmenuMenuitem *guint。第一个是指向发出信号的 DBus 菜单项的指针,第二个是时间戳。即使您不使用它们,两者也必须出现在签名中。

g_signal_connect在运行时调用,无法知道您的函数接受哪些参数,并且假设签名匹配,则盲目地将参数推入堆栈。(这与您在 Qt 中可能习惯的不同,其中 MOC(元对象编译器)会生成足够的信息来了解您何时尝试将信号连接到具有不兼容签名的插槽。)