What's the counterpart of QX11EmbedWidget in Qt5?

1.8k Views Asked by At

In Qt4, we could use the embedInto method of the QX11EmbedWidget class to embed any QWidget into another application. But in Qt5, the QX11EmbedWidget class was dropped. I've been searching Google for many hours, but all I found indicated to use the QWidget::createWindowContainer method instead. However, as far as I understand, this method is rather a replacement for QX11EmbedContainer.

So here's the question: How do we embed a Qt5-Widget into a non-Qt window? - And in case it metters: The non-Qt window is Gtk3.


I've been asked for the use-case, so let me illustrate briefly: Consider a Gtk-based application, which you are willing to extend - and the UI component, which you need, already exists, but is written in Qt. The goal is to avoid porting the application to Qt or the component to Gtk.

2

There are 2 best solutions below

0
On BEST ANSWER

My initial approach was to request the embedding from Qt side. I had no luck with this, as I couldn't find any equivalent for QX11EmbedWidget::embedInto in Qt5. After giving up on this, I decided to give XReparentWindow a try, which was reportedly used with success. However, due to lack of documentation, I wasn't able to reproduce this. My third attempt was to initiate the embedding from server side, which is the Gtk window in my case. I'm glad to report, that it worked :-)


For the Record: How to embed an arbitrary Qt5 Widget into a Gtk window

TL;DR:

  1. Make sure that you write your Gtk code in a unit which is separate from the Qt part. This is necessary in order to avoid name conflicts between Qt5 and Gtk3.
  2. Simply use gtk_socket_add_id to embed any X11 window into a GtkSocket.

Also see the documentation for reference, but note that the example given their doesn't even compile, because they forgot the GTK_SOCKET macro. On the contrary, the following code works.

proof

Details

The QGtkWindow class provides an interface to the Gtk window.

class QGtkWindow : public QObject
{

public:

    QGtkWindow();
    virtual ~QGtkWindow();

    void setCentralWidget( QWidget* const widget );
    void show();

private:

    GtkWindowAdapter gtkWindow;
    QWidget* const container;

}; // QGtkWindow

The GtkWindowAdapter class wraps the Gtk calls and isolates them from the Qt part of the application. An object of this class represents the Gtk window.

class GtkWindowAdapter
{

public:

    GtkWindowAdapter();
    ~GtkWindowAdapter();

    void show();
    void embed( unsigned long clientWinId );

private:

    struct Details;
    const std::auto_ptr< Details > pimpl;

}; // GtkWindowAdapter

When instantiated, an GtkWindowAdapter object first initializes Gtk,

static bool gtkInitialized = false;

struct GtkWindowAdapter::Details
{
    GtkWidget* widget;
    GtkWidget* socket;

    void setupUi();
};

GtkWindowAdapter::GtkWindowAdapter()
    : pimpl( new Details() )
{
    if( !gtkInitialized )
    {
        int argc = 0;
        gtk_init( &argc, NULL );
        gtkInitialized = true;
    }
    pimpl->setupUi();
}

and then setups the Gtk window:

void GtkWindowAdapter::Details::setupUi()
{
    widget = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    socket = gtk_socket_new();
    gtk_widget_show( socket );
    gtk_container_add( GTK_CONTAINER ( widget ), socket );
    gtk_widget_realize( socket );
}

You might already have noted the embed method which this class provides. This method initiates the embedding of any X11 window. The show method turns the encapsulated Gtk window visible.

void GtkWindowAdapter::embed( unsigned long clientWinId )
{
    gtk_socket_add_id( GTK_SOCKET( pimpl->socket ), clientWinId );
}

void GtkWindowAdapter::show()
{
    gtk_widget_show( pimpl->widget );
}

Now, the implementation of the QGtkWindow class is fairly simple. When created, it initializes a Gtk window by using the GtkWindowApdater and puts an empty QWidget into that window:

QGtkWindow::QGtkWindow()
    : container( new QWidget() )
{
    container->setLayout( new QVBoxLayout() );
    gtkWindow.embed( container->winId() );
    container->show();
}

When the user of the QGtkWindow class decides to put some widget into the window, the setCentralWidget is the way to go. It simply clears the parent widget, which was embedded into the Gtk window originally, then inserts the user's widget instead:

void QGtkWindow::setCentralWidget( QWidget* const widget )
{
    qDeleteAll( pimpl->container->layout()->children() );
    pimpl->container->layout()->addWidget( widget );
}

void QGtkWindow::show()
{
    pimpl->gtkWindow.show();
}

Hope this might spare somebody many hours.

6
On

To quote from this link:

https://forum.qt.io/topic/32785/qwindow-qwidget-qt5-x11embedding-how/2

All the x11 stuff has moved to the "extras" department found on Gitorious. (QX11EmbedWidgets and QX11EmbedContainer and the likes are not in 5.x)

Try this:

http://qt-project.org/doc/qt-5.1/qtx11extras/qx11info.html