Funktion über QPushButton beenden



  • Hey Leute,
    in meinem Programm hab ich ne eigene Klasse von QObject abgeleitet.
    Diese Klasse besitzt eine Slot-Funktion, die durch einen click Signal gestartet wird.
    Die Funktion führt eine Suche durch, jetzt hab ich nen anhalten-Button, wie kann ich es hinbekommen, dass die Funktion durch das click Signal des Buttons gestoppt wird bzw abgebrochen wird?

    Danke schonmal für eure Hilfe.

    Gruß freeG



  • Die Suche in einen Thread auslagern und den Stop-Button mit dem quit-Slot des Threads verbinden



  • Ok, erst mal danke für die schnelle Antwort.

    Hab zwar noch nix über Threads gelesen und noch nichts mit gemacht, aber ich werd mich heut Nachmittag nachm Feierabend mal hinsetzten und es probieren.

    Vielen Dank,

    Gruß freeG



  • Hey,
    also ich bekomm es leider nicht hin.
    Wie in der Doku hab ich ne Klasse von QThread abgeleitet und in ihr die run() Funktion implementiert.
    Könnte mir vll jemand sagen wie die ungefähr aussehen muss?

    Bei mir so:

    void run( widget* fenster )
    {
    fenster->foo();
    exec();
    }
    

    Mein Problem, wenn ich den Button klicke um die Funktion zu starten, kann ich den anhalten Button gar nicht drücken, erst wenn die Funktion beendet ist.
    Habe auch probiert meiner Fenster-Klasse einfach ne boolsche Variable zu geben, hab dann ne Funktion die einfach den Wert auf true macht, und diesen Slot eben mit nem Klick Signal vom anhalte-Button verbunden.
    Meine Funktion prüft dann bei jedem Schleifendurchlauf den Wert der Variable und falls er true ist beendet sich die Funktion.
    Jedoch kann ich auch so den anhalte-Button gar nicht drücken.

    Hoffe ihr könnt mir helfen, schonmal Danke für eure Mühe.

    Gruß freeG



  • du solltest beachten, dass nur im Hauptthread Zugriff auf die GUI besteht. Startst du einen neuene Thread musst du eventuelle GUI-Updates über Signale wieder an den Hauptthread schicken.

    Also lass den Thread wirklich nur die Suche ausführen



  • ...



  • 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...


Anmelden zum Antworten