Проблема
Не могу передать данные выделенные в другом потоке через указатель в основной поток программы для того чтобы заполнить 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);
}
Проблема решилась.
Заключалась в связи Поток<--->База данных.
Проще говоря если создать подключение в другом потоке и получить потом ссылку на результирующие данные(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
Виртуальный выделенный сервер (VDS) становится отличным выбором
я уже с ума схожу где то = не так стоит, хееелп
Возникла проблема, сейчас пользователи могут заполнить поле как цифрами так и не заполнять его вообще (заполнится автоматически текстом)