对于一些数据量小的列表我们使用QListWidget往往能满足开发的需求,但是对于大数据量的展示来说(几十万,上百万)来说的话,全部加载是一个不是很合适的方法。因此我们使用MVC,这里可能应该叫MVD(model,view,delegation)。这样做个人认为有两个好处:
1.效率问题,view和delegation配合只负责从model中取显示需要的数据,因此带来高效。
2.model的数据可以由不同view来显示。
这里的话封装下通用QListView的通用方法(Tree和Table之后做),去除其中一些不必要业务逻辑(有空我自己精简下,目前没时间),可作为通用模板使用,基本就是三个类,有不明白的可以私信我。
一.View :.h文件
/// @brief 视图
class PreviewLeftWin:public QListView
{
Q_OBJECT
public:
PreviewLeftWin(QWidget* parent = 0);
~PreviewLeftWin();
void Initialize(); //总体初始化
void InitUi(); //初始化Ui
void SwitchStyleSheet(); //设置样式表
void ClearViewData(); //清空当前视图的数据
void LoadData(QVector>, QSharedPointer); //加载数据,丙切换选择到某数据
private:
virtual void startDrag(Qt::DropActions supportedActions) Q_DECL_OVERRIDE; //开始拖拽事件
virtual void mousePressEvent(QMouseEvent* e); //鼠标按下事件
signals:
void SigShowResource(QSharedPointer); //选中项改变通知外部改变
private:
PreviewLeftWinModel* m_model; //模型
PreviewLeftWinItemDelegate* m_delegate; //委托
BFScrollBar* m_bfscroll; //滚动条
private slots:
virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
};
View:cpp文件
PreviewLeftWin::PreviewLeftWin(QWidget* parent ) :QListView(parent)
{
this->Initialize();
}
PreviewLeftWin::~PreviewLeftWin()
{
}
void PreviewLeftWin::Initialize()
{
//初始化UI
this->InitUi();
//初始化Model
m_model = new PreviewLeftWinModel(this);
this->setModel(m_model);
//初始化委托
m_delegate = new PreviewLeftWinItemDelegate(this);
this->setItemDelegate(m_delegate);
}
void PreviewLeftWin::InitUi()
{
setAttribute(Qt::WA_TranslucentBackground, false);
setAcceptDrops(false);
setDragDropMode(QAbstractItemView::NoDragDrop);
setDragEnabled(false);
setDropIndicatorShown(false);
setMouseTracking(true);
setAutoFillBackground(true);
setRowHidden(0, true);
setViewMode(QListView::ListMode);
setResizeMode(QListView::Adjust);
setMovement(QListView::Snap);
setSelectionMode(QAbstractItemView::SingleSelection);
//样式表
this->SwitchStyleSheet();
//搭载自定义滚动条
m_bfscroll = new BFScrollBar(Qt::Vertical, this);
m_bfscroll->SetScrollArea(this);
}
void PreviewLeftWin::SwitchStyleSheet()
{
QString _bkStyle = QString("QListView{border:none;background-color:rgba(0,0,0,0);}QListView::item:selected{background-color:rgba(0,0,0,0);border:0px solid;}");
setStyleSheet(_bkStyle);
}
void PreviewLeftWin::ClearViewData()
{
//model赋值数据
m_model->ClearData();
//刷新结构
doItemsLayout();
//刷新
this->update();
}
void PreviewLeftWin::LoadData(QVector>iphoto_vec, QSharedPointeripoto)
{
//读取新数据
m_model->ClearData();
m_model->SetUserData(iphoto_vec);
this->clearSelection();
//切换到选择的那个
int index = iphoto_vec.indexOf(ipoto);
QModelIndex qindex = m_model->index(index, 0);
this->setCurrentIndex(qindex);
//滚动条也要跟上
}
void PreviewLeftWin::startDrag(Qt::DropActions supportedActions)
{
return;
}
void PreviewLeftWin::mousePressEvent(QMouseEvent* e)
{
this->update();
return QListView::mousePressEvent(e);
}
void PreviewLeftWin::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
//获取当前选中的
QModelIndexList index_list = selected.indexes();
if (index_list.isEmpty())
{
//第一次可能为空
return;
}
if (index_list.size() != 1)
{
Q_ASSERT(0);
return;
}
QModelIndex cur_index = index_list[0];
QSharedPointer iphoto = cur_index.data(PreviewLeftWinModel_Data).value< QSharedPointer >();
if (!iphoto)
{
Q_ASSERT(0);
}
SigShowResource(iphoto);
}
二.model:.h文件
class PreviewLeftWinModel : public QAbstractListModel
{
Q_OBJECT
public:
PreviewLeftWinModel(QObject* parent);
~PreviewLeftWinModel();
public:
void ClearData(); //清空数据结构
void SetUserData(QVector>);//设置用户数据
private:
virtual QVariant data(const QModelIndex &index, int role) const;
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
virtual QVariant headerData(int section, Qt::Orientation orientation,int role = Qt::DisplayRole) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex& parent) const;
private:
QVector> m_data_vec;
};
model:.cpp文件
PreviewLeftWinModel::PreviewLeftWinModel(QObject* parent)
{
ClearData();
}
PreviewLeftWinModel::~PreviewLeftWinModel()
{
}
void PreviewLeftWinModel::ClearData()
{
m_data_vec.clear();
}
void PreviewLeftWinModel::SetUserData(QVector>datas)
{
m_data_vec = datas;
}
//
QVariant PreviewLeftWinModel::data(const QModelIndex &index,int role) const
{
//无数据
if (m_data_vec.size()==NULL)
{
return QVariant();
}
//无效节点
if( !index.isValid())
{
return QVariant();
}
//数据结构对应index(容器index)
int row_index = index.row();
if (row_index > m_data_vec.size() - 1)
{
return QVariant();
}
//↓↓↓↓↓↓↓↓↓取数据↓↓↓↓↓↓↓↓↓↓
if (role == PreviewLeftWinModel_Data)
{
//取素材数据
QSharedPointer photo = m_data_vec.at(row_index);
if ( photo != NULL )
{
return QVariant::fromValue(photo);
}
else
{
return QVariant(0);
}
}
return QVariant();
}
Qt::ItemFlags PreviewLeftWinModel::flags(const QModelIndex& index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
Qt::ItemIsEditable ;
}
bool PreviewLeftWinModel::setData(const QModelIndex &index,const QVariant &value, int role)
{
return true;
}
QVariant PreviewLeftWinModel::headerData(int section, Qt::Orientation orientation,int role ) const
{
return QVariant();
}
int PreviewLeftWinModel::rowCount(const QModelIndex &parent ) const
{
return m_data_vec.size();
}
int PreviewLeftWinModel::columnCount(const QModelIndex& parent) const
{
return 1;
}
三.Delegate:.h文件
/// @brief 委托
class PreviewLeftWinItemDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
PreviewLeftWinItemDelegate(QObject* parent = 0);
virtual ~PreviewLeftWinItemDelegate();
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
private:
//类似网格布局需要根据rect_image来进一步适配
QRect GetimageRect(QRect, QSize) const;
};
Delegate:.cpp文件
PreviewLeftWinItemDelegate::PreviewLeftWinItemDelegate(QObject* parent )
{
}
PreviewLeftWinItemDelegate::~PreviewLeftWinItemDelegate()
{
}
void PreviewLeftWinItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
//先判断下该项是否被选中
bool is_select = false;
if (option.state & QStyle::State_Selected)
{
is_select = true;
}
//取数据
QSharedPointer iphoto = index.data(PreviewLeftWinModel_Data).value< QSharedPointer >();
if (!iphoto)
{
Q_ASSERT(0);
}
//取区域大小
QRect item_rect = option.rect.adjusted(10,10,-10,-10);
if (is_select)
{
item_rect= option.rect.adjusted(22, 2, -22, -2);
}
else
{
item_rect = option.rect.adjusted(33, 10, -33, -10);
}
//选中的话画一下选中的边框
if (is_select)
{
painter->save();
QPen pen;
pen.setColor(BFSkinSetting::GetInstance()->GetCommonFirstColor());
pen.setWidth(2);
painter->setPen(pen);
painter->drawRect(item_rect.adjusted(-3, -3, 3, 3));
painter->restore();
}
//取图片尺寸
QSize image_size = QSize(100,100);
if (iphoto)
{
if (RESOURCE_TYPE == iphoto->m_phototype)
{
IBFSource* cur_resource = iphoto->m_pIBFResource;
if (cur_resource)
{
const uint32_t iWidth = static_cast(reinterpret_cast(cur_resource->GetValue(BF_SOURCE_TYPE_WIDTH)));
const uint32_t iHeight = static_cast(reinterpret_cast(cur_resource->GetValue(BF_SOURCE_TYPE_HEIGHT)));
if ((iWidth > 0) && (iHeight > 0))
{
image_size = QSize(iWidth, iHeight);
}
else
{
image_size = QSize(100, 100);
}
}
}
}
//得到图片实际区域
QRect image_rect = GetimageRect(item_rect, image_size);
//在最开始设置3个反走样选项,之后不用再设置,只需设置颜色
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
//绘制
//配置painter
painter->setPen(Qt::NoPen);
painter->setBrush(Qt::NoBrush);
//根据文件是否隐藏,变更显示透明度
if (iphoto->GetHide())
painter->setOpacity(0.3);
else
painter->setOpacity(1.0);
//是否支持解析
bool analysis = CAppSingleton::getInstance()->GetSourceIsAnalysis(iphoto->m_pIBFResource);
//是否解析成功
bool anaSuc = (SourceWidth(iphoto->m_pIBFResource) != 0) && (SourceHeight(iphoto->m_pIBFResource) != 0);
//1、索引素材且路径没有丢失;2、支持解析的素材,解析成功 3.解析失败 4.存在webp缩略图文件
bool b1 = iphoto->GetIndexValid();
bool b2 = analysis && anaSuc;
bool b3 = !analysis;
bool b4 = false;
QString strPathName = iphoto->GetThumbnail();
if (strPathName.endsWith(".webp") && QFileInfo::exists(strPathName))
{
b4 = true;
}
if ((b1 && b2) || (b3 && b4))
{
QString cachekey = iphoto->GetCacheKey();
QPixmap pixmap;
if (!QPixmapCache::find(cachekey, &pixmap))
{
if (!iphoto.isNull()) {
QRect grid_image_rect = image_rect;
// 没找到缓存 cachemanager 去加载缓存
BFCacheManager::GetInstance()->AddPhote(iphoto.data(),
grid_image_rect.size());
//connect(BFCacheManager::GetInstance(),
// &BFCacheManager::sigThumbnailLoad, this,
// &GridItem::slotThumbnailLoad, Qt::UniqueConnection);
}
//没取到图片,根据色域表取值
if (!iphoto->ResIsNull())
{
int iCnt = 5;
PSCOLOR pClr = iphoto->m_pIBFResource->GetColours(&iCnt);
QColor clr;
if (pClr) {
clr.setRgb(pClr[2].clr.R, pClr[2].clr.G, pClr[2].clr.B);
}
else {
clr = BFBackGroundColorGet();
}
painter->fillRect(image_rect,QBrush(QColor(clr)));
painter->setBrush(QColor(clr));
painter->drawRoundedRect(image_rect, 10, 10,
Qt::AbsoluteSize);
}
else
{
painter->fillRect(image_rect,
QBrush(BFBackGroundColorGet()));
painter->setBrush(BFBackGroundColorGet());
painter->drawRoundedRect(image_rect, 10, 10,
Qt::AbsoluteSize);
}
}
else
{
QRect grid_image_rect = image_rect;
const int64_t iType = iphoto->GetType();
if (CAppSingleton::getInstance()->isAudioResource(iType))
{
// 音频特殊处理
painter->setBrush(QColor(162, 171, 195));
painter->drawRoundedRect(grid_image_rect, 4, 4, Qt::AbsoluteSize);
painter->drawPixmap(grid_image_rect, pixmap);
}
else
{
painter->drawPixmap(grid_image_rect, pixmap);
}
}
}
//如果 索引素材路径丢失 ,显示【索引素材路径丢失图片】
else if (!iphoto->GetIndexValid())
{
QString pix_str = BFSkinSetting::GetInstance()->GetResourceAbsolutePath("file_index_lose.svg");
QSvgRenderer svg_render(pix_str);
QSize default_size = svg_render.defaultSize();
QPixmap need_pix(default_size.width() * g_fGlobalDPI, default_size.height() * g_fGlobalDPI);
need_pix.fill(Qt::transparent);//设置背景透明
QPainter p(&need_pix);
svg_render.render(&p);
//索引丢失背景
painter->setBrush(QColor("#6A748B"));
painter->drawRoundedRect(image_rect, 4, 4);
//索引丢失图片
QSize pic_size = default_size;
QRect item_image_rect = image_rect;
int icon_x = item_image_rect.x() + (item_image_rect.width() - pic_size.width()) / 2;
int icon_y = item_image_rect.y() + (item_image_rect.height() - pic_size.height()) / 2;
painter->drawPixmap(icon_x, icon_y, pic_size.width(), pic_size.height(), need_pix);
}
//如果是 【支持导入的文件,不支持分析】
else if (!analysis)
{
//画背景色
painter->setBrush(BFSkinSetting::GetInstance()->GetMainColor());
painter->drawRoundedRect(image_rect, 10, 10);
//计算文字
QFont font;
font.setFamily("Microsoft YaHei");
font.setPixelSize(20);
font.setBold(true);
QString typeStr = CAppSingleton::getInstance()->GetSourceType(iphoto->m_pIBFResource);
typeStr = BFQtUtils::FontAdjustWidth(font, image_rect.width(), typeStr);
int fontWidth = BFQtUtils::WidthWithFixString(font, typeStr);
QFontMetrics fms(font);
int fontHeight = fms.height() + 10;
int text_width = fontWidth;
int text_height = fontHeight;
int text_x = image_rect.x() + (image_rect.width() - text_width) / 2;
int text_y = image_rect.y() + (image_rect.height() - text_height) / 2;
QRect text_rect = QRect(text_x, text_y, text_width, text_height);
painter->setPen(BFSkinSetting::GetInstance()->GetTextColor(0));
painter->setFont(font);
painter->drawText(text_rect, typeStr);
}
//其他情况,比如解析失败或者支持解析但是没有缩略图
else
{
int imageWidth = 320;
int imageHeight = (imageWidth * image_rect.height()) / image_rect.width();
QImage defaultImage(imageWidth, imageHeight, QImage::Format_ARGB32);
QPainter ImagePainter(&defaultImage);
ImagePainter.setBrush(QColor("#FFE6E6E6"));
ImagePainter.drawRect(defaultImage.rect());
QFont font;
font.setFamily("Microsoft YaHei");
font.setPixelSize(50);
font.setBold(true);
QString typeStr = CAppSingleton::getInstance()->GetSourceType(iphoto->m_pIBFResource);
typeStr = BFQtUtils::FontAdjustWidth(font, 320, typeStr);
int fontWidth = BFQtUtils::WidthWithFixString(font, typeStr);
QFontMetrics fms(font);
int fontHeight = fms.height() + 10;
//计算文字位置,设置字体并绘制。
QPoint textTopLeftPoint((defaultImage.width() - fontWidth) / 2, (defaultImage.height() - fontHeight) / 2);
QRect fontRect(textTopLeftPoint, QSize(fontWidth, fontHeight));
ImagePainter.setFont(font);
ImagePainter.setPen(QColor("#0FB1B9"));
ImagePainter.drawText(fontRect, Qt::AlignCenter, typeStr);
QPixmap need_pixmap = QPixmap::fromImage(defaultImage);
BFQtUtils::SetPixmapToRound(need_pixmap, need_pixmap.size(), 10);
painter->drawImage(image_rect, defaultImage);
}
}
bool PreviewLeftWinItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
return QStyledItemDelegate::editorEvent(event,model,option,index);
}
QWidget* PreviewLeftWinItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return NULL;
//return QStyledItemDelegate::createEditor(parent, option, index);
}
void PreviewLeftWinItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
}
QSize PreviewLeftWinItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return QSize(0, PreviewLeftWinItemHeight);
}
QRect PreviewLeftWinItemDelegate::GetimageRect(QRect all_rect,QSize ori_size) const
{
//算出宽高比
float rate = (float)ori_size.width() / ori_size.height();
float now_rect_rate = (float)all_rect.width() / all_rect.height();
QSize need_size = QSize();
//左上角点
int x_pos = 0;
int y_pos = 0;
if (rate >= now_rect_rate)
{
//宽图;图片宽比大于容器宽比,则图片宽设置为容器宽,再求图片高
int image_width = all_rect.width();
int image_height = image_width / rate;
need_size = QSize(image_width, image_height);
//算下新左上角点
x_pos = all_rect.topLeft().x();
y_pos = all_rect.topLeft().y() + (all_rect.height() - image_height) / 2;
}
else
{
//高图;图片宽比小于容器宽比,则图片高设置为容器高,再求图片宽
int image_height = all_rect.height();
int image_width = image_height * rate;
need_size = QSize(image_width, image_height);
//算下新左上角点
x_pos = all_rect.topLeft().x() + (all_rect.width() - image_width) / 2;
y_pos = all_rect.topLeft().y();
}
//转换为rect内部矩形
QRect need_rect = QRect(QPoint(x_pos, y_pos), need_size);
return need_rect;
}



