Proper elide and peridically set background color of QTableView cell and possible for user to edit value - qt

To summarize:
I need a solution for QTableView cells which fulfills
Proper elide
Possible to set background color by emitting dataChanged() from the model in a method called by a timer at ~50ms
Possible for user to edit value as normal.
QTBUG for the elide: https://bugreports.qt.io/browse/QTBUG-87178
Solution which fulfills req 1 but not 2 and 3: How to prevent too aggressive text elide in QTableview?
Sample program which demonstrates the issue:https://udokaelectronics.com/files/untitled.zip
Comment out
ui->tableView->setItemDelegate(elideDelegate);
to see behavior with and without elide delegate.
Anyone with ideas?

This is what I've come up with.
A delegate for keeping track of when an edit is in progress. Then use the state of the delegate in the table models to avoid emitting dataChanged()
#pragma once
#include <QStyledItemDelegate>
class EditInProgressDelegate : public QStyledItemDelegate
{
public:
EditInProgressDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent), editInProgress_(false){};
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void destroyEditor(QWidget* editor, const QModelIndex& index) const override;
bool editInProgress() const { return editInProgress_; }
int editInProgressRow() const { return editInProgressRow_; }
int editInProgressColumn() const { return editInProgressColumn_; }
protected:
mutable bool editInProgress_;
mutable int editInProgressRow_;
mutable int editInProgressColumn_;
};
#include "EditInProgressDelegate.h"
QWidget* EditInProgressDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
editInProgress_ = true;
editInProgressColumn_ = index.column();
editInProgressRow_ = index.row();
return QStyledItemDelegate::createEditor(parent, option, index);
};
void EditInProgressDelegate::destroyEditor(QWidget* editor, const QModelIndex& index) const
{
editInProgress_ = false;
QStyledItemDelegate::destroyEditor(editor, index);
}
A second delegate with a magic 10 to handle elide
#pragma once
#include "EditInProgressDelegate.h"
// This entire class is a plaster because QTableWidgetItem or
// QStyledItemDelegate malfunctions, see
// https://stackoverflow.com/questions/64198197/how-to-prevent-too-aggressive-text-elide-in-qtableview
// and
// https://bugreports.qt.io/browse/QTBUG-87178
class ElideDelegate : public EditInProgressDelegate
{
public:
ElideDelegate(QObject* parent = nullptr) : EditInProgressDelegate(parent){};
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
#include "ElideDelegate.h"
#include <QApplication>
#include <QPainter>
void ElideDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if (!index.isValid())
{
return;
}
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
opt.text = opt.fontMetrics.elidedText(opt.text,
Qt::ElideRight,
opt.rect.width() - 10); // 10 was determined by experimentation
opt.textElideMode = Qt::ElideNone;
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
}

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

Qt delegate not calling createEditor()

I have ComboBox filled with CheckBoxes when the ComboBox is opened the first item's delegate createEditor is not called but when you move to second item it is called and delegate is properly created. After this when you move back to first item then delegate is working. The problem is only with selecting first item for first time. If you have got only one item in comboBox this item is unselectable.
This is my delegate's code:
#include "checkboxlistdelegate.h"
CheckBoxListDelegate::CheckBoxListDelegate(QObject *parent)
: QItemDelegate(parent) {
}
void CheckBoxListDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const {
//Get item data
bool value = index.data(Qt::UserRole).toBool();
QString text = index.data(Qt::DisplayRole).toString();
// fill style options with item data
const QStyle *style = QApplication::style();
QStyleOptionButton opt;
opt.state |= value ? QStyle::State_On : QStyle::State_Off;
opt.state |= QStyle::State_Enabled;
opt.text = text;
opt.rect = option.rect;
opt.palette = QPalette(Qt::white);
// draw item data as CheckBox
style->drawControl(QStyle::CE_CheckBox,&opt,painter);
}
QWidget* CheckBoxListDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem& option,
const QModelIndex & index ) const {
QCheckBox *editor = new QCheckBox(parent);
editor->setStyleSheet("QCheckBox {background-color: #aaaaaa; color: white;}");
return editor;
}
void CheckBoxListDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const {
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
myEditor->setText(index.data(Qt::DisplayRole).toString());
myEditor->setChecked(index.data(Qt::UserRole).toBool());
}
void CheckBoxListDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const {
//get the value from the editor (CheckBox)
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
bool value = myEditor->isChecked();
//set model data
QMap<int,QVariant> data;
data.insert(Qt::DisplayRole,myEditor->text());
data.insert(Qt::UserRole,value);
model->setItemData(index,data);
emit indexChanged(index);
}
void CheckBoxListDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const {
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
And this is code of my QComboBox subclass:
CheckBoxList::CheckBoxList(QWidget *widget)
: QComboBox(widget),title(""),selection() {
// set delegate items view
view()->setItemDelegate(new CheckBoxListDelegate(this));
view()->setStyleSheet("QAbstractItemView {background-color:white;}");
// Enable editing on items view
view()->setEditTriggers(QAbstractItemView::AllEditTriggers);
connect(view()->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(onItemClicked(QModelIndex)));
}
void CheckBoxList::paintEvent(QPaintEvent *) {
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
QStyleOptionComboBox opt;
initStyleOption(&opt);
opt.currentText = title;
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
void CheckBoxList::setDisplayText(QString text) {
title = text;
}
QString CheckBoxList::getDisplayText() const {
return title;
}
bool CheckBoxList::isChecked(const QModelIndex& index) const {
return view()->model()->itemData(index).value(Qt::UserRole).toBool();
}
Thank you.

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.

How to make QComboBox as MultiSelect in QT?

How to make QComboBox as MultiSelect in QT ?
There is no option of MultiSelect in Combox in QT ?
OR
Anybody can suggest me some diffrent control but look & feel should be like QCombobox only.
//This is the file named as CheckBoxList.h
#ifndef CHECKBOXLIST_H
#define CHECKBOXLIST_H
#include <QtGui>
class CheckBoxList: public QComboBox
{
Q_OBJECT;
public:
CheckBoxList(QWidget *widget = 0);
virtual ~CheckBoxList();
bool eventFilter(QObject *object, QEvent *event);
virtual void paintEvent(QPaintEvent *);
void SetDisplayText(QString text);
QString GetDisplayText() const;
private:
QString m_DisplayText;
};
#endif // CHECKBOXLIST_H
//This is the file named as CheckBoxList.cpp
#include "CheckBoxList.h"
#include <QtGui>
// internal private delegate
class CheckBoxListDelegate : public QItemDelegate
{
public:
CheckBoxListDelegate(QObject *parent)
: QItemDelegate(parent)
{
;
}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
//Get item data
bool value = index.data(Qt::UserRole).toBool();
QString text = index.data(Qt::DisplayRole).toString();
// fill style options with item data
const QStyle *style = QApplication::style();
QStyleOptionButton opt;
opt.state |= value ? QStyle::State_On : QStyle::State_Off;
opt.state |= QStyle::State_Enabled;
opt.text = text;
opt.rect = option.rect;
// draw item data as CheckBox
style->drawControl(QStyle::CE_CheckBox,&opt,painter);
//QMessageBox::information(0,"Info",text);
}
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem & option ,
const QModelIndex & index ) const
{
// create check box as our editor
QCheckBox *editor = new QCheckBox(parent);
return editor;
}
void setEditorData(QWidget *editor,
const QModelIndex &index) const
{
//set editor data
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
myEditor->setText(index.data(Qt::DisplayRole).toString());
myEditor->setChecked(index.data(Qt::UserRole).toBool());
//
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
//get the value from the editor (CheckBox)
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
bool value = myEditor->isChecked();
//set model data
QMap<int,QVariant> data;
data.insert(Qt::DisplayRole,myEditor->text());
data.insert(Qt::UserRole,value);
model->setItemData(index,data);
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
editor->setGeometry(option.rect);
}
};
//min-width:10em;
CheckBoxList::CheckBoxList(QWidget *widget )
:QComboBox(widget),m_DisplayText(0)
{
// set delegate items view
view()->setItemDelegate(new CheckBoxListDelegate(this));
//view()->setStyleSheet(" padding: 15px; ");
// Enable editing on items view
view()->setEditTriggers(QAbstractItemView::CurrentChanged);
// set "CheckBoxList::eventFilter" as event filter for items view
view()->viewport()->installEventFilter(this);
// it just cool to have it as defualt ;)
view()->setAlternatingRowColors(true);
}
CheckBoxList::~CheckBoxList()
{
;
}
bool CheckBoxList::eventFilter(QObject *object, QEvent *event)
{
// don't close items view after we release the mouse button
// by simple eating MouseButtonRelease in viewport of items view
if(event->type() == QEvent::MouseButtonRelease && object==view()->viewport())
{
return true;
}
return QComboBox::eventFilter(object,event);
}
void CheckBoxList::paintEvent(QPaintEvent *)
{
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
// if no display text been set , use "..." as default
if(m_DisplayText.isNull())
opt.currentText = "......";
else
opt.currentText = m_DisplayText;
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
void CheckBoxList::SetDisplayText(QString text)
{
m_DisplayText = text;
}
QString CheckBoxList::GetDisplayText() const
{
return m_DisplayText;
}
One alternative is to set menu with checkable actions to a button, as I've shown here.
Or you can change the selection model of the combo and control the hiding and showing of the pop up window as shown here.

Formatting data in a QTableView

I'm using a custom delegate to display QDoubleSpinBoxes in a QTableView. Those spinboxes display their contents with two decimals.
My problem is that I would like the QTableView to also display those numbers with two decimals while they are not being edited (at which point they are not in a QDoubleSpinBox). Or, rather, I would like to be able to specifiy a format for the QTableView's contents.
I tried to subclass QStyledItemDelegate to override displayText, but for a strange reason it crashes. It works correctly if I simply subclass QItemDelegate.
I'm using Qt 4.6.3, on Windows.
I'm not really sure what to make of the exception you are getting. Here is a simple QStyledItemDelegate that we are using without problems. Perhaps there is something different?
#include "model_view/color_combo_delegate.h"
#include <QTimer>
#include "map_elements/common/color_combo_box.h"
ColorComboDelegate::ColorComboDelegate(QObject *parent)
: QStyledItemDelegate(parent) {
}
QWidget *ColorComboDelegate::createEditor(
QWidget *parent,
const QStyleOptionViewItem & /*option*/,
const QModelIndex & /*index*/) const {
ColorComboBox *color_combo_box = new ColorComboBox(parent);
connect(color_combo_box, SIGNAL(currentIndexChanged(int)),
this, SLOT(IndexChanged()));
QTimer::singleShot(0, color_combo_box, SLOT(Popup()));
return color_combo_box;
}
QString ColorComboDelegate::displayText(const QVariant &value,
const QLocale &/*locale*/) const {
Map::Color color = static_cast<Map::Color>(value.toInt());
return Map::color_name(color);
}
void ColorComboDelegate::IndexChanged() {
ColorComboBox *color_combo_box = qobject_cast<ColorComboBox *>(sender());
emit commitData(color_combo_box);
emit closeEditor(color_combo_box);
}
void ColorComboDelegate::setEditorData(QWidget * editor,
const QModelIndex & index) const {
ColorComboBox *color_combo_box = qobject_cast<ColorComboBox *>(editor);
Map::Color color = static_cast<Map::Color>(index.data().toInt());
color_combo_box->set_color(color);
}
void ColorComboDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const {
ColorComboBox *color_combo_box = qobject_cast<ColorComboBox *>(editor);
model->setData(index, color_combo_box->color());
}
Well, I don't know what happened, but now it no longer crashes. And it now works.
For the record, this is my displayText method:
QString sqxSpinBoxDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
return locale.toString(value.toDouble(), 'f', Decimals);
}

Resources