ein thread und etwas mehr docu
authorKonrad Rosenbaum <konrad@silmor.de>
Mon, 25 Feb 2013 19:17:33 +0000 (20:17 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Mon, 25 Feb 2013 19:17:33 +0000 (20:17 +0100)
LIESMICH.txt
main/main.cpp
main/main.h
main/main.pro
main/thready.cpp [new file with mode: 0644]
main/thready.h [new file with mode: 0644]

index 9b772e3..b2e262e 100644 (file)
@@ -19,8 +19,29 @@ intern einsetzt...
 Ich benutze auch einige Features von C++11, deswegen GCC 4.7 (ob es auch mit
 4.4-4.6 geht habe ich nicht getestet).
 
+Kompilieren
+-------------
+
+Im Hauptverzeichnis einfach qmake && make aufrufen. Egal ob qmake von Qt 4 oder 5.
+
+Dann einfach bin/main starten.
+
+Vorsicht: make -j ... kann schiefgehen, da ich mir nicht die Mühe gemacht habe alle
+Abhängigkeiten explizit zu formulieren.
+
+Repo-Struktur
+---------------
+
+example.pro -> Verlinkt nur den Rest, ist schuld an fehlenden Abhängigkeiten
+
+main -> GUI und Lib-Loader, thread-Beispiel
+
+base -> Basisbibliothek, davon sind die eigentlichen Libs abgeleitet
+
+dylib[12] -> die ausgelagerten dynamisch geladenenn Bibliotheken
+
 Deutsch nix gutt!
--------
+------------------
 
 Alles was weiter als nur über Deine eigenen 4 Wände verbreitet werden soll,
 sollte in Englisch kommentiert sein. Ich kenne inzwischen die Panik die
@@ -115,3 +136,17 @@ http://de.wikipedia.org/wiki/Vermittler_(Entwurfsmuster)
 Dynamische Lib
 ---------------
 
+Es ist möglich auf vielen Unixoiden Systemen und Linux ein Plugin zu bauen
+welches Symbole des Programms benutzt. In den Standardeinstellungen fügt qmake
+aber nicht die passenden Argumente ein und ich habe mir auch nicht die Mühe
+gemacht das selbst zu tun. Grund: auf vielen Systemen geht es nicht! Zum Beispiel
+unter Windows muss eine DLL zur Compile-Zeit alle benötigten Symbole auflösen können
+und der Linker wird sich weigern eine EXE auch nur in Betracht zu ziehen.
+
+Also wird normalerweise alles was von einem Plugin gebraucht wird in ein eigenes
+.so ausgelagert und sowohl Plugin als auch Programm dagegen gelinkt. Im Extremfall 
+ist der Source des Programms ein Fünfzeiler (nur main) und alle Funktionalität
+liegt in einer DLL/so. (Das mache ich zum Beispiel in einem Projekt auf
+Arbeit so.)
+
+Im Beispiel ist die Funktions-DLL in base und die Plugins in dylib*.
index cfe0050..f462e62 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "main.h"
 #include "../base/thingy.h"
+#include "thready.h"
 
 #include <QLabel>
 #include <QPushButton>
 
 MainWin::MainWin()
 {
+        //initialize pointers
         mthinglib=nullptr;
         mthingylabel=nullptr;
         mthingy=nullptr;
         
+        //create buttons
         QVBoxLayout*vl;
         setLayout(vl=new QVBoxLayout);
         vl->addWidget(mthingylabel=new QLabel("..."));
@@ -27,19 +30,26 @@ MainWin::MainWin()
         connect(p,SIGNAL(clicked(bool)),this,SLOT(unloadLib()));
         vl->addWidget(p=new QPushButton("Call Thingy"));
         connect(p,SIGNAL(clicked(bool)),this,SLOT(callThingy()));
+        vl->addSpacing(10);
+        vl->addWidget(p=new QPushButton("Start Thread"));
+        connect(p,SIGNAL(clicked(bool)),this,SLOT(startThread()));
 }
 
 
 void MainWin::callThingy()
 {
+        //if we have a thingy, do things and show them in the label
         if(mthingy)mthingylabel->setText(mthingy->getThingy());
+        //otherwise apologize
         else mthingylabel->setText("Sorry, no thingy");
 }
 
 void MainWin::unloadLib()
 {
+        //delete the thingy or we might be in trouble next time we use it!
         if(mthingy)delete mthingy;
         mthingy=nullptr;
+        //delete/unload the library
         if(mthinglib){
                 mthinglib->unload();
                 delete mthinglib;
@@ -54,8 +64,10 @@ void MainWin::loadLib1()
         mthinglib=new QLibrary(qApp->applicationDirPath()+ "/libdylib1.so");
         if(!mthinglib->load())
                 qDebug()<<"load error"<<mthinglib->errorString();
+        //try to get the thingy factory
         ThingyFunc f=(ThingyFunc)mthinglib->resolve("getthing");
         qDebug()<<"tried to load, have?"<<mthinglib->isLoaded()<<(intptr_t)f;
+        //use the factory to create a thingy
         if(f)mthingy=f();
 }
 
@@ -64,13 +76,21 @@ void MainWin::loadLib2()
         unloadLib();
         //load new lib
         mthinglib=new QLibrary(qApp->applicationDirPath()+ "/libdylib2.so");
+        //try to get the thingy factory
         if(!mthinglib->load())
                 qDebug()<<"load error"<<mthinglib->errorString();
         ThingyFunc f=(ThingyFunc)mthinglib->resolve("getthing");
         qDebug()<<"tried to load, have?"<<mthinglib->isLoaded()<<(intptr_t)f;
+        //use the factory to create a thingy
         if(f)mthingy=f();
 }
 
+void MainWin::startThread()
+{
+        //start a thread and let it update the label automatically
+        MyThread::startMe(mthingylabel,SLOT(setText(QString)));
+}
+
 
 
 
index c332bd1..fc52e19 100644 (file)
@@ -6,20 +6,32 @@
 
 class QLabel;
 class QLibrary;
+
+///main window class
 class MainWin:public QDialog
 {
         Q_OBJECT
         public:
+                ///instantiate and populate it
                 MainWin();
         private slots:
+                ///loads library one
                 void loadLib1();
+                ///loads library two instead
                 void loadLib2();
+                ///unloads either library
                 void unloadLib();
+                ///starts a thread to do stuff in the background
+                void startThread();
                 
+                ///call that thingy from one of those libraries
                 void callThingy();
         private:
+                //label to show what we are doing
                 QLabel*mthingylabel;
+                //the library that we loaded
                 QLibrary*mthinglib;
+                //the thingy from that library
                 Thingy*mthingy;
 };
 
index e383a18..2694a77 100644 (file)
@@ -2,8 +2,8 @@ TEMPLATE = app
 QT += widgets
 DESTDIR = ../bin
 
-SOURCES += main.cpp
-HEADERS += main.h thingy.h
+SOURCES += main.cpp thready.cpp
+HEADERS += main.h thready.h
 
 QMAKE_CXXFLAGS += -std=gnu++11
 
diff --git a/main/thready.cpp b/main/thready.cpp
new file mode 100644 (file)
index 0000000..0683aef
--- /dev/null
@@ -0,0 +1,44 @@
+#include "thready.h"
+#include <QTimer>
+
+void MyThread::startMe(QObject* c, const char* s)
+{
+        //create thread and remember connection to the outside world
+        MyThread *m=new MyThread(c,s);
+        //start it
+        m->start();
+}
+
+MyThread::MyThread(QObject* callee, const char* slot): QThread(callee)
+{
+        //remember till run
+        mcallee=callee;
+        mcallslot=slot;
+}
+
+
+void MyThread::run()
+{
+        MyTask task;
+        //call gotime once per second
+        QTimer tmr;
+        connect(&tmr,SIGNAL(timeout()),&task,SLOT(gotime()));
+        tmr.start(1000);
+        //connect updateString to the outside world
+        // Qt::AutoConnection effectively: Qt::QueuedConnection
+        connect(&task,SIGNAL(updateString(QString)),mcallee,mcallslot);
+        //kill the thread in 10s
+        // uses Qt::AutoConnection -> Qt::QueuedConnection, quit() happens in the main thread, but that does not bother me -> it is bounced back to this thread
+        QTimer::singleShot(10000,this,SLOT(quit()));
+        //make sure it deletes itself
+        // Qt::AutoConnection effectively: Qt::DirectConnection - same object
+        connect(this,SIGNAL(finished()),this,SLOT(deleteLater()));
+        //now loop for a while
+        exec();
+}
+
+void MyTask::gotime()
+{
+        //simply count up and tell everyone
+        emit updateString(QString("Hello, I'm a thread and can count to %1").arg(ctr++));
+}
diff --git a/main/thready.h b/main/thready.h
new file mode 100644 (file)
index 0000000..5b20600
--- /dev/null
@@ -0,0 +1,43 @@
+#include <QThread>
+
+///Thread class, it starts, initializes and controls the thread.
+///It does not know much about what is done there.
+class MyThread:public QThread
+{
+        public:
+                ///not needed, use startMe
+                MyThread()=delete;
+                
+                ///start the thread
+                ///\param callee the object that gets the updated information, also owner of the thread object
+                ///\param slot the slot to call for updates, must be SLOT(foo(QString)) or SLOT(foo())
+                static void startMe(QObject*callee,const char*slot);
+        protected:
+                ///internal: creates the thread and remembers who to call
+                MyThread(QObject* callee,const char*slot);
+                ///runs the thread
+                void run()override;
+        private:
+                //remember who to call between startMe and run
+                QObject*mcallee;
+                const char*mcallslot;
+};
+
+///the actual class that performs the task of that thread
+class MyTask:public QObject
+{
+        Q_OBJECT
+        public:
+                ///create task object
+                MyTask()=default;
+                
+        private slots:
+                ///called to update the thread status
+                void gotime();
+        signals:
+                ///sent to the outside world when the thread has something to say
+                void updateString(QString);
+        private:
+                ///internal status
+                int ctr=0;
+};
\ No newline at end of file