Funktion über QPushButton beenden



  • ...



  • Mhh, könntest du mir vielleicht nen kleinen Codeschnippsel, also Minimalbeispiel posten.
    Verstehe das net so ganz, ich soll die Suche in nen Thread packen( weiß net genau wie ) und den anhalte-Button mit dem quit Slot vom Thread verbinden?

    Gruß freeG



  • Deine run-Funktion hat eine falsche Signatur! run nimmt keinen Parameter, so wirst du auch nie einen laufenden (arbeitenden) Thread bekommen.
    Und connecten tust du in mainWindow.
    Schemenhaft so (wird nicht kompilieren :P)

    class Suche : public QThread
    {
        Q_OBJECT
        void run() {
            suche_mich_zu_tode();
        }
    // usw.
    };
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
        Suche* suche;
        QPushButton* btn;
    public:
        MainWindow() {
            suche = new Suche(this);
            btn = new QPushButton("Beende Suche");
            QObject::connect(btn, SIGNAL(clicked()), suche, SLOT(terminate()));
            suche->start();
        }
    };
    

    exec() in run() brauchst du nur, wenn du in dem eigenen Thread auch eine eigene Eventloop brauchst, z.B. dass im Thread-Kontext SLOTS ausgeführt werden.
    terminate ist nicht wirklich schön, weil es den Thread nur abwürgt aber nicht aufräumt. quit() stoppt die Eventloop. Ich weiß nur nicht, wie du rein über SIGNALS/SLOTS deine Suche erledigen willst. Eine Suche läuft normalerweise über Schleifen, und die blockiert so lange bis terminiert, da bringt dir ne eventloop rein gar nix.



  • Ja so wie dus es mir aufgezeigt hast, sollte es doch gehen oder nicht?

    Versteh ich net so ganz:D
    Oder was wäre denn deine Idee?

    Gruß freeG

    Und schonmal vielen Dank



  • fr33g schrieb:

    Ja so wie dus es mir aufgezeigt hast, sollte es doch gehen oder nicht?

    Versteh ich net so ganz:D
    Oder was wäre denn deine Idee?

    Gruß freeG

    Und schonmal vielen Dank

    Ja, so kannste es versuchen (auch wenn es schönere Ansätze gibt).
    Und was konkret verstehst du noch nicht?



  • Also ich hab das nun so gemacht, kann jedoch trotzdem den anhalten-Button nicht betätigen, der start-Button ist so makiert bis die Suche vorbei ist.

    Verstehe dass net, könnt ihr mir vll helfen?

    Gruß freeG und schonmal vielen Dank



  • Immer eine Suche Wert: http://doc.qt.nokia.com/4.6/examples.html

    mit String+f && "Thread" landet man bei:

    http://doc.qt.nokia.com/4.6/threads-mandelbrot.html

    Kurzform:

    Pack einen boolean namens ENDE in deine QThread-Klasse und spendiere ihr einen Slot der den Boolean invertiert.

    Du connectest den Button-click() mit dem Slot. Beim Starten der run() setzt du den boolean auf TRUE, beim Verlassen der run() auf FALSE.

    Im run() Teil fragst du öfter

    void YourQThread::run()
    {
        ENDE=FALSE;
        for(int i=0;i<1000000000;++i)
        {
           // kurze Teilsuche 
           if (ENDE) break;
        }
        ENDE=TRUE;
    }
    

    oder

    void YourQThread::run()
    {
        ENDE=FALSE;
        while (!ENDE) 
        {
           // kurze Teilsuche 
        }
        ENDE=TRUE;
    }
    

    nützlich ist dabei sind auch:

    class QMutex;
    
    bool 	isFinished  () const
    bool 	isRunning () const
    

    Würde ich zumindest in etwa so versuchen...



  • Vielen Dank für die Anwort,
    werde ich gleich heute Nachmittag nach dem Geschäft testen, melde mich dann nochmal.

    Gruß freeG



  • Also ich habe es so gemacht, es funktioniert jedoch leider trotzdem nicht.
    Ich kann einfach den anhalten-Button nicht betätigen.
    Der Starte-Button bleibt nachdem ich ihn betätige einfach aktiv bis die Suche beendet ist.

    Weiß jemand wo dran dies liegen kann?

    Danke schonmal für die Hilfe bisher=)

    Gruß freeG



  • Startbutton bleibt aktiv? Das klingt als ob da was ganz falsch läuft.
    Poste mal ein Minimalbeispiel...



  • # -------------------------------------------------
    # Project created by QtCreator 2010-06-15T09:42:55
    # -------------------------------------------------
    TARGET = ThreadStop
    TEMPLATE = app
    SOURCES += main.cpp \
        mainwindow.cpp \
        mythread.cpp
    HEADERS += mainwindow.h \
        mythread.h
    

    ** main.cpp **

    #include <QtGui/QApplication>
    #include "mainwindow.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }
    

    ** mainwindow.h **

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    class MyThread;
    class QLabel;
    
    class MainWindow : public QMainWindow {
        Q_OBJECT
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    public slots:
        void getThreadLoopsCounter();
    
    private:
        MyThread * th; // work thread
        QLabel * result; // private pointer for result visulization
    };
    
    #endif // MAINWINDOW_H
    

    ** mythread.h **

    #ifndef MYTHREAD_H
    #define MYTHREAD_H
    
    #include <QThread>
    
    class MyThread : public QThread
    {
        Q_OBJECT
    
    public:
        MyThread(QObject * parent = 0);
    
        void run();
    
        const quint32 howManyTimes() const { return counter;} // returns count of cylces
    
    public slots:
        void stopMyThread();
    
    private:
        bool stopIt;
        quint32 counter; // counts cycles while in run()
    };
    
    #endif // MYTHREAD_H
    

    ** mainwindow.cpp **

    #include "mainwindow.h"
    #include "mythread.h"
    
    #include <QtGui/QGridLayout>
    #include <QtGui/QPushButton>
    #include <QtGui/QLabel>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
    {
        // GUI SETUP: 2 buttons, 1 Label, 1 Result(label)
    
        QWidget * center = new QWidget();
        QGridLayout * layout = new QGridLayout;
    
        QPushButton * start = new QPushButton("Start");
        QPushButton * stop = new QPushButton("Stop");
        QLabel * label = new QLabel("RESULT:");
    
        result = new QLabel(QString(11,' '));
    
        layout->addWidget(start,0,0,1,2);
        layout->addWidget(stop,0,2,1,2);
        layout->addWidget(label,1,0,1,1);
        layout->addWidget(result,1,1,1,3);
    
        center->setLayout(layout);
        center->resize(layout->sizeHint());
    
        setCentralWidget(center);
        resize(sizeHint());
        // DONE WITH GUI SETUP
    
        th = new MyThread(this); // create thread
    
        connect(start, SIGNAL(clicked()), th, SLOT(start())); // connect startbutton to thread-slot start()
        connect(stop, SIGNAL(clicked()), th, SLOT(stopMyThread())); // connect stopbutton to thread-slot stopMyThread()
        connect(th, SIGNAL(finished()), this, SLOT(getThreadLoopsCounter())); // connect finished signal of thread to slot that updates the result(label)
    }
    
    MainWindow::~MainWindow()
    {
    }
    
    void MainWindow::getThreadLoopsCounter()
    {
        // set result(label)'s text to result as String
        result->setText(QString::number(th->howManyTimes()));
    }
    

    ** mythread.cpp **

    #include "mythread.h"
    
    MyThread::MyThread(QObject * parent) :
            QThread(parent), stopIt(false), counter(0)
    {
    }
    
    void MyThread::stopMyThread()
    {
        stopIt = true; // stop it soon
    }
    
    void MyThread::run()
    {
        stopIt = false; // reset stop it
        counter = 0; // reset counter
    
        while (! stopIt) // loop while stopIt == false 
        {
            ++counter; // increadse counter
            msleep(100); // sleep for 0.1 seconds
        }
    }
    

    Das sollte ein [gelöst] vor dem Titel verdienen...



  • Hey erst mal vielen Dank für die ausführliche Antwort mit Code, jedoch funktioniert
    das einfach nicht=(.

    Ich post jetzt mal mein Code als Minimalbeispiel:

    Die main() ist genau wie bei dir.

    mainWidget.h

    class mainWidget : public QMainWindow
    {
    public:
    mainWidget( QMainWindow* parent = 0 );
    // hier sind die ganzen Buttons und layouts und so weiter...
    myThread* suchThread;
    };
    

    myThread.h

    class myThread : public QThread
    {
    public:
    suchThread( QWIdget* window = 0 );
    mainWindow* fenster;
    bool stop;
    void run();
    public slots:
    void stopRun();
    };
    

    myThread.cpp

    myThread::myThread( mainWindow* window ):
    QMainWindow( parent ), fenster( parent ), stop( false )
    {
    }
    
    void myThread::run()
    {
    stop = false;
    for( bedingung )
    if( stop == true )
    break;
    // hier sucht er
    }
    
    void myThread::stopRun()
    {
    stop = true;
    }
    

    mainWidget.cpp

    mainWidget::mainWidget( QWidget* parent ):
    QMainWindow( parent )
    {
    // hier wird alles erzeugt
    suchThread = new myThread( this );
    connect( start, SIGNAL( clicked() ), suchThread, SLOT( start() ) );
    connect( stop, SIGNAL( clicked() ), suchThread, SLOT( stopRun() ) );
    }
    

    Vll könnt ihr mir so helfen, ich kapier einfach net wieso des net funktioniert.
    Der Button bleibt einfach gedrücke bis die suche vorbei ist=(

    Aber echt schonmal vielen Dank, das [gelöst] muss leider noch warten 😃

    Gruß freeG

    EDIT:

    Ich hab glaub den Fehler, ich habe zum Starten des Threads nie start() benutzt, sondern ein eigenen Slot, der dann run() aufruft.
    Denn jetzt stürzt das Programm komischerweise ab sobald ich auf starten klicke, und er sagt mir was von: cannot send posted events for objects in another thread.

    Mein problem, die suche die in run läuft, muss auf QLineEdits und andere Objekte
    des Hauptfensters zugreifen, wie bekomme ich das hin ohne dass das Programm absürzt, im Moment wollte ich das so laufen, dass mein Konstruktor vom Thread ein Zeiger auf meine von MainWindow abgeleitete Klasse entegennimmt, und damit nicht nur sein parent initialisiert, sondern auch einen Zeiger auf die von mir von MainWindow abgeleite Klasse, und über diesen zeiger wollte ich dann in run auf die Elemente zugreifen, aber so stürzt mein programm ja wie gessagt leider ab.

    Hoffe ihr könnt mir vll dabei noch helfen, auf jeden Fall hat mir das Beispiel von oben schon mal sehr gut geholfen, denn dadurch bin ich auf das start() gestoßen=)

    Danke, Gruß freeG



  • Das sind eher Designprobleme (jmho).
    Warum sollte der Search Thread IRGENDWAS von deinem mainThread wissen geschweige denn Zugriff auf GUI Elemente haben?

    Besser so:

    ** myThread.h

    public:
            void setSearchParams(const QString &, const QString &, const bool &, const float &); // usw
    
         private: // vars
             QString param1, param2;   
             bool essig;               
             float sense;
    

    ** myThread.cpp

    void myThread::setSearchParams(const QString &lineEdit1, const QString &lineEdit2, const bool &SuchauchDa, const float &boarder)
    {
        // Avoid Thread Problems 101:
        // Du gehst sicher das run() nur mit lokalen Kopien deiner Parameter arbeitet, d.h. du kopierst alle Parameter am Anfang der run()
        // in lokale Variabeln und nutzt auschließlich diese - ändern sich die globalen Vars ist das für den aktuellen Suchvorgang egal ...
        // oder du schützt du die suchparameter vor Veränderungen während der Suchthread noch läuft:
    
        if ( isRunning() )
        { 
            stop = true;
            while (isRunning()) 
            {  /* do nothing  */ }   
        }
    
        param1 = lineEdit1;
        param2 = lineEdit2;
        essig = SuchauchDa;
        sense = boarder;
    
    }
    

    ** mainWidget.h

    public slots:
           void startTheSearchThread();
    

    ** mainWidget.cpp

    mainWidget::mainWidget( QWidget* parent ):
    QMainWindow( parent )
    {
    // hier wird alles erzeugt
    suchThread = new myThread( this );
    connect( start, SIGNAL( clicked() ), this, SLOT( startTheSearchThread() ) ); // <--- anders
    connect( stop, SIGNAL( clicked() ), suchThread, SLOT( stopRun() ) );
    }
    
    void mainWidget::startTheSearchThread()
    {
        suchThread.setSearchParams(lineEdit1.text(), lineEdit2.text(), TRUE, 24.122010);
        suchThread.start();
    }
    


  • @padreigh
    Dir ist klar, dass in deinem mainWidget.cpp:13 "setSearchParams" im Context des main-thread ausgeführt wird. Deshalb wird damit auch deine GUI blockieren, solange der Thread läuft!
    Desweiteren sollte AFAIK lesender Zugriff auf GUI-Elemente (z.B. lineEdit->text()) schon möglich sein, nur selber in die GUI ändernd eingreifen (lineEdit->setText("lalala")) geht nicht, denn (wie oben) erfolgt der Zugriff im SearchThread und damit im Kontext des non-main-threads. Alles was irgendwie ein GUI-Element zum Zeichnen veranlasst darf nur aus dem main-Thread heraus erfolgen. 😉

    Lösung: SIGNAL/SLOT, denn über ein SIGNAL aus dem non-gui-thread kann sehrwohl ein SLOT im gui-thread aktiviert werden. Qt kümmert sich dann darum, dass der SLOT im GUI-Kontext ausgeführt wird.

    BTW: fr33g, in deiner Thread-Klassen-Definition fehlt das Q_OBJECT. Du hast aber einen SLOT in dem Thread, das sollte nicht funktionieren 😉



  • Oh ja, aber hab das nur vergessen hier in den Post zu packen, in meinem Projekt hab ichs drinne;-).

    Heißt dass ich darf zum beispiel lineEdit->getText() benutzen, aber zum Beispiel nicht über die run() des anderen Threads ein Objekt meinem listWidget auf dem MainWindow hinzufügen, sprich die Objekte verändern?

    Wenn das so wäre, dann müsste ich meinen Code nochmal durchforsten nach solch einem Aufkommen, weil dachte es liegt zum beispiel an getText() oder so.?

    Danke, Gruß freeG



  • fr33g schrieb:

    Heißt dass ich darf zum beispiel lineEdit->getText() benutzen, aber zum Beispiel nicht über die run() des anderen Threads ein Objekt meinem listWidget auf dem MainWindow hinzufügen, sprich die Objekte verändern?

    So hab ich das in Erinnerung. Und ich hab dir sogar nen kleinen Test geschrieben, und bin total aus den Wolken gefallen!

    test.h

    #include <QThread>
    #include <QLineEdit>
    #include <QDebug>
    #include <QMetaObject>
    
    class Thread : public QThread
    {
        Q_OBJECT
        QLineEdit* edit;
        void run() {
            while(true) {
                qDebug() << "Text:" << edit->text();
                qDebug() << edit->thread() << thread();
                QThread::sleep(1);
                edit->setText("BÖÖÖÖHSE!");
                QMetaObject::invokeMethod(edit, "setText", Qt::QueuedConnection, Q_ARG(QString, "Wunderbar!"));
            }
        }
    public:
        Thread(QLineEdit* e)
         : edit(e)
        {
        }
    };
    

    main.cpp

    #include <QLineEdit>
    #include <QApplication>
    #include "test.h"
    
    int main(int argc, char** argv) {
        QApplication app(argc, argv);
        QLineEdit e;
        Thread t(&e);
        e.show();
        t.start();
        return app.exec();
    }
    

    Der direkte Aufruf sollte scheitern, mit QMetaObject::invokeMethod + QueuedConnection sollte der Zugriff save sein.
    Die böse Überraschung hier (Linux, Qt-4.6.3): setText() hat auch funktioniert 😮
    Ich konnte sogar vom anderen Thread aus die geometry des lineEdit ändern (resize()), ebenso das lineEdit in den anderen Thread verschieben!
    Ich weiß aus früheren Tests, dass hier immer eine Meldung seitens Qt zur Laufzeit auftauchte, dass dies nicht möglich ist, und basta. Ich schau mal, das hört sich verdammt nach Bug an...



  • Ja, ich hab bei mir mal gedebugt und da is er immer an der Stelle rausgeflogen, wo ich zum Beispiel nen Button sichtbar machen wollte, sprich ein Objekt vom Hauptthread verändern wollte.

    Ich probier das ganze jetzt mal mit SIGNAL und SLOTS hinzubekommen, müsste ja funktionieren wenn ich dann an der Stelle nen selbst erstelltes Signal von meinem Thread auslöse, und im Hauptthread dadrauf reagiere.
    Denk ich mal oder nicht?

    Ich probiers jetzt auf jeden Fall mal.

    Gruß freeG und vielen Dank nochmals für Eure Mühe schon bis hier hin=)



  • fr33g schrieb:

    Ich probier das ganze jetzt mal mit SIGNAL und SLOTS hinzubekommen, müsste ja funktionieren wenn ich dann an der Stelle nen selbst erstelltes Signal von meinem Thread auslöse, und im Hauptthread dadrauf reagiere.
    Denk ich mal oder nicht?

    Probiers mal erst mit QMetaObject::invokeMethod (wie oben gezeigt). Geht aber nicht mit "normalen" Methoden.



  • Oh sorry, habs zu spät gelesen.
    Hab es schon probiert, und siehe da, er stürzt net ab, es läuft, das mit dem Thread klappt auch, da jetzt der start-Button nicht dauerhaft makiert ist, und ich auch den anhalte-Button betätigen kann.

    Das Problem ist blos, den Thread stoppen tut er trotzdem nicht=(

    Hat jemand ne Idee wo dran das noch liegen könnte?

    Gruß freeG

    EDIT:

    Es funtkionieeert endlich=)
    Ich habe ausversehen die Funktion die die boolean Variable ändert so implementiert, dass sie stop auf false setzt anstatt true 😃

    Jetzt funktioniert alles einwandfrei.

    Vielen Lieben Dank für Eure Hilfe.

    Gruß freeG



  • l'abra d'or schrieb:

    @padreigh
    Dir ist klar, dass in deinem mainWidget.cpp:13 "setSearchParams" im Context des main-thread ausgeführt wird. Deshalb wird damit auch deine GUI blockieren, solange der Thread läuft!

    Mhh .. das das im MainThread läuft ist mir klar, das es [länger] blockiert nicht. Ich habe bewußt auf jegliche SLOTs im QThread verzichtet da allein das QThread dem Fragesteller schon Schwierigkeiten machte ;). Aber deine Lösung wäre performanter in Hinsicht auf GUI-Interaktion und das GUI-Erleben des Anwenders. Ich vermute mal du meinst den Teil hier der blockiert:

    if ( isRunning() )
        {
            stop = true;
            while (isRunning())
            {  /* do nothing  */ }  
        }
    

    Du hast recht, es ist unsauber und blockiert ... bei suffizient kleinen Sucheinzelschritten ist das kaum merkbar da die run() nach diesem Suchschritt beendet wird (stop == true). Im heutigen Zeitalter sind die Computer fix, daher könnte es unbemerkt gehen - muss aber nicht. Ich würde der Methode mit lokalen Kopien in der run() dann wohl den Vorzug geben und die eventuell noch mutex'n damit nicht jemand ganz fix auf "Start" drückt, einen Parameter ändert und wieder auf "Start" drückt und somit privates ändert wärend run() sie gerade in lokale Kopien steckt 😉


Anmelden zum Antworten