Seite 1 von 1

Eine existierende Qt C++ App mit Python erweitern können

Verfasst: 4. Februar 2010 19:22
von FrancisA
Hallo ich war mir nicht sicher ob besser hier oder in "Einfach Qt" Unterforum. Ich habe praktisch kaum Ahnung von qt bis jetzt.

Folgende größere Sache schwant mir vor (es geht jetzt für mich darum, in Erfahrung zu bringen, wie weit das möglich ist) :

Ihr kennt doch sicher die meisten den Krusader.

Es geht mir um die Möglichkeit (ich bin ohnehin nicht Entwickler bei diesem Projekt).

In dem ganzen Dschungel sip, pythonqt, kross, swig, boost, direkt embedden und extenden kenne ich mich bald nicht mehr aus.

Meine Frage ist, wie kann man mit möglichst wenig Aufwand den maximalen Zugriff auf Krusader von Python aus haben. Ohne jedoch beim Source code
viel ändern zu müssen

Also als erstes bräuchte ich ein Plugin Verwaltung (Managment) (also wie gesagt verwalten, laden, starten, entfernen, deaktivieren)

Wenn ich das richtig sehe, brauche ich sowohl embedding (von C++ heraus auf Pyhton zuzugreifen) als auch extending (also auf die Objekte sollen (am besten alles, nicht wie beim kross nur die slot funktionien), member Variablen (und Objektpointer), Funktionen; wie auch interene queue Variablen zB). Das heisst, ich möchte
mich nicht auf ein paar exportierte Methoden verlassen können, sondern auch die Variablen im Objekt ändern können.

zB. in der Form: kapp.getmainview().getactivepanel()->feedListbox(<myvars>). Da wäre am einfachsten (pseudocode):

import kapp (oder import krusader)
kapp...

Es sollte auch möglich sein, Menü Einträge anhängen zu könenn und Keyboard Shortcuts zuzuweisen.

Ist das richtig so? Ist das schaffbar? Wäre das viel Aufwand?

Vielen Dank schon mal im voraus!

Verfasst: 5. Februar 2010 10:02
von RHBaum
Schaffbar iss sicher alles.
Nur der Aufwand iss IMHO heftig .

Krusader kenn ich gar ned, also keine ahnung von was du sprichst ^^

Aber:

python code von ner c++ app ausfuehren:
Das eigentliche iss gar ned so schwer. py dateien in den interpreter laden + einsprungsfunktion aufrufen = 10 zeiler, wenn keinerlei daten uebergeben werden.
Müssen daten uebergeben werden, und von python heraus manipuliert werden -> aua, du breuchst python wrapper, die deine daten in python verstaendliche Daten aendern. Kann je nach Typ der daten unendlicher aufwand bedeuten. Pods gehen sicher recht schnell, aber komplexe c++ datenstrukturen abstracte klassen Hirarchien usw wird sicher nen schoenes gefrimmel :-)

Ne App von python aus fernsteuern:
Du brauchst nen IPC mechanismus !
unter linux wuerde sich gleich DBus anbieten, das kann python nativ auch. Brauchst nur noch bissi den dbus in python wrappen, so das intuitiv verwendbare objecte rausbekommst. aber auch hier, komplexere datenstrukturen werden dir imense probleme machen, bzw musst recht komplex soweiso ueber dbus abbilden.
Knackpunkt iss eher, die App gescheit auf Dbus umzustellen. Das kann auch ne weile dauern.

Ciao ....

Verfasst: 5. Februar 2010 15:24
von FrancisA
Hallo RHBaum,

erstmals danke

Stufe 1 (Python aufrufen von C++) geht schon seit längerem (kein Wunder, das war ja das leichteste). Geht mit kross oder "direkt (der offizielle steinige Weg" mit Python's hauseigene Mitteln.

Ja, aua, da brauche ich einen python wrapper, und da gehts ans Eingemachte.

Nachtrag: ich habe mir einmal das Superkaramba angesehen. Aber das ganze (der Riesenaufwand) ist doch sehr entmutigend. Da könnte mans ja gleich fast völlig neu in pyqt schreiben, mit extending von zeitkritischen Sachen, oder die man sonst nicht in Python hineinbekommen würde.

Verfasst: 8. Februar 2010 11:13
von RHBaum
Diese option besteht immer ....

und ne reine c-dll aufrufen mit pods als parameter aus python raus iss kein Thema.

Komplexere datenstrukturen und hirarchien nach python hineinzuwrappen lohnt sich IMHO nur, wenn man seine funktionalitaet ner breiten masse an anderen Entwicklern zur verfuegung stellen will, also quasi wenn fuer ein atabliertes C++ programm ne python Plugin/erweiterungsschnittstelle oder aehnliches hinzukommen soll. für einmalige verwendung isses sicher overkill.

Auf der anderen seite isses so schwierig nu auch wieder nich. Nur halt eben bissi fleiss. Als erstes c++ Objecte nach c- bringen. Dazu koennte man SWIG verwenden. Und die c-strukturen nach python zu wrappen iss dann nimmer so schlimm. Nur intuitive strukturen dann in python schaffen, das koennt wieder bissi aufwendiger werden.

Ciao ...

Verfasst: 8. Februar 2010 11:33
von franzf
Mit SWIG + CMake ist es absolut easy, seine C++-Klassen in Python verfügbar zu machen. Es geht sogar, dass man in Python eine eigene Klassen von einer in C++ geschriebenen Basisklassen ableitet und virtuelle Funktionsaufrufe erhält.

Was ich aber im Moment versuche (und auf die Schnelle keine Lösung gefunden habe) ist eine Factory-Methode in python, die mir ein PyObject (wieder im C++-Code) liefert (kein Problem), und ich dann dieses in ein C++-Objekt casten kann, welches sich dann wie ein C++-Plugin verhält.
Vielleich weiß ja da einer weiter.
THX!

Verfasst: 8. Februar 2010 11:48
von RHBaum
Factory-Methode in python, die mir ein PyObject (wieder im C++-Code) liefert (kein Problem), und ich dann dieses in ein C++-Objekt casten kann, welches sich dann wie ein C++-Plugin verhält.
Also du erstellst in ner python funktion ein Object, was ueber ne C++ dll erstellt wird, und in python nur gewarappt ist ....
dann willst das object ueber ne andere python funktion wieder an ne c++ dll uebergeben und die soll irgendwie das object ned ueber die exportierten funktionen ansprechen sondern "einfach" wieder nach c++ casten und internas verwenden ???

Sicher das beide c++ funktionen im selben prozessraum liegen, so das sie zeiger austauschen koennen ??? Sicher das beide c++ dlls/binaries den selben kompiler verwenden und damit auch binaerkompatibel sind ??? Wenn ja, dann spendier deiner c++ und python Klasse eine "Handle" funktion, die die Adresse der Instanz als rohen zeiger (void * ) nach aussen gibt.

Besser waer aber wenn die 2.te dll nur die auch von python zur verfuegung gestellten und dann wieder nach c++ gewrappten funktionen benutzt. Vielleicht kommst irgendwann doch mal auf den gedanken, die c++ basierte factory durch ne reine python factory zu ersetzen ?

Verfasst: 8. Februar 2010 12:07
von franzf
Ich stell mir das so vor (ist schneller mit Code erklärt als mit langen Worten):

Eine Basisklasse für meine Plugins

Code: Alles auswählen

namespace cpp { // Dass es klarer ist
class Test
{
    virtual void testMe()=0;
public:
    virtual ~Test() {}
    void test() {
        testMe();
    }
};
}
Jetzt wrapped SWIG das ganze nach python. Dort mache ich ein:

Code: Alles auswählen

class PyTest (cpp.Test):
    def __init__(self):
        cpp.Test.__init__(self)
    def testMe(self):
        print("Python Test")

def create():
    return PyTest()
Und nun lädt meine Applikation (also nicht irgendeine DLL) das python-Script, holt sich die create()-Methode (->PyObject*). Ich habe jetzt ein PyObject* und möchte es in ein C++-Object des Typs "cpp::Test*" casten.
Wie geht das?

Code: Alles auswählen

int main()
{
    [...]
    PyObject* module = PyImport_ImportModule("PyTest");  // PyTest.py
    PyObject* factory = PyObject_GetAttrString(module, "create");
    PyObject* pytester = PyEval_CallObject(factory, 0);
    
    // Und das geht natürlich nicht...
    cpp::Test* t = (cpp::Test*)(pytester);   /// wie geht das? Geht das überhaupt?
    t->test();
    [...]
}
Es ist sicher, dass die main-app und der SWIG-Wrapper mit dem selben Compiler übersetzt werden.

Verfasst: 8. Februar 2010 17:35
von FrancisA
Hallo RHBaum und franzf,

danke schon mal. Irgendwie schwirrt mir der Kopf. Ich denke am einfachsten wäre es, so etwas auf minimalistischer Ebene aufzubauen.

Ein ganz kleines Qt C++ Programm (ein, zwei Klassen) zu erstellen.

Dann ruft es einmal ganz normal mit PyInit, pyrunfile, und pyfinalize ein python script auf.

Bis hierher ist es einfach.

Nun wäre es interessant, wie man mit SWIG o. ä. es einmal soweit hinbekomt, dass dann das python script einige Objekte (oder Methoden) sehen kann.

Dann wäre der Bereich Datenaustausch interessant. In wie weit können sich Python und C++ Daten, Objekte, Zeiger teilen. Ideal wäre, wenn Python C++ Objekte ändern kann und andersherum auch.

Mir wäre einmal der "konservative" Weg (also ohne kross) einmal lieber, da man hier das von der Picke auf sehen kann.

Das wäre sicher toll, so ein Template zu haben. Das würde, da bin ich mir sicher, viele andere auch interessieren. Wäre auch eine Wiki Seite wert, IMHO.

Vielleicht ist meine Sichtweise zu naiv... :)

Nachtrag: Ich sehe gerade, dass es hier eh ein Wiki gibt. Wenn das hier nicht zu offtopic ist, könnte man das ja hier einmal anlegen(?)

Verfasst: 9. Februar 2010 09:50
von RHBaum
@franzf
also wie gesagt, wenn du sicher bist, das beides im selben adressraum residiert, dann

Code: Alles auswählen

namespace cpp { // Dass es klarer ist
class Test
{
    virtual void testMe()=0;
public:
    virtual ~Test() {}
    void test() {
        testMe();
     }
     void * getHandle() const 
     {
          return this;
     }
};
}
Die handle funktion auch mit nach python exportieren, keine ahnung welcher typ fuer void* gut geeignet waer, und obs da was gibt.
notfalls irgend einen 32bit typ


wieder in c++ geht dann sowas:

Code: Alles auswählen

    cpp::Test* t = reinterpret_cast<cpp::Test*>(pytester.gethandle());
Aber ehrlich, ich wuerd sowas meiden wie die Pest.
entweder wuerd ich mit python reine daten ohne klassenfunktionalitaet portieren, oder ich wuerd über python nur die funktionspointer übertragen.
Alles andere waer mir zu unsicher.

und btw: C-casts sind poese ! ^^

Ciao ....

Verfasst: 9. Februar 2010 11:41
von franzf
RHBaum hat geschrieben:@franzf
also wie gesagt, wenn du sicher bist, das beides im selben adressraum residiert
Da hab ich keine Ahnung... libpython ist ja eine dynamische Lib. Da kenn ich mich zu wenig aus, in welchem Adressraum jetzt die ganzen PyObjects liegen. Ich würde sagen die liegen in dem Speicherbereich, in dem auch mein "main" liegt, da erstell ich die ja auch. Ich wrapp das ganze ja nicht nochmal extra und lad das dynamisch nach.
Von dem her sollte das klappen.

Code: Alles auswählen

namespace cpp { // Dass es klarer ist
struct Test
{
     unsigned long getHandle() const
     {
          return reinterpret_cast<unsigned long>(this);
     }
};
}
In der main dann

Code: Alles auswählen

PyObject* module = PyImport_ImportModule("testapp");
PyObject* factory = PyObject_GetAttrString(module, "create");
PyObject* pytester = PyEval_CallObject(factory, 0);
char method[] = "getHandle";
PyObject* ret = PyObject_CallMethod(pytester, method, 0);
unsigned long address = PyLong_AsUnsignedLong(ret);
Tester* t = reinterpret_cast<Tester*>(address);
t->test();
Funktioniert tadellos (bis jetzt), danke dafür!
Aber ehrlich, ich wuerd sowas meiden wie die Pest.
entweder wuerd ich mit python reine daten ohne klassenfunktionalitaet portieren, oder ich wuerd über python nur die funktionspointer übertragen.
Alles andere waer mir zu unsicher.
Ich habe mir auch schon überlegt, ob ich nicht ein reines Wrapper-Plugin schreibe, welches ein PyObject* hält (die Python-Instanz meines Plugins) und dann die Kommunikation Aussenwelt<->PyObject übernimmt.
Ich frag mich halt, wie andere Frameworks das hinbekommen...
Ich kann Python-Plasmoids auf meinem Desktop anzeigen. Plasma ist aber in C++ geschrieben.
Die Plasmoids können wild mit DataEngines kommunizieren, dabei ist es egal in welcher Sprache die geschrieben sind...

Ich hab schon in den Sourcen geschau, aber bisher noch nicht den entscheidenden Codeabschnitt gefunden :(
und btw: C-casts sind poese ! ^^
Ja, schon klar ;)
Aber mit den C++-casts bin ich nicht weiter gekommen :P

Danke nochmal, ich werde mein Anliegen nochmal überschlafen :)