qtubuntuqt5systrayappindicator

QT Systray icon appears next to launcher on Ubuntu instead of on the panel


I am new to QT and need to build an app with an app-indicator. As QT seems easier than GTK+, I am making it in QT.

I would mention that I have sni-qt installed and app indicators of vlc and skype appear normal on the panel. I am using QT5 on Ubuntu 13.04 64-bit.

I followed this tutorial step by step: http://qt-project.org/doc/qt-4.8/desktop-systray.html

But when I run it, here's how it appears (The cross is the icon I am using):

https://i.sstatic.net/4bT33.png

How do I fix this?


Solution

  • I'm afraid that Qt5 is not supported by sni-qt at the moment, so you either have to wait for a new release that will support it, or code it using gtk+ and libappindicator using this guide. There are even examples for various of languages. Since Qt5 also distributes GLib events that makes the integration a lot more easier. First you need to find out whether you're running on Unity or not (to support more desktops than just unity), that you can do by retrieving XDG_CURRENT_DESKTOP environment variable and if it returns Unity you create appindicator, otherwise create QSystemTrayIcon.

    First you need to include required headers:

    #undefine signals                                                  
    extern "C" {                                                                 
      #include <libappindicator/app-indicator.h>                                 
      #include <gtk/gtk.h>                                                       
    }                                                                            
    #define signals public                                                       
    

    Since app-indicator directly uses "signals" name we need to undefine the default Qt "keyword" signals which normally translates to public. Then, since we're coding C++ and libappindicator is coded in C we need to use extern "C" not to use C++ name mangling.

    Next create the AppIndicator/QSystemTrayIcon based on what desktop are we on:

    QString desktop;
    bool is_unity;
    
    desktop = getenv("XDG_CURRENT_DESKTOP");                                     
    is_unity = (desktop.toLower() == "unity");                             
    
    if (is_unity) { 
      AppIndicator *indicator;
      GtkWidget *menu, *item;                                                       
    
      menu = gtk_menu_new(); 
    
      item = gtk_menu_item_new_with_label("Quit");                             
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);                          
      g_signal_connect(item, "activate",                                          
                   G_CALLBACK(quitIndicator), qApp);  // We cannot connect 
                   // gtk signal and qt slot so we need to create proxy 
                   // function later on, we pass qApp pointer as an argument. 
                   // This is useful when we need to call signals on "this" 
                   //object so external function can access current object                         
      gtk_widget_show(item);
    
      indicator = app_indicator_new(                                       
      "unique-application-name",                                                   
          "indicator-messages",                                                                  
        APP_INDICATOR_CATEGORY_APPLICATION_STATUS                                
      );                                                                         
    
      app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE); 
      app_indicator_set_menu(indicator, GTK_MENU(menu));   
    } else {
      QSystemTrayIcon *icon;
      QMenu *m = new QMenu();                                                   
    
      m->addAction(tr("Quit"), qApp, SLOT(quit()));                      
    }                                                                            
    

    Finally we create the proxy function to call Qt signal from it, to declare the function we need to use extern "C" so there will not be any undefined behaviour.

    extern "C" {                                                                    
      void quitIndicator(GtkMenu *, gpointer);                                            
    }                                                                               
    

    Now the proxy function:

    void quitIndicator(GtkMenu *menu, gpointer data) {                                    
      Q_UNUSED(menu);                                                               
      QApplication *self = static_cast<QApplication *>(data);                       
    
      self->quit();                                                                 
    }