Set background color to a cell of a tableView



  • I have a tableview that is using a QSqlRelationalTableModel. The table view has a column that I want to color the cell (or row) with the highest sales total (column 5). I can iterate through the model and get the row that the highest value is in. I have tried several methods to color the cell to no avail. I have read that it can be done with a custom proxy or delegate but no samples of code so I am not sure what they mean. From what I have read, the following should work.
    @ printModel= new QSqlRelationalTableModel (this);
    printModel-> setEditStrategy(QSqlTableModel::OnRowChange);
    printModel-> setTable (mTableName);

    printModel-> setRelation (2, QSqlRelation("customer", "id", "Name"));
    printModel->select();
    

    QAbstractItemModel *model = ui->printView->model();
    QModelIndex modelIdx = model->index(rowHighSales, 5, QModelIndex()); //rowHighSales is the row with the highest sales
    model->setData(modelIdx, yellow, Qt::BackgroundRole); //this returns false if I use debug. red is a QBrush
    @
    Can this work or is there a better way to do it?

    Thanks



  • I'm not sure. You would need to look at the source for QSqlRelationalTableModel::setData() to see how it handles the background role - I suspect it doesn't support it.

    If that is the case a simple approach would be to use a very simple proxy model that returns the correct background color in the data() function.



  • Using a simple proxy model or a delegate is the safe way of doing it.
    I, personally, would like to use a proxy model, derived from QSortFilterProxyModel.
    You only have to overwrite data() and return the correct color if needed. In all other cases, you just call the default impl.



  • Agreed, plus you also get all the nice features of QSFPM too so you could even sort on yoru column too if you like.



  • Yes, indeed, a proxy model is the way to go.

    What you do is that you subclass QSortFilterProxyModel, and reimplement the data() method to return the source data for the DisplayRole and the EditRole, and returns your color for the BackgroundRole when asked for it.

    I have written a generic proxy model that basically just overlays an existing table-type model (that is: it doesn't work on trees, only on a specific level of one). The overlay model supplies one set of roles, while the underlying model supplies the rest. You can use setData() on it to set data on all roles, and it will either store the value in the overlay model or forward the call to the source model. You can also use it to adjust flags. You could use a setup like that to set your color like you do now, but store it in your own "overlay model" instead of in the QSqlRelationalTableModel. It is not very hard to implement.



  • Ok, as I am new to all this, I am not sure how to use a proxy model, derived from QSortFilterProxyModel. Could you give me some code to get me started? I will research this as well, but from reading the docs, I am not sure how to do what you recommend. Thanks



  • You could start reading the "docs":http://doc.qt.nokia.com/4.7/model-view-programming.html#proxy-models , they are really good.



  • Ok so I re read the docs, and searched, but couldn't get it to work. Here is what I have....where did I go wrong? In setting up the table I use
    @QAbstractItemModel myModel;
    QSortFilterProxyModel *printModel = new QSortFilterProxyModel(parent);
    printModel->setSourceModel(myModel);
    ui->printView->setModel(printModel);@
    and I get an error: cannot declare variable 'myModel' to be of abstract type 'QAbstractItemModel' because the following virtual functions are pure within 'QAbstractItemModel': virtual QModelIndex QAbstractItemModel::index(int, int, const QModelIndex&) const .(and many more)
    I think I have to add something else...how and what do I need to do? I copied the code almost exactly.



  • It tells you what is wrong, QAbstractItemModel is not creatable. It is the abstract base class of all Models, and it is ABSTRACT. you normally go this way:

    @
    printModel= new QSqlRelationalTableModel (this);
    printModel-> setEditStrategy(QSqlTableModel::OnRowChange);
    printModel-> setTable (mTableName);

    printModel-> setRelation (2, QSqlRelation("customer", "id", "Name"));
    printModel->select();

    MyQSFPM* proxy = new MyQSFPM(this);
    proxy->setSourceModel(printModel);
    ui->printView->setModel(proxy);
    @

    now you have to create MyQSFPM as derived class of QSortFilterProxyModel abd reimplement data.

    @
    class MyQSFPM : public QSortFilterProxyModel
    {
    ...
    }
    @



  • I feel stupid. I have read several docs and searched and still do not understand how to reimplement the data in the derived class. Do I simply copy the implementation of the functions I need to use and put them in the derived class? I am going to continue reading and trying things. Sorry for not understanding.



  • Hi poporacer.

    Overwriting methods is standard C++ knowledge.
    For more information about model / view, look here:

    "Qt documentation - Model/View Programming":http://doc.qt.nokia.com/4.7/model-view-programming.html



  • [quote author="poporacer" date="1301546336"]I feel stupid. I have read several docs and searched and still do not understand how to reimplement the data in the derived class. Do I simply copy the implementation of the functions I need to use and put them in the derived class? I am going to continue reading and trying things. Sorry for not understanding.[/quote]

    As Gerolf points out, overriding virtual methods is a key feature of object oriented programming in C++. It is an essential part of subclassing.

    What you need to do in this case, is to handle the cases you want to handle in your reimplementation of data() in your subclass, and use the base implementation for all other cases. Note that it is often a good idea to call the base class implementation of an overridden method in the reimplementation.

    In your case, your data method might look something like this*:

    @
    QVariant myProxyModel::data ( const QModelIndex & index, int role ) const
    {
    if (!index.isValid())
    return QVariant();

    QModelIndex sourceIndex = mapToSource( index );
    if ( role == Qt::BackgroundRole ) {
    if (sourceIndex.data( Qt::DisplayRole ).toDouble == m_theValueIWant ) {
    return Qt::red;
    } else {
    return QVariant();
    }
    } else {
    return sourceIndex.data( role );
    }
    }
    @

    Perhaps that can help you get started.

    *) That means: typed in the forum editor, don't expect this to compile or be complete

    Edit: changed sample to return a color, not a checkbox state. I was confused with another question



  • [quote author="Andre" date="1301558057"]
    In your case, your data method might look something like this*:
    [/quote]

    I think you got two threads mixed up, this one is about the background role ;-)

    So in this case you reimplementation may look something like:

    @
    QVariant myProxyModel::data ( const QModelIndex & index, int role ) const
    {
    if (!index.isValid())
    return QVariant();

    if ( sourceIndex.row() == m_maxRow && role == Qt::BackgroundRole ) {
    QModelIndex sourceIndex = mapToSource( index );
    return QVariant( Qt::red );
    } else {
    return QSortFilterProxyModel::data( index, role );
    }
    }
    @

    where m_maxRow is a member variable that you need to set.



  • [quote author="ZapB" date="1301558444"][quote author="Andre" date="1301558057"]
    In your case, your data method might look something like this*:
    [/quote]

    I think you got two threads mixed up, this one is about the background role ;-)
    [/quote]

    Yeah, that's what I just realized. There is another running question on putting checkboxes from an SQL model in a QTableView, and I confused the two. I was already editing my response.



  • Yeah, I thought so. I'm following that thread too. Easily done ;-)



  • I have read the code, read the docs you suggested and yet have not been able to get it to work. I am not sure if I am reading too much into this. So let me explain what I understand that I shoul do so far. I need to create a new class (MyProxyModel) and inherit QSortFilterProxyModel. Then in my class reimplement the data function ( I think that in ZapB's code the declaration
    @QModelIndex sourceIndex = mapToSource( index ); @
    should go at the top of the function?) I am not sure what is meant by having to set m_maxRow is this to the total number of rows or the row number that I want to set the background color? Then in my main class I set up the table following Gerolf's suggestion. Here is what I have:
    myproxymodel.h
    @
    #ifndef MYPROXYMODEL_H
    #define MYPROXYMODEL_H

    #include <QSortFilterProxyModel>

    class MyProxyModel : QSortFilterProxyModel
    {
    Q_OBJECT

    public:
    MyProxyModel();
    ~MyProxyModel();
    QVariant data ( const QModelIndex & index, int role );
    void setSourceModel ( QAbstractItemModel * sourceModel );

    };
    #endif //MYPROXYMODEL_H@

    myproxymmodel.cpp:
    @
    #include "myproxymodel.h"

    MyProxyModel::MyProxyModel (QObject *parent) :
    QSortFilterProxyModel(parent)
    {

    }

    QVariant MyProxyModel::data ( const QModelIndex & index, int role ) const
    {
    QModelIndex sourceIndex = mapToSource( index );
    if (!index.isValid())
    return QVariant();
    if ( sourceIndex.row() == m_maxRow && role == Qt::BackgroundRole )
    {

    return QVariant( Qt::red ); 
    } 
    else 
    { 
    return QSortFilterProxyModel::data( index, role ); 
    } 
    

    }@
    And in my main class I have
    @#include "myproxymodel.h"
    .
    .
    .
    void MainClass::createReportTable(QStringList stringList)
    {
    printModel= new QSqlRelationalTableModel (this);
    printModel-> setEditStrategy(QSqlTableModel::OnRowChange);
    printModel-> setTable (mTableName);
    printModel-> setRelation (2, QSqlRelation("customer", "id", "Name"));
    printModel->select();
    MyProxyModel* proxy = new MyProxyModel(this);
    proxy->setSourceModel(printModel);
    ui->printView->setModel(proxy);
    @

    I get several compile errors and I have tried to figure out why, but can't figure it out.
    Line 12 in the main class gives me the error
    no matching function for call to 'MyProxyModel::MyProxyModel(MainClass* const)

    Line 14 in the main class gives me the error
    'QAbstractItemModel' is an inaccessible base of 'MyProxyModel'

    Am I going about this correctly? I am trying to figure it out on my own by "reading the docs" but the docs I have trouble understanding. Where am I going wrong?

    In reading, I see this function:
    bool QAbstractItemModel::setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ) [virtual] Is this of any use?



  • In your myproxymodel.h code, you declare setSourceModel on line 14. You don't need that; QSortFilterProxyModel already has a good implementation of that method. You can reimplement it if you want to do something special when the source model is set, but it doesn't seem to be the case.

    m_maxRow is supposed to indicate the row you want to highlight. Of course, this is only one way to do that; you can also invent something more flexible that allows you to highlight different rows with different colors.

    Note that you need to publicly inherit from QSortFilterProxyModel. That is: change the first line of your myproxymodel.h to:

    @
    class MyProxyModel : public QSortFilterProxyModel
    @

    That should get rid of your error on line 14 of your main: ‘QAbstractItemModel’ is an inaccessible base of ‘MyProxyModel’. Remember, C++ uses private as the default, not public.

    bool QAbstractItemModel::setData is used to modify data through the QAbstractItemModel interface, so, mostly through the views themselves. In your case, you don't need to reimplement this method. You do, of course, need to have some other method to determine or set the row number to highlight.



  • Hi,

    there is one more bug in your code. This error:

    bq. no matching function for call to ‘MyProxyModel::MyProxyModel(MainClass* const)

    is issued due to this code:

    @
    class MyProxyModel : QSortFilterProxyModel
    {
    Q_OBJECT
    public:
    MyProxyModel(); // <-- !!!
    };
    @

    It should be like this;

    @
    class MyProxyModel : QSortFilterProxyModel
    {
    Q_OBJECT
    public:
    MyProxyModel(QObject* parent); // <-- !!!
    };
    @



  • Thanks for your help. What would be the best way to pass the m_maxRow variable to the MyProxyModel class? The two options I can think are, create a global variable (usually considered bad) or create a signal/slot to pass the variable? I tried to pass it via a signal/slot and my class declaration is not working correctly. In my mainclass.h I have:
    @#include "myproxymodel.h"
    class MyProxyModel;@

    in my mainclass.cpp I have:
    @void MainClass::colorCells()
    {
    float lowSales=99999999;
    float highSales=0;
    int rowLow=0;
    int rowHigh=0;
    int totalRows = 0;

    MyProxyModel myProxyModel; //error here

    connect(this,SIGNAL(sendRows(int, int)),
    &myProxyModel, SLOT(getRows(int, int))); //to pass the high and low sales

    int numRows =ui->printView->model()->rowCount();

    for (int r=0; r<numRows; r++ )
    {
    if (ui->printView->isRowHidden(r))
    continue;
    float currentSales = ui->printView->model()->index(r,32).data(Qt::DisplayRole).toFloat();
    if (currentSales <lowSales)
    {
    lowSales = currentRunTime;
    rowLow = r;
    }
    if (currentSales > highSales)
    {
    highSales = currentSales;
    rowhigh = r;
    }
    totalRows++;
    totalTime += currentRunTime;
    }

    emit sendRows (rowSlow, rowFast);
    QModelIndex modelIdx = printModel->index(rowFast,32, QModelIndex());
    printModel->data(modelIdx, Qt::BackgroundRole);//Is this correct implementation?
    @
    where I try to declare the MyProxyModel variable I get the error:
    no matching function for call to 'MyProxyModel::MyProxyModel()'
    candidates are: MyProxyModel::MyProxyModel(QObject*)
    MyProxyModel::MyProxyModel(const MyProxyModel&)
    I don't see why it thinks I am trying to declare a function.
    I keep reading, trying to keep from asking questions here but I keep coming back!
    Again, I appreciate your patience with my questions.



  • Don't create your proxy model on the stack, create it on the heap with a proper parent. Otherwise, it will be deleted as soon as the method finishes (so before a result is even drawn). What's more, the code as you wrote it now can only be run once. Otherwise, you'll keep creating more proxy models. That can't be what you want. You also don't set the proxy model as the model for your view, nor do you give the proxy model a source model.

    I think, you should calculate the row to highlight in the proxy model itself. That nicely encapsulates the functionality you're after in a single class. Or, make a more flexible API on the proxy model to add highlights to selected rows at will, so you can re-use it later on. Your mid-way solution doesn't look very nice to me. Also: what happens if two rows share the same maximum or minimum value?

    I don't quite get your implementation. Especially the last two lines are quite mystifying. Why to you request data from your model, and then don't use it? Also, I would not initialize to 0 and 99999999. Those are rather arbitrairy. Instead, initialize to the maximum and minimum values of float. You can find those by including <float.h>.

    First, however, try to get the proxy model working with a fixed (set of) row(s) of to highlight. If that works ok, take the next step and determine the rows dymanically. I would do that by responding to changes in the (source) model, so you can update the source model and be sure that the highlights will be properly updated as well.



  • Ok I created
    @MyProxyModel* proxy = new MyProxyModel;@
    And put it before the constructor as it looks like I need it in more than one spot. I still get the error:
    no matching function for call to 'MyProxyModel::MyProxyModel()'
    candidates are: MyProxyModel::MyProxyModel(QObject*)
    MyProxyModel::MyProxyModel(const MyProxyModel&)

    In my MainClass I have a function that creates the table and there I set the model. I use:
    @void MainClass::createReportTable(QStringList stringList)
    {
    mReportOptions=stringList;

    printModel= new QSqlRelationalTableModel (this);
    printModel-> setEditStrategy(QSqlTableModel::OnRowChange);
    printModel-> setTable (mTableName);
    printModel-> setRelation (2, QSqlRelation("customer", "id", "Name"));
    printModel->select();
    proxy->setSourceModel(printModel);
    ui->printView->setModel(proxy);
    

    @
    So my implementation is wrong? How do I properly call the function to color the row?
    The table will be getting rows added or deleted. After each change, I iterate through the table to get the high and low sales. This will identify the row to color. The table will need to be continually updated. It will only highlight the highest and lowest row. Am I close or am I in right field?



  • The error on the first line of code you show, is quite clear. You have no constructor for MyProxyModel without arguments. So, I suggest you add an argument to your heap allocation of MyProxyModel. Just put the constructor of your proxy model in the same piece of code as the one you show below, and create a pointer member variable in your class declaration just like you have for printModel.

    How you properly call the method to color the rows, depends on how you implemented it. Could you tell us what route you choose?



  • Here is what I have/need. I have a QSqlRelationalTableModel that will display a table. Some of the columns will be hidden based on criteria selected by the user. The highest sales will be highlighted in yellow and the lowest sales highlighted in red. Rows will be added and removed. After each change to the table, the table needs to be evaluated and the appropriate cells or rows highlighted.
    [quote author="Andre" date="1302069303"]
    How you properly call the method to color the rows, depends on how you implemented it. Could you tell us what route you choose?[/quote]
    I was following the advice here on how to implement it. I think I have posted all the relevant code in the past posts. But to make it easier to read I will re-post in this post.
    myproxymodel.h
    @#ifndef MYPROXYMODEL_H
    #define MYPROXYMODEL_H

    #include <QSortFilterProxyModel>

    class MyProxyModel : public QSortFilterProxyModel
    {
    Q_OBJECT

    public:
    MyProxyModel(QObject* parent);
    ~MyProxyModel();
    QVariant data ( const QModelIndex & index, int role );

    private:
    int m_lowRow; //to store the row number of the lowest row
    int m_highRow; //to store the row number of the highest row

    signals:

    private slots:
    void getRows(int, int); // to get the row numbers that need to be highlighted

    };

    #endif //MYPROXYMODEL_H@

    myproxymodel.cpp
    @#include "myproxymodel.h"

    MyProxyModel::MyProxyModel (QObject *parent) :
    QSortFilterProxyModel(parent)
    {

    }

    QVariant MyProxyModel::data ( const QModelIndex & index, int role ) //const ** I had to remove this because of a compiler error
    {
    QModelIndex sourceIndex = mapToSource( index );
    if (!index.isValid())
    return QVariant();
    if ( sourceIndex.row() == m_highRow && role == Qt::BackgroundRole )
    {

    return QVariant( Qt::yellow );
    } 
    else 
    { 
    return QSortFilterProxyModel::data( index, role ); 
    } 
    

    }
    void MyProxyModel::getRows( int rowLow,int rowHigh)
    {
    m_highRow=rowHigh;
    m_lowRow=rowLow;
    }
    @
    mainclass.h
    @#ifndef MAINCLASS_H
    #define MAINCLASS_H

    #include <QDialog>
    #include <QSqlRelationalTableModel>
    #include<QSortFilterProxyModel>
    #include "myproxymodel.h"

    class MyProxyModel;

    namespace Ui {
    class MainClass;
    }

    class MainClass : public QDialog
    {
    Q_OBJECT

    public:
    explicit MainClass(QWidget *parent = 0);
    ~MainClass();

    private:
    Ui::MainClass *ui;
    QString mTableName;
    QString mFilterString;
    QStringList mReportOptions;
    void highlightCells();
    QSqlRelationalTableModel *printModel;

    signals:
    void sendRows(int, int);

    private slots:
    void on_btnPrint_clicked();
    void on_pushButton_2_clicked();
    void createReportTable(QStringList);
    void getTableName(QString);
    void getFilterData(QString);

    };

    #endif // MAINCLASS_H
    @

    mainclass.cpp
    @#include "mainclass.h"
    #include "ui_mainclass.h"
    #include <QtSql>
    #include <QSqlRecord>
    #include "myproxymodel.h"
    #include <float.h>

    MyProxyModel* proxy = new MyProxyModel;

    MainClass::MainClass(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::MainClass)
    {
    ui->setupUi(this);
    mNumTables=0;

    }

    MainClass::~MainClass()
    {
    delete ui;
    delete proxy;
    }

    //get table name from user selection

    void MainClass::getTableName(QString tString)
    {
    mTableName=tString;
    }

    //get user filter string

    void MainClass::getFilterData(QString fString)
    {
    mFilterString=fString;
    }

    //Create report table based on user info stringList holds criteria

    void MainClass::createReportTable(QStringList stringList)
    {
    mReportOptions=stringList;

    printModel= new QSqlRelationalTableModel (this);
    printModel-> setEditStrategy(QSqlTableModel::OnRowChange);
    printModel-> setTable (mTableName);
    printModel-> setRelation (2, QSqlRelation("rider", "id", "LName"));
    printModel-> setRelation (3, QSqlRelation("track", "id", "TrackName"));
    printModel-> setRelation (4, QSqlRelation("bike", "id", "BikeName"));
    
    proxy->setSourceModel(printModel);
    ui->printView->setModel(proxy);
    ui->printView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->printView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->printView->setColumnHidden(0,true);//id
        
    
    printModel->select();
    //routine here to hide additional columns based on user selection
    

    printModel->setFilter(mFilterString);
    ui->printView->setSortingEnabled(true);
    ui->printView->resizeColumnsToContents () ;
    ui->printView->horizontalHeader()->setStretchLastSection(true);
    highlightCells();
    printModel->select();

    }

    //here we should iterate the table to get the rows that need to be highlighted

    void MainClass::highlightCells()
    {
    float lowSales=FLT_MAX;
    float highSales=FLT_MIN;
    int rowHigh=0;
    int rowLow=0;
    int totalRows = 0;
    float currentSales;

    connect(this,SIGNAL(sendRows(int, int)),
    proxy, SLOT(getRows(int, int))); //to pass the rows to be highlighted to the proxy

    //get fastest and slowest sales and save row

    int numRows =ui->printView->model()->rowCount();

    for (int r=0; r<numRows; r++ )
    {
    if (ui->printView->isRowHidden(r))
    continue;
    currentSales= ui->printView->model()->index(r,5).data(Qt::DisplayRole).toFloat();
    if (currentSales < lowSales)
    {
    lowSales = currentSales;
    rowLow = r;
    }
    if (currentSales > highSales)
    {
    highSales = currentSales;
    rowHigh = r;
    }

    }

    emit sendRows (rowSlow, rowFast);

    //here is where I need to call proxy to highlight the appropriate cell or column

    }@

    Did this clear things up? Where do I go from here?



  • Hi,

    I see one problem in your code:
    you calculate highest / lowest value based on the proxy row,but in data ypou comparte to source row.

    @
    MyProxyModel* proxy = new MyProxyModel;
    @

    proxy should be a member of MainClass and mainClass should be the parent. so like this:

    @
    class MainClass : public QDialog
    {
    Q_OBJECT
    ...
    private:
    MyProxyModel* proxy
    };
    @

    @
    void MainClass::createReportTable(QStringList stringList)
    {
    ...
    printModel= new QSqlRelationalTableModel (this);
    ...

    proxy = new MyProxyModel(this);
    proxy->setSourceModel(printModel);
    ui->printView->setModel(proxy);
    ui->printView->setSelectionMode(QAbstractItemView::SingleSelection);
    

    }
    @

    where do you call createReportTable????



  • Yes, but you did not take previous advice. Why is proxy a global variable?

    First things first: does the code work so far? Do you get it to compile, and can you set rows to highlight (even if they are not actually the low and the high yet)?

    There are different ways to determine what the highest and the lowest values are:

    Use your SQL backend to calculate the data for you with a clever query

    Determine from inside your proxy model. Upside: nicely encapsulated; Downside: less re-usable

    Determine from outside your proxy model.

    You seem to have chosen the last option for now. No matter if you choose option 2 or 3, you will need to iterate over the model to find the highest and lowest values, and you need to repeat that each time your model changes (rows added, values changed in the relevant column). You can use the model's signals to trigger that iteration, but beware of ending up in an endless loop (you changing your data, triggering a dataChanged() signal, tiggering a recalculation of the highlight, triggering an update of the data, etc...)

    I would not prefer setting row numbers, but setting the actual values to highlight. This makes sure that you can highlight two rows if they share the same lowest or highest value.



  • Well, it compiles ok now, thanks Gerolf and Andre. I call createReportTable from a Signal/Slot in a different dialog. None of the rows are highlighted. I don't see where the information gets passed to the proxy and in debug it never gets there. I think putting the highlightCell code in the proxy model will be a good thing, but I want to figure it out first before I start changing a bunch of the code. Do I need to call the data function in my proxy?



  • Hi poporacer,

    this MyProxyModel::data function MUST be const. It's not an option. You created a second function which will never be called, as the const one is called.



  • That is an easily made mistake, by the way. Always check the signature of the function if you think it doesn't get called.

    Then, in the slot in the proxy where you receive the rows to highlight, what you are actually doing is changing data in the model. You need to tell the world about such a change, by emitting the void dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight ) signal (defined in QAbstractItemModel). This way, the connected views know they need to update themselves.

    When you just hardcode the rows to highlight (for now), do you get a highlight then?



  • Ok, I changed the const reference. Now I get a row highlighted, but it is not the row with the lowest number.
    Gerolf, I am pretty sure this is because of the problem you identified
    " I see one problem in your code:
    you calculate highest / lowest value based on the proxy row,but in data ypou comparte to source row."
    I can't figure out how to use the proxy role in the MainClass or the source Role in the data function.
    I did get the following error:
    QSortFilterProxyModel: index from wrong model passed to mapToSource
    I just got home so I will take a look at these two errors. Does anything stick out to you?
    What triggers this proxy to be called?
    I think it would be a cleaner method to have the high and low highlighted in the proxy class. How might start this?



  • As you calculate high and low value based on the proxy index, you just should compare to the index without mapping it, like in the below code snippet.

    @
    QVariant MyProxyModel::data ( const QModelIndex & index, int role ) const
    {
    if (!index.isValid())
    return QVariant();
    if ( index.row() == m_highRow && role == Qt::BackgroundRole )
    {
    return QVariant( Qt::yellow );
    }
    else
    {
    return QSortFilterProxyModel::data( index, role );
    }
    }
    @

    you never need to call data directly for displaying data, that is done by the views.

    please step through your code to check, where the mapToSource throws the problem.

    I've also rewritten your sales calculation a bit, just to show a bit some things:
    ui->printView->model() ==> proxy

    @
    void MainClass::highlightCells()
    {
    float lowSales=FLT_MAX;
    float highSales=FLT_MIN;
    int rowHigh=0;
    int rowLow=0;
    int totalRows = 0;
    float currentSales;

    // this should be done only once! --&gt; move it to the place where you create the proxy
    connect(this, SIGNAL(sendRows(int, int)), proxy, SLOT(getRows(int, int))); //to pass the rows to be highlighted to the proxy
    
    //get fastest and slowest sales and save row
    int numRows = proxy->rowCount();
    
    for (int r = 0; r < numRows; r++ )
    {
        if (ui->printView->isRowHidden(r)) // you hide rows from the view ???
            continue;
            
        currentSales = proxy->index(r,5).data(Qt::DisplayRole).toFloat();
        if (currentSales < lowSales)
        {
            lowSales = currentSales;
            rowLow = r;
        }
        
        if (currentSales > highSales)
        {
            highSales = currentSales;
            rowHigh = r;
        }
    }
    
    emit sendRows (rowSlow, rowFast);
    

    }
    @



  • THANKS! I think I am starting to understand the model/view/delegate concept (very slightly!!!) I will continue to read about it so I can understand it better. I haven't been real successful in my search to get the answers on my own. I have just a couple of questions that I hope you would be so kind to answer. From the information I have gathered (and your insights) This is how I think this works. The Model is an interface between the data and "attaches" roles (background, foreground, font, etc) to the raw data. This will keep the roles persistent within the model so that any view will display the data appropriately? The view merely displays that data from the model with a few options available to the view (sort, column/row hidden, etc) Any changes to either the model or view will generate a signal to the other item so that they can update their data accordingly. You can have more than one model between the data and view for additional functionality.
    In my case the code that we have works like this? (I could be totally off course here) When the Model (in my case the sub-classed QSortFilterProxyModel) is getting the data from the base model (it steps through each item as it is added) and it checks the state of several items (valid Index, current role) then sets the role requested and returns the role variable back the the view so that it could be set appropriately? So with this, you can set any of the roles in ItemDataRole through this method? The index passed to the data function of the QSFPM is the index of the base model? And the MaptoSource function gets the index of the proxymodel? That is why you shouldn't use the model index as a persistent item as it is fluid and can change if any changes to the models or view have been made? (this might help other people to understand the concept as well)



  • [quote author="poporacer" date="1302473664"]THANKS! I think I am starting to understand the model/view/delegate concept (very slightly!!!) I will continue to read about it so I can understand it better. I haven't been real successful in my search to get the answers on my own. I have just a couple of questions that I hope you would be so kind to answer. From the information I have gathered (and your insights) This is how I think this works. The Model is an interface between the data and "attaches" roles (background, foreground, font, etc) to the raw data. This will keep the roles persistent within the model so that any view will display the data appropriately? [/quote]

    This is correct.

    [quote author="poporacer" date="1302473664"]The view merely displays that data from the model with a few options available to the view (sort, column/row hidden, etc) Any changes to either the model or view will generate a signal to the other item so that they can update their data accordingly.[/quote]

    This needs a bit correction:

    • Data changes in the model will be signaled, so all connected views update themselves.
    • Data changes done in a view are handled by a delegate which usually calls setData in the model (custommodels and custom delegates might differ here). The model then signals all connected views about the changes.

    [quote author="poporacer" date="1302473664"]You can have more than one model between the data and view for additional functionality.[/quote]

    I would rephrase this:

    • A view is connected to one model
    • A model can be a proxy model to add functionality, filtering etc.
    • Proxy models may contain models or proxy models, which means it is possible to cascade proxy models.

    [quote author="poporacer" date="1302473664"]In my case the code that we have works like this? (I could be totally off course here) When the Model (in my case the sub-classed QSortFilterProxyModel) is getting the data from the base model (it steps through each item as it is added) and it checks the state of several items (valid Index, current role) then sets the role requested and returns the role variable back the the view so that it could be set appropriately? [/quote]

    I'm not sure, whether I did not understand what you wrote or you had some misunderstanding. I try to say how it is normally:

    The view requests data based on a role and an index. If you have a proxy model, it converts the index to the source index and requests the data in the source model and returns it. In your case, you have some additional data in the proxy model. If the view requests this data, it is directly returned from the proxy model.

    With setData it is the same. The view directly writes to your proxy model and the proxy decides, whether the data is needed inside your proxy model (only if you have a derived proxy model) otherwise the index is converted and the source model setData is called.

    [quote author="poporacer" date="1302473664"]So with this, you can set any of the roles in ItemDataRole through this method? The index passed to the data function of the QSFPM is the index of the base model?[/quote]

    No, this is wrong. The view does not handle the source model indexes. From views point of view, there is only one model. It never checks, whether it is a proxy or not. You always get QSFPM indexes. If you then call the source model from QSFPM you have to call mapToSource yourself.

    [quote author="poporacer" date="1302473664"]And the MaptoSource function gets the index of the proxymodel? [/quote]

    Yes.

    [quote author="poporacer" date="1302473664"]That is why you shouldn't use the model index as a persistent item as it is fluid and can change if any changes to the models or view have been made? (this might help other people to understand the concept as well)[/quote]

    Incorrect. You can use QPersistentModelIndex for storing model indexes. The proxy models (and also source models) catch a list of persitant indexes and update them due to changes. A persitant index might become invalid later on, if a QSFPM filters out the element.

    Only QPersistentModelIndex may be stored inside your class for later usage.



  • Well, I thought I had it figured out...then I sorted the table and the row that was highlighted in the view was still the same row that was highlighted before the sort. Not the correct row. Is there some signal I should be emitting or somehow capturing the sort routine? I looked at the QSortFilterProxyModel class and I didn't see any slots that would work. I thought the model retained the info for the data roles and the view would signal the changes and update the view. What am I missing?



  • My guess is, that you are now trying to let your proxy model do both the highlighting and the sorting, right? While that is possible, it complicates things a bit. You are now probably highlighting based on the row number for the requested index your proxy's data method, right? Well, sorting obviously changes the order of the rows, so those row numbers are not going to be valid anymore. You might, instead of triggering your highlight based on the requested row number, trigger it based on the row number of the source model that the row in the proxy model represents. For instance.

    However, what I would do, is just keep the proxy doing one thing. Simply don't use the sorting facilities of your proxy model (or better yet, disable them), and use a separate proxy for the sorting and the filtering. So you end up with:

    [Source model] -> [Highlight proxy] -> [Sort/filter proxy] -> [View]

    Downside is that a highlighted row might be filtered out this way. I guess if you want to filter too, you might even have to go for:

    [Source model] -> [Filter proxy] -> [Highlight proxy] -> [Sort proxy] -> [View]



  • Hi,

    I would go ab it another way:

    [Source model] -> [Filter proxy] -> [Sort proxy] -> [Highlight proxy] -> [View]

    if you sort the base model of the highlight model, the row indexes also change. This way it does not. :-)



  • But the problem with this order is, that the view by default will change the sort property on the model that it is displaying. In this case, that would be the Highlight proxy, while you want it to be the sort proxy. If you are going to have the filter and the sort proxies consecutive, you might as well use a single proxy to do the filtering and the sorting.

    If the base model of the highlight proxy changes, it needs to re-query for the correct rows to highlight anyway. It would be the same if the base SQL model would be refreshed, for instance.



  • Hello,
    I want to color a cell on a SqlRelationalTableModel.
    The condition is, if the value of the cell is 1, I want it blue otherwise it should be red.
    After reading the whole topic, I'm very confused. Could someone please help me out?
    For example, how to start etc.
    Is it the same way like poporacer did? "Just" inherit from QSortFilterProxyModel ?

    Thanks in advice



  • Yes. The same method is applicable. Sub-class QSFPM and set it as a proxy between your source model and the view.



  • Indeed, same idea, thought the implementation is obviously simpler. It is easier to determine value == 1 than it is to determine value == maximumValue, of course.



  • [quote author="Andre" date="1302589505"]My guess is, that you are now trying to let your proxy model do both the highlighting and the sorting, right? While that is possible, it complicates things a bit. You are now probably highlighting based on the row number for the requested index your proxy's data method, right? [/quote]

    I guess that is correct because I am using the proxy model. I don't implement anything else in the proxy except for the highlight part (data function)

    [quote author="Andre" date="1302589505"]Well, sorting obviously changes the order of the rows, so those row numbers are not going to be valid anymore. You might, instead of triggering your highlight based on the requested row number, trigger it based on the row number of the source model that the row in the proxy model represents. [/quote]

    This seems like a clean method that would probably work. How would I do that?

    [quote author="Andre" date="1302589505"] Simply don't use the sorting facilities of your proxy model (or better yet, disable them), and use a separate proxy for the sorting and the filtering. [/quote]

    Since I only implement the data function in my proxy, I am not sure how to disable the sorting function in one proxy and implement it in another.

    [quote author="Andre" date="1302589505"] So you end up with: [Source model] -> [Highlight proxy] -> [Sort/filter proxy] -> [View] Downside is that a highlighted row might be filtered out this way. I guess if you want to filter too, you might even have to go for: [Source model] -> [Filter proxy] -> [Highlight proxy] -> [Sort proxy] -> [View] [/quote]

    So to implement this it would be something like this?
    @printModel= new QSqlRelationalTableModel (this);
    printModel-> setTable (mTableName); //or should this be called from sortProxy?
    printModel->setFilter(mFilterString);//or should this be called from sortProxy?

    filterproxy->setSourceModel(printModel);
    proxy->setSourceModel(filterProxy);
    sortProxy->setSourceModel(proxy);

    ui->printView->setModel(sortProxy);

    @

    If the highlighted row is part of the view, can you identify the row and highlight the correct row via a setData function in the view and then capture the sort routine and then re-iterate through the view and make the appropriate color change? It seems like this is more difficult than it should be just to highlight an identified row. but if that is the way to do it, then I want to learn and understand how.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.