Resizing a QTableView column horizontally to the content of an item delegated column which is painted with a new text in Qt - 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

Related

How to set icon center in QTableView?

NetworkPageForm::NetworkPageForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::NetworkPageForm),
devicesModel(NULL)
{
ui->setupUi(this);
devicesModel = new QStandardItemModel(0, 4, parent);
devicesModel->setHeaderData(0, Qt::Horizontal, QObject::tr("IP"));
devicesModel->setHeaderData(1, Qt::Horizontal, QObject::tr("Name"));
devicesModel->setHeaderData(2, Qt::Horizontal, QObject::tr("Last Online"));
devicesModel->setHeaderData(3, Qt::Horizontal, QObject::tr("Status"));
ui->devicesTableView->setModel(devicesModel);
ui->devicesTableView->resizeColumnsToContents();
}
void NetworkPageForm::addDevice(const QString &ip, int device_type) {
bool haveSameItem = false;
for(int i=0; i<devicesModel->rowCount(); i++) {
QStandardItem * ipItem = devicesModel->item(i, 0);
QStandardItem * nameItem = devicesModel->item(i, 1);
if(QString::compare(ipItem->text(), ip)== 0 && QString::compare(nameItem->text(), deviceStr)==0) {
devicesModel->setData(devicesModel->index(i, 2), BaseModel::now());
haveSameItem = true;
}
}
if(!haveSameItem)
{
int last = devicesModel->rowCount();
devicesModel->insertRow(last);
devicesModel->setData(devicesModel->index(last, 0), ip);
devicesModel->setData(devicesModel->index(last, 1), device_type);
devicesModel->setData(devicesModel->index(last, 2), BaseModel::now());
devicesModel->setData(devicesModel->index(last, 3), QIcon(":/res/images/online_icon.png"), Qt::DecorationRole);
// This function does not work, the icon is algin left.
// devicesModel->item(last, 3)->setTextAlignment(Qt::AlignCenter);
}
ui->devicesTableView->resizeColumnsToContents();
}
Is there a way to set QIcon item center in QTableView?
Update:
I create my own QStyledItemDelegate sub class as #RazrFalcon answered.
#include <QStyledItemDelegate>
class MyDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
private slots:
};
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
if(index.column() == 3) {
// TODO
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
QSize MyDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
if(index.column() == 3) {
// TODO
} else {
return QStyledItemDelegate::sizeHint(option, index);
}
}
void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
QStyledItemDelegate::setModelData(editor, model, index);
}
And set ui->devicesTableView->setItemDelegate(new MyDelegate);
Could someone help me how to set icon column center in QTableView?
There is no default way. You should implement your own QStyledItemDelegate.
UPD: example added
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
// disable default icon
opt.icon = QIcon();
// draw default item
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, 0);
const QRect r = option.rect;
// get pixmap
QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
QPixmap pix = icon.pixmap(r.size());
// draw pixmap at center of item
const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2);
painter->drawPixmap(r.topLeft() + p, pix);
}
It's no need to do it in delegate. The delegate will make your style sheet unavailable.
You can paint the icon in the middle of a rectangle pixmap which is as same size as the cell, and return it in the QAbstractItemModel::data() function with Qt::DecorationRole. Like this:
Qt::DecoratoinRole:
QPixmap pixmap(w, h); //w=cell width, h=cell
pixmap.fill(Qt::transparent); // draw a transparent rectangle
QPixmap iconPixmap(":/xx.png");
QPainter painter(&pixmap);
//Calculate the center coordinate x,y for iconPixmap
painter.draw(x, y, iconWidth, iconHeight, iconPixmap);
return pixmap;

Access pointer to QWidget(Combobox) of the customdelegate

I have derived a class from QStyledItemDelegate. I am using a QComboBox in this delegate. This delegate is used in QTableView.
My question is, how can i change the index of the Combobox in the delegate programatically i.e how to access the pointer to that widget outside the delegate class ?
I have checked that CreateEditor, SetEditorData, SetModelData functions (of QStyledItemDelegate) are called automatically when we click on the combobox and we cannot call them manually to maniplate the data in the model.
afaik any time you start editing and the combobox is shown, it will allocate a new one. if you want to have a permanent combobox, you should look at
QTableView::setIndexWidget(const QModelIndex&, QWidget*)
so you could access the combobox with the following code:
const QMoodelIndex idx = model->index(row, column);
QWidget* wid = view->indexWidget(idx);
QComboBox* box = qobject_cast<QComboBox*>(wid);
if (box)
// do your thing
You can have the contents of your combobox as a class member of your delegate in a QStringList. Your item delegate can be like :
#include <QStyledItemDelegate>
#include <QComboBox>
class ComboBoxDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject *parent = 0);
QWidget *createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
void setEditorData( QWidget *editor,
const QModelIndex &index ) const;
void setModelData( QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index ) const;
void updateEditorGeometry( QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
QStringList comboItems;
mutable QComboBox *combo;
private slots:
void setData(int val);
};
ComboBoxDelegate::ComboBoxDelegate(QObject *parent ):QStyledItemDelegate(parent)
{
}
QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
combo = new QComboBox( parent );
QObject::connect(combo,SIGNAL(currentIndexChanged(int)),this,SLOT(setData(int)));
combo->addItems(comboItems);
combo->setMaxVisibleItems(comboItems.count());
return combo;
}
void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString text = index.model()->data( index, Qt::DisplayRole ).toString();
int comboIndex = comboItems.indexOf(QRegExp(text));
if(comboIndex>=0)
(static_cast<QComboBox*>( editor ))->setCurrentIndex(comboIndex);
}
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
model->setData( index, static_cast<QComboBox*>( editor )->currentText() );
}
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry( option.rect );
}
void ComboBoxDelegate::setData(int val)
{
emit commitData(combo);
//emit closeEditor(combo);
}
When you want to update the items in combobox somewhere in your code just get a pointer to the item delegate by calling itemDelegateForColumn and access the comboItems member :
ComboBoxDelegate * itemDelegate = qobject_cast<ComboBoxDelegate *>(ui->tableView->itemDelegateForColumn(columnIndex));
//Updating combobox items
itemDelegate->comboItems.append("newItem");
...

how to add stylesheet for separator in QCombobox

i added two items in a qcombobox with a separator
addItem("New");
addItem("Delete");
insertSeparator(2);
in order to highlight the selection of item with different style i used QLIstView for the QComboBox view with the stylesheet as
QListView * listView = new QListView(this);
this->setView(listView);
listView->setStyleSheet("QListView::item { \
color: black; \
background: white; } \
QListView::item:selected { \
color: white; \
background-color: #0093D6 \
} \
");
now the problem is the separator is not visible at all .. it is showing a empty white space between the items . I'm not good with stylesheets so I don't have much clear idea about how to make a new stylesheet for the separator ..
You will have to create a custom itemDelegate for your QListView.
You can subclass QItemDelegate to create your own delegate class. Use sizeHint function to set the size of your separator and paint it in the paint function. Check if the items is a separator with index.data(Qt::AccessibleDescriptionRole).toString().
#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H
#include <QItemDelegate>
class ComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit ComboBoxDelegate(QObject *parent = 0);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
#endif // COMBOBOXDELEGATE_H
 
void ComboBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator"))
{
painter->setPen(Qt::red);
painter->drawLine(option.rect.left(), option.rect.center().y(), option.rect.right(), option.rect.center().y());
}
else
QItemDelegate::paint(painter, option, index);
}
QSize ComboBoxDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QString type = index.data(Qt::AccessibleDescriptionRole).toString();
if(type == QLatin1String("separator"))
return QSize(0, 2);
return QItemDelegate::sizeHint( option, index );
}
Then just set your custom delegate to your listView:
listView->setItemDelegate(new ComboBoxDelegate);.

add custom widget to QTableWidget cell

I have custom widget made with qt designer and i want to add it to QTableWidget cell.
But it doesn't work.
Here is the code :
int nRows =10;
for(int row = 0; row < nRows;row++;)
{
QTableWidgetItem* item = new QTableWidgetItem();
CustomWdg* wdg=new CustomWdg( );
mTableWdg->insertRow( row );
mTableWdg->setItem(row, 0, item);
mTableWdg->setCellWidget( row, 0, wdg );
}
If you want to add custom widget into table cell you can use QItemDelegate.
Create your own Delegate class and inherit it from QItemDelegate.
class MyDelegate : public QItemDelegate
{
public:
CChoicePathDelegate (QObject *parent = 0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; //delegate editor (your custom widget)
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const; //transfer editor data to model
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
};
And then set delegate for Table with this methods on your own.
setItemDelegate(QAbstractItemDelegate *)
setItemDelegateForColumn(int, QAbstractItemDelegate *)
setItemDelegateForRow(int, QAbstractItemDelegate *)
I have tried this code:
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QHBoxLayout *l = new QHBoxLayout();
l->addWidget((new QPushButton("I`m in cell")));
l->addWidget((new QLabel("Test label")));
QWidget *w = new QWidget();
w->setLayout(l);
ui->tableWidget->setCellWidget(1,1, w);
}
Widget::~Widget()
{
delete ui;
}
and result is:
Your code is correct, so the only thing that comes to my mind is that you didn't setColumnCount(1) before for loop. If that's not the case, you could try to set row and column count before that for loop instead inserting row by row in loop:
int nRows =10;
mTableWdg->setRowCount(nRows);
mTableWdg->setColumnCount(1);
for(int row = 0; row < nRows;row++;)
{
//QTableWidgetItem* item = new QTableWidgetItem();// line one
CustomWdg* wdg=new CustomWdg( );
//mTableWdg->setItem(row, 0, item);// line three
mTableWdg->setCellWidget( row, 0, wdg );
}
If you really need item ("line one" and "line three") you should set it like this: QTableWidgetItem* item = new QTableWidgetItem("");, otherwise you don't need those lines, your CustomWdg is properly set with setCellWidget

QAbstractTableModel data return html code to display

I want my AbstracttableModel subclass data() method to return html i.e.
PreText<b>Text</b>PostText
And this text must be displayed int table as in html:
PreTextTextPostText
How can I do this?
You can create a delegate for the view that will display the html.
class HtmlDelegate : public QItemDelegate {
public:
HtmlDelegate(QObject *parent = 0) : QItemDelegate(parent) {}
// This function is only called to paint the text
void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &rect, const QString &text) const
{
QTextDocument doc;
// Since the QTextDocument will do all the rendering, the color,
// and the font have to be put back inside the doc
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
cg = QPalette::Inactive;
QColor textColor = option.palette.color(cg, QPalette::Text);
doc.setDefaultStyleSheet(QString("body { color: %1}")
.arg(textColor.name()));
doc.setDefaultFont(option.font);
doc.setHtml(text);
doc.setDocumentMargin(1); // the default is 4 which is too much
painter->save();
painter->translate(rect.topLeft());
doc.drawContents(painter);
painter->restore();
}
// bold and underlined characters take more space
// so you have to redefine this function as well
// (if you have a checkbox or an icon in the item, you will have
// to include their size to the returned value)
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QTextDocument doc;
doc.setDefaultFont(option.font);
doc.setHtml(index.data(Qt::DisplayRole).toString());
doc.setDocumentMargin(1);
return doc.size().toSize();
}
};
Then assign it to a view:
view->setItemDelegateForColumn(0, new HtmlDelegate(view));
alexisdm answer works fine, but maybe using a QLabel is lighter than QTextDocument and it works smartly too:
class HtmlDelegateWithLabel : public QItemDelegate
{
public:
HtmlDelegateWithLabel(QObject *parent = 0) : QItemDelegate(parent)
{
}
inline void setupLabel( QLabel& label, const QModelIndex &index ) const
{
QString txt = index.model()->data( index, Qt::DisplayRole ).toString();
label.setText( txt );
}
// This function is only called to paint the text
void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &rect, const QString &text) const
{
QLabel label;
setupLabel( label, option.index );
label.setEnabled( option.state & QStyle::State_Enabled );
label.setAttribute(Qt::WA_TranslucentBackground);
painter->save();
painter->translate(rect.topLeft());
label.resize( rect.size() );
label.render( painter );
painter->restore();
}
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QLabel label;
setupLabel( label, index );
return label.sizeHint();
}
};
Moreover, text gets aligned vertically on the row, which is not the case when using QTextDocument.

Resources