Russian Qt Forum
Ноябрь 22, 2024, 18:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Не получается разобраться в механизме потоков  (Прочитано 4428 раз)
nata267
Гость
« : Июль 19, 2011, 10:35 »

Я - никогда не использовала раньше потоки . Но у меня такая задача. Я написала свой класс потока, в нем должна выполняться единственная операция по выборке данных для модели QSqlQueryModel, так как данных много то выборка тормозиться, соответственно я решила вынести в отдельный поток. Получилась полная чушь. Что я делаю не так?? Данные выбираются криво, в конце куча пустых строк, и столбцы которые я скрываю показываются.

Код:
.h
class ContentRefresher : public QThread
{
    Q_OBJECT
public:
    ContentRefresher(QTableView *contentView);
    ~ContentRefresher();
    void refreshContent();

signals:
    void contentRefreshed();

private:
    void run();

    volatile bool m_abort;
    QTableView *m_contentView;
    QMutex m_mutex;
};

.cpp
ContentRefresher::ContentRefresher(QTableView *contentView)
{
    m_abort = false;
    m_contentView = contentView;
}

ContentRefresher::~ContentRefresher()
{
    if(!isRunning())
       return;

    m_mutex.lock();
    m_abort = true;
    m_mutex.unlock();
    wait();
}

void ContentRefresher::refreshContent()
{
    start();
}

void ContentRefresher::run()
{

    ContentModel *model = qobject_cast<ContentModel *>(m_contentView->model());
    model->refresh();

    m_contentView->setColumnHidden(0,true);
    m_contentView->setColumnHidden(2,true);
    m_contentView->resizeColumnsToContents();
    m_contentView->resizeRowsToContents();
    m_contentView->horizontalHeader()->setStretchLastSection(true);

    emit contentRefreshed();
}


использую его так:

Код:
CentralWidget::CentralWidget(QWidget *parent) : contentRefresher(0),........
{
   .....
   contentModel = new ContentModel(this);
    m_ui.contentView->setModel(contentModel);    
    m_ui.contentView->setSelectionBehavior(QAbstractItemView::SelectRows);
    m_ui.contentView->setSelectionMode(QAbstractItemView::SingleSelection);
    m_ui.contentView->verticalHeader()->hide();
    m_ui.contentView->setItemDelegate(new ContentDelegate(m_ui.contentView));
    m_ui.contentView->installEventFilter(this);

 .............
  QTimer::singleShot(0, this, SLOT(refreshContentModel()));
}

CentralWidget::~CentralWidget()
{
    .......  
    delete contentRefresher;
}

void CentralWidget::refreshContentModel()
{
    contentRefresher = new ContentRefresher(m_ui.contentView);
    connect(contentRefresher, SIGNAL(contentRefreshed()), this, SLOT(contentRefreshed()));

    m_ui.progressWidget->setVisible(true);
    contentRefresher->refreshContent();
}

void CentralWidget::contentRefreshed()
{
    m_ui.progressWidget->setVisible(false);
}
« Последнее редактирование: Июль 19, 2011, 10:37 от nata267 » Записан
nata267
Гость
« Ответ #1 : Июль 19, 2011, 10:41 »

Кроме того функция refreshContentModel(), будет вызываться неоднократно из программы, каждый раз когда пользователь будет нажимать кнопку Поиск, соответственно будет создаваться новый поток. Наверно это тоже неправильно??
Записан
GreatSnake
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2921



Просмотр профиля
« Ответ #2 : Июль 19, 2011, 11:27 »

Для начала не мешало бы усвоить правило - всё что связано с gui должно работать в основном потоке.
Записан

Qt 5.11/4.8.7 (X11/Win)
nata267
Гость
« Ответ #3 : Июль 19, 2011, 12:34 »

Для начала не мешало бы усвоить правило - всё что связано с gui должно работать в основном потоке.

я переписала, но проблемы остались, почему-то  ProgressBar зависает как рас во время работы потока, и программа через раз вылетает, и опять же куча пустых строчек вконце добавляется:

Код:
ContentRefresher::ContentRefresher(ContentModel *contentModel)
{
    m_abort = false;
    m_contentModel = contentModel;
}

ContentRefresher::~ContentRefresher()
{
    if(!isRunning())
       return;

    m_mutex.lock();
    m_abort = true;
    m_mutex.unlock();
    wait();
}

void ContentRefresher::refreshContent()
{
    start();
}

void ContentRefresher::run()
{
    while(!m_abort) {
        m_contentModel->refresh();
        emit contentRefreshed();
    }
    m_mutex.lock();
    m_abort = false;
    m_mutex.unlock();
}

void ContentRefresher::stop() {
   m_mutex.lock();
   m_abort = true;
   m_mutex.unlock();
}
[codе]

вот его использование:

[code]
void CentralWidget::refreshContentModel()
{
    contentRefresher = new ContentRefresher(contentModel);
    connect(contentRefresher, SIGNAL(contentRefreshed()), this, SLOT(contentRefreshed()), Qt::QueuedConnection);

    m_ui.progressWidget->setVisible(true);
    m_ui.contentView->setVisible(false);
    contentRefresher->refreshContent();
}

void CentralWidget::contentRefreshed()
{
    contentRefresher->stop();

    m_ui.contentView->setModel(contentModel);
    m_ui.contentView->setColumnHidden(0,true);
    m_ui.contentView->setColumnHidden(2,true);
    m_ui.contentView->resizeColumnsToContents();
    m_ui.contentView->resizeRowsToContents();
    m_ui.contentView->horizontalHeader()->setStretchLastSection(true);
    m_ui.contentView->setVisible(true);
    
    m_ui.progressWidget->setVisible(false);
}

[/code]
« Последнее редактирование: Июль 19, 2011, 12:35 от nata267 » Записан
Странник
Гость
« Ответ #4 : Июль 19, 2011, 14:05 »

соединение с БД может использоваться только из того потока, в котором было создано. создавайте соединение в run() потока, потом устанавливайте модели запрос через void QSqlQueryModel::setQuery ( const QString & query, const QSqlDatabase & db = QSqlDatabase() ), где вторым параметром передается созданное соединение.
Записан
nata267
Гость
« Ответ #5 : Июль 21, 2011, 13:43 »

щас поменяла. Модель создается и выборка производится в другом потоке, а потом через сигнал/слот передается в главный поток, но все равно интерфейс пользователя на время выборки зависает, не пойму почему, вот класс потока:

Код:
class ContentRefresher : public QThread
{
    Q_OBJECT
public:
    ContentRefresher(QObject *parent = 0);
    ~ContentRefresher();  

    void processQuery(const QString &qStr, const QString &hStr);

signals:
    void sendContent(ContentModel *contentModel);

public slots:
    void stopProcess();

protected:
    void run();

private:
    QString m_queryString;
    QString m_headerString;
    
    bool m_abort;
    ContentModel *m_contentModel;
    QMutex mutex;
};

ContentRefresher::ContentRefresher(QObject *parent)
    : QThread(parent)
{
    m_abort = false;
}

ContentRefresher::~ContentRefresher()
{
    mutex.lock();
    m_abort = true;
    mutex.unlock();

    wait();
}

void ContentRefresher::processQuery(const QString &qStr, const QString &hStr)
{
    m_queryString = qStr;
    m_headerString = hStr;
    m_abort = false;
    start();
}

void ContentRefresher::run()
{
    ContentModel *contentModel = new ContentModel();
    contentModel->setQueryString(m_queryString, m_headerString);
    contentModel->refresh();
    emit sendContent(contentModel);
    if (m_abort)
        return;
}

void ContentRefresher::stopProcess()
{
    mutex.lock();
    m_abort = true;
    mutex.unlock();
}

вот его использование из главного потока:

Код:
....
contentRefresher = new ContentRefresher();
connect(contentRefresher, SIGNAL(finished()), this, SLOT(resetUi()));
connect(contentRefresher, SIGNAL(sendContent(ContentModel *)), this, SLOT(setContent(ContentModel *)));
....


void CentralWidget::refreshContentModel(const QString &qStr, const QString &hStr)
{
    m_ui.progressWidget->setVisible(true);
    contentRefresher->processQuery(qStr, hStr);
}

void CentralWidget::setContent(ContentModel *contentModel)
{
    m_ui.contentView->setModel(contentModel);
    m_ui.contentView->setColumnHidden(0,true);
    m_ui.contentView->setColumnHidden(2,true);
    m_ui.contentView->resizeColumnsToContents();
    m_ui.contentView->resizeRowsToContents();
    m_ui.contentView->horizontalHeader()->setStretchLastSection(true);
    m_ui.contentView->setVisible(true);
loadFirstRecipe();
}

void CentralWidget::resetUi()
{
    m_ui.progressWidget->setVisible(false);
}
Записан
nata267
Гость
« Ответ #6 : Июль 21, 2011, 14:04 »

может нельзя выбирать данные функцией setQuery, а разбить на шаги и добавлять каждую строчку??
Записан
nata267
Гость
« Ответ #7 : Июль 21, 2011, 14:34 »

или может быть изза того что приложение запускается в Windows Mobile, там потоки работают, никто не в курсе??
Записан
danquimby
Гость
« Ответ #8 : Июль 21, 2011, 15:30 »

нет девушка , вы как то странно подходите к "велосипеду"
не с той стороны.
И вообще в потоке virtual run должен быть
зациклен который будет ждать данных на обработку
или который будет забирать в отдельном потоке эти данные.
Так же прочитайте про QWaitCondition тоже может помочь
А так вы вообще не так делаете . Посмотрите в примерах от qtsdk

И еще потоки это очень пухленькая нагрузка на программу .
если что то хотите инициализировать то

Код:
void run(){
  CreateWidgets();
  forever(){
     // что то делаете
  }
   exec();
}
« Последнее редактирование: Июль 21, 2011, 16:01 от danquimby » Записан
nata267
Гость
« Ответ #9 : Июль 22, 2011, 14:29 »

опять я все переписала, вот:

класс потока:
Код:
class ContentRefresher : public QThread
{
    Q_OBJECT
public:
    ContentRefresher(QObject *parent = 0);
    ~ContentRefresher();  

    void processQuery(const QString &qStr, const QString &hStr);

signals:
    void sendRecord(const ContentRecord &record);

public slots:
    void stopProcess();

protected:
    void run();

private:
    bool m_abort;
    QMutex mutex;

    QString m_queryString;
    QString m_headerString;
};

ContentRefresher::ContentRefresher(QObject *parent)
    : QThread(parent)
{
    m_abort = false;
}

ContentRefresher::~ContentRefresher()
{
    mutex.lock();
    m_abort = true;
    mutex.unlock();
    wait();
}

void ContentRefresher::processQuery(const QString &qStr, const QString &hStr)
{
    m_queryString = qStr;
    m_headerString = hStr;
    m_abort = false;
    start();
}

void ContentRefresher::run()
{
    QSqlQuery query(m_queryString);
    while(query.next()) {
       ContentRecord record(query.record());
       emit sendRecord(record);
       if (m_abort)
            return;
       msleep(10);
    }
}

void ContentRefresher::stopProcess()
{
    mutex.lock();
    m_abort = true;
    mutex.unlock();
}


вот использование из главного потока:
Код:
...
contentRefresher = new ContentRefresher();
    connect(contentRefresher, SIGNAL(finished()), this, SLOT(resetUi()));
    connect(contentRefresher, SIGNAL(sendRecord(ContentRecord)), this, SLOT(addRecord(ContentRecord)));
...

void CentralWidget::refreshContentModel(const QString &qStr, const QString &hStr)
{
   // m_ui.progressWidget->setVisible(true);
    contentModel->clear();
    contentRefresher->processQuery(qStr, hStr);
}

void CentralWidget::addRecord(const ContentRecord &record)
{
    contentModel->addContentRecord(record.record());    
    qApp->processEvents();
}

void CentralWidget::resetUi()
{
    //m_ui.progressWidget->setVisible(false);
   m_ui.contentView->resizeRowsToContents();
   loadFirstRecipe();
}


проблема в том, что m_ui.contentView->resizeRowsToContents(); нужно вызывать после окончания выполнения процесса, если выполнять в функции addRecord, т.е. после вставки каждой строки, то программа зависнет. и таким образом сначала строчки добавляются неправильной высоты, то есть кривые, и толко по завершении выборки они становятся такими какие должны быть. но это уже вопрос наверно не в теме потоков, а в теме модель-отображение

и интерфейс все равно немного подвисает, смысл тогда этого потока в чем?? можно выбирать в основном потоке и в цикле делать   qApp->processEvents(); не могу я понять смысл этих потоков((((((((((((
« Последнее редактирование: Июль 22, 2011, 14:33 от nata267 » Записан
danquimby
Гость
« Ответ #10 : Июль 22, 2011, 14:40 »

девушка вы снова что то ... написали если честно не хотца смотреть (((
1.run это цикл а я как видел , при завершении вы выходите из цыкла , то бишь
треда закрывается (нуна правда проверить)

смотрите как я делал .
в треде вам нужно делать выборку по базе и выложить куда нить результат поиска и всяких там
преобразований.
А в главном потоке сделайте так.
1.дайте данные , и возбудите поток thread.start();
2.а в главном потоке ну не знаю, повесьте qtimer который будет смотреть данные, которая треда положил
по завершению. Пока их нет(после нажатии кнопки Search делайте что бы клиент мог нажать тока Cancel)
Ну так вот как данные появятся их и показать.
В поток не нужно пихать ... какие то странные вещи, дайте ему заниматься своими делами, то есть
трудоемкими и времязатратными.
И при этом у вас главный поток (GUI) будет в работе (ну кроме таймер будет смотреть данные)
а треда будет заниматься своим делом.

ps сумбурно но надеюсь ... хоть что то дошло(не учитель))) )
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.241 секунд. Запросов: 22.