QListView 仅在视图中显示单个项目
QListView only showing a single item in the view
我正在利用 Qt 使用的模型视图委托框架来显示具有自定义 'views' 或布局的对象列表。
背景:
我需要在列表中显示国旗、国家名称、城市名称和可选的 'premium' 评级星,用户可以 select 编辑。
为此,我使用了以下组件:
模型 - QStandardItemModel
,参见 doc 页面,其中包含 QListView
的所有数据和交互问题,文档页面
Delegate - 对于 drawing/displaying 自定义布局方式中的数据,我使用 QStyledItemDelegate
,参见 doc 页面。
View - 对于视图,一个简单的 QListView
就足够了,因为这是一个包含具有自定义布局的对象集合的单列列表。
教程和帮助:
使用以下教程,
1. messageviewer 应用程序展示了如何根据以下内容实施详细的 model-delegate-view
概念,
2. 诺基亚智能手机的简单 messaging menu 系统的基础知识,我已经能够相对轻松地为我的 QListView
.
创建所需的布局
问题:
我需要将 QStandardItem
个项目添加到我的模型中,这些项目将添加到我的视图中。我这样做了,并且委托确认了每个项目都是由 paint
覆盖方法绘制的。
但是,在运行时,QListView
只显示 1 个项目。但是我可以使用 up/down 箭头键来 select 列表中的其他各种项目。
请看下面的代码:
正在设置 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
):
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)
}
最后列表显示了数据,但是列表只填充了项目,但只有第一个有可见的文本和图像。
注意:向下滚动时,只有顶部项目可见,示例见下图。
例如
初始加载列表:
向下滚动一次
选择没有 text/images 的项目:
下面的附加代码:
ServerDelegate
class 处理自定义布局
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();
}
您描述的问题是由于在自定义 QStyledItemDelegate. You assume (0, 0) as the origin and paint everything relative to that point. However, each item has a vertical offset, which you have to take into account by using the geometry of QStyleOption::rect 中重新实现 paint
方法时出错造成的。
这里是 an example 带有自定义绘图和动画的奇特代表,可以进一步帮助您。
在我 post 我的回答之前,向 scopchanov 大喊大叫,感谢他提供的帮助和示例,还暗示了每次调用 paint
时都会发生偏移的事实,我很幸运地没有意识到这一点。
问题解释:
option.rect
提供的偏移值与 sizeHint
方法返回的 QSize
相同。就我而言,我关心的是 y offset
的值 56
。
我的理解
使用迭代偏移值的概念,我修改了我的代码(之前有所需的输出),其中我假设 paint
方法是从 [0,0]
的原点绘制的,事实上,我必须考虑偏移量,即我没有得到放置在下一个 QListView
项目位置的 container,而是得到了下一个 QListView
的项目起点,我需要从那里构建项目。
解决方法:
在我的例子中,我有想要的项目布局,但由于 [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();
}
我正在利用 Qt 使用的模型视图委托框架来显示具有自定义 'views' 或布局的对象列表。
背景:
我需要在列表中显示国旗、国家名称、城市名称和可选的 'premium' 评级星,用户可以 select 编辑。
为此,我使用了以下组件:
模型 -
QStandardItemModel
,参见 doc 页面,其中包含QListView
的所有数据和交互问题,文档页面Delegate - 对于 drawing/displaying 自定义布局方式中的数据,我使用
QStyledItemDelegate
,参见 doc 页面。View - 对于视图,一个简单的
QListView
就足够了,因为这是一个包含具有自定义布局的对象集合的单列列表。
教程和帮助:
使用以下教程,
1. messageviewer 应用程序展示了如何根据以下内容实施详细的 model-delegate-view
概念,
2. 诺基亚智能手机的简单 messaging menu 系统的基础知识,我已经能够相对轻松地为我的 QListView
.
问题:
我需要将 QStandardItem
个项目添加到我的模型中,这些项目将添加到我的视图中。我这样做了,并且委托确认了每个项目都是由 paint
覆盖方法绘制的。
但是,在运行时,QListView
只显示 1 个项目。但是我可以使用 up/down 箭头键来 select 列表中的其他各种项目。
请看下面的代码:
正在设置 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
):
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)
}
最后列表显示了数据,但是列表只填充了项目,但只有第一个有可见的文本和图像。
注意:向下滚动时,只有顶部项目可见,示例见下图。
例如
初始加载列表:
向下滚动一次
选择没有 text/images 的项目:
下面的附加代码:
ServerDelegate
class 处理自定义布局
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();
}
您描述的问题是由于在自定义 QStyledItemDelegate. You assume (0, 0) as the origin and paint everything relative to that point. However, each item has a vertical offset, which you have to take into account by using the geometry of QStyleOption::rect 中重新实现 paint
方法时出错造成的。
这里是 an example 带有自定义绘图和动画的奇特代表,可以进一步帮助您。
在我 post 我的回答之前,向 scopchanov 大喊大叫,感谢他提供的帮助和示例,还暗示了每次调用 paint
时都会发生偏移的事实,我很幸运地没有意识到这一点。
问题解释:
option.rect
提供的偏移值与 sizeHint
方法返回的 QSize
相同。就我而言,我关心的是 y offset
的值 56
。
我的理解
使用迭代偏移值的概念,我修改了我的代码(之前有所需的输出),其中我假设 paint
方法是从 [0,0]
的原点绘制的,事实上,我必须考虑偏移量,即我没有得到放置在下一个 QListView
项目位置的 container,而是得到了下一个 QListView
的项目起点,我需要从那里构建项目。
解决方法:
在我的例子中,我有想要的项目布局,但由于 [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();
}