From b2e3b61c1f228556d37b0c502cd27dfaafde0697 Mon Sep 17 00:00:00 2001 From: Konrad Rosenbaum Date: Mon, 25 Feb 2013 20:17:33 +0100 Subject: [PATCH] ein thread und etwas mehr docu --- LIESMICH.txt | 37 ++++++++++++++++++++++++++++++++++++- main/main.cpp | 20 ++++++++++++++++++++ main/main.h | 12 ++++++++++++ main/main.pro | 4 ++-- main/thready.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ main/thready.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 main/thready.cpp create mode 100644 main/thready.h diff --git a/LIESMICH.txt b/LIESMICH.txt index 9b772e3..b2e262e 100644 --- a/LIESMICH.txt +++ b/LIESMICH.txt @@ -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*. diff --git a/main/main.cpp b/main/main.cpp index cfe0050..f462e62 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2,6 +2,7 @@ #include "main.h" #include "../base/thingy.h" +#include "thready.h" #include #include @@ -11,10 +12,12 @@ 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"<errorString(); + //try to get the thingy factory ThingyFunc f=(ThingyFunc)mthinglib->resolve("getthing"); qDebug()<<"tried to load, have?"<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"<errorString(); ThingyFunc f=(ThingyFunc)mthinglib->resolve("getthing"); qDebug()<<"tried to load, have?"<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))); +} + diff --git a/main/main.h b/main/main.h index c332bd1..fc52e19 100644 --- a/main/main.h +++ b/main/main.h @@ -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; }; diff --git a/main/main.pro b/main/main.pro index e383a18..2694a77 100644 --- a/main/main.pro +++ b/main/main.pro @@ -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 index 0000000..0683aef --- /dev/null +++ b/main/thready.cpp @@ -0,0 +1,44 @@ +#include "thready.h" +#include + +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 index 0000000..5b20600 --- /dev/null +++ b/main/thready.h @@ -0,0 +1,43 @@ +#include + +///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 -- 1.7.2.5