Qt4: X11 Widget embedden.

Alles rund um die Programmierung mit Qt
Antworten
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Qt4: X11 Widget embedden.

Beitrag von nando »

hi,

ich suche eine moeglichkeit ein beliebiges X11 window in meine applikation zu embedden.

hat jemand schon erfahrungen gemacht.
auf das X11 window, das ich in meine applikation einbinden will habe ich kein einfluss.

gruss,
nando
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Doku schon mal angeschaut? Auf den ersten Blick findet man da QX11EmbedContainer.
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

ja, habe ich.....

ich habe es auch so versucht, aber ich bekomme immer den fehler:

QX11EmbedWidget::InvalidWindowID
The X11 window ID of the container was invalid. This error is usually triggered by passing an invalid window ID to embedInto().

Die id, die ich uebergebe habe ich mit dem tool wmctrl -l ermittelt.


Gruss,
Nando
ObeliX
Beiträge: 59
Registriert: 14. November 2007 17:47

Beitrag von ObeliX »

nando hat geschrieben:QX11EmbedWidget::InvalidWindowID
wie kommst du denn auf QX11EmbedWidget:: embedInto() ? das brauchst du wenn deine app sich wo 'embedden' lassen soll (so hab ich das jedenfalls verstanden).

ich denke du brauchst void QX11EmbedContainer::embedClient ( WId id ).


gruß Obel
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

Ja sorry, das hatte ich nur falsch gepostet..
Also mittlerweile kann ich mit embedClient() auch ein X-Fenster embedden, ABER nur in ein eigenes widget, also mit parent=0.

Wenn ich das fenster in ein layout packen will funktioniert es nicht ?
Also wenn ich z.b das widget nehme, wo das embedden funktioniert und
dann in mein layout sowas wie

pLayout->addWidget(pEmbeddedWidget);

...mache, kommt nur ein leeres widget.....mache ich es ohne layout dann geht es ...
ich versteh nicht warum...????

Bis denn,
Nando
ObeliX
Beiträge: 59
Registriert: 14. November 2007 17:47

Beitrag von ObeliX »

tja, mit der version ne andere X11-app 'einzufangen' hab ich mich leider nicht näher beschäftigt. zum einen, weil das problem besteht die WindowID des fremdprozesses in erfahrung zu bringen und zum anderen, weil das (auch ohne layout, also mit parent = NULL) bei mir immer nur solange funktioniert hat, bis ich zB. mal den desktop gewechselt habe. danach war mein widget auch immer leer (bzw. zeiget nur alle anderen, normalen child-widgets).

ich denke der window manager kommt nicht damit zurecht, wenn man ein prog embedet, bei dem dies nicht vorgesehen ist (da steht auch sowas ähnliches in der doku). als hauptfenster geht's noch solange gut bis sich der WM mal einschalten muß. vermutlich ist dies aber sofort der fall, wenn das widget in nem layout steckt (also child ist), sodaß man von anfang an nichts sieht. außerdem fand ich es unschön, daß der 'weggeklaute' prozess auch ein totes fenster hinterläßt.

ich mach das genauso wie im doku-beispiel. ich starte das gewünschte prog in einem QProcess, gebe ihm die WindowID des containers mit und das prog emdedet sich dann selber (das proggi ist dafür auch ausgelegt). und schon ist die welt in ordnung. verwendung in geschachtelten layouts kein problem, Qt kümmert sich um das durchrouten aller signale und events und es belibt kein zombi-fenster des embedeten proggi stehen.


MfG Obel
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

Hi,

danke fuer deine ausfuehrliche antwort!
okay, dann werde ich es auch mal so probieren und dann mal schreiben ob es / wie es funktioniert hat.
trotzdem schon mal danke!

gruss,
nando
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

Hi folgenden code habe ich vom Qt support bekommen,
aber auch das funktioniert nicht :(
wie bekomme ich denn die X11 window ID ?
ich hab das mit wmctrl -l probiert und die ID (im hex format) genommen.
aber auch in dem Qt example bekomme ich immer Invalid ID ....

Code: Alles auswählen

#include <QtCore>
#include <QtGui>
#include <QX11EmbedContainer>

class Embedder : public QX11EmbedWidget
{
    Q_OBJECT
public:
    Embedder()
    {
        winIdEdit = new QLineEdit;

        topContainer = new QX11EmbedContainer;
        bottomContainer = new QX11EmbedContainer;
        topEmbedButton = new QPushButton(tr("Embed into top container"));
        bottomEmbedButton = new QPushButton(tr("Embed into bottom container"));
        topDiscardButton = new QPushButton(tr("Discard top"));
        bottomDiscardButton = new QPushButton(tr("Discard bottom"));

        QLabel *winIdLabel = new QLabel(tr("&Window ID:"));
        statusLabel = new QLabel(tr("Ready to embed"));
        QGroupBox *clientStatusBox = new QGroupBox(tr("Client info"));
        QGroupBox *topContainerBox = new QGroupBox(tr("Top embedded client"));
        QGroupBox *bottomContainerBox = new QGroupBox(tr("Bottom embedded client"));
        clientWinId = new QLabel;

        winIdLabel->setBuddy(winIdEdit);
        topEmbedButton->setDefault(true);

        QHBoxLayout *winIdLayout = new QHBoxLayout;
        winIdLayout->addWidget(winIdLabel);
        winIdLayout->addWidget(winIdEdit);

        QGridLayout *clientStatusLayout = new QGridLayout(clientStatusBox);

        clientStatusLayout->addWidget(new QLabel(tr("Client Window ID:")), 0, 0);
        clientStatusLayout->addWidget(clientWinId, 0, 1);

        QVBoxLayout *leftLayout = new QVBoxLayout;
        leftLayout->addLayout(winIdLayout);
        leftLayout->addWidget(topEmbedButton);
        leftLayout->addWidget(topDiscardButton);
        leftLayout->addWidget(bottomEmbedButton);
        leftLayout->addWidget(bottomDiscardButton);

        leftLayout->addWidget(statusLabel);
        leftLayout->addWidget(clientStatusBox);
        leftLayout->addStretch();

        topContainerBox->setLayout(new QVBoxLayout);
        topContainerBox->layout()->setMargin(2);
        topContainerBox->layout()->addWidget(topContainer);

        bottomContainerBox->setLayout(new QVBoxLayout);
        bottomContainerBox->layout()->setMargin(2);
        bottomContainerBox->layout()->addWidget(bottomContainer);

        QSplitter *embedContainerSplitter = new QSplitter;
        embedContainerSplitter->setOrientation(Qt::Vertical);
        embedContainerSplitter->addWidget(topContainerBox);
        embedContainerSplitter->addWidget(bottomContainerBox);

        QHBoxLayout *layout = new QHBoxLayout(this);
        layout->addLayout(leftLayout, 0);
        layout->addWidget(embedContainerSplitter, 1);

        connect(topEmbedButton, SIGNAL(clicked()), this, SLOT(embed()));
        connect(bottomEmbedButton, SIGNAL(clicked()), this, SLOT(embed()));
        connect(topDiscardButton, SIGNAL(clicked()), this, SLOT(discard()));
        connect(bottomDiscardButton, SIGNAL(clicked()), this, SLOT(discard()));
        connect(topContainer, SIGNAL(clientIsEmbedded()), this, SLOT(clientEmbedded()));
        connect(topContainer, SIGNAL(clientClosed()), this, SLOT(clientClosed()));
        connect(topContainer, SIGNAL(error(QX11EmbedContainer::Error)), this, SLOT(embedError(QX11EmbedContainer::Error)));
        connect(bottomContainer, SIGNAL(clientIsEmbedded()), this, SLOT(clientEmbedded()));
        connect(bottomContainer, SIGNAL(clientClosed()), this, SLOT(clientClosed()));
        connect(bottomContainer, SIGNAL(error(QX11EmbedContainer::Error)), this, SLOT(embedError(QX11EmbedContainer::Error)));

        connect(this, SIGNAL(embedded()), this, SLOT(wasEmbedded()));

        setWindowTitle(tr("XEmbed Embedder"));
        setButtonsEnabled();
    }

    QSize sizeHint() const
    {
        return QSize(400, 300);
    }

    void setButtonsEnabled()
    {
        topEmbedButton->setEnabled(topContainer->clientWinId() == 0);
        topDiscardButton->setEnabled(topContainer->clientWinId() != 0);
        bottomEmbedButton->setEnabled(bottomContainer->clientWinId() == 0);
        bottomDiscardButton->setEnabled(bottomContainer->clientWinId() != 0);
    }

private slots:
    void embed()
    {
        QApplication::setOverrideCursor(Qt::WaitCursor);

        QString winId = winIdEdit->text();
        QTextStream stream(&winId);
        int id = 0;
        stream >> id;

        if (sender() == topEmbedButton)
            topContainer->embedClient(id);
        else
            bottomContainer->embedClient(id);
        setButtonsEnabled();
    }

    void discard()
    {
        if (sender() == topDiscardButton)
            topContainer->discardClient();
        else
            bottomContainer->discardClient();
        setButtonsEnabled();
    }

    void clientEmbedded()
    {
        statusLabel->setText(tr("Client is embedded"));
        if (sender() == topEmbedButton)
            clientWinId->setText(QString::number(topContainer->clientWinId(), 16));
        else
            clientWinId->setText(QString::number(bottomContainer->clientWinId(), 16));
        QApplication::restoreOverrideCursor();
        setButtonsEnabled();
    }

    void clientClosed()
    {
        statusLabel->setText(tr("Ready to embed"));
        clientWinId->setText(QString("<none>"));
        QApplication::restoreOverrideCursor();
        setButtonsEnabled();
    }

    void wasEmbedded()
    {
        statusLabel->setText(tr("Someone embedded me!"));
    }

    void embedError(QX11EmbedContainer::Error error)
    {
        clientWinId->setText(QString("<none>"));
        switch (error) {
        case QX11EmbedContainer::InvalidWindowID:
            statusLabel->setText(tr("Invalid window ID"));
            break;
        default:
            statusLabel->setText(tr("Unknown error"));
            break;
        }
        QApplication::restoreOverrideCursor();
        setButtonsEnabled();
    }

private:
    QX11EmbedContainer *topContainer;
    QX11EmbedContainer *bottomContainer;
    QPushButton *topEmbedButton;
    QPushButton *bottomEmbedButton;
    QPushButton *topDiscardButton;
    QPushButton *bottomDiscardButton;
    QLineEdit *winIdEdit;
    QLabel *statusLabel;
    QLabel *clientWinId;
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Embedder embedder;
    embedder.show();

    return app.exec();
}

#include "xembed.moc"
ObeliX
Beiträge: 59
Registriert: 14. November 2007 17:47

Beitrag von ObeliX »

also bei mir funktioniert das qt-test-prog. und zwar genauso wie mein eigenes test-proggi von damals, dh. so wie ich es gepostet habe. das fenster wird dem window-manager weggeklaut und hinterläßt einen toten rahmen. die embeddete app läuft normal, bis man den desktop wechselt. danach ist der container ohne jeglichen embeddet inhalt (ist aber nicht tot, dh. wird mit dem normalen widget-hintergrund gezeichnet).

nach dem discarden der geklauten applikation erscheint diese wieder auf dem desktop von welchem sie entwendet wurde ... allerdings als standalone-applikation. der WM weiß nicht mehr recht was damit anzufangen. das fenster wird oben links ohne rahmen/fensterdekoration eingeblendet, also nicht wieder zurückgebeamt in den alten rahmen.

was der WM aber scheinbar noch weiß, ist, daß beides irgendwie zusammen gehört. wenn man die zurückgebeamte app beendet (zb. per menü-eintrag), dann verschwindet auch der alte, tote rahmen. und das funktioniert auch andersrum. wenn man beim rahmen den 'close'-button betätigt, dann verschwindet auch das zurückgebeamte app-fenster.

die window-id als hex-wert von wmctrl ist korrekt. das funzt bei mir mit dem qt-test-code. du schreibst die zahl doch sicher mit dem führenden 0x in das eingabefeld !?


ich nutz übrigens kubuntu.

gruß Obel
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

Hi,
ich habe es bei nem kollegen aufm pc probiert und da funzt es...
komisch..
wir beide nutzen debian... (Lenny)....
vielleicht liegt es an den Parametern mit denen wie X starten, oder irgendwelchen xorg.conf einstellungen?

ich habe da ne geringe abweichung.. werde das auch noch mal porbieren...


Uebrigens auf einem SuseEnterprise 10.1 mit KDE funktioniert es hier auch nicht.....

Hmmm... Seltsam...

Gruss,
Nando
GoaSkin
Beiträge: 103
Registriert: 13. Juni 2007 00:14
Wohnort: Darmstadt

Re: Qt4: X11 Widget embedden.

Beitrag von GoaSkin »

Wenn man eine X11 Anwendung einbetten möchte, der man keine Window-ID in der Kommandozeile mitgeben kann, gibt es dann einen eleganteren Weg, als Folgenden?

1.) Anwendung per QProcess starten
2.) In Timer-Funktion 'wmctrl -l' ausführen und Ausgabe nach Zeilen mit einem String parsen
3.) nach jedem Timer-Ablauf die embedClient-Funktion mit der aus wmctrl ausgelesenen Fenster-ID ausführen?

Gibt es eine andere Möglichkeit, unbekannte Window-IDs herauszufinden als über Shell-Kommandos und eine Möglichkeit, auch auf einen Timer zu verzichten?
Antworten