QTableWidget, different selection colors for the different cells - qt

I'd like to have the different selection colors for the different cells of the QTableWidget.
To change selection color for the whole table I can use
QTableWidget* table = new QTableWidget;
table->setStyleSheet("QTableWidget::item{selection-background-color:#ff0000;}");
To set the usual background color of the single cells it's possible to write in the following way:
table->setItem(row, column, new QTableWidgetItem(""));
table->item(row, column)->setBackgroundColor(QColor(255,255,0));
But I couldn't find any info about the different selection colors for the different cells.
Please, help!

it was a very interesting question for me and I wrote an example))
I use the delegate to resolve this issue
class MyDelegate : public QItemDelegate
{
public:
MyDelegate( QObject *parent ) : QItemDelegate( parent ) { }
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
};
void MyDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
QItemDelegate::paint( painter, option, index );
QColor background = QColor(rand()%255, rand()%255, rand()%255);
QColor background2 = QColor(255, 255, 255);
painter->fillRect(option.rect, background);
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, background);
} else {
painter->fillRect(option.rect, background2);
}
}
how will it work? with each selection, it will generate a new color for selected item
delegate integration
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableWidget* m_pTableWidget = new QTableWidget();
m_pTableWidget->setRowCount(10);
m_pTableWidget->setColumnCount(10);
for (int i = 0; i < m_pTableWidget->rowCount(); ++i) {
m_pTableWidget->setItemDelegateForRow(i, new MyDelegate(m_pTableWidget));
}
m_pTableWidget->show();
return a.exec();
}
results
you can change the code and give a specific color for certain item .

Related

Resizing a QTableView column horizontally to the content of an item delegated column which is painted with a new text in Qt

I want to show my database tabel content in a QTableView. I use the following codes to do that:
QSqlDatabase test = QSqlDatabase::addDatabase("QMYSQL");
test.setDatabaseName("dbText");
test.setHostName("localhost");
test.setUserName("***");
test.setPassword("***");
if (!test.open())
qDebug()<<"ERROR ";
QSqlTableModel *model = new QSqlTableModel(this,test);
model->setTable("textTable");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableView->setModel(model);
ui->tableView->show();
As it is depicted in the following picture, ui->tableView columns are resized to the content of the database table columns.
Now, I want to clear the display text of Description column and paint it with new text and color. For this propose, I have used the function QTableView::setItemDelegateForColumn as follow:
ui->tableView->setItemDelegateForColumn(2,new PowerColorDelegate(this));
And here are PowerColorDelegate header and source file content:
PowerColorDelegate.h
#include <QStyledItemDelegate>
class PowerColorDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
PowerColorDelegate(QObject *parent = 0);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QString displayText(const QVariant &value, const QLocale &locale) const;
};
PowerColorDelegate.cpp
#include <QApplication>
#include <QPainter>
#include "powercolordelegate.h"
PowerColorDelegate::PowerColorDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
void PowerColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
if(!index.isValid())
{
QStyledItemDelegate::paint(painter, option, index);
return;
}
// Position our pixmap
const int x = option.rect.left();
const int y = option.rect.top();
QString newColor = "#fcaf9e";
QString newText = "This is my new text which I want to paint!";
painter->fillRect(option.rect, newColor);
painter->drawText(QRect(x, y, 80, 20), newText);
QStyledItemDelegate::paint(painter, opt, index);
}
QString PowerColorDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
return "";
}
The result of using PowerColorDelegate on ui->tableView is shown in this picture:
How can I resize the third ui->tableView column (Description) horizontally to the content of the painted column?
Implement sizeHint for your delegate according to required text format

How to remove QTreeView indentation

I want to have a QTreeView without an indentation on the left side increasing at each nesting level. I tried setting QTreeView::setIndentation(0). It removes the indentations just as I want, however it also hides the tree arrows.
Default behavior:
With indentations ✗
With arrows ✔
After setIndentation(0):
Without indentations ✔
Without arrows ✗
Desired behavior:
Without indentations ✔
With arrows ✔
So how can I achieve the result shown in the third example? Is there any standard way of doing it, or I will have to reimplement the QTreeView::paintEvent(), QTreeView::drawBranches(), etc.?
To solve the problem I used a delegate to translate the paint of the items, and paint the arrows.
#include <QtWidgets>
class BranchDelegate: public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override{
QStyleOptionViewItem opt(option);
if(index.column() == 0)
opt.rect.adjust(opt.rect.height(), 0, 0, 0);
QStyledItemDelegate::paint(painter, opt, index);
if(index.column() == 0){
QStyleOptionViewItem branch;
branch.rect = QRect(0, opt.rect.y(), opt.rect.height(), opt.rect.height());
branch.state = option.state;
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_IndicatorBranch, &branch, painter, widget);
}
}
};
class TreeView: public QTreeView
{
public:
TreeView(QWidget *parent=nullptr):QTreeView(parent)
{
BranchDelegate *delegate = new BranchDelegate(this);
setItemDelegate(delegate);
setIndentation(0);
}
protected:
void mousePressEvent(QMouseEvent *event) override{
QModelIndex index = indexAt(event->pos());
bool last_state = isExpanded(index);
QTreeView::mousePressEvent(event);
if(index.isValid() && last_state == isExpanded(index))
setExpanded(index, !last_state);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TreeView w;
QFileSystemModel model;
model.setRootPath(QDir::rootPath());
w.setModel(&model);
w.setRootIndex(model.index(QDir::homePath()));
/*for (int i = 1; i< model.columnCount() ; ++i) {
w.hideColumn(i);
}*/
w.expandAll();
w.resize(640, 480);
w.show();
return a.exec();
}
eyllanesc's falls appart if there is horizontal scrolling. Also usually the view only expands/collapses when clicking the branch-indicator, not the index.
My Solution: Only change the Rect of indices which have a parent but no children. Also dont set the indentation to 0. No need to subclass QTreeView.
#include <QtWidgets>
class BranchDelegate: public QStyledItemDelegate
{
public:
mIndent = 50;
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt(option);
if(index.parent().isValid && (!index.model() || !index.model()->index(0, 0, index).isValid()))
{
opt.rect.adjust(-mIndent, 0, 0, 0);
}
QStyledItemDelegate::paint(painter, opt, index);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView apView;
BranchDelegate* const apDelegate = new BranchDelegate(apView);
apDelegate->mIndent = 50;
apView->setIndentation(apDelegate->mIndent);
apView->setItemDelegateForColumn(0, apDelegate);
QFileSystemModel model;
model.setRootPath(QDir::rootPath());
apView.setModel(&model);
apView.setRootIndex(model.index(QDir::homePath()));
/*for (int i = 1; i< model.columnCount() ; ++i) {
apView.hideColumn(i);
}*/
apView.expandAll();
apView.resize(640, 480);
apView.show();
return a.exec();
}
ellyanesc's answer works, but is incorrect by one small detail in the line:
branch.rect = QRect(0, opt.rect.y(), opt.rect.height(), opt.rect.height());
The reason is because when the view is horizontally scrolled, option.rect.x() becomes negative. If branch.rect.x() is 0 (as in ellyanesc's answer), the branch indicator will always be shown, which also causes artifacts during scroll:
https://i.stack.imgur.com/O518A.png
https://i.stack.imgur.com/qYJMj.png
To solve this, replace the above line with:
branch.rect = QRect(option.rect.x(), opt.rect.y(), opt.rect.height(), opt.rect.height());
(I would have just pointed that out as a comment in ellyanesc's answer, but I don't have enough reputation for that.)

qstyleditemdelegate subclassing paint method not working right

I extended the qstyleditemview class. When I am in the editing mode for the qtreeview item, the paint method seems not to be executing right. When I change state to QStyle::State_Selected it works - it paints the selected row (text) in the qtreeview.
Any idea why it is not working in editing mode?
void myQItemDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option,const QModelIndex &index) const
{
QStyleOptionViewItem s = *qstyleoption_cast<const QStyleOptionViewItem*>(&option);
if(s.state & QStyle::State_Editing)
{
painter->fillRect(s.rect, s.palette.highlight());
s.palette.setColor(QPalette::HighlightedText, QColor(Qt::blue));
}
QStyledItemDelegate::paint(painter, s, index);
}
In the State_Editing state the editor that is a widget created in createEditor() method is opened so that it will not be affected by the QStyleOptionViewItem palette.
Also instead of overwriting the paint method, use the initStyleOption() method:
#include <QtWidgets>
class StyledItemDelegate: public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
QWidget * widget = QStyledItemDelegate::createEditor(parent, option, index);
QPalette pal(widget->palette());
pal.setColor(QPalette::HighlightedText, QColor(Qt::blue));
pal.setBrush(QPalette::Highlight, option.palette.highlight());
widget->setPalette(pal);
return widget;
}
protected:
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{
QStyledItemDelegate::initStyleOption(option, index);
option->palette.setColor(QPalette::HighlightedText, QColor(Qt::blue));
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView w;
QStandardItemModel model;
w.setModel(&model);
w.setItemDelegate(new StyledItemDelegate);
for(int i=0; i<3; ++i){
auto it = new QStandardItem(QString::number(i));
model.appendRow(it);
for (int j=0; j<4; ++j) {
it->appendRow(new QStandardItem(QString("%1-%2").arg(i).arg(j)));
}
}
w.expandAll();
w.show();
return a.exec();
}
Thanks for helping me out.
I understand now. I added code in QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const metod to style QLineEdit. I am trying to get the same background color for QLineEdit when it is created- when I am editing qtreeview item. The problem is when I select the item in qtreeview the whole row gets colored. That's ok. Now when I edit the item for example to change the text in qtreeview item, only the text part gets selected and colored with the same color as previous row selection color. The rest of the QLineEdit is white. In editing mode I would like to color the whole row which is edited with the same color. I could as obviously from my code color it with RGB but I don't know the exact RGB values. Is there any way to get the exact RGB color from item selection and then use it in
pal.setColor(QPalette::Highlight,QColor(qRgb(0,0,255)));
my code:
QWidget* myQItemDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option,const QModelIndex &index) const
{
QLineEdit *lineEdit = new QLineEdit(parent);
connect(lineEdit,SIGNAL(editingFinished()),this,SLOT(commitAndCloseEditor()));
QPalette pal;
pal.setColor(QPalette::HighlightedText, QColor(Qt::white));
pal.setColor(QPalette::Highlight,QColor(qRgb(0,0,255)));
lineEdit->setPalette(pal);
lineEdit->setFrame(false);
return lineEdit;
}
Thanks, Tom

Qt5 Subclassing QStyledItemDelegate Formatting

I am developing a GUI for a SQLite database in Qt5. I use QSqlQueryModel and QTableView for storing and displaying the data.
I then created a custom delegate to replace the numeric values of certain columns with their literals in the table view (e.g. 1 = "Hello", 2 = "World") using a switch statement.
The delegate displays the data as it should and works functionally. However, the columns that the custom delegate paints over have a different format compared to the default paint method of QStyledItemDelegate. The values are up in the top left rather than centre left, the altered column no longer automatically expands the column to display the full values, and the cells in column do not turn blue or have the dotted outline when selected.
I created this example program:
#include <QApplication>
#include <QModelIndex>
#include <QPainter>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QTableView>
class TestDelegate: public QStyledItemDelegate {
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
const Q_DECL_OVERRIDE
{
if (index.column() == 0) {
int value = index.model()->data(index, Qt::DisplayRole).toInt();
QString str;
switch (value) {
case 1:
str = "Hello0000";
break;
case 2:
str = "World0000";
break;
}
if (option.state.testFlag (QStyle::State_Selected)) {
painter->fillRect(option.rect, option.palette.highlight());
qApp->style()->drawItemText(painter, option.rect, option.displayAlignment, option.palette, true, str, QPalette::HighlightedText);
} else {
painter->drawText(option.rect, option.displayAlignment, str);
}
} else {
return QStyledItemDelegate::paint(painter, option, index);
}
}
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
QStandardItemModel model(2, 2);
model.setHorizontalHeaderItem(0, new QStandardItem(QString("A")));
model.setHorizontalHeaderItem(1, new QStandardItem(QString("B")));
model.setData(model.index(0, 0, QModelIndex()), 1);
model.setData(model.index(1, 0, QModelIndex()), 2);
model.setItem(0, 1, new QStandardItem(QString("Hello")));
model.setItem(1, 1, new QStandardItem(QString("World0000")));
QTableView view;
view.setItemDelegate(new TestDelegate);
view.setModel(&model);
view.resizeColumnsToContents();
view.show();
app.exec();
}
This fixes the text alignment by adding options.displayAlignment to painter->drawText(); I have also added additional code in the if(option.state & QStyle::State_Selected) statement that paints the cell according to its selection state. So if it isn't selected the text is black, if it is the text turns white and the background blue. However I still cannot get the columns to expand to fit the cells' content or add a dotted line around the outside of the cell as it does with the standard delegate.
Is there a simple way to maintain the default style of the table view when using my custom paint method?
The delegate is a rather circuitous and unnecessary way of going about it. We already have a view that paints the elements perfectly fine, no need to redo that. We only need to pass modified data to the view. Thus we insert a QIdentityProxyModel viewmodel between the source and the view.
// https://github.com/KubaO/stackoverflown/tree/master/questions/proxy-reformat-39244309
#include <QtWidgets>
class RewriteProxy : public QIdentityProxyModel {
QMap<QVariant, QVariant> m_read, m_write;
int m_column;
public:
RewriteProxy(int column, QObject * parent = nullptr) :
QIdentityProxyModel{parent}, m_column{column} {}
void addReadMapping(const QVariant & from, const QVariant & to) {
m_read.insert(from, to);
m_write.insert(to, from);
}
QVariant data(const QModelIndex & index, int role) const override {
auto val = QIdentityProxyModel::data(index, role);
if (index.column() != m_column) return val;
auto it = m_read.find(val);
return it != m_read.end() ? it.value() : val;
}
bool setData(const QModelIndex & index, const QVariant & value, int role) override {
auto val = value;
if (index.column() == m_column) {
auto it = m_write.find(value);
if (it != m_write.end()) val = it.value();
}
return QIdentityProxyModel::setData(index, val, role);
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QStandardItemModel model{2,2};
model.setData(model.index(0, 0), 1);
model.setData(model.index(1, 0), 2);
model.setData(model.index(0, 1), "Zaphod");
model.setData(model.index(1, 1), "Beeblebrox");
RewriteProxy proxy{0};
proxy.setSourceModel(&model);
proxy.addReadMapping(1, "Hello");
proxy.addReadMapping(2, "World");
QTableView ui;
ui.setModel(&proxy);
ui.show();
return app.exec();
}

Painting QPixmap in the center of QTableView cell

I have a QTableView that works very well, the first column holds some thumbnails, in each cell of this column the thumbnails are vertically centered, but not horizontally centered.
Do I really need to use a delegate?
If yes, How to center them horizontally using QStyledItemDelegate?
Construct your own delegate and inherit QStyledItemDelegate. Override the paint method.
Then do something like this:
void
MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
QPixmap pixmap;
pixmap.load("Your pixmap file path");
pixmap = pixmap.scaled(option.rect.width(), option.rect.height(), Qt::KeepAspectRatio);
// Position our pixmap
const int x = option.rect.center().x() - pixmap.rect().width() / 2;
const int y = option.rect.center().y() - pixmap.rect().height() / 2;
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
painter->drawPixmap(QRect(x, y, pixmap.rect().width(), pixmap.rect().height()), pixmap);
}
Drawing by yourself is not necessary, but a custom delegate - is. The styled item delegate uses the style's control element drawing code to draw a CE_ItemViewItem - see the source code for Qt 5.5.0. The drawing code takes the style option's decorationAlignment member into account. Unfortunately, there's no data role that would pass that alignment to the styles's implementation. Instead, you have to override the alignment in your delegate:
class DecorationAligningDelegate : public QStyledItemDelegate {
Q_OBJECT
Qt::Alignment const m_alignment;
public:
explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject * parent = 0) :
QStyledItemDelegate(parent), m_alignment(alignment) {}
Qt::Alignment alignment() const { return m_alignment; }
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
auto opt = option;
opt.decorationAlignment = m_alignment;
QStyledItemDelegate::paint(painter, opt, index);
}
};
Then, to center the thumbnails:
view.setItemDelegateForColumn(0,
new DecorationAligningDelegate(Qt::AlignHCenter, &view));
//or
view->setItemDelegateForColumn(0,
new DecorationAligningDelegate(Qt::AlignHCenter, view));
If you really wished to paint it all yourself, even though it's unnecessary, the rectangle of the item to be painted is given in the style option (option.rect). To draw the pixmap centered in the item's rectangle, you could do as follows:
QStyleOption option;
QPixmap pix;
QPainter painter;
...
painter.save();
auto loc = option.rect.center() - pix.rect().center()
painter.drawPixmap(loc, pix);
painter.restore();
I will just leave my version that literary is a combination of the two answers.
class DecorationAligningDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject *parent = nullptr)
: QStyledItemDelegate(parent), m_alignment(alignment) {}
Qt::Alignment alignment() const { return m_alignment; }
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QIcon icon = QIcon(qvariant_cast<QIcon>(index.data(Qt::DecorationRole)));
if (option.state & QStyle::State_Selected)
{
painter->fillRect(option.rect, option.palette.highlight());
}
icon.paint(painter, option.rect, m_alignment);
}
private:
Q_DISABLE_COPY(VDecorationAligningDelegate)
Qt::Alignment const m_alignment;
};
I assume you define your item like this:
auto *item = new QTableWidgetItem();
item->setIcon(QIcon("Your pixmap file path"));
Don't forget about setting a delegate.

Resources