Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: AntonUfo от Декабрь 21, 2009, 15:31



Название: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 21, 2009, 15:31
Всем привет. Заранее извеняюсь что выложил много кода, хочется что бы при выполнении длительных расчетов не замирал интерфейс пользователя. В книге по QT "Бланшет,Саммерфилд - QT4 Программирование GUI на С++.2ed.2008.djvu" нашел примерчик, вроде все вычисления проводятся в отдельном потоке, но мне нужно записывать в статус бар значений счетчика после каждой итерации (функция Run::apply(const QString &str)), как это можно сделать в моем случае ? (код выложен полностью в том виде в котором у меня есть сейчас, с исправлениями спасибо niXman)

Код:
//main.cpp

#include <QApplication>

#include "mythread.h"
#include <QTextCodec>

int main(int argc, char *argv[])
{
QTextCodec *codec = QTextCodec::codecForName("CP1251");
    QTextCodec::setCodecForTr(codec);
    QApplication app(argc, argv);
    Program ProgramWin;
    ProgramWin.resize(400, 300);
    ProgramWin.show();
    return app.exec();
}




Код:
//mythread.h

#ifndef Program_H
#define Program_H

#include <QMainWindow>

#include "transactionthread.h"

class QAction;
class QLabel;
class QMenu;

class Program : public QMainWindow
{
    Q_OBJECT

public:
    Program();

protected:
   void closeEvent(QCloseEvent *event);

private slots:
    void run();
void allTransactionsDone();

private:
    void createActions();
    void createMenus();
    void addTransaction(Transaction *transact);

    TransactionThread thread;
    QLabel *imageLabel;

    QMenu *fileMenu;
QAction *runAction;
};

#endif




Код:
//mythread.cpp

#include <QtGui>

#include "mythread.h"
#include "ui_resizedialog.h"

Program::Program()
{
    imageLabel = new QLabel;
    imageLabel->setBackgroundRole(QPalette::Dark);
    imageLabel->setAutoFillBackground(true);
    imageLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    setCentralWidget(imageLabel);

    createActions();
    createMenus();

    statusBar()->showMessage(tr("Ready"), 2000);

    connect(&thread, SIGNAL(transactionStarted(const QString &)),
            statusBar(), SLOT(showMessage(const QString &)));
connect(&thread, SIGNAL(transactionRun(const QString &)),
            statusBar(), SLOT(showMessage(const QString &)));

    connect(&thread, SIGNAL(allTransactionsDone()),
            this, SLOT(allTransactionsDone()));

}
void Program::closeEvent(QCloseEvent *event)
{
 event->accept();
}

void Program::run()
{
addTransaction(new Run());
}


void Program::addTransaction(Transaction *transact)
{
    thread.addTransaction(transact);
    runAction->setEnabled(false);
}

void Program::allTransactionsDone()
{
    runAction->setEnabled(true);
    setWindowModified(true);
    statusBar()->showMessage(tr("Ready"), 2000);
}

void Program::createActions()
{
    runAction = new QAction(tr("&Run.."), this);
    runAction->setStatusTip(tr("Запуск"));
    connect(runAction, SIGNAL(triggered()), this, SLOT(run()));
}

void Program::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("&Run"));
    fileMenu->addAction(runAction);
}





Код:
//transactionthread.h

#ifndef TRANSACTIONTHREAD_H
#define TRANSACTIONTHREAD_H

#include <QMutex>
#include <QQueue>
#include <QThread>
#include <QWaitCondition>
#include <QString>

class Transaction
{
public:
    virtual ~Transaction() { }
    virtual QString apply(const QString &str) = 0;
    virtual QString message() = 0;
};


class Run : public Transaction
{
public:
    Run();
    QString apply(const QString &str)
    QString message();

private:
int i;

};

class TransactionThread : public QThread
{
    Q_OBJECT

public:
    TransactionThread();
    ~TransactionThread();

    void addTransaction(Transaction *transact);

signals:
    void transactionStarted(const QString &message);
void transactionRun(const QString &message);
    void allTransactionsDone();

protected:
    void run();

private:
    QQueue<Transaction *> transactions;
    QWaitCondition transactionAdded;
    QMutex mutex;

};

#endif


Код:
//transactionthread.h

#include <QtGui>

#include "transactionthread.h"

Transaction * const EndTransaction = 0;

Run::Run()
{



}

QString Run::apply(const QString &str){
QString str2;
//непосредственно расчеты
           int i=1;
int a = 10;
while (a !=i){
a=a/i;
i++;
str2 = QString::number(i,10);
}
return (str2);
}

QString Run::message()
{
   return QObject::tr("Выполняю расчет...");

}

TransactionThread::TransactionThread()
{
    start();
}

TransactionThread::~TransactionThread()
{
    {
        QMutexLocker locker(&mutex);
        while (!transactions.isEmpty())
            delete transactions.dequeue();
        transactions.enqueue(EndTransaction);
        transactionAdded.wakeOne();
    }

    wait();
}

void TransactionThread::addTransaction(Transaction *transact)
{
    QMutexLocker locker(&mutex);
    transactions.enqueue(transact);
    transactionAdded.wakeOne();
}

void TransactionThread::run()//в этой функции будем выполнять долгие расчеты, при этом интерфейс приложения зависать не должен
{
    Transaction *transact = 0;
    forever {
        {
            QMutexLocker locker(&mutex);
            if (transactions.isEmpty())
                transactionAdded.wait(&mutex);
            transact = transactions.dequeue();
            if (transact == EndTransaction)
                break;
        }

        emit transactionStarted(transact->message()); //выводим надпись для статусбара
        QString str = transact->apply(str); //выполняем необходимые расчеты в зависимости от выбранного типа расчетов с использованием интерфейса

        delete transact;
        {
            QMutexLocker locker(&mutex);
            if (transactions.isEmpty())
                emit allTransactionsDone();
        }
    }
}



Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: niXman от Декабрь 21, 2009, 15:49
методы напутал ;)
тот метод, в котором выполняются вычисления, не является субклассом QThread


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 21, 2009, 15:52
методы напутал ;)
тот метод, в котором выполняются вычисления, не является субклассом QThread

во блин, а где будет правильно выполнять ? я не очень шарю пока в программирование воообще, учусь :(


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: niXman от Декабрь 21, 2009, 15:56
ты же по книжке учишься? а там написано - в виртуальном защищенном методе void run(), в классе потомке, производном от QThread


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 21, 2009, 16:03
ты же по книжке учишься? а там написано - в виртуальном защищенном методе void run(), в классе потомке, производном от QThread

спасибо понял..., до меня как то с трудом все доходит.... :'(


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: SASA от Декабрь 21, 2009, 16:13
QtConcurrent. Спецом для таких вещей.


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 21, 2009, 17:54
ты же по книжке учишься? а там написано - в виртуальном защищенном методе void run(), в классе потомке, производном от QThread

черт все равно виснет, я перенес вычисления в защищенный метод void run(),

добавил в mythread.cpp
Код:
    connect(&thread, SIGNAL(transactionStarted(const QString &)),
            statusBar(), SLOT(showMessage(const QString &)));

Код:
Run::Run()
{
//теперь пустой
}


void TransactionThread::run() //теперь вот такой
{
    Transaction *transact = 0;
    forever {
        {
            QMutexLocker locker(&mutex);

            if (transactions.isEmpty())
                transactionAdded.wait(&mutex);
            transact = transactions.dequeue();
            if (transact == EndTransaction)
                break;

 //           oldImage = currentImage;
        }

        emit transactionStarted(transact->message());
///////////////////// положил вот сюда////////////////

//в этой функции будем выполнять долгие расчеты, при этом интерфейс приложения зависать не должен, а он зависает....!!!
    int i=1;
int a = 10;
while (a !=i){
a=a/i;
i++;
}

//вывожу на интерфейс значения счетчика//
QString str;
str = QString::number(i,10);
emit transactionRun(str)

        delete transact;

        {
            QMutexLocker locker(&mutex);
//           currentImage = newImage;
            if (transactions.isEmpty())
                emit allTransactionsDone();
        }
    }
}



значения счетчика на интерфейс выводятся, но сдвинуть его никуда немогу.....


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Vass от Декабрь 21, 2009, 20:40
Вот вы пишите:

Код:
//вывожу на интерфейс значения счетчика//
.....
emit transactionRun(str)

а я вообще в коде у Вас такого сигнала не нахожу, к чему он подключен?


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 22, 2009, 10:51
Вот вы пишите:

Код:
//вывожу на интерфейс значения счетчика//
.....
emit transactionRun(str)

а я вообще в коде у Вас такого сигнала не нахожу, к чему он подключен?

подправил, но проблема осталась, мне необходимо выводить промежуточные значения при долгих расчетах на интерфейс без его залипания..


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Vass от Декабрь 22, 2009, 11:41
Читаем Assistant, а там написано:

Код:
void QThread::run ()   [virtual protected]

The starting point for the thread. After calling start(), the newly created thread calls this function. The default implementation simply calls exec().

You can reimplemented this function to do other useful work. Returning from this method will end the execution of the thread.

---------

int QThread::exec ()   [protected]

Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit().

It is necessary to call this function to start event handling.

See also quit() and exit().

А это значит, что чтобы все работало, Вам просто необходимо где-то вызвать exec();
Но вот где я чего-то не пойму никак.

Дело в том что из exec(); он не выйдет пока не будет вызван слот quit();

------

О! кажется сообразил. в общем Вам надо ваш цикл вынести в отдельный слот, который будет вызываться по сигналу started(); соответственно когда будете выходить из цикла вызывайте слот quit();


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Igors от Декабрь 22, 2009, 17:44
Код:
QString Run::apply(const QString &str){
QString str2;
//непосредственно расчеты
           int i=1;
int a = 10;
while (a !=i){
a=a/i;
i++;
str2 = QString::number(i,10);
}
return (str2);
}
Смотрим:
a = 10, i = 1
a = 10, i = 2
a = 5, i = 3
a = 1, i = 4
a = 0, i = 5
a = 0, i = 6
Бесконечный цикл поскольку (a !=i)  всегда. Поэтому что имеем то и имеем - да, интерфейс не  виснет, я могу двигать и сайзить главное окно. Но вычисления (и нитка) никогда не кончатся

Примечание: не беда если много исходников но пожалуйста в следующий раз прикрепляйте их zip файлом компилябельного примера (выдирать через copy/paste никому не хочется  :))
 


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 23, 2009, 11:22
Код:
QString Run::apply(const QString &str){
QString str2;
//непосредственно расчеты
           int i=1;
int a = 10;
while (a !=i){
a=a/i;
i++;
str2 = QString::number(i,10);
}
return (str2);
}
Бесконечный цикл поскольку (a !=i)  всегда. Поэтому что имеем то и имеем - да, интерфейс не  виснет, я могу двигать и сайзить главное окно. Но вычисления (и нитка) никогда не кончатся

я понимаю что цикл бесконечный это не важно, у меня не получается послать сигнал на форму из (QString Run::apply(const QString &str)), как это можно сделать.....
сейчас программа посылает сигнал только из потока (void TransactionThread::run() ), но мне необходимо посылать на форму значения ну к примеру I


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Igors от Декабрь 23, 2009, 15:42
я понимаю что цикл бесконечный это не важно, у меня не получается послать сигнал на форму из (QString Run::apply(const QString &str)), как это можно сделать.....
сейчас программа посылает сигнал только из потока (void TransactionThread::run() ), но мне необходимо посылать на форму значения ну к примеру I
В нитке у Вас 2 цикла и оба бесконечные. Поэтому строка во внешнем цикле
Код:
emit allTransactionDone();
не получит управления никогда т.к. она стоит после вложенного цикла а он никогда не кончится


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 23, 2009, 16:04
я понимаю что цикл бесконечный это не важно, у меня не получается послать сигнал на форму из (QString Run::apply(const QString &str)), как это можно сделать.....
сейчас программа посылает сигнал только из потока (void TransactionThread::run() ), но мне необходимо посылать на форму значения ну к примеру I
В нитке у Вас 2 цикла и оба бесконечные. Поэтому строка во внешнем цикле
Код:
emit allTransactionDone();
не получит управления никогда т.к. она стоит после вложенного цикла а он никогда не кончится

прикрипил все исходники,  с ними будет более понятно. После запуска нитки выполняется QString Run::apply(const QString &str)), я знаю что он бесконечен в цикле, мне это не важно, я так сделал специально, мне нужно понять каким образом я могу передать из QString Run::apply(const QString &str)) сигнал в статус бар формы во время каждой итерации бесконечного цикла и каким образом этот сигнал принять в форме
ЗЫ: из void TransactionThread::run() сигнал в статус бар формы приходит нормально, я не знаю как его послать именно из QString Run::apply(const QString &str))


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Igors от Декабрь 23, 2009, 17:35
прикрипил все исходники,  с ними будет более понятно. После запуска нитки выполняется QString Run::apply(const QString &str)), я знаю что он бесконечен в цикле, мне это не важно, я так сделал специально, мне нужно понять каким образом я могу передать из QString Run::apply(const QString &str)) сигнал в статус бар формы во время каждой итерации бесконечного цикла и каким образом этот сигнал принять в форме
ЗЫ: из void TransactionThread::run() сигнал в статус бар формы приходит нормально, я не знаю как его послать именно из QString Run::apply(const QString &str))
Это легко сделать "технически" (см. attachment). Но это идет вразрез с логикой примера, с именами переменных и.т.п. Подразумевается что "transaction" (класс Run) - это неделимая единица вычислений, которая не должна ничего знать об UI, не должна посылать сигналов и.т.п. Всем этим занимается класс TransactionThread.

Поэтому посмотрите как но не используйте, уберите бесконечность в Run но оставьте ее в TransactionThread


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Marat(Qt) от Декабрь 24, 2009, 03:55
Если бы использовался QProcess вместо QThread, то можно было бы использовать сигнал readyReadStandardOutput. Альтернатива не самая веселая, но, судя по всему, вполне подходящая.


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 24, 2009, 11:35
это легко сделать "технически" (см. attachment). Но это идет вразрез с логикой примера, с именами переменных и.т.п. Подразумевается что "transaction" (класс Run) - это неделимая единица вычислений, которая не должна ничего знать об UI, не должна посылать сигналов и.т.п. Всем этим занимается класс TransactionThread.

Поэтому посмотрите как но не используйте, уберите бесконечность в Run но оставьте ее в TransactionThread

спасибо большое все работает теперь так как мне было нужно !!!!

а как тогда делать правильно, какой должен быть подход ?

у меня есть графический редактор, после построения в нем схемы мне нужно провести некотырые довольно медленные расчеты вызвав некую функцию, нужно что бы интерфейс не зависал и была показана какая то видимость работы, прогрэссбар не подходит по причине что я незнаю заранее чему равно 100% работы...

как решение была выбрана работа с потоками и вывод информации в статус бар, можно бы ло бы просто создавать поток и делать всю работу в нем но дело в том что расчетов (функций) как минимум 3-4 варианта и для каждого из них описывать свой поток как мне кажется не совсем читабельно, используя выше приведенный подход можно вызывать только требуемые режимы расчета (функции) при этом поток всегда один, а вызывать несколько расчетов сразу мне ненужно


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Igors от Декабрь 24, 2009, 14:09
а как тогда делать правильно, какой должен быть подход ?
Правильный подход в исходном примере. Смысл простой: не смешивать расчеты с UI, выделить все расчеты в отдельные классы (а лучше в отдельный файл(ы)). Сделать промежуточное звено (в примере это TransactionThread) которое обеспечивает взаимодействие "расчеты - UI". На первый взгляд это может показаться заумным и.т.п. но когда в один прекрасный день Вы услышите "нужна версия программы с командной строкой" - придется пожалеть о содеянном   :)

как решение была выбрана работа с потоками и вывод информации в статус бар, можно бы ло бы просто создавать поток и делать всю работу в нем но дело в том что расчетов (функций) как минимум 3-4 варианта и для каждого из них описывать свой поток как мне кажется не совсем читабельно, используя выше приведенный подход можно вызывать только требуемые режимы расчета (функции) при этом поток всегда один, а вызывать несколько расчетов сразу мне ненужно
У Вас в примере показано как :) Класс Run порождается от Transaction. Пусть Run - это "расчет 1". Делаете еще классы порожденные от Transaction, напр. Run2, Run3 (расчет 2, расчет 3) для каждого делаете виртуальный метод где выполняются сами расчеты (у Вас это apply) - и одна и та же TransactionThread работает со любыми расчетами, хотите по очереди, хотите вперемежку - зависит от того какие Transaction Вы кладете в очередь.


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: AntonUfo от Декабрь 24, 2009, 16:29
Правильный подход в исходном примере. Смысл простой: не смешивать расчеты с UI, выделить все расчеты в отдельные классы (а лучше в отдельный файл(ы)). Сделать промежуточное звено (в примере это TransactionThread) которое обеспечивает взаимодействие "расчеты - UI". На первый взгляд это может показаться заумным и.т.п. но когда в один прекрасный день Вы услышите "нужна версия программы с командной строкой" - придется пожалеть о содеянном   :)
[/quote]

еще один глупый вопрос :(, долгий расчет записан в отдельный файл formula.cpp, соответственно при необходимости расчета вызывается функция int formula(int param), как предоставить возможность функции formula посылать сигнал (emit changedStatus(QString) ), я же его объявил в классе Run...   ?


Название: Re: Зависает интерфейс программы нужна помощь....
Отправлено: Igors от Декабрь 24, 2009, 16:50
еще один глупый вопрос :(, долгий расчет записан в отдельный файл formula.cpp, соответственно при необходимости расчета вызывается функция int formula(int param), как предоставить возможность функции formula посылать сигнал (emit changedStatus(QString) ), я же его объявил в классе Run...   ?
Run у Вас кто? Расчетная часть, вот пусть она и занимается расчетами, а не посылкой сигналов. Поэтому место класса Run в файлах formula.h и formula.cpp. И формула должна вычисляться в одном из методов Run.

Если расчет короткий то достаточно что TransactionThread сама обновит UI. Если же длинный (2 сек и больше) и разбить его на части неудобно - тогда через прокладочку (см. интерфейс CProgressShow в ishodniki2.zip). В любом случае formula.h и formula.cpp ничего не должны знать о QtGUI, сигналах и.т.п.