QStyledItemDelegate custom widget drawn incorrect, when scrolling - qt

One of my earlier Question deals with a customized QStyledItemDelegate containing a QWidget with two QLabels side by side.
I was really satisfied with the easy solution given by Joseph Ireland. Unfortunately, the given is solution is broken, but I didn't realized this immediately. If the QTableWidget containing my QStyledItemDelegate gets too small, the scrollbars are activated.
Now scrolling destroys the correct drawing of my cell elements. This seems to be some kind of update problem. I realized this, after I draw a rectangle around the viewport region of my QAbstractScollArea.
My table looks like the following, if you will scroll violently:
Cells contents are not drawn at the right place or seemingly not draw after all. Happens strangely for every second row. Even more the rectangle drawn around the viewport region of my QAbstractScrollArea is messed up. If the window is redrawn (hide/show window) everything is fine.
What might the solution to this kind of update/repaint problem? Maybe I need to repaint after scrolling is finished?
My adapted solution posted by Joseph Ireland was:
Header-File: TwoNumbersDelegate.h
#pragma once
#include <QStyledItemDelegate>
class QLabel;
class TwoNumbersDelegate : public QStyledItemDelegate {
public:
TwoNumbersDelegate(QObject* parent = nullptr);
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
QLabel* mLeft;
QLabel* mRight;
QFrame* mFrame;
};
Source File: TwoNumbersDelegate.cpp
#include "TwoNumbersDelegate.h"
#include <QLabel>
#include <QPainter>
#include <QDebug>
#include <QHBoxLayout>
#include <QTableWidget>
TwoNumbersDelegate::TwoNumbersDelegate(QObject* parent /*= nullptr*/) : QStyledItemDelegate(parent)
{
mLeft = new QLabel("%1");
mRight = new QLabel("%2");
mFrame = new QFrame;
mFrame->setLayout(new QHBoxLayout);
mFrame->layout()->addWidget(mLeft);
// you could add a spacer here maybe
mFrame->layout()->addWidget(mRight);
mFrame->setAttribute(Qt::WA_DontShowOnScreen, true);
mFrame->show();
}
void TwoNumbersDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
auto data = index.data(Qt::EditRole);
auto list = data.toList();
if (list.size() != 2) {
QStyledItemDelegate::paint(painter, option, index);
}
mLeft->setText(list.at(0).toString());
mRight->setText(list.at(1).toString());
mFrame->resize(option.rect.size());
qDebug() << option.rect.size();
// mFrame->layout()->invalidate();
// mFrame->layout()->activate();
if (auto tableWidget = qobject_cast<QTableWidget*>(parent())) {
auto cellRegion = QRegion(option.rect);
auto viewportRegion = QRegion(tableWidget->viewport()->rect());
auto intersectedRegion = cellRegion.intersected(viewportRegion);
intersectedRegion.translate(-option.rect.topLeft());
painter->drawRect(tableWidget->viewport()->rect().adjusted(0,0,-1,-1));
painter->save();
painter->translate(option.rect.topLeft());
mFrame->render(painter, QPoint(), intersectedRegion, QWidget::DrawChildren);
qDebug() << cellRegion << viewportRegion << intersectedRegion;
painter->restore();
}
}
QSize TwoNumbersDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return mFrame->minimumSizeHint();
}
The following program served me as a test runner:
#include <QApplication>
#include <QTableWidget>
#include <QVBoxLayout>
#include "TwoNumbersDelegate.h"
int main(int argc, char** args) {
QApplication q(argc, args);
auto frame = new QFrame;
frame->setLayout(new QVBoxLayout);
//frame->setStyleSheet("QFrame { background: green}"); // Activate this to see overdrawing
auto table = new QTableWidget;
table->setAlternatingRowColors(true);
table->setItemDelegate(new TwoNumbersDelegate);
table->setRowCount(20);
table->setColumnCount(10);
for (auto iter = 0; iter < 20; iter++) {
for (auto colIter = 0; colIter < 10; colIter++) {
auto item = new QTableWidgetItem;
QVariantList map;
map << iter << iter*colIter;
item->setData(Qt::EditRole, map);
table->setItem(iter, colIter, item);
}
}
frame->layout()->addWidget(table);
frame->show();
q.exec();
}

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

Accessing a delegate's input value from the model

I'm using a custom QTableView with a custom QAbstractTableModel and a QItemDelegate. I'd need to access the contents of the delegate's editor while the user is editing it, and after several attempts, I couldn't find anything satisfying.
Indeed, I've tried several things.
First: trying to access the delegate's current input (created through createEditor) through a property defined in QItemDelegate but... it seems that none exists. That's why I tried to add a QWidget* editor property and setting it in the createEditor.
Unfortunately, QItemDelegate's createEditor is supposed to be const, which makes me unable to set my property there (and since I don't control what calls createEditor, I can't do it before or after).
I don't really know what to do here. Actually, I also needed to know when the user started (or stopped) editing the cell content, which I eventually achieved by creating two const signals (editingStarted and editingStopped). I could probably create a const editorOpened(QWidget*) signal but it just feels bad and ugly...
I can't believe nothing "official" exists to achieve what I'm trying to do, hence this question. If I have everything wrong from the beginning, I'd be glad to know. If you have any other ideas, please suggest.
EDIT: Here is a minimal working example
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QTableView>
#include "mytableview.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
auto tableView = new MyTableView(this);
setCentralWidget(tableView);
}
MainWindow::~MainWindow()
{
}
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QItemDelegate>
#include <QLineEdit>
#include <QStandardItemModel>
class MyItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual void onCloseEditor();
virtual ~MyItemDelegate() = default;
signals:
// Const signals trick
void editingStarted() const;
void editingFinished() const;
void editorOpened(const QWidget*) const;
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
MyItemDelegate::MyItemDelegate(QObject* parent) : QItemDelegate(parent)
{
connect(this, &QItemDelegate::closeEditor, this, &MyItemDelegate::onCloseEditor);
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
emit editingStarted();
emit editorOpened(lineEdit);
return lineEdit;
}
void MyItemDelegate::onCloseEditor()
{
emit editingFinished();
}
MyTableView.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include <QTableView>
#include <QDebug>
#include "myitemdelegate.h"
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget *parent = nullptr);
signals:
public slots:
};
#endif // MYTABLEVIEW_H
MyTableView.cpp
#include "mytableview.h"
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::editingStarted, []() {
qDebug() << "Editing started";
});
connect(delegate, &MyItemDelegate::editingFinished, []() {
qDebug() << "Editing finished";
});
connect(delegate, &MyItemDelegate::editorOpened, [](const QWidget* editor) {
auto lineEdit = qobject_cast<const QLineEdit*>(editor);
connect(lineEdit, &QLineEdit::textChanged, [](const QString& text) {
qDebug() << text;
});
});
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
The following solution maybe fits your needs. I just defined a new signal inside the delegate and connected to it inside the class owning the delegate.
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QStyledItemDelegate>
class MyItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual ~MyItemDelegate() = default;
signals:
void valueChanged(const QString&);
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
#include <QLineEdit>
MyItemDelegate::MyItemDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
connect(lineEdit, &QLineEdit::textChanged, this, &MyItemDelegate::valueChanged);
return lineEdit;
}
MyTableView.cpp
#include "mytableview.h"
#include <QStandardItemModel>
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::valueChanged, [](auto v) { qDebug() << v; });
}

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.)

Apply QGraphicsColorizeEffect to a part of a QGraphicsSvgItem (only to the svg itself)

I have a QGraphicsSvgItem subclass where I would like to modify the svg color. I want to use for that QGraphicsColorizeEffect, and it works great.
My items also have a custom selection rectangle, highlighted - similar to other item types.
When I apply the colorize effect, the highlight also turns to the same color...
I have tried to setEnabled(false); in paint but it seems to have no effect.
sample code:
file mysvg.h
#ifndef MYSVG_H
#define MYSVG_H
#include <QGraphicsSvgItem>
#include <QGraphicsColorizeEffect>
class MySvg : public QGraphicsSvgItem
{
public:
MySvg();
~MySvg();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL);
private:
QGraphicsColorizeEffect* m_effect;
void drawSelectionRectangle(QPainter* painter, const QStyleOptionGraphicsItem* option, const QRectF& rectangle);
};
#endif // MYSVG_H
file mysvg.cpp
#include <QStyleOptionGraphicsItem>
#include <QStyle>
#include <QPainterPath>
#include <QPainter>
#include <QFileDialog>
#include <QSvgRenderer>
MySvg::MySvg()
{
m_effect = new QGraphicsColorizeEffect();
m_effect->setColor(Qt::red);
setGraphicsEffect(m_effect);
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0, tr("Open Svg File"),
QString(), tr("Svg files (*.svg *.svgz)"));
setSharedRenderer(new QSvgRenderer(filename));
}
MySvg::~MySvg()
{
delete renderer();
delete m_effect;
}
void MySvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
QGraphicsSvgItem::paint(painter, &opt, widget);
//m_effect->setEnabled(false); // no effect though seemed logical
QRectF rectangle = boundingRect();
if (option->state & (QStyle::State_Selected))
drawSelectionRectangle(painter, option, rectangle);
//m_effect->setEnabled(true);
}
void MySvg::drawSelectionRectangle(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
file main.cpp
#include <QApplication>
#include <QGraphicsView>
#include "mysvg.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene s;
QGraphicsView view;
view.setScene(&s);
s.setSceneRect(-50, -50, 500, 650);
view.show();
MySvg* svg = new MySvg();
s.addItem(svg);
return app.exec();
}
file mysvg.pro
QT += core gui svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = mysvg
TEMPLATE = app
SOURCES += main.cpp \
mysvg.cpp
HEADERS += mysvg.h
I have considered making a QGraphicsSvgItem a private member of the MySvg item - but the MySvg item has to do a lot of other things, and I don't know what to do with the renderer (who would own it...) - if I can figure out how to make a QGraphicsSvgItem subclass a member of the MySvg class, I can apply the colorize to the member and perform all other operations on the MySvg item...
Please help me figure a way to apply color to the svg, but not other drawing portion of the item.
Edit - I have tried to add a member item to the class and apply the colorize effect to the member - but it doesn't apply the colorize effect at all... The svg loads with all original colors.
Here is the code containing a member item:
new mysvg.h
class SvgMember : public QGraphicsSvgItem
{
public:
SvgMember (const QByteArray &content, const QColor& c);
~SvgMember ();
private:
QGraphicsColorizeEffect* m_effect;
};
class MySvg : public QGraphicsItem
{
public:
MySvg();
~MySvg();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL);
virtual QRectF boundingRect() const;
virtual QPainterPath shape() const;
private:
void drawSelectionRectangle(QPainter* painter, const QStyleOptionGraphicsItem* option, const QRectF& rectangle);
SvgMember * m_member;
};
new mysvg.cpp
MySvg::MySvg()
{
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0, QObject::tr("Open Svg File"),
QString(), QObject::tr("Svg files (*.svg *.svgz)"));
QFile f(filename);
f.open(QFile::ReadOnly | QFile::Text);
QByteArray svgContents = f.readAll();
f.close();
m_member = new SvgMember (svgContents, Qt::red);
}
MySvg::~MySvg()
{
delete m_member;
}
void MySvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
m_member->paint(painter, &opt, widget);
QRectF rectangle = boundingRect();
if (option->state & (QStyle::State_Selected))
drawSelectionRectangle(painter, option, rectangle);
}
/*! \brief reimplemented to use member rectangle */
QRectF MySvg::boundingRect() const
{
return m_member->boundingRect();
}
/*! \brief reimplemented to use member shape */
QPainterPath MySvg::shape() const
{
return m_member->shape();
}
void MySvg::drawSelectionRectangle(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
SvgMember ::SvgMember (const QByteArray &content, const QColor &c)
{
m_effect = new QGraphicsColorizeEffect();
setGraphicsEffect(m_effect);
m_effect->setColor(c);
setSharedRenderer(new QSvgRenderer(content));
}
SvgMember ::~SvgMember ()
{
delete renderer();
delete m_effect;
}
What can I do to apply the colorize effect to the svg - but not to the selection rectangle ?
The effect is a feature of the base class QGraphicsItem. It is applied to the entire graphics item and all its children. So, everything that is painted inside the item is affected by its effect.
The selection rectangle should be painted outside of the SVG item object.
It can be achieved by enclosing QGraphicsSvgItem by the composite class QGraphicsItemGroup.
When a QGraphicsItem is added to QGraphicsItemGroup it becomes reparented. Thus the item is destroyed when the group object is destroyed. So, it is not needed to delete it manually. QGraphicsItem takes ownership of effect, so it is not needed to delete the effect object.
The following class MyGraphicsItemGroup works as you expect.
Implementation "mygraphicsitemgroup.cpp"
#include "mygraphicsitemgroup.h"
#include <QGraphicsColorizeEffect>
#include <QGraphicsSvgItem>
#include <QStyleOptionGraphicsItem>
#include <QPainter>
#include <QFileDialog>
MyGraphicsItemGroup::MyGraphicsItemGroup()
{
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0,
QObject::tr("Open Svg File"), QString(),
QObject::tr("Svg files (*.svg *.svgz)"));
QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect();
effect->setColor(Qt::red);
QGraphicsSvgItem *svg = new QGraphicsSvgItem(filename);
svg->setGraphicsEffect(effect);
addToGroup(svg);
}
void MyGraphicsItemGroup::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
QGraphicsItemGroup::paint(painter, &opt, widget);
QRectF rectangle = boundingRect();
if (option->state & QStyle::State_Selected)
drawSelectionRectangle(painter, option, rectangle);
}
void MyGraphicsItemGroup::drawSelectionRectangle(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
Header "mygraphicsitemgroup.h"
#ifndef MYGRAPHICSITEMGROUP_H
#define MYGRAPHICSITEMGROUP_H
#include <QGraphicsItemGroup>
class MyGraphicsItemGroup : public QGraphicsItemGroup
{
public:
MyGraphicsItemGroup();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget);
void drawSelectionRectangle(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QRectF &rectangle);
};
#endif // MYGRAPHICSITEMGROUP_H

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

Resources