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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Segmentation fault. QSqlquerymodel + QTableView в разных потоках.  (Прочитано 7576 раз)
lolobotik
Гость
« : Март 28, 2017, 14:37 »

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

Есть наброски программы для работы с базой данных. Под все sql'вские задачи вроде как думал выделить отдельный поток, чтоб совсем не зависнуть в случае чего.

Объект класса sqlroutine с полем QSqlQueryModel и парочкой слотов/сигналов путём moveToThread засунут в отдельный тред. В GUI потоке есть некая форма с таблицей QTableView. В таблицу пока вычитывается по 20 (или меньше) записей, то есть записи выводятся постранично (страница для тестов - 20 записей). Запросы в БД делаются только по текущей странице. Переключение между страницами по кнопкам вперед/назад на форме.

В main.cpp имеем:

Код:
    sqlRoutine sql;
    QThread *Thread;
    Thread = new QThread(); // не должно быть родителя!
    sql.moveToThread(Thread);
    Thread->start();

    ClientBase CB;
    CB.show();


    //Эта связка запрашивает некоторое кол-во строк из таблицы (с какой-то по какую то). В ответ строка с текстом ошибки ( или пустая, если всё ок).
    QObject::connect(&CB,SIGNAL(GetClientTableData(quint32, quint32)),&sql,SLOT(RequestedClientTableData(quint32, quint32)));
    QObject::connect(&sql,SIGNAL(ReturnClientTableData(QString)),&CB,SLOT(RefreshClientTableData(QString)));

    //Здесь спрашиваем у БД, сколько записей в таблице и возвращаем количество в поток с интерфейсом. По возвращению, вешаем QSqlQueryModel на QTableView (setModel)
    QObject::connect(&CB,SIGNAL(GetClientsCount()),&sql,SLOT(RequestedClientsCount()));
    QObject::connect(&sql,SIGNAL(ReturnClientsCount(int)),&CB,SLOT(RefreshClientsCount(int)));


К началу какого-либо взаимодействия между потоком с sqlroutine и таблицей TableView (из ClientBase CB) база уже открыта (плюс проверен логин/пароль на соответствие таковым в таблице базы).
Выглядит таким образом:

Код:
    db = QSqlDatabase::addDatabase("QODBC3");
    db.setDatabaseName("DRIVER={SQL Server};Server=172.22.121.2;Database=xxx_test;Uid=xxxx;Port=1433;Pwd=xxxxx;");
    bool ok = db.open();
    user_struct currentUser;

    if (ok) //Корректное открытие объекта базы данных
    {
          // тут проверяем введённый логин и пароль, отправляем куда-то там сигнал о результате (объект, отвечающий за форму логина, тут не описывается)
    }
    else
    {
         //если не открыли - ошибка с сигналом (в тот же неописанный объект)
    }

Далее максимально упрощенные слоты.

На стороне треда с SQL:

Код:
void sqlRoutine::RequestedClientsCount()
{
    bool ok = query->exec("SELECT COUNT(*) FROM ClientsTable");
    if (ok) //Запрос корректно сформирован и исполнен
    {
        query->first();
        int Result = query->value(0).toInt();
        emit ReturnClientsCount(Result);
    }
    else
    {
        emit ReturnClientsCount(-1);
    }
}

void sqlRoutine::RequestedClientTableData(quint32 FromEntryNum, quint32 ToEntryNum)
{
    model.setQuery("SELECT Client_ID,FullName,PhoneNumber,LastCallDate,LastCallTime "
                       "FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY Client_ID) AS row FROM ClientsTable) a "
                       "WHERE row > "+QString::number(FromEntryNum)+" AND row <= "+QString::number(ToEntryNum));

    QString errorString = model.lastError().text();
    emit ReturnClientTableData(errorString);
}

Со стороны GUI:

Код:
void ClientBase::RefreshClientTableData(QString SqlError)
{
    if (SqlError == " " )
    {

        WaitingForAnswer = false;
    }
    else
    {
        msgBox2= new QMessageBox("Ошибка", SqlError,QMessageBox::NoIcon,0,0,0,0,Qt::Dialog);
        msgBox2->setAttribute(Qt::WA_DeleteOnClose, true);
        msgBox2->show();
    }


}

void ClientBase::RefreshClientsCount(int RowCount)
{
    TotalEntryCount = RowCount;
    ui->TotoalCountLabel->setText(QString::number(TotalEntryCount));
    if (TotalEntryCount<=PageSize)
    {
        ui->NextPageButton->setEnabled(false);
    }

    if (FirstRequest) //Первый раз заполняем таблицу
    {
        FirstRequest = false;
        emit GetClientTableData(0, (TotalEntryCount<PageSize)? TotalEntryCount:PageSize);
        sql.model.setHeaderData(0, Qt::Horizontal, tr("ID"));
        sql.model.setHeaderData(1, Qt::Horizontal, tr("Имя"));
        sql.model.setHeaderData(2, Qt::Horizontal, tr("Телефон"));
        sql.model.setHeaderData(3, Qt::Horizontal, tr("Дата звонка"));
        sql.model.setHeaderData(4, Qt::Horizontal, tr("Время звонка"));
        sql.model.setHeaderData(5, Qt::Horizontal, tr("Статус"));
        ui->ClientBaseTableWidget->setModel(&(sql.model));
        refreshPageInfo(); //тут просто вывод в QLabel номеров записей на текущей странице.
    }

}


На кнопках висят такие обработчики (в кратце - вычисляются нужные записи для страницы и шлётся сигнал в sqlRoutine):

Код:
void ClientBase::on_NextPageButton_clicked()
{

    PageNum++;
    if (PageNum*PageSize +PageSize > TotalEntryCount)
    {
        ui->NextPageButton->setEnabled(false);
    }
    ui->PrevPageButton->setEnabled(true);
    emit GetClientTableData(PageNum*PageSize, (TotalEntryCount<PageNum*PageSize+PageSize)? TotalEntryCount:PageNum*PageSize+PageSize);
    refreshPageInfo();

}

void ClientBase::on_PrevPageButton_clicked()
{
    PageNum--;
    if (PageNum == 0)
    {
        ui->PrevPageButton->setEnabled(false);
    }

    ui->NextPageButton->setEnabled(true);
    emit GetClientTableData(PageNum*PageSize, (TotalEntryCount<PageNum*PageSize+PageSize)? TotalEntryCount:PageNum*PageSize+PageSize);
    refreshPageInfo();
}


Вот как-то так выглядит всё это дело. Теперь к проблеме. При переключении страниц с некоторой вероятностью (может после 3 нажатий на кнопку Вперед или Назад, может после 100) вылетает SegmentationFault. При этом косяк, судя по всему именно из-за нескольких потоков (что-то видно не стыкуется у меня), так как, если оставить всё в GUI'шном потоке, то все проблемы исчезают. Так же, при использование в конектах DirectConnection (что, насколько я понял, практически сводит всё опять же к 1 потоку).

Буду очень благодарен, если кто-то либо укажет на явный косяк, либо подскажет, куда рыть. С Model/View и SQL на Qt не имею никакого опыта, с потоками опыт тоже крайне сомнительный, так что догадок особо нет.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #1 : Март 28, 2017, 14:45 »

Модель нельзя в поток, ибо вьюха ее напрямую дергает. Ты попробовал без потока обойтись прежде чем такое городить?
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
lolobotik
Гость
« Ответ #2 : Март 28, 2017, 15:01 »

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

Изначальный посыл был в том, чтобы при "протупке" sql'вских запросов не вешать интерфейс. В частности, пока делал авторизацию, выяснил, что некоторые вещи (а ля остутсвие сервера по нужному адресу) блокируют поток примерно на минуту, что не есть хорошо. Вот и решил вынести всё это дело.

Как выводить всё считанное добро в таблицу стал думать уже подобравшись поближе к самой таблице (про model/view вообще слыхом не слыхивал, так что спланировать не мог заранее). Так что нет, не пробовал. Попытался навесить на сделанный ранее кусок то, что описал в посте. Уже постфактум старался вычитать что-то по поводу корректности распихивания моедльки и вьюхи в ранзые потоки - писали, что в принципе всё легально возможно. Но интернет, такой интернет, так что уверенности нет)

Неужто так категорично всё (нельзя)? И если да, то, может посоветуете чего для такого разнесения по потокам. Можно, конечно вручную расковыривать query и переписывать данные в свой буфер, но думаю, это не самый правильный вариант.
« Последнее редактирование: Март 28, 2017, 15:03 от lolobotik » Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #3 : Март 28, 2017, 15:32 »

Наличие сервера можно отдельно проверять. ODBC плохой выбор, лучше использовать нормальный драйвер. Что за БД?
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
lolobotik
Гость
« Ответ #4 : Март 28, 2017, 15:48 »

Наличие сервера можно отдельно проверять. ODBC плохой выбор, лучше использовать нормальный драйвер. Что за БД?

SQL Server 2008. ODBC  по принципу "что не надо было доприкручивать то  и взял" используется. Он же вроде стандартно к винде приклеен?

Ну а по поводу "наличия сервера": нет уверености, что затуп может только при его отсутствии произойти (а отсутвие опыта не даёт предсказать, где он вообще возможен).
« Последнее редактирование: Март 28, 2017, 15:50 от lolobotik » Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #5 : Март 28, 2017, 16:04 »

ODBC лучше не использовать, ИМХО. БД тебе навязана или можно выбрать другую?
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
lolobotik
Гость
« Ответ #6 : Март 28, 2017, 16:07 »

ODBC лучше не использовать, ИМХО. БД тебе навязана или можно выбрать другую?

Крутится уже на сервере с другими базами. Поднимать еще 1 вряд ли станут.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #7 : Март 28, 2017, 16:11 »

Я с MSSQL не работал, но с постгресом затупов никаких не было. Главное, не делать огромные выборки.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
lolobotik
Гость
« Ответ #8 : Март 28, 2017, 16:25 »

Я с MSSQL не работал, но с постгресом затупов никаких не было. Главное, не делать огромные выборки.

Ясно. Ну буду надеяться, что у меня тоже не возникнет. Большие объёмы за раз вроде не планирую выжимать.

Пока повешу на слоты с обновлением модели Qt::DirectConnection для блокировки гуишного потока до выполнения кода с слотах. Так оно вроде не шалит. Если всплывёт еще что-то - тогда уж в один поток всё засуну и буду думать, что с этим делать.

Спасибо за пояснения.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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