0

我正在利用 Qt 使用的 Model View Delegate 框架来显示具有自定义“视图”或布局的对象列表。

背景:

我需要在列表中显示国旗、国家名称、城市名称和可选的“高级”评级星,用户可以选择。

为此,我使用以下组件:

  • Model - a QStandardItemModel,见doc page,它保存了所有的数据QListView和交互关注点,doc page

  • Delegate - 为了以自定义布局方式绘制/显示数据,我使用 a QStyledItemDelegate,请参阅文档页面。

  • 视图 - 对于视图,一个简单的QListView就足够了,因为这是一个包含具有自定义布局的对象集合的单列列表。

教程和帮助:

使用以下教程,1. 一个消息查看器应用程序,展示了如何实现一个详细的model-delegate-view概念,2. 诺基亚智能手机的简单消息菜单系统的基础知识,我已经能够相对容易地创建我想要的布局QListView

问题:

我需要将QStandardItem项目添加到我的模型中,这些项目将添加到我的视图中。我这样做了,并且由paint覆盖方法绘制每个项目的委托确认。

但是,在运行时,QListView仅显示 1 个项目。但我可以使用向上/向下箭头键来选择列表中的其他各种项目。

请看下面的代码:

设置 MVC(委托):

mainwindow.h

//...
QStandardItemModel *modelServers;
ServerDelegate* serverDelegate;
//...

mainwindow.cpp

modelServers = new QStandardItemModel(0);
serverDelegate = new ServerDelegate(0);

ui->listServers->setModel(modelServers);
ui->listServers->setItemDelegate(serverDelegate);

并将项目(QStandardItem's)添加到列表中:

for (int i = 0; i < someList->length(); ++i) {
    Server server = someList->value(i);
    QStandardItem *item = new QStandardItem();
    item->setData(server.getCountryName,         item->setData(QPixmap("", "PNG"), ServerDelegate::DataRole::CountryFlag);
    item->setData(server.getCountry(), ServerDelegate::DataRole::CountryText);
    item->setData(server.getCity(), ServerDelegate::DataRole::CityText);
    item->setData(i, ServerDelegate::ListIndex);
    //...
    modelServer->appendRow(item)
}

最后列表显示数据,但列表仅填充项目,但只有第一个具有可见的文本和图像。

注意:向下滚动时,仅顶部项目可见,请参见下图示例。

例如

初始加载列表:

在此处输入图像描述

向下滚动一个

在此处输入图像描述

选择没有文本/图像的项目:

在此处输入图像描述

附加代码如下:

ServerDelegate处理自定义布局的类

ServerDelegate.h

#ifndef SERVERDELEGATE_H
#define SERVERDELEGATE_H

#include <QApplication>
#include <QtGui>
#include <QStyledItemDelegate>
#include <QtWidgets>
#include <qglobal.h>

#include "global.h"

class ServerDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    ServerDelegate(QStyledItemDelegate* parent = 0);
    virtual ~ServerDelegate();

    enum DataRole{
        CountryText = Qt::UserRole + 100,
        CityText = Qt::UserRole+101,
        CountryFlag = Qt::UserRole+102,
        SideIconFlag = Qt::UserRole+103,
        ListIndex = Qt::UserRole+105
    };

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;

private:
    QFont fontCountry, fontCity;
};

#endif // SERVERDELEGATE_H

ServerDelegate.cpp

#include "serverdelegate.h"

ServerDelegate::ServerDelegate(QStyledItemDelegate *parent)
    : QStyledItemDelegate(parent)
{
    fontCountry = QApplication::font();
    fontCountry.setBold(true);
    fontCountry.setPointSize(QApplication::font().pointSize() + 3);

    fontCity = QApplication::font();
    fontCity.setItalic(true);
    fontCity.setPointSize(QApplication::font().pointSize() - 1);
}

ServerDelegate::~ServerDelegate(){
}

QSize ServerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const{
    Q_UNUSED(index)
    QSize totalCountrySize = QPixmap("","").size();
    QSize totalSideIcon = QPixmap(":/res/images/premium", "PNG").size();

    QFontMetrics fmCountry(fontCountry);
    QFontMetrics fmCity(fontCity);

    int fontHeight = (2 * 2) + (2 * 5) + fmCountry.height() + fmCity.height();
    int iconHeight = (2 * 2) + (totalCountrySize.height() > totalSideIcon.height() ? totalCountrySize.height() : totalSideIcon.height());
    int height = (fontHeight > iconHeight) ? fontHeight : iconHeight;

    int width = option.rect.width();
    QSize size = QSize(width, height);

    return size;
}

void ServerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
    QStyledItemDelegate::paint(painter, option, index);

    QRect rec = option.rect;

    painter->save();
    painter->setClipRect(rec);

    QString countryText = index.data(DataRole::CountryText).toString();
    QString cityText = index.data(DataRole::CityText).toString();
    QPixmap countryFlag = QPixmap(qvariant_cast<QPixmap>(index.data(DataRole::CountryFlag)));
    QPixmap sideIcon = qvariant_cast<QPixmap>(index.data(DataRole::SideIconFlag));

    // Get a rectangle by size x, y.
    // With cooridinates [0,0]; [x,0]; [x,y]; [0,y]
    QRect topLine = option.rect,
            bottomLine = option.rect;

    // create top 'seperator' of X px's width, green in color;
    topLine.setTop(0);
    topLine.setLeft(0);
    topLine.setRight(option.rect.width());
    topLine.setBottom(2); // 1px down

    painter->setPen(QColor(116, 183, 151));
    painter->fillRect(topLine, QColor(116, 183, 151));
    painter->drawRect(topLine);

    // create bottom 'seperator' of X px's width, green in color;
    bottomLine.setTop(option.rect.height() - 2);
    bottomLine.setLeft(0);
    bottomLine.setRight(option.rect.width());
    bottomLine.setBottom(option.rect.height()); // 1px down

    painter->setPen(QColor(116, 183, 151));
    painter->fillRect(bottomLine, QColor(116, 183, 151));
    painter->drawRect(bottomLine);

    // create background rectangle
    QRect content(0, topLine.bottom(), option.rect.width(), bottomLine.top());

    painter->setPen(QColor(116, 183, 151));
    painter->fillRect(content, QColor(116, 183, 151));
    painter->drawRect(content);

    // create content rectangles from content container.

    QRect rectCountryFlag = content,
            rectSideIcon = content;

    //    create country icon rectangle
    QSize countryFlagSize = countryFlag.size();
    int cFPos = (rectCountryFlag.height() / 2) - (countryFlagSize.height() / 2) - 8;
    rectCountryFlag.setTop(cFPos);
    rectCountryFlag.setBottom(content.height() - cFPos);
    rectCountryFlag.setLeft(20 - 8);
    rectCountryFlag.setRight(20 + 16 + countryFlagSize.width());

    painter->drawPixmap(rectCountryFlag, countryFlag);

    //    create side icon rectangle
    QSize sideIconSize = sideIcon.size();
    int siPos = (rectSideIcon.height() / 2) - (sideIconSize.height() / 2) - 4;
    rectSideIcon.setTop(siPos);
    rectSideIcon.setBottom(content.height() - siPos);
    rectSideIcon.setLeft(rec.width() - (10 + 8 + sideIconSize.width()));
    rectSideIcon.setRight(rec.width() - 10);

    painter->drawPixmap(rectSideIcon, sideIcon);

    const QRect textContent(rectCountryFlag.right() + 5 + 20, content.top() + 5,
                            rectSideIcon.left() - 5, content.bottom() - 5);

    // create country text rectangle

    QRect rectCountryText = content,
            rectCityText = content;

    rectCountryText.setLeft(textContent.left());
    rectCountryText.setTop(textContent.top());
    rectCountryText.setRight(textContent.right());
    rectCountryText.setBottom(qRound(textContent.height() * 0.6) - 5);

    painter->setPen(QColor(26, 26, 26));
    painter->setFont(fontCountry);
    painter->drawText(rectCountryText, countryText);

    // create city text rectangle

    rectCityText.setLeft(textContent.left() + ( 2 * 5));
    rectCityText.setTop(rectCountryText.bottom() + 5);
    rectCityText.setRight(textContent.right());
    rectCityText.setBottom(textContent.height());

    painter->setPen(QColor(77, 77, 77));
    painter->setFont(fontCity);
    painter->drawText(rectCityText, cityText);

    // restore painter
    painter->restore();
}
4

2 回答 2

1

paint您描述的问题是由于自定义QStyledItemDelegate中方法的重新实现错误引起的。您假设 (0, 0) 作为原点并绘制与该点相关的所有内容。但是,每个项目都有一个垂直偏移,您必须通过使用QStyleOption::rect的几何来考虑这一点。

这是一个带有自定义绘图和动画的精美委托示例,可进一步帮助您。

于 2017-12-13T03:17:33.370 回答
1

在我发布答案之前,向scopchanov 大喊大叫,感谢他提供的帮助和示例,同时暗示每次调用paint.

问题解释:

提供的偏移值与方法返回的值option.rect相同。就我而言,我关心的是价值为.QSizesizeHinty offset56

我的理解

使用迭代偏移值的概念,我修改了我的代码(之前具有所需的输出),我假设该paint方法是从 的原点绘制的[0,0],实际上我必须考虑偏移量,即我不是提供了一个放置在下一个项目位置的容器,而不是给我下一个项目起点的位置,并且需要从那里构建项目。QListViewQListView

解决方案:

在我的情况下,我有所需的项目布局,但由于 [0,0] 原点,项目被绘制在彼此之上,从而导致single item效果。在更改了一些值并构建了各种依赖层次结构后,我看到了一个具有所需布局的项目列表。

编码

更新了 ServerDelegate.cpp

#include "serverdelegate.h"

ServerDelegate::ServerDelegate(QStyledItemDelegate *parent)
    : QStyledItemDelegate(parent)
{
    fontCountry = QApplication::font();
    fontCountry.setBold(true);
    fontCountry.setPointSize(QApplication::font().pointSize() + 3);

    fontCity = QApplication::font();
    fontCity.setItalic(true);
    fontCity.setPointSize(QApplication::font().pointSize() - 1);
}

ServerDelegate::~ServerDelegate(){
}

QSize ServerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const{
    Q_UNUSED(index)
    QSize totalCountrySize = Global::getCountryFlagFromCache(index.data(DataRole::CountryText).toString()).size();
    QSize totalSideIcon = QPixmap(":/res/images/premium", "PNG").size();

    QFontMetrics fmCountry(fontCountry);
    QFontMetrics fmCity(fontCity);

    int fontHeight = (2 * AppGlobal::Style_List_Seperator_Width) + (2 * AppGlobal::Style_List_Text_Item_Margin) + fmCountry.height() + fmCity.height();
    int iconHeight = (2 * AppGlobal::Style_List_Seperator_Width) + (totalCountrySize.height() > totalSideIcon.height() ? totalCountrySize.height() : totalSideIcon.height());
    int height = (fontHeight > iconHeight) ? fontHeight : iconHeight;

    int width = option.rect.width();
    QSize size = QSize(width, height);

    return size;
}

void ServerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
    QStyledItemDelegate::paint(painter, option, index);

    QFontMetrics fmCountry(fontCountry);
    QFontMetrics fmCity(fontCity);

    QRect rec = option.rect;

    painter->save();
    painter->setClipRect(rec);

    QString countryText = index.data(DataRole::CountryText).toString();
    QString cityText = index.data(DataRole::CityText).toString();
    QPixmap countryFlag = QPixmap(qvariant_cast<QPixmap>(index.data(DataRole::CountryFlag)));
    QPixmap sideIcon = qvariant_cast<QPixmap>(index.data(DataRole::SideIconFlag));

    // Get a rectangle by size x, y.
    // With cooridinates [0,0]; [x,0]; [x,y]; [0,y]
    QRect topLine = option.rect,
            bottomLine = option.rect;

    // create top 'seperator' of X px's width, green in color;
    topLine.setTop(rec.top());
    topLine.setLeft(rec.left());
    topLine.setRight(rec.right());
    topLine.setBottom(rec.top() + AppGlobal::Style_List_Seperator_Width); // 1px down

    painter->setPen(AppGlobal::Style_List_Seperator_Color);
    painter->fillRect(topLine, AppGlobal::Style_List_Seperator_Color);
    painter->drawRect(topLine);

    // create bottom 'seperator' of X px's width, green in color;
    bottomLine.setTop(rec.bottom() - AppGlobal::Style_List_Seperator_Width);
    bottomLine.setLeft(rec.left());
    bottomLine.setRight(rec.right());
    bottomLine.setBottom(rec.bottom()); // 1px down

    painter->setPen(AppGlobal::Style_List_Seperator_Color);
    painter->fillRect(bottomLine, AppGlobal::Style_List_Seperator_Color);
    painter->drawRect(bottomLine);

    // create background rectangle
    QRect content(rec.left(), topLine.bottom(), (rec.right() - rec.left()), (bottomLine.top() - topLine.bottom()));

    painter->setPen(AppGlobal::Style_List_Background_Color);
    painter->fillRect(content, ((option.state & QStyle::State_MouseOver) ? AppGlobal::Style_List_Hover_Color : AppGlobal::Style_List_Background_Color ));
    painter->drawRect(content);

    // create content rectangles from content container.

    QRect rectCountryFlag = content,
            rectSideIcon = content;

    //    create country icon rectangle
    QSize countryFlagSize = countryFlag.size();
    int cFPos = ((rectCountryFlag.bottom() - rectCountryFlag.top()) / 2) - (countryFlagSize.height() / 2) - 8;
    rectCountryFlag.setTop(rectCountryFlag.top() + cFPos);
    rectCountryFlag.setBottom(content.bottom() - cFPos);
    rectCountryFlag.setLeft(AppGlobal::Style_List_Left_Item_Margin - 8);
    rectCountryFlag.setRight(AppGlobal::Style_List_Left_Item_Margin + 16 + countryFlagSize.width());

    painter->drawPixmap(rectCountryFlag, countryFlag);

    //    create side icon rectangle
    QSize sideIconSize = sideIcon.size();
    int siPos = ((rectSideIcon.bottom() - rectSideIcon.top()) / 2) - (sideIconSize.height() / 2) - 4;
    rectSideIcon.setTop(rectSideIcon.top() + siPos);
    rectSideIcon.setBottom(content.bottom() - siPos);
    rectSideIcon.setLeft(rec.width() - (AppGlobal::Style_List_Right_Item_Margin + 8 + sideIconSize.width()));
    rectSideIcon.setRight(rec.width() - AppGlobal::Style_List_Right_Item_Margin);

    painter->drawPixmap(rectSideIcon, sideIcon);

    int textContentLeft = rectCountryFlag.right() + AppGlobal::Style_List_Text_Item_Margin + AppGlobal::Style_List_Left_Item_Margin,
            textContentTop = content.top() + AppGlobal::Style_List_Text_Item_Margin;

    const QRect textContent( textContentLeft , textContentTop,
                            (rectSideIcon.left() - AppGlobal::Style_List_Text_Item_Margin) - textContentLeft, (content.bottom() - AppGlobal::Style_List_Text_Item_Margin) - textContentTop);

    // create country text rectangle

    QRect rectCountryText = content,
            rectCityText = content;

    rectCountryText.setLeft(textContent.left());
    rectCountryText.setTop(textContent.top());
    rectCountryText.setRight(textContent.right());
    rectCountryText.setBottom(textContent.top() + fmCountry.height());

    painter->setPen(AppGlobal::Style_Heading_Color);
    painter->setFont(fontCountry);
    painter->drawText(rectCountryText, countryText);

    // create city text rectangle

    rectCityText.setLeft(textContent.left() + ( 2 * AppGlobal::Style_List_Text_Item_Margin));
    rectCityText.setTop(rectCountryText.bottom());
    rectCityText.setRight(textContent.right());
    rectCityText.setBottom(textContent.bottom() + fmCity.height());

    painter->setPen(AppGlobal::Style_SubText_Color);
    painter->setFont(fontCity);
    painter->drawText(rectCityText, cityText);

    // restore painter
    painter->restore();
}
于 2017-12-14T09:47:31.417 回答