Qt работа с потоками и данными [закрыт]

136
30 сентября 2019, 20:20

Проблема

Не могу передать данные выделенные в другом потоке через указатель в основной поток программы для того чтобы заполнить ComboBox.

Для многопоточности использую QFuture / QtConcurrent::run / QFutureWatcher

Пробовал через QSharedPointer но без результативно ссылка корректная но данных по ней нет.

Github проекта

Database.cpp

bool DataBase::setComboBox(
                    QComboBox *ComboBoxView,
                    QString TableName,
                    qint32 ColumTable,
                    QSharedPointer<QSqlTableModel> &Table)
{
    QFuture<QSharedPointer<QSqlTableModel>> future = QtConcurrent::run(
        [this,TableName]()
        {
            objDatabase = QSqlDatabase::addDatabase("QMYSQL");
            objDatabase.setDatabaseName("librarydb");
            objDatabase.setHostName("127.0.0.1");
            objDatabase.setPort(3306);
            objDatabase.setUserName("hays0503");
            objDatabase.setPassword("hays0503");
            objDatabase.open();
            QSharedPointer<QSqlTableModel> p_Table =
            QSharedPointer<QSqlTableModel>(new QSqlTableModel);
            p_Table ->setTable(TableName);
            p_Table ->select();
            return p_Table;
         });
    QFutureWatcher<QSharedPointer<QSqlTableModel>> *watcher = new QFutureWatcher<QSharedPointer<QSqlTableModel>>(this);
    connect(watcher,&QFutureWatcherBase::finished,[watcher,ComboBoxView,ColumTable,&Table]() mutable
    {// Запускаем этот код в UI потоке, когда объект future завершит свою работу в рабочем потоке
        QSharedPointer<QSqlTableModel> Tables = watcher->result();
        Table = Tables;
        ComboBoxView->setModel(Table.data());
        ComboBoxView->setModelColumn(ColumTable);
        ComboBoxView->showPopup();
        //watcher->deleteLater(); // Удалим ненужный watcher
    });
    watcher->setFuture(future); // Связываем watcher с feature. Это быстрая операция и не тормозит поток UI
    return true;
}

DialogSearch.cpp Метод инициализации элемента ComboBox

void DialogSearch::InitComboBox(
                  DataBase *p_connect, //Ссылка на базу данных
                  QComboBox *comboBox, //Ссылка на иницализируемый м
                  QString table_name, //Название таблицы в БД
                  qint32 row, //Номер колонки в таблице
                  QSharedPointer<QSqlTableModel> &Table) // Попытка взять адрес выделенной памяти в потоке.
{
    ui->progressBar->setValue(20);
    p_connect->setComboBox(comboBox,table_name,row,Table);
    ui->progressBar->setValue(40);
    comboBox->setMaxVisibleItems(15);
    ui->progressBar->setValue(60);
    comboBox->setEditable(true);
    ui->progressBar->setValue(80);
}

DialogSearch.cpp Метод переключения tabWidget'ов.

void DialogSearch::on_tabWidget_currentChanged(int index)
{
    //Определенны в DialogSearch.h
    //QSharedPointer<QSqlTableModel> p_Table_genre; 
    //QSharedPointer<QSqlTableModel> p_Table_author;
    ui->progressBar->setValue(0);
    switch (index) {
    case 1: if(!completeWorkComboBox[0]){// Работали ли мы с ним до этого ?
    // Если нет то работаем иначе берём данные с прошлого раза
            InitComboBox(p_connect,
                         ui->comboBox_genre,
                         "genre",
                         1,p_Table_genre);}
            else{
            ui->comboBox_genre->setModel(p_Table_genre.data());// <--- Ссылка правильная но данных в ней нет из другого потока.
            ui->comboBox_genre->setModelColumn(1);
            }break;
    case 2: if(!completeWorkComboBox[1]){
            InitComboBox(p_connect,
                         ui->comboBox_author,
                         "author",
                         1,p_Table_author);}
            else{
            ui->comboBox_author->setModel(p_Table_author.data());//<--- Ссылка правильная но данных в ней нет из другого потока.
            ui->comboBox_author->setModelColumn(1);
            }
        break;
        if(!completeWorkComboBox[index-1])//Выполнили работу ? 
           completeWorkComboBox[index-1] = true;
        ui->progressBar->setValue(100);
}
Answer 1

Проблема решилась.

Заключалась в связи Поток<--->База данных.

Проще говоря если создать подключение в другом потоке и получить потом ссылку на результирующие данные(QFutureWatcher->result();) , которые связаны через модели по типу (QSqlTableModel/QSqlQueryModel) то у вас не будет доступа к подключению а следственно и к данным.

Решение проблемы: заключается в создание подключения за приделами потока там где в дальнейшем понадобятся они.

Пример.

MainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFuture>
#include <QFutureWatcher>
#include <QSqlQuery>
#include <QtConcurrent>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    objDatabase = QSqlDatabase::addDatabase("QMYSQL");
    objDatabase.setDatabaseName("librarydb");
    objDatabase.setHostName("127.0.0.1");
    objDatabase.setPort(3306);
    objDatabase.setUserName("hays0503");
    objDatabase.setPassword("hays0503");
    objDatabase.open();
}
void MainWindow::LoadTable(QString query)
{
    QFuture<QSqlQueryModel*> future = QtConcurrent::run(
        [this,query]()
        {
            // Исполняем этот код в другом потоке

            //QSqlTableModel *objTableModel;
            objQuery = new QSqlQueryModel(nullptr);
            objQuery->setQuery(query,objDatabase);
            ui->tableView->setModel(objQuery);
            ui->tableView->update();
            ui->tableView->show();
            return objQuery;
        });
    QFutureWatcher<QSqlQueryModel*> *watcher = new QFutureWatcher<QSqlQueryModel*>(this);
    connect(watcher,&QFutureWatcherBase::finished,[this, watcher]()
    {// Запускаем этот код в UI потоке, когда объект future завершит свою работу в рабочем потоке
        pointer = watcher->result();
        ui->tableView->setModel(pointer);
        watcher->deleteLater(); // Удалим ненужный watcher
    });
     watcher->setFuture(future); // Связываем watcher с feature. Это быстрая операция и не тормозит поток UI
}
MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //Долгий и тяжёлый запрос
    QString query =
  "   SELECT \n\
                `books`.id_books, \n\
                `books`.name_book, \n\
                GROUP_CONCAT(DISTINCT `author`.author_record), \n\
                GROUP_CONCAT(DISTINCT `genre`.genre_record), \n\
                `bbk`.bbk_record, \n\
                `udc`.udc_record, \n\
                `books`.isbn, \n\
                `publisher`.publisher_record, \n\
                GROUP_CONCAT(DISTINCT `book_binding_type`.binding_type), \n\
                `books`.description_record \n\
      FROM \n\
                `books` \n\
                    JOIN \n\
                `author_join_table` ON `author_join_table`.id_books = `books`.id_books \n\
                    JOIN \n\
                `author` ON `author`.id_author = `author_join_table`.id_author \n\
                    JOIN \n\
                `genre_join_table` ON `genre_join_table`.id_books = `books`.id_books \n\
                    JOIN \n\
                `genre` ON `genre`.id_genre = `genre_join_table`.id_genre \n\
                    JOIN    \n\
                `book_binding_type` ON `book_binding_type`.id_book_binding_type = `books`.index_book_binding_type \n\
                    JOIN \n\
                `publisher` ON `publisher`.id_publisher = `books`.index_publisher \n\
                    JOIN \n\
                `udc` ON `udc`.id_udc = `books`.index_udc \n\
                    JOIN \n\
                `bbk` ON `bbk`.id_bbk = `books`.index_bbk \n\
      GROUP BY `books`.id_books;";
    LoadTable(query);
}
void MainWindow::on_pushButton_2_clicked()
{
    QString query =
        "SELECT * FROM librarydb.bbk;";
    LoadTable(query);
}
void MainWindow::on_pushButton_3_clicked()
{
    ui->tableView_2->setModel(pointer);
    pointer_1 = pointer;
}
void MainWindow::on_pushButton_4_clicked()
{
    ui->tableView->setModel(pointer_1);
}

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTableView>
#include <QDebug>
#include <QSql>
#include <QSqlError>
#include <QSqlDatabase>
#include <QSqlTableModel>

namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    void LoadTable(QString query);
    ~MainWindow();
public slots:

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();
    void on_pushButton_3_clicked();
    void on_pushButton_4_clicked();
private:
    Ui::MainWindow *ui;
    QSqlDatabase objDatabase;
    QSqlQueryModel *objQuery;
    QSqlQueryModel* pointer;
    QSqlQueryModel* pointer_1;

};
#endif // MAINWINDOW_H
READ ALSO
Поясните фрагмент кода #pragma comment

Поясните фрагмент кода #pragma comment

в VS2008 создал из шаблона MFC проектВсе работает

95
QSettings Обработка данных и работа с реестром

QSettings Обработка данных и работа с реестром

Всем приветУ меня есть текстовый редактор

151
E/SQLiteLog: (1) near &ldquo;=&rdquo;: syntax error [закрыт]

E/SQLiteLog: (1) near “=”: syntax error [закрыт]

я уже с ума схожу где то = не так стоит, хееелп

223
Если в базе поле заполнено как text, то вывести текст, если цифры, то вывести цифры

Если в базе поле заполнено как text, то вывести текст, если цифры, то вывести цифры

Возникла проблема, сейчас пользователи могут заполнить поле как цифрами так и не заполнять его вообще (заполнится автоматически текстом)

97