How can I change QTableView's color of the area that doesn't have cells on it?



  • How can be changed QTableView's color of the area that doesn't have cells on it?

    Thanks in advance!



  • Could you be more precise ? What do you want to change exactly ? The header's color ?



  • Set the background color. You can do that either with setting the palette, or by using a style sheet.



  • This snippet does the trick:

    @
    QTableWidget tw;
    QPalette p = tw.palette();
    p.setColor(QPalette::Base, QColor("yellow"));
    tw.setPalette(p);
    @

    Or set the respective color in the palette property in Qt Designer, if you're using .ui files.



  • Volker, I've tried like that but it also does changing the cells color.



  • [quote author="Edico" date="1314534038"]Volker, I've tried like that but it also does changing the cells color.[/quote]

    That's correct. The background color is behind the cells and behind the widget :-(
    What you could do is use a style sheet and set the background color of the table there and another background color for the cells.



  • I didn't made it even with style sheets. I've reached doing something like that:

    @
    QTableView {
    alternate-background-color: blue;
    background: red
    }

    QTableView::item:selected {
    background-color: green
    }

    QTableView::item {
    background-color: white
    }
    @

    All items are white with no alternate row colors.



  • Are you using some kind of special, non-standard style?



  • Andre, no, I don't think so. With the above code I see al items white and the area that doesn't have cells on is red, but I see no alternating row colors (one white - one blue).



  • I can reproduce this problem here and it looks like a bug. When setting the background-color for the item in addition to the background -color and the alternate-background-color for the table, then the alternate-background-color will be ignored. I suggest you report this issue in "Jira":https://bugreports.qt.nokia.com//secure/Dashboard.jspa.



  • if you set the color for cells in general and also alternate background color, then the cell wins, that makes sense. and where else will you see the alternate background color?



  • Is there a way to have a color for the outside area of the cells, another for cells and other color for alternate background color?



  • you will not have alternate color if you set cells color via style sheet. alternate color is a cell color.



  • So, the only way is to subclass QSqlRelationalTableModel (the model I use to access data)? That looks like a big pain.



  • if you want alternate row colors and a different cell background then empty background? I think yes. Or use a proxy model instead of a subclass, that's much less work.



  • I can't figure how to use a proxy model for this job.



  • if you use a proxy model, the proxy models data method is called for each data query instead of the methods of the original model. This then calls the original (source) model. So you can do everything you want there, like changing backroundColor via the Qt::BackgroundRole. IMHO ProxyModels are easier then derived SQL models ;-)



  • Gerolf, this mean that I must subclass QSortFilterProxyModel and reimplement data() method?



  • Yes, you can create your custom ProxyModel by sublcassing QSortFilterProxyModel and reimplementing the data() method. From there, you can do everything you want, like Gerolf said.

    Finally, you just set the sourceModel of your ProxyModel, and it should work :)



  • Yes. If you were using 4.8, I would suggest you use -QIdentityModel- [[doc:QIdentityProxyModel]] as your base class, but if you are still on 4.7, use [[doc:QSortFilterProxyModel]] instead. You only need to reimplement the data() method. In that method, you only need to implement the case where the Qt::BackgroundRole is requested. Based on the row number (odd or even), you can return a different color. For all other cases, you get the parent QModelIndex by calling mapToSource() on the model index, and then return the result of calling the data() method on the source model with the same role and the model index you just mapped.



  • bq. For all other cases, you get the parent QModelIndex by calling mapToSource() on the model index, and then return the result of calling the data() method on the source model with the same role and the model index you just mapped.

    Or just calling the base class method ? Like :

    @
    QVariant MyProxyModel::data(const QModelIndex &index, int role)
    {
    if (role == Qt::BackgroundRole)
    {
    return ...
    }

    return QSortFilterProxyModel::data(index, role);
    

    @

    bq. If you were using 4.8, I would suggest you use QIdentityModel as your base class

    I made a research and I guess you meant "QIdentityProxyModel":http://doc.trolltech.com/4.8-snapshot/qidentityproxymodel.html

    Interesting :)



  • Yes, I guess you can also call the base class implementation. That does the same:

    @
    Q_D(const QSortFilterProxyModel);
    QModelIndex source_index = mapToSource(index);
    if (index.isValid() && !source_index.isValid())
    return QVariant();
    return d->model->data(source_index, role);
    @

    I guess it does not matter much.



  • I've set the outside area of the cells like that:

    @
    QPalette p = view->palette();
    QColor color = p.color(QPalette::Window);
    p.setColor(QPalette::Base, color);
    view->setPalette(p);
    @

    Then data() in MyProxyModel:

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

    int row = index.row();
    
    QWidget widget;  // I didn't found a wiser method to obtain the palette colors than creating a QWidget inside the data() method
    QColor alternateColor1 = widget.palette().color(QPalette::AlternateBase);
    QColor alternateColor2 = widget.palette().color(QPalette::Base);
    
    if (role == Qt::BackgroundRole) {
        QBrush rowBackground;
        if (row % 2 == 0)
            rowBackground = QBrush(alternateColor1);
        else if (row % 2 != 0)
            rowBackground = QBrush(alternateColor2);
        return rowBackground;
    }
    return QSortFilterProxyModel::data(index, role);
    

    }
    @

    It does what I wanted.
    Thank you all for the help.



  • The data method is called very frequently. It is not a good idea to create a widget in this method. Instead, why not just create a pair of getters and setter methods for the colors on the proxy model. Set them once when you create the proxy, and use them in your data method. That will be much more performant.



  • I know it's a little bit outside the subject but, how is much better:
    to have setters like this:

    @
    void MyProxyModel::setAlternateColor1()
    {
    QWidget widget;
    alternateColor1 = widget.palette().color(QPalette::Base);
    }

    void MyProxyModel::setAlternateColor2()
    {
    QWidget widget;
    alternateColor2 = widget.palette().color(QPalette::AlternateBase);
    }
    @

    and data() calling the getters,
    or setting the colors in the constructor like this:

    @
    MyProxyModel::MyProxyModel()
    {
    QWidget widget;
    alternateColor1 = widget.palette().color(QPalette::Base);
    alternateColor2 = widget.palette().color(QPalette::AlternateBase);
    }
    @

    and data using directly QColor alternateColor1 and QColor alternateColor2?



  • I think you misunterstood what Andre meant.

    bq. Instead, why not just create a pair of getters and setter methods for the colors on the proxy model.

    A pair of getters/setters would be :

    @
    void MyProxyModel::setAlternateColor1(const QColor &color)
    {
    mAlternateColor1 = color;
    }

    QColor MyProxyModel::alternateColor1() const
    {
    return mAlternateColor1;
    }
    @



  • Octal:
    Indeed, that's what I meant, and also a getter and a setter for the primary color. You can set them at initialization of your proxy model, which is very likely to be in the constructor of a widget class. Hence, you have cheap access to a widget pointer and the palette there, and you only need that access once.



  • I've created setters and getters. Now I use the proxy like that:

    @
    proxyModel = new MyProxyModel;
    QColor altColor1 = this->palette().color(QPalette::Base);
    QColor altColor2 = this->palette().color(QPalette::AlternateBase);
    proxyModel->setAlternateColor1(altColor1);
    proxyModel->setAlternateColor2(altColor2);
    proxyModel->setSourceModel(model);
    @

    proxyModel's data() method calls the getters.
    Is it better like that?

    Andre, what do you mean with primary color?



  • As long as you use the colors you set there in your data() method: much better :-)

    One small tip: you will want to give your MyProxyModel object a parent object. Seeing that this proxy is really doing presentation work, I suggest you use the view that it is for as the parent object. Also: a more descriptive name than "MyProxyModel" would probably work better in the long run.



  • I've called it PresentationProxyModel :-)
    Thanks a lot for the help.



  • The presentation details should normally be handled by the delegates, for example:
    @class AlternateBackgroundColorDelegate : public QStyledItemDelegate {
    QColor colors[2];
    public:
    AlternateBackgroundColorDelegate(const QColor & color1,
    const QColor & color2,
    QObject *parent = 0)
    : QStyledItemDelegate(parent)
    {
    colors[0] = color1;
    colors[1] = color2;
    }
    void initStyleOption(QStyleOptionViewItem *option,
    const QModelIndex &index) const
    {
    QStyledItemDelegate::initStyleOption(option, index);

        QStyleOptionViewItemV4 *optionV4 =
                qstyleoption_cast<QStyleOptionViewItemV4*>(option);
        // If the brush hasn't explicitly been set in the model
        if (optionV4->backgroundBrush.style() == Qt::NoBrush)
            optionV4->backgroundBrush = QBrush(colors[index.row()%2]);
    }
    

    };

    // And you use it like this:
    tableView->setItemDelegate(
    new AlternateBackgroundColorDelegate(Qt::blue, Qt::red, tableView));
    @
    Or using a different palette for the items and the view:
    @class ChangePaletteDelegate : public QStyledItemDelegate {
    QPalette palette;
    public:
    ChangePaletteDelegate(const QPalette &palette,
    QObject *parent = 0)
    : QStyledItemDelegate(parent), palette(palette)
    {
    }
    void initStyleOption(QStyleOptionViewItem *option,
    const QModelIndex &index) const
    {
    QStyledItemDelegate::initStyleOption(option, index);
    option.palette = palette;
    // Edit: the palette isn't used for background color if QPalette::Base is set
    QStyleOptionViewItemV4 optionV4 =
    qstyleoption_cast<QStyleOptionViewItemV4
    >(option);
    // If the brush has explicitly been set in the model, do nothing
    if (optionV4->backgroundBrush.style() != Qt::NoBrush)
    return;

        if(optionV4->features & oQStyleOptionViewItemV2::Alternate) {
            optionV4->backgroundBrush = palette.brush(QPalette::AlternateBase);
        } else {
            optionV4->backgroundBrush = palette.brush(QPalette::Base);
        }
    }
    

    };

    // And you use it like this:
    QPalette palette = tableView->palette();
    // Use the original palette here, for the items
    tableView->setItemDelegate(new ChangePaletteDelegate(palette, tableView));
    // and change the background for the view
    palette.setColor(QPalette::Base, Qt::yellow);
    tableView->setPalette(palette);

    @

    Edit Fixed ChangePaletteDelegate.



  • alexisdm, l like your way for solving this problem.
    The palette method doesn't work for me.



  • The palette method only works if you don't use stylesheets at the same time, I think.



  • I don't use stylesheets. With ChangePaletteDelegate I have the cells and the outside area of the cells yellow (other color for the alternating row color);



  • Sorry, I fixed it. It was only working if the QPalette::Base wasn't set explicitly.
    The alternate row background is painted directly by the view, and not by the delegate as I thought.
    So in both methods, QStyleOptionViewItemV4::backgroundBrush must be used to change the background color.


Log in to reply
 

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