QT - sygnał z wątku, slot w gui

0

Witam,

Dopiero zaczynam z QT i mam takie pytanko. Jest sobie aplikacja, z gui. Jest też wątek (QThread) który coś robi w tle. Wygląda to mniej więcej tak:

backthread.h

#ifndef BACKTHREAD_H
#define BACKTHREAD_H

#include <QThread>
#include "Counter.h"


class BackThread : public QThread
{
    public:
        virtual void run();

    private:
        Counter counter;


};

#endif // BACKTHREAD_H

backthread.cpp

#include "backthread.h"

void BackThread::run()
{
    int i=0;
    while(1)
    {
        counter.setValue(i);
        sleep( 1 );
        qDebug( "Ping!" );
        i+=10;
        if(i==100) i=0;
    }
}

Counter.h:

#ifndef COUNTER_H
#define COUNTER_H


#include <QObject>

    class Counter : public QObject
    {
        Q_OBJECT

    public:
        Counter() { m_value = 0; }

        int value() const { return m_value; }

    public slots:
        void setValue(int value);

    signals:
        void valueChanged(int newValue);

    private:
        int m_value;
    };

#endif

Counter.cpp:

#include "Counter.h" 

void Counter::setValue(int value)
    {
        if (value != m_value) {
            m_value = value;
            emit valueChanged(value);
        }
    }

I główne okno:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    BackThread th;
    th.start();
}

MainWindow::~MainWindow()
{
    delete ui;
}

W głównym oknie jest sobie progress bar, który powinien uaktualniać się wartościami Counter::m_value, zwiększanymi w wątku. W jaki sposób mam to osiągnąć? W klasie Counter jest emitowany sygnał valueChanged - w jaki sposób teraz go przechwycić za pomocą slota w oknie głównym? A może ten Counter jest wcale niepotrzebny i sygnały mam wysyłać z klasy BackThread?

0

Może zrób slota w klasie Counter i przekaż tej klasie wskaźnik do progress bara- ja to tak widzę.

0

A nie spowoduje to żadnego wyjątku typu InvalidCrossThreadOperation? Bo to co napisales sprowadza się do odwołania bezpośrednia do progress bara z wątku.

Chcę mieć postęp przekazany jak liczbę z wątku, w bezpieczny sposób i dopiero w wątku gui robić z nią to co trzeba - np, przypisac do progress bara. Tak powinno się postępować niezależnie od frameworka - pytanie tylko jak to zrobić w QT. Aha i w ogólnym przypadku nie musi to być tylko liczba - równie dobrze może być string. Ale na razie aby pojąć zasadę, cwicze na najprostszych przykładach.

0

No to do klasy MainWindow dodaj slota i połącz go z sygnałem z klasy jakiej chcesz, np.:

//gdzieś w kodzie, np. w konstruktorze MainWindow
         connect(th.counter, SIGNAL(valueChanged), this, SLOT(jakisSlotDlaSygnalu());
0

Nie zauważyłem, że counter w klasie BackThread jest prywatny.

0

Oj, sorry teraz też zauważyłem błąd, ma być tak:

connect(th.counter, SIGNAL(valueChanged()), this, SLOT(jakisSlotDlaSygnalu());

Chyba tak, bo w definicji valuChanged przyjmuje parametr.

Można to wszystko do jednego posta dać.

0

Ok zmieniłem counter na public, ale niestety, twój sposób nie działa. W ten sposób slot nigdy nie zostaje wywoływany, chociaż kod się kompiluje i niby są emitowane sygnały z countera.

0

Zrób tak:

class BackThread : public QThread
{
    Q_OBJECT
    public:
        virtual void run();

    signals:
        counterChanged(int);
};

void BackThread::run()
{
    int i=0;
    Counter counter;
    QObject::connect(&counter, SIGNAL(valueChanged(int)), this, SIGNAL(counterChanged(int)));
    while(1)
    {
        counter.setValue(i);
        sleep( 1 );
        qDebug( "Ping!" );
        i+=10;
        if(i==100) i=0;
    }
}

Oczywiście połącz sygnał counterChanged(int) ze swoim progressbar'em.

Poczytaj manual o QObject::connect, tam jest wyjaśnione czemu to zadziała.


PS. Rozumiem, że to jest tylko ćwiczenie z wątków Qt, a naprawdę chcesz nie zmieniać tylko porgressbar'a. Jeśli jednak chcesz tylko zmieniać progressbar to użyj QTimer zamiast tego wątku.
0

Zrobiłem to w ogóle bez klasy Counter, bo w zasadzie nie jest ona do niczgo potrzebna. Po prostu sygnał z wątku, slot w gui. I jakos działa.

PS. Rozumiem, że to jest tylko ćwiczenie z wątków Qt, a naprawdę chcesz nie zmieniać tylko porgressbar'a. Jeśli jednak chcesz tylko zmieniać progressbar to użyj QTimer zamiast tego wątku.

No raczej, przeciez nie było moim celem pisanie aplikacji, której jedynym zadaniem jest "machanie" progress bar-em...

0

Mam problem z progress barem. Mam klasę która sluzy do obliczen i chcialbym aby podczas obliczen pasek sie przesowal. Klasa jest watkiem. Mam blad gdy pasek przesuwam w watku wiem ze trzeba to jakos polaczyc poleceniem connect ale nie wiem jak. Tutaj jest kod aplikacji zrobionej w qt-creator http://rapidshare.com/files/319505952/symulacja2.zip.html . Pomozcie jak to zrobic. Chce aby okno nie wieszalo sie podczas obliczen i zeby pasek postepu dzialal.

0

Przede wszystkim biblioteka Qt nie jest przystosowana do pracy na wątkach, tzn, nie można uzywać czegokolwiek co dziedziczy po QWidget w wątku, więc przekazywanie wskaznika do QProgressBar nie ma sensu, ale widze że sam do tego doszedłeś.

To co zmieniłem:

void MainWindow::on_pushButton_clicked()
{
    QString *tab=new QString[5];
    s_a=ui->a_edit->text();
    s_mu=ui->mu_edit->text();
    s_f=ui->f_edit->text();
    s_TM=ui->TM_edit->text();
    s_mem=ui->mem_edit->text();
    ui->progressBar->setMaximum( (int)s_TM.toDouble() );
    symulacja nowa(s_a.toDouble(),s_mu.toDouble(),s_f.toDouble(),s_TM.toDouble(),s_mem.toDouble(),tab,ui->progressBar);
    connect( &nowa, SIGNAL( dataChanged( int ) ), ui->progressBar, SLOT( setValue(int)) );
    nowa.start();
    nowa.wait();
    ui->textEdit->setText(tab[0]+"\n"+tab[1]+"\n"+tab[2]+"\n"+tab[3]+"\n"+tab[4]);
}

i

class symulacja : public QThread
{
    Q_OBJECT
	public:
                symulacja(double,double,double,double,double,QString *,QProgressBar *);
                virtual void run();
        signals:
                void dataChanged( int );
	private:
                double a, mu, f, TM, mem;
                QString *tab;
                QProgressBar *pr;
		double generacja(double x);
		double min(double a,double b, double c);
};

i

void symulacja::run()
{
        //pr->setMinimum(0);
        //pr->setMaximum(TM);
        emit dataChanged( 0 );
...
while(m!=DMT3)
	{
            emit dataChanged( m );
            //pr->setValue(m);
...
free(V);
	double ro=a/mu;
	double alpha11=1/(mu*f);
	double fi1=1/f;
	double beta2=2/(mu*mu);
        emit dataChanged( (int)TM );
        //pr->setValue(pr->maximum());
..
0

Teraz mam taki błąd
"/home/skoczo/symulacja2/mainwindow.cpp:39: error: ‘QObject’ is an inaccessible base of ‘symulacja’"
Co to oznacza? Mam źle ustawiony kompilator?
Edit: Nie zauważyłem public przy QThread. Dzięki za pomoc.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.