This is my current Data function of Tree model
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = getItem(index);
if (item)
{
switch (role)
{
case Qt::DisplayRole: case Qt::EditRole:
return QString::fromStdString(item->data().GetName());
break;
case Qt::DecorationRole:
{
Container *cont = item->GetContainer();
if (cont->GetGeometry()->isValid())
{
QString qstrIconName = cont->GetGeometry()->GetType().c_str();
QString qstrIconPath = QCoreApplication::applicationDirPath();
QPixmap pixmap;
QIcon icon;
qstrIconPath = qstrIconPath + "/Icons/" + qstrIconName + ".png";
pixmap.load(qstrIconPath);
icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
return icon;
}
// Function returns from here since we have returned the icon
// How can i add next icon to the same tree item
int numberOfFunctions = cont->getNumberOfFunctions();
if (numberOfFunctions > 0)
{
QString qstrIconName = "FUNCTION";
QString qstrIconPath = QCoreApplication::applicationDirPath();
QPixmap pixmap;
QIcon icon;
qstrIconPath = qstrIconPath + "/Icons/" + qstrIconName + ".png";
pixmap.load(qstrIconPath);
icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
return icon;
}
break;
}
}
return QVariant();
}
}
//////////////////////////////////////////////////////////////////////////////////
I am able to set only icon how can i set multiple icons in the Tree Item.
I am able to set the either of the icons on the tree model but not the both icons.
I used a QStyledItemDelegate to acheive this and this is the paint function
void PixelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option , const QModelIndex &index) const
{
int offset = 0;
int offsetIcon = 0;
const QAbstractItemModel *model = index.model();
const TreeModel *myModel = (TreeModel*)(model);
TreeItem* item = myModel->getItem(index);
Container* cont = item->GetContainer();
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen);
painter->translate(option.rect.x(), option.rect.y());
painter->scale(.4, .4);
if (option.state & QStyle::State_Selected)
painter->setBrush(option.palette.highlightedText());
else
painter->setBrush(option.palette.text());
if (cont->GetGeometry()->GetType() != "NO_TYPE")
{
QString qstrIconName = cont->GetGeometry()->GetType().c_str();
QString qstrIconPath = QCoreApplication::applicationDirPath();
QPixmap pixmap;
qstrIconPath = qstrIconPath + "/Icons/" + qstrIconName + ".png";
pixmap.load(qstrIconPath);
QImage image = pixmap.toImage();
painter->drawImage(0, 6, image);
offset += 20;
offsetIcon += 50;
}
if (cont->getNumberOfFunctions() > 0)
{
QString qstrIconName = "FUNCTION";
QString qstrIconPath = QCoreApplication::applicationDirPath();
QPixmap pixmap;
qstrIconPath = qstrIconPath + "/Icons/" + qstrIconName + ".png";
pixmap.load(qstrIconPath);
QImage image = pixmap.toImage();
painter->drawImage(offsetIcon, 6, image);
offset += 20;
offsetIcon += 50;
}
if (cont->GetNumberOfAnimationChannels() > 0)
{
QString qstrIconName = "ANIMATION";
QString qstrIconPath = QCoreApplication::applicationDirPath();
QPixmap pixmap;
qstrIconPath = qstrIconPath + "/Icons/" + qstrIconName + ".png";
pixmap.load(qstrIconPath);
QImage image = pixmap.toImage();
painter->drawImage(offsetIcon, 6, image);
offset += 20;
}
painter->restore();
QStyleOptionViewItem newoption = option;
newoption.rect.setX(option.rect.x() + offset + 3);
newoption.rect.setY(option.rect.y());
QStyledItemDelegate::paint(painter, newoption, index);
}
Related
when i am deleting Treeitem from the the tree model the destructor for the tree item is not being called.
This is my code for deleting the tree item from the model.
void TreeModel::removeItem(TreeItem *node)
{
const int row = node->row();
QModelIndex idx = createIndex(row, 0, node);
TreeItem* itm = getItem(idx);
beginRemoveRows(idx.parent(), row, row);
node->parent()->removeChild(row);
endRemoveRows();
}
The code for Treeitem RemoveChild.
void TreeItem::removeChild(int row)
{
childItems.removeAt(row);
}
The code for tree item header file.
#include <QList>
#include <QVariant>
#include <QVector>
#include "Container.h"
class TreeItem
{
public:
explicit TreeItem( Container *data , TreeItem *parent = 0 );
~TreeItem();
TreeItem *parent();
void appendChild(TreeItem *child);
TreeItem *child(int iNumber);
int childCount() const;
int childNumber() const;
Container data() const ;
Container* GetContainer();
bool setData(Container* data , QVariant value);
void setContainer( Container* data);
bool insertChildren(int position, int count );
bool removeChildren( int position , int count );
void removeChild(int row);
void removeChild(TreeItem* itm);
std::string getChildName(int row);
std::string getName();
int row() const;
void insertChild(int pos, TreeItem *child);
private:
QList<TreeItem*> childItems;
Container* itemData;
TreeItem* parentItem;
};
The code for the tree item Cpp file.
/////////////////////////////////////////////
//////////////////////////////////////////////////////
TreeItem::TreeItem( Container *data, TreeItem *parent )
{
parentItem = parent;
itemData = new Container;
*itemData = *data;
}
TreeItem::~TreeItem()
{
qDebug() << itemData->GetName().c_str();
if (itemData != nullptr)
{
delete itemData;
qDebug() << "deleting Item Data";
}
qDeleteAll(childItems);
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
TreeItem *TreeItem::child(int iNumber)
{
return childItems.value(iNumber);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*> (this));
return 0;
}
Container TreeItem::data() const
{
return *itemData;
}
bool TreeItem::setData( Container* data , QVariant value )
{
//*itemData = *data; // Do Not !!!! uncomment this as it will set the
value of default container constructor.
itemData->SetName(value.toString().toStdString() );
return true;
}
bool TreeItem::insertChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
Container cont;
TreeItem *item = new TreeItem(&cont, this);
childItems.insert(position, item);
return true;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
for (int row = 0; row < count; ++row)
{
delete childItems.takeAt(position);
}
return true;
}
void TreeItem::setContainer( Container* cont)
{
*itemData = *cont;
}
void TreeItem::appendChild(TreeItem *node)
{
childItems.append( node );
}
int TreeItem::row() const
{
if (parentItem)
return parentItem->childItems.indexOf( const_cast<TreeItem*>(this) );
return 0;
}
void TreeItem::removeChild(int row)
{
childItems.removeAt(row);
}
void TreeItem::insertChild(int pos, TreeItem *child)
{
childItems.insert(pos, child);
child->parentItem = this;
}
void TreeItem::removeChild(TreeItem* itm)
{
childItems.removeOne(itm);
}
std::string TreeItem::getChildName(int row)
{
return childItems.value(row)->getName();
}
std::string TreeItem::getName()
{
return itemData->GetName();
}
Container* TreeItem::GetContainer()
{
return itemData;
}
The Header file for the TreeModel Class///////////////////////////////////
#pragma once
#include <QAbstractItemModel>
#include <QString>
#include <QMimedata.h>
#include <Qdatastream.h>
class TreeItem;
class Container;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel(const QString &header, Container *data, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &parent) const override;
bool insertRows(int position, int rows, const QModelIndex &parent);
// bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Container* GetContainer(const QModelIndex &index);
void SetContainer(const QModelIndex &index, Container* cont);
////////////////////// Drag And Drop Actions ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
void setupModelData(const QStringList &lines, TreeItem *parent);
void removeItem(TreeItem *item);
bool FindChild(std::string stdstrChildName);
TreeItem *getItem(const QModelIndex &index) const;
TreeItem *getRoot();
private:
//void setupModelData(const Container &cont, TreeItem *parent);
TreeItem *rootItem;
};
The Cpp file for the TreeModel
#include "TreeModel.h"
#include "TreeItem.h"
#include <qcoreapplication.h>
#include <qdebug.h>
#include "Container.h"
TreeItem *TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item)
return item;
}
return rootItem;
}
TreeModel::TreeModel(const QString &header, Container *data, QObject
*parent) : QAbstractItemModel(parent)
{
qDebug() << "First level done";
rootItem = new TreeItem( data);
}
TreeModel::~TreeModel()
{
delete rootItem;
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
TreeItem *item = getItem(index);
return QString::fromStdString(item->data().GetName());
//return QVariant::fromValue(item->data());
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return QVariant::fromValue(rootItem->data());
return QVariant();
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &val, int
role)
{
if (role != Qt::EditRole)
return false;
Container c = val.value<Container>();
TreeItem *item = getItem(index);
bool result = true;
item->setData(&c, val);
if (result)
emit dataChanged(index, index, { role });
return result;
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem *parentItem = getItem(parent);
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
bool TreeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &val, int role)
{
if (role != Qt::EditRole || orientation != Qt::Horizontal)
return false;
Container c = val.value<Container>();
bool result = rootItem->setData(&c, val);
if (result)
emit headerDataChanged(orientation, section, section);
return result;
}
bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows);
endInsertRows();
return success;
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsDropEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 1;
TreeItem *parentItem = getItem(parent);
//qDebug() << "the child count = " << parentItem->childCount() << parentItem->data().GetName().c_str();
return parentItem->childCount();
}
int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
return 1;
}
Container* TreeModel::GetContainer(const QModelIndex &index)
{
TreeItem *item = getItem(index);
return item->GetContainer();
}
void TreeModel::SetContainer(const QModelIndex &index, Container* Cont)
{
TreeItem *item = getItem(index);
item->setContainer(Cont);
}
static const char s_treeNodeMimeType[] = "application/x-treenode";
QStringList TreeModel::mimeTypes() const
{
return QStringList() << s_treeNodeMimeType;
}
QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData;
QByteArray data; //a kind of RAW format for datas
//QDataStream is independant on the OS or proc architecture
//serialization of C++'s basic data types, like char, short, int, char *, etc.
//Serialization of more complex data is accomplished
//by breaking up the data into primitive units.
QDataStream stream(&data, QIODevice::WriteOnly);
QList<TreeItem *> nodes;
//
foreach(const QModelIndex &index, indexes) {
TreeItem *node = getItem(index);
if (!nodes.contains(node))
nodes << node;
}
stream << QCoreApplication::applicationPid();
stream << nodes.count();
foreach(TreeItem *node, nodes) {
stream << reinterpret_cast<qlonglong>(node);
}
mimeData->setData(s_treeNodeMimeType, data);
return mimeData;
}
bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction
action, int row, int column, const QModelIndex &parent)
{
//Q_ASSERT(action == Qt::MoveAction);
//Q_UNUSED(column);
//test if the data type is the good one
if (!mimeData->hasFormat(s_treeNodeMimeType)) {
return false;
}
QByteArray data = mimeData->data(s_treeNodeMimeType);
QDataStream stream(&data, QIODevice::ReadOnly);
qint64 senderPid;
stream >> senderPid;
if (senderPid != QCoreApplication::applicationPid()) {
// Let's not cast pointers that come from another process...
return false;
}
TreeItem *parentNode = getItem(parent);
// Q_ASSERT(parentNode);
int count;
stream >> count;
if (row == -1) {
// valid index means: drop onto item. I chose that this should insert
// a child item, because this is the only way to create the first child
of an item...
// This explains why Qt calls it parent: unless you just support
replacing, this
// is really the future parent of the dropped items.
if (parent.isValid())
row = 0;
else
// invalid index means: append at bottom, after last toplevel
row = rowCount(parent);
}
//qDebug() << "The row" << row << parentNode->data().GetName().c_str() ;
for (int i = 0; i < count; ++i) {
// Decode data from the QMimeData
qlonglong nodePtr;
stream >> nodePtr;
TreeItem *node = reinterpret_cast<TreeItem *>(nodePtr);
// Adjust destination row for the case of moving an item
// within the same parent, to a position further down.
// Its own removal will reduce the final row number by one.
if (node->row() < row && parentNode == node->parent())
--row;
// Remove from old position
// qDebug() << "The remove item " << node->data().GetName().c_str();
removeItem(node);
// Insert at new position
//qDebug() << "Inserting into" << parent << row;
beginInsertRows(parent, row, row);
parentNode->insertChild(row, node);
endInsertRows();
++row;
}
return true;
}
void TreeModel::removeItem(TreeItem *node)
{
const int row = node->row();
QModelIndex idx = createIndex(row, 0, node);
TreeItem* itm = getItem(idx);
beginRemoveRows(idx.parent(), row, row);
node->parent()->removeChild(row);
endRemoveRows();
}
Qt::DropActions TreeModel::supportedDropActions() const
{
return Qt::MoveAction;
}
Qt::DropActions TreeModel::supportedDragActions() const
{
return Qt::MoveAction;
}
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
position++;
}
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount() - 1);
indentations << position;
}
}
else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
Container c;
// Append a new node to the current parent's list of children.
parents.last()->appendChild(new TreeItem(&c, parents.last()));
}
++number;
}
}
TreeItem *TreeModel::getRoot()
{
return rootItem;
}
childItems.removeOne(itm); and childItems.removeAt(row); just remove TreeItem * from your QList<TreeItem*> childItems; so memory is not freed.
You should explicitly call delete, once they are removed as you did with delete childItems.takeAt(position); for example.
I have a problem with the text height of a QTextDocument in my tree views item delegate.
My paint() and sizeHint() methods are almost exactly the same, but calculate different height of the text (the same text).
The difference of height varies depending on the font. If everything is written in one size, then the height is correct, but if I use different sizes, the heights doesn't match.
I read a topic about using font metrics, or calcuating the font height form paiter.boundingRect, but I don't know how to use this in a QTextDocument.
I thought that the problem is with the font, so I changed the font globaly to one with the code below. It didn't help.
//in main():
QFont font("MS Shell Dlg 2",8.25,50);
font.setPointSizeF(8.25);
QApplication::setFont(font);
My code in my custom itemDelegate is:
QSize MyItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
int scrollBarrWidth = -2;
if(scrollBarVisible){
scrollBarrWidth = 10; //options.widget->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
}
int checkboxWidth = 0;
if(checkboxesForConferenceActive){
checkboxWidth = 18;
}
QRect rectLeft(checkboxWidth,0,26,26);
QRect rectRight((options.widget->rect().right()-30-scrollBarrWidth),0,26,26);
QRect rectCenter((rectLeft.right()+5),-2,(options.widget->rect().width()-(rectLeft.width()+rectRight.width()+scrollBarrWidth+checkboxWidth+8+4)),26);
QRect boundingRect;
QString nameText(index.data(Qt::UserRole).toString());
QString extNoText(index.data(Qt::UserRole+1).toString());
QString phoneText(index.data(Qt::UserRole+100).toString());
QTextDocument textDoc;
textDoc.setDefaultFont(options.font);
textDoc.setTextWidth(rectCenter.width());
textDoc.setDocumentMargin(0);
QTextCursor textCursor(&textDoc);
QString wholeText("");
QImage statusIcon(*icoChooser->choseCustomStateImageForSubscriber(index.data(Qt::UserRole+12).toInt()));
bool wczytajfote = !statusIcon.isNull();
wholeText.append("<b>");
wholeText.append(nameText);
wholeText.append("</b>");
textCursor.insertHtml(wholeText);
if(wczytajfote){
textCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
textCursor.insertImage(statusIcon.scaledToWidth(12));
textCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
} else {
textCursor.insertHtml(" ");
}
wholeText.clear();
if(!extNoText.isEmpty()){
wholeText.append("<font color = #005c99>");
wholeText.append(QString(" ("+extNoText+") "));
wholeText.append("</font>");
wholeText.append("<br><font style=\"font-size:10px; color: #6d6d78\">");
wholeText.append(index.data(Qt::UserRole+4).toString());
wholeText.append("</font>");
textCursor.insertHtml(wholeText);
if(textDoc.idealWidth() < rectCenter.width() ){
QRect clip(0,0,textDoc.idealWidth(), textDoc.size().height());
return QSize(textDoc.idealWidth(), textDoc.size().height()+4);
} else {
QRect clip(0,0,textDoc.idealWidth(), textDoc.size().height());
return QSize(textDoc.idealWidth(), textDoc.size().height()+4);
}
} else {
wholeText.append("<br>");
wholeText.append("<font size = 2 color = #005c99>");
wholeText.append(QString(" ("+phoneText+") "));
wholeText.append("</font>");
textCursor.insertHtml(wholeText);
QRect clip(0,0,textDoc.idealWidth(), textDoc.size().height());
return QSize(textDoc.idealWidth(), textDoc.size().height()+6);
}
}
and the paint method is almost exactly the same.
void MyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
painter->setRenderHint(QPainter::TextAntialiasing, true);
options.text = "";
int scrollBarrWidth = -2;
if(scrollBarVisible){
scrollBarrWidth = 10; //options.widget->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
}
int checkboxWidth = 0;
if(checkboxesForConferenceActive){
checkboxWidth = 18;
}
QRect rectLeft(checkboxWidth,0,26,26);
QRect rectRight((options.widget->rect().right()-30-scrollBarrWidth),0,26,26); //obliczam na podstawie prostokąta, który jest przed nim i który ma dynamicznie ustawiane wymiary
QRect rectCenter((rectLeft.right()+5),-2,(options.widget->rect().width()-(rectLeft.width()+rectRight.width()+scrollBarrWidth+checkboxWidth+8+4)),26); //obliczam prostokąt na podstawie wcześniejszego prostokąta i prostokąta wymiarów okna
QRect boundingRect;
QString nameText(index.data(Qt::UserRole).toString());
QString extNoText(index.data(Qt::UserRole+1).toString());
QString phoneText(index.data(Qt::UserRole+100).toString());
painter->translate(options.widget->rect().left(), options.rect.top());
QTextDocument textDoc;
textDoc.setTextWidth(rectCenter.width());
QTextCursor textCursor(&textDoc);
QString wholeText("");
QImage statusIcon(*icoChooser->choseCustomStateImageForSubscriber(index.data(Qt::UserRole+12).toInt()));
bool wczytajfote = !statusIcon.isNull();
wholeText.append("<b>");
wholeText.append(nameText);
wholeText.append("</b>");
textCursor.insertHtml(wholeText);
if(wczytajfote){
textCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
textCursor.insertImage(statusIcon.scaledToWidth(12));
textCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
} else {
textCursor.insertHtml(" ");
}
wholeText.clear();
if(!extNoText.isEmpty()){
wholeText.append("<font size = 2 color = #005c99>");
wholeText.append(QString(" ("+extNoText+") "));
wholeText.append("</font>");
wholeText.append("<br><font style=\"font-size:10px; color: #6d6d78\">");
wholeText.append(index.data(Qt::UserRole+4).toString());
wholeText.append("</font>");
} else {
wholeText.append("<br>");
wholeText.append("<font size = 2 color = #005c99>");
wholeText.append(QString(" ("+phoneText+") "));
wholeText.append("</font>");
}
textCursor.insertHtml(wholeText);
painter->translate(rectCenter.left(), rectCenter.top());
QRect clip(0,0,textDoc.size().width(), textDoc.size().height());
textDoc.drawContents(painter, clip);
painter->restore();
}
}
The text in sizeHint is used to determine the item height, so now it looks like the pic below:
I'm using Qt 5.7.1 MSVC 2015 64 bit on windows, but the problem is the same on Ubuntu 16.04 with gcc.
(Posted on behalf of the asker)
It turned out to be my fault all along.
The following line responsible in the sizeHint() method was responsible:
textDoc.setDocumentMargin(0);
I have a class which inherits QAbstractItemDelegate and I use QTextDocument inside the paint() method. My model contains two items, but when I run my qt application, the items are drawn in the first item of QListView.
CODE
void ProductItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
bool selected = (option.state & QStyle::State_Selected) == QStyle::State_Selected;
if (selected)
{
painter->fillRect(option.rect, option.palette.highlight());
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
if (selected)
{
painter->setPen(option.palette.highlightedText().color());
}
else
{
painter->setPen(option.palette.text().color());
}
mTextDocument.drawContents(painter);
painter->restore();
}
QSize ProductItemDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
MyItem *myItem = index.data(Qt::UserRole + 1).value<MyItem *>();
mTextDocument->clear();
mTextDocument->setDefaultFont(option.font);
mTextDocument->setPageSize(QSizeF(option.rect.width(), -1));
QTextCursor cursor = QTextCursor(mTextDocument);
QVector<QTextLength> columnConstraints;
columnConstraints << QTextLength(QTextLength::PercentageLength, 60);
columnConstraints << QTextLength(QTextLength::PercentageLength, 30);
columnConstraints << QTextLength(QTextLength::PercentageLength, 10);
QTextTableFormat tableFormat;
tableFormat.setBorder(1);
tableFormat.setBorderBrush(QBrush(Qt::black));
tableFormat.setColumnWidthConstraints(columnConstraints);
QTextTable *table = cursor.insertTable(2, 3, tableFormat);
table->mergeCells(0, 0, 1, 3);
QTextCursor cellCursor;
QTextTableCell cell00 = table->cellAt(0, 0);
cellCursor = cell00.firstCursorPosition();
cellCursor.insertText(myItem->name());
QTextTableCell cell10 = table->cellAt(1, 0);
cellCursor = cell10.firstCursorPosition();
cellCursor.insertText(myItem->text1());
QTextTableCell cell11 = table->cellAt(1, 1);
cellCursor = cell11.firstCursorPosition();
cellCursor.insertText(myItem->text2());
return mTextDocument->size().toSize();
}
These are the result of the code above.
The item was not drawn in second entry.
Both items are painted in the first entry.
You should place your painter to the right spot before painting with it.
After the first painter->save() add :
painter->resetTransform();
painter->translate(option.rect.topLeft());
I'm working on an applications launcher and decided to use a QListView to display the Items. I wrote a Model and an ItemDelegate but it seems to be wasting memory somewhere I can't find.
The issue is that even scrolling on the app list makes my program to consume more and more memory.
I think it is related to the way I paint but I cant see where the mistake is.
Paint method on the QStyledItemDelegate
void DashViewItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
QString name = index.model()->data(index, Qt::DisplayRole).toString();
QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
QRect iconRect = QRect(option.rect.x() + 10, option.rect.y(), option.rect.width() - 20, option.rect.height() - 43);
QRect textRect = QRect(option.rect.x(), option.rect.y() + 60, option.rect.width(), option.rect.height() - 60);
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
icon.paint(painter, iconRect, Qt::AlignCenter | Qt::AlignTop);
QTextLayout textLayout(name);
textLayout.setFont(option.font);
int widthUsed = 0;
int lineCount = 0;
textLayout.beginLayout();
while (++lineCount < 3) {
QTextLine line = textLayout.createLine();
if (!line.isValid())
break;
line.setLineWidth(option.rect.width());
widthUsed += line.naturalTextWidth();
}
textLayout.endLayout();
widthUsed += option.rect.width();
QString newText = painter->fontMetrics().elidedText(name, Qt::ElideRight, widthUsed);
painter->drawText(textRect, Qt::AlignCenter | Qt::AlignTop | Qt::TextWordWrap , newText);
painter->save();
}
help will be really appreciated
I have a QGraphicPixmapItem which holds a png image, present on the scene.
The Image itself is a rectangular image. My requirement is that, I should be able to resize/scale the image when I resize the rectangular image on mouse move event.
I can resize the rectangle by dragging on any side of the rectangular image.
The problem is that, I am able to resize the rectangular image by dragging any of its sides but the original image is awefully distorted beyond recognition. The image basically
turns into a lump of solid mass on continuous resizing( expanding/decreaing width or height).
How to achieve the resizing/scaling of the image in Qt without damaging the original image too much? What I experienced is not pixilation but smethg worse.
The code below is a snapshot of mouseMoveEvent() of QGraphicsPixmapItem, implemented for resizing the rectangle by dragging right/left side of rectangle image.
void PersonSizeGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
const QPointF event_pos = event->pos();
const QPointF event_scene_pos = event->scenePos();
QPixmap current_pixmap = this->pixmap();
QImage current_image = current_pixmap.toImage();
QRect current_image_rect = current_image.rect();
QPoint current_top_left = current_image_rect.topLeft();
QPoint current_bottom_right = current_image_rect.bottomRight();
if((event->scenePos().x() > this->scene()->width()) || (event->scenePos().y() > this->scene()->height())
|| (event->scenePos().x() < 0) || (event->scenePos().y() < 0) )
{
return;
}
if( this->cursor().shape() == Qt::SizeHorCursor )
{
if(rect_right_condition)
{
new_rect = QRect( current_top_left, QPoint( event->pos().x(), current_bottom_right.y()) );
scaled_pixmap = QPixmap::fromImage(current_image.scaled(QSize(new_rect.width(),new_rect.height()),Qt::IgnoreAspectRatio,Qt::FastTransformation));
setPixmap(scaled_pixmap);
}
if(rect_left_condition)
{
new_rect = QRect( QPoint(event_pos.x(), 0), current_bottom_right );
scaled_pixmap = QPixmap::fromImage(current_image.scaled(QSize(new_rect.width(),new_rect.height()),Qt::IgnoreAspectRatio,Qt::FastTransformation));
setPixmap(scaled_pixmap);
QPoint new_top_left = new_rect.topLeft();
QPointF mapped_topLeft = mapToParent(QPointF(new_top_left.x(),new_top_left.y()));
this->setPos(mapped_topLeft);
rect_resize_occurred = true;
}
}
}
The image basically turns into a lump of solid mass on continuous resizing( expanding/decreaing width or height).
This is normal. What you need to do is change the method for the effect you desire. Rather than constantly changing the same image, start with a source image of the maximum size you expect the user will require. This image will not be altered.
In the mouseMove function, update member variables to store the size of the image that you display.
Then, in the PersonSizeGraphicsItem's paint function, use drawImage to specify the source image and the target rect to draw the image at the size that was updated in mouseMoveEvent:-
void QPainter::drawImage(const QRectF & rectangle, const QImage & image);
So, by keeping the source image at its original size, you won't be distorting it more and more with each successive resize.
class PersonSizeGraphicsItem : public QGraphicsItem
{
public:
PersonSizeGraphicsItem(QGraphicsItem* parent);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0)
private:
QRectF m_imageRect;
QImage m_image;
};
void PersonSizeGraphicsItem::mouseMoveEvent()
{
// Update the image rect here
}
void PersonSizeGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem *, QWidget *)
{
painter->drawImage(m_imageRect, m_image);
}
Reimplemented code based on Merlin069's answer:
PersonSizeGraphicsItem::PersonSizeGraphicsItem(const QPixmap &pixmap, QGraphicsScene *scene)
:QGraphicsPixmapItem(pixmap, 0, scene)
{
this->setFlag(QGraphicsItem::ItemIsMovable, true);
this->setFlag(QGraphicsItem::ItemIsSelectable, true);
this->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
this->setFlag(QGraphicsItem::ItemIsFocusable, true);
this->setFocus(Qt::MouseFocusReason);
this->setAcceptHoverEvents(true);
//this->setScale(0.5);
rect_left_condition = false;
rect_right_condition = false;
rect_top_condition = false;
rect_bottom_condition = false;
rect_resize_occurred = false;
image_rect = QRect();
image_rect = this->pixmap().toImage().rect();
}
void PersonSizeGraphicsItem::setSourceImage(const QImage& source_image)
{
this->source_image = source_image;
}
void PersonSizeGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
const QPointF event_pos = event->pos();
const QPointF event_scene_pos = event->scenePos();
QPoint current_top_left = image_rect.topLeft();
QPoint current_bottom_right = image_rect.bottomRight();
if((event->scenePos().x() > this->scene()->width()) || (event->scenePos().y() > this->scene()->height())
|| (event->scenePos().x() < 0) || (event->scenePos().y() < 0) )
{
return;
}
if( this->cursor().shape() == Qt::SizeHorCursor )
{
if(rect_right_condition)
{
image_rect = QRect( current_top_left, QPoint( event->pos().x(), current_bottom_right.y()) );
}
if(rect_left_condition)
{
image_rect = QRect( QPoint(event_pos.x(), 0), current_bottom_right);
QPoint new_top_left = image_rect.topLeft();
QPointF mapped_topLeft = mapToParent(QPointF(new_top_left.x(),new_top_left.y()));
this->setPos(mapped_topLeft);
rect_resize_occurred = true;
//qDebug() << "new rectangle top left**:" << this->pixmap().rect().topLeft();
}
}
if( this->cursor().shape() == Qt::SizeVerCursor )
{
if(rect_bottom_condition)
{
image_rect = QRect(current_top_left, QPoint(current_bottom_right.x(), event->pos().y()));
}
if(rect_top_condition)
{
image_rect = QRect(QPoint(0, event_pos.y()), current_bottom_right);
QPoint new_top_left = image_rect.topLeft();
QPointF mapped_topLeft = mapToParent(QPointF(new_top_left.x(),new_top_left.y()));
this->setPos(mapped_topLeft);
}
}
this->update();
}
void PersonSizeGraphicsItem::paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->drawImage(image_rect, source_image);
}