Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

TabWidget tabs, each with a model TableView. Crashing switching between tabs.



  • Hello all! I'm back again.

    Currently:
    I have a UI AnimalSearch. I have implemented a TabWidget with two tabs, Search, and Previous Search. Each tab has a model TableView. The Previous Search tab will display previous animal searches in a TableView. This data is collected from a DB and is stored in a model when the UI is created. (I'd prefer to collect the data at this time, because i'd like to do it at the beginning once and only once. The data will not update with the users current sessions searches.) The data from the DB is stored in a model.

    When the UI is first started I can switch between the search tab, and the Previous search tab as many times as I'd like without fail. The search tab tableview isn't populated with any data because at this point I haven't searched for anything. Once I do search for an animal, and the tableview in search populates with the returned search data, i can no longer switch tabs back to Previous search without crashing. The error I'm getting from the debugger is the same I was getting in my previous post, Exception at 0x7ffa3827c1ea, code: 0xc0000005: read access violation at 0x0, flags=0x0 (first chance). I believe this indicates that the model (SearchHistoryProxyModel) that populates the previous search tableview is no longer filled with data but is empty. Is that correct? If that is true, is this a scope issue? How do I preserve the data in the model that feeds the previous search tableview for the duration of the animalsearch ui's existance? Will I have the same issue with the SearchModel once the user has performed a search but for some reason clicks the Previous search tab and then clicks back to the Search tab?

    The following is the code I'm using up to this point in time:

    AnimalSearch.h

    namespace Ui {
    class AnimalSearch;
    }
    class AnimalSearch : public QDialog 
    {
         Q_OBJECT
    public:
       stuff
    
    private slots:
        stuff
    
    signals: 
        stuff
    
    private:
        QCompleter *TurtleCompleter=0;
        QCompleter *RabbitCompleter=0;
        QCompleter *DogCompleter=0;
        QCompleter *CatCompleter=0;
        //model to hold current search data
        QSqlQueryModel *SearchModel=0; //tried with =0 with no change
        //model to hold historic searches
        QSqlQueryModel *PreviousSearchModel=0;
        //proxy model to enable filtering per field in the tableview
        QSortFilterProxyModel *SearchHistoryProxyModel;
    };
    

    AnimalSearch.cpp

    #includes section
    
    AnimalSearch::AnimalSearch(QWidget *parent):
        QDialog(parent),
        ui(new Ui::AnimalSearch)
    {
        QStringList animals << "Turtle" << "Rabbit" << "Dog" << "Cat";
        ui->cmbFilter->addItems(animals);
    
        //section that collects the past animal search and stores in a model. 
        db = QSqlDatabase...blahblahblah
        if(db.open()){
            QString GetPreviousSearchsQuery = "crazy long query string";
            //Create a new QSqlQueryModel using SearchHistoryModel
            this->SearchHistoryModel = new QSqlQueryModel();
            SearchHistoryModel->setQuery(GetPreviousSearchsQuery);
            //By adding this ProxyModel in between the SearchHistoryModel and PreviousSearch_View you can now sort by data field
            QSortFilterProxyModel *SearchHistorySortProxyModel = new QSortFilterProxyModel(this);
            SearchHistorySortProxyModel->setSourceModel(SearchHistoryModel);
            //Enable the sorting of the view
            ui->PreviousSearch_View->setSortingEnabled(true);
            //add the SortProxyModel to the view to display data.
            ui->PreviousSearch_View->setModel(SearchHistorySortProxyModel);
        }
        else{
            //do something if db isn't open
        }
    
    
        //Then I parse the data out of the model into individual QStringLists: 
       TurtleList, RabbitList, DogList, CatList;
    
        //Display each string list to make certain it is good. (the data looks good when i run.)
        qDebug() << TurtleList;
        qDebug() << RabbitList;
        qDebug() << DogList;
        qDebug() << CatList;
    
        //Then I build the completers
        TurtleCompleter = new QCompleter(TurtleList, this);
        ui->lineBy->setCompleter(TurtleList); //Default Completer
    
        RabbitCompleter = new QCompleter(RabbitList, this);
        DogCompleter = new QCompleter(DogList, this);
        CatCompleter = new QCompleter(CatList, this);
    
    }
    
    //Section about cmbFilter selection
    void AnimalSearch::on_cmbFilter_currentIndexChanged()
    {
        //for a test just neglect the current index and try to change the qcompleter being used by the lineBy box.
        qDebug() << "Does this print?";
        ui->lineBy->setCompleter(RabbitCompleter);
    }
    

    Thank you for your time if you read through all of this. I know it is a lot of info.



  • Something I forgot to mention, I tried and experiment where I introduced a

    slot void ProfileSearch::on_tabWidget_currentChanged(int index)
    {
        if(index==0){ //This is the search tab
            //here i placed all the code for creating a model and called the DB for the current search the user has entered int othe cmbFilter and lineBy box.  this works.
        }
        if(index==1){
            //here i placed all the code for creating the previoussearchmodel and query the db for that data and displayed the data to the PreviousSearch_View.  this worked, but isn't a great idea because the code will keep hitting the database for the same data as many times as the user clicks on the previous search tab.
        }
    }
    

    This code worked. The program no longer would crash when switching between tabs. This reconfirms my suspicions that the PreviousSearchModel data is lost after the user searches for something. I just don't understand why.


  • Lifetime Qt Champion

    Hi
    In
    void ProfileSearch::on_tabWidget_currentChanged(int index)
    {
    if(index==0){ //This is the search tab
    Did you use NEW here to create the models ?
    SomeModel * model = new SomeModel;
    so they survive after the function ends?



  • @mrjj

    Also worth of note in this test I didn't use the proxysortmodel.

    I used:

     QString GetPreviousSearchsQuery = "crazy long query string";
    this->PreviousSearchModel = new QSqlQueryModel();
    PreviousSearchModel->setQuery(GetPreviousSearchsQuery);
    ui->PreviousSearchView->setModel(PreviousSearchModel);
    

    I don't know for certain if they survive. I would think they do not, but I don't know.


  • Lifetime Qt Champion

    Hi
    Since you have the syntax
    PreviousSearchModel->xxx
    ( and not PreviousSearchModel.xxx)
    It seems like you use pointers and
    hence the models should survive fine.

    at what line does it crash ?



  • @mrjj

    I'm trying to reply with a long post, but my post keeps getting flagged as spam.

    I'm going to try to break my reply up into smaller chuncks. Maybe Akismet.com will like that better.



  • @mrjj
    Reply Part 1
    For the purpose of bettering my understanding of the language could you explain the differences in the syntax you just referred to? PreviousSearchModel->xx vs PreviousSearchModel. xx

    I can't answer your question truthfully. I was feeling overwhelmed by how unmanageable my code had become. The reason for that is bad coding practices and inexperience. I saved everything and created a new project and built just the bare minimum to experiment with this issue i'm having. This is the reason for the delay in my response to your question.

    One major difference in my code is a created a sqlconnect.h file that contains a class sqlconnect that makes my life easier gathering data from a sql db from anywhere in my code. Before I had redundant code that was a pain to manage. Here is the sqlconnect.h contents:

    #ifndef SQLCONNECT_H
    #define SQLCONNECT_H
    #include <QString>
    #include <QtSql>
    
    class sqlconnect
    {
    
    public:
        QSqlDatabase db;
        QSqlQueryModel *QueryModel;
    
        QSqlQueryModel* QueryDB(QString Query)
        {
             db = QSqlDatabase::addDatabase("QODBC");
             db.setDatabaseName("AnimalsDB");
             db.setUserName("Spiderman");
             db.setPassword("12345");
             this->QueryModel = new QSqlQueryModel();
             if(db.open())
             {
                 QueryModel->setQuery(Query);
             }
             else
             {
                 qDebug() << "Didn't work yo";
             }
             return QueryModel;
        }
    };
    

    Now in my animalsearch.h

    #ifndef ANIMALSEARCH_H
    #define ANIMALSEARCH_H
    #include <QDialog>
    #include <QtSql>
    
    namespace Ui {
    class AnimalSearch;
    }
    class AnimalSearch : public QDialog
    {
        Q_OBJECT
    
    public:
        explicit AnimalSearch(QWidget *parent =0);
        ~AnimalSearch();
        QSqlQueryModel *previousSearchModel;
        QSqlQueryModel *SearchModel;
    
    private slots:
        void on_OkButton_clicked();
        void on_Searchbutton_clicked();
    
    private:
        Ui::AnimalSearch *ui;
    };
    #endif //
    

  • Lifetime Qt Champion

    Yeah
    Akismet.com can be a little flower sometimes but i hope it catches some real spam from time to time.
    The syntax for accessing members depending on the allocation method.
    http://net-informations.com/faq/net/stack-heap.htm
    When you do
    ClassX a; // stack. its a local variable. meaning it dies with the scope its in. function/ class
    To access its member, you use var.xxx ( dot )
    ClassX *a = new ClassX(); // heap. variable now points to memory and lives until delete is called on it
    To access its member, you use var->xxx ( -> )

    When you have first type allocation, one should always be vary if passing to another class.
    Especially if a & a needed ( take address )
    but new (heap) allocation is often need for objects that we need to live longer than where they are allocated.
    Hope this clears it up a bit.



  • This post is deleted!


  • This post is deleted!


  • @Core2

    @mrjj
    Reply Part 2
    animalsearch.cpp

    includes section //akismet didn't like my includes...ugh
    
    AnimalSearch:: AnimalSearch (QWidget *parent) :
        QDialog(parent),
        ui(new Ui::AnimalSearch)
    {
        ui->setupUi(this);
        sqlconnect PreviousSearch;
        QString uname = qgetenv ("USER");
        if (uname.isEmpty())
            uname = qgetenv("USERNAME");
        QString GetComplete = "long query to get previous searches using the currently logged on user";
        this->PreviousSearchModel = new QSqlQueryModel();
        Previous Search Model = PreviousSearch. QueryDB (GetComplete);
        ui->Previous Search Table View->set Model(Previous Search Model);
    }
    AnimalSearch:~AnimalSearch()
    {
        delete ui;
    }
    
    void AnimalSearch::on_OkButton_clicked()
    {
        close();
    }
    
    void AnimalSearch::onSearchButton_clicked()
    {
        sqlconnect CurrentSearch;
        QString SearchQuery = "Hard coded query to search for box turtles";
        this->SearchModel = new QSqlQueryModel();
        SearchModel = CurrentSearch.QueryDB(SearchQuery);
        ui->SearchTableView->setModel(SearchModel);
    }
    


  • @Core2

    Reply Part 3

    mainwindow.cpp

    include mainwindow.h
    include ui_mainwindow.h
    include animalsearch.h
    //trust me the syntax is correct in the program.
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        AnimalSearch animalsearch;
        animalsearch.setModal(true);
        animalsearch.exec();  //This is line 21.  According to debugger this is where the crash occurs.
    }
    

    Now, when this code is ran the previous search tabs QTableView is populated with previous searches.
    I can click between the current search tab and the previous search tab without fail as many times as I'd like.
    If i click the search button, which is hard coded to search for a box turtle the search tab QTableView populates with box turtle results. Cool good stuff.
    But, when i click back on previous search tab i have a crash. I am shown the same error message as before. Exception at 0x7ffa38f1c1ea, code: 0xc0000005: read access violation at: 0x0, flgs=0x0 (first chance).
    The program stops at line 21 of the mainwindow in the pushbutton slot that launches the animalsearch ui.

    I hope I did not complicate things moving away from the originally posted code.

    Again thanks for your time.


  • Lifetime Qt Champion

    Hi
    I think you going to need to use the debugger and put break point in button, and single step into creation and showing animalsearch. until you find the line that makes it crash.

    I cant see/guess guess from the source, maybe others can guess the reason for the crash.

    It really sounds like a dangling pointer :)



  • @mrjj

    Ok. I will do just that. After reading your link from one of your previous posts about stack and heap, but still not completely understanding, i watched this guys videos, ReelLearning. I think I have a good grasp of what I should be looking for now. I will spend the day using debugging to try and solve the problem.

    This video was very informative.
    Pointers and Dynamic Memory in C++ (Memory Management)

    Good video as well.
    Introduction to Pointers in C++, Tutorial on Pointers, C++ Pointers

    Interesting observation. The more I learn about c++/Qt the more I realize I don't know much:)

    Thanks for the guidance.


  • Lifetime Qt Champion

    Hi
    if you stil have doubts about stack vs heap, please ask/post
    description of that still needs more info.
    As its a critical part of C++ so one has to know when to use
    heap in the cases where it will be ultra important.

    • Interesting observation. The more I learn about c++/Qt the more I realize I don't know much:)
      Heh yep. I get that feeling from models and views in Qt :)


  • Hello,

    Isn't it just because you re-declare your proxy model in the constructor body of AnimalSearch ?

    QSortFilterProxyModel *SearchHistorySortProxyModel = new QSortFilterProxyModel(this);
    

    I think it shadows your member declared in the header as you have used the same name.

    This should work better

    this->SearchHistorySortProxyModel = new QSortFilterProxyModel(this);
    


  • @Gojir4
    I will try that out when i switch back to the master branch. Currently I'm working on a new branch where i cut all the fat of the program. now it is simple and easy to understand.

    Also i noticed something while using the dbugger today. The error happens in qsql_odbc.cpp Unfortunately I don't see the file code i see the assembly code when the error pops up. So it looks Greek to me. Any suggestions?

    @mrjj


  • Qt Champions 2019

    @Core2 said in TabWidget tabs, each with a model TableView. Crashing switching between tabs.:

    The error happens in qsql_odbc.cpp

    Just check which line of your code was executed just before qsql_odbc.cpp
    And did you fix the issue @Gojir4 pointed out?



  • @jsulm

    No I have not implemented the changes suggested by Gojir4 because I moved away from that code. At this time I'm not using a proxy model to simplify things.


Log in to reply