Qt实现QQ好友下拉列表(用QListView实现,所以还得定义它的Model)
2016-02-01 18:51
573 查看
偶然发现Qt有个控件可以实现下拉列表,所以就试着实现一下类似QQ面板的下拉列表,这里主要实现几个功能:
1.可以删除列表中图标
2.可以像qq一样的,把某个分组下的图标转移到另外的分组
3.添加分组
代码里写了注释了,这里就不重复了,下面直接看代码吧。
[cpp] view plain copy
struct ListItemData
{
QString iconPath;
QString Name;
};
class ListModel:public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject *parent = NULL);
~ListModel();
void init();
void addItem(ListItemData *pItem);
QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const ;
int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
void deleteItem(int index);
ListItemData* getItem(int index );
protected:
private:
vector<ListItemData*> m_ItemDataVec;
};
[cpp] view plain copy
<pre name="code" class="cpp">ListModel::ListModel( QObject *parent /*= NULL*/ ):QAbstractListModel(parent)
{
init();
}
ListModel::~ListModel()
{
}
QVariant ListModel::data( const QModelIndex & index, int role /*= Qt::DisplayRole */ ) const
{
if (index.row() > m_ItemDataVec.size())
{
return QVariant();
}
else
{
switch (role)
{
case Qt::DisplayRole:
{
return m_ItemDataVec[index.row()]->Name;
}
break;
case Qt::DecorationRole:
{
return QIcon(m_ItemDataVec[index.row()]->iconPath);
}
break;
case Qt::SizeHintRole:
{
return QSize(10,50);
}
}
}
return QVariant();
}
int ListModel::rowCount( const QModelIndex & parent /*= QModelIndex() */ ) const
{
return m_ItemDataVec.size();
}
void ListModel::init()
{
for (int i = 1; i < 26; ++i)
{
ListItemData *pItem = new ListItemData;
pItem->Name = QString::number(i);
pItem->iconPath = QString(":/QQPanel/Resources/%1.jpg").arg(i);
QFile Iconfile(pItem->iconPath);
if (Iconfile.exists())
{
m_ItemDataVec.push_back(pItem);
}
}
}
void ListModel::deleteItem( int index )
{
vector<ListItemData*>::iterator it = m_ItemDataVec.begin();
m_ItemDataVec.erase(it + index);
}
void ListModel::addItem( ListItemData *pItem )
{
if (pItem)
{
this->beginInsertRows(QModelIndex(),m_ItemDataVec.size(),m_ItemDataVec.size() + 1);
<span style="white-space:pre"> </span>m_ItemDataVec.push_back(pItem);
<span style="white-space:pre"> </span>this->endInsertRows();
}
}
ListItemData* ListModel::getItem( int index )
{
if (index > -1 && index < m_ItemDataVec.size())
{
return m_ItemDataVec[index];
}
}
</pre><br>
<br>
<pre></pre>
<h1><a name="t1"></a><br>
</h1>
<h1><a name="t2"></a>自定义的列表</h1>
<div>这个类才是重点,因为这里实现了删除和转移图标的几个重要的函数。</div>
<pre name="code" class="cpp">class MyListView:public QListView
{
Q_OBJECT
public:
MyListView(QWidget *parent = NULL);
~MyListView();
void setListMap(map<MyListView*,QString> *pListMap);
void addItem(ListItemData *pItem);
protected:
void contextMenuEvent ( QContextMenuEvent * event );
private slots:
void deleteItemSlot(bool bDelete);
void moveSlot(bool bMove);
private:
int m_hitIndex;
ListModel *m_pModel;
////记录分组和分组名字的映射关系,这个值跟QQPanel类中的映射组的值保持一致
//这里还有一个用处就是在弹出的菜单需要分组的名称
map<MyListView*,QString> *m_pListMap;
//记录每个菜单项对应的列表,才能知道要转移到那个分组
map<QAction*,MyListView*> m_ActionMap;
};</pre><br>
<pre name="code" class="cpp">MyListView::MyListView( QWidget *parent /*= NULL*/ ):QListView(parent)
{
m_hitIndex = -1;
m_pModel = new ListModel;
this->setModel(m_pModel);
m_pListMap = NULL;
}
MyListView::~MyListView()
{
}
void MyListView::contextMenuEvent( QContextMenuEvent * event )
{
int hitIndex = this->indexAt(event->pos()).column();
if (hitIndex > -1)
{
QMenu *pMenu = new QMenu(this);
QAction *pDeleteAct = new QAction(tr("删除"),pMenu);
pMenu->addAction(pDeleteAct);
connect(pDeleteAct,SIGNAL(triggered (bool)),this,SLOT(deleteItemSlot(bool)));
QMenu *pSubMenu = NULL;
map<MyListView*,QString>::iterator it = m_pListMap->begin();
for (it;it != m_pListMap->end();++it)
{
if (!pSubMenu)
{
pSubMenu = new QMenu(tr("转移联系人至") ,pMenu);
pMenu->addMenu(pSubMenu);
}
if (it->first != this)
{
QAction *pMoveAct = new QAction( it->second ,pMenu);
//记录菜单与分组的映射,在moveSlot()响应时需要用到。
m_ActionMap.insert(pair<QAction*,MyListView*>(pMoveAct,it->first));
pSubMenu->addAction(pMoveAct);
connect(pMoveAct,SIGNAL(triggered (bool)),this,SLOT(moveSlot(bool)));
}
}
pMenu->popup(mapToGlobal(event->pos()));
}
}
void MyListView::deleteItemSlot( bool bDelete )
{
int index = this->currentIndex().row();
if (index > -1)
{
m_pModel->deleteItem(index);
}
}
void MyListView::setListMap( map<MyListView*,QString> *pListMap )
{
m_pListMap = pListMap;
}
void MyListView::addItem( ListItemData *pItem )
{
m_pModel->addItem(pItem);
}
void MyListView::moveSlot( bool bMove )
{
QAction *pSender = qobject_cast<QAction*>(sender());
if (pSender)
{
//根据点击的菜单,找到相应的列表,然后才能把图标转移过去
MyListView *pList = m_ActionMap.find(pSender)->second;
if (pList)
{
int index = this->currentIndex().row();
ListItemData *pItem = m_pModel->getItem(index);
pList->addItem(pItem);
//添加到别的分组,就在原来的分组中删除掉了
m_pModel->deleteItem(index);
}
}
//操作完了要把这个临时的映射清空
m_ActionMap.clear();
}
</pre><br>
<h1><a name="t3"></a>自定义的主控件</h1>
[cpp] view plain copy
class QQPanel : public QWidget
{
Q_OBJECT
public:
QQPanel(QWidget *parent = 0, Qt::WFlags flags = 0);
~QQPanel();
protected:
void contextMenuEvent ( QContextMenuEvent * event );
protected slots:
void addGroupSlot(bool addgroup);
private:
QToolBox *m_pBox;
map<MyListView*,QString> *m_pListMap; //记录分组和分组名字的映射关系,好在转移图标时知道转移到那个分组
};
[cpp] view plain copy
QQPanel::QQPanel(QWidget *parent, Qt::WFlags flags)
: QWidget(parent, flags)
{
m_pBox = new QToolBox(this);
m_pListMap = new map<MyListView*,QString>();
MyListView *pListView = new MyListView(this);
pListView->setViewMode(QListView::ListMode);
pListView->setStyleSheet("QListView{icon-size:40px}");
m_pBox->addItem(pListView,tr("我的好友"));
m_pListMap->insert(pair<MyListView*,QString>(pListView,tr("我的好友")));
MyListView *pListView1 = new MyListView(this);
pListView1->setViewMode(QListView::ListMode);
pListView1->setStyleSheet("QListView{icon-size:40px}");
m_pBox->addItem(pListView1,tr("陌生人"));
m_pListMap->insert(pair<MyListView*,QString>(pListView1,tr("陌生人")));
pListView->setListMap(m_pListMap);
pListView1->setListMap(m_pListMap);
m_pBox->setFixedWidth(150);
m_pBox->setMinimumHeight(500);
this->setMinimumSize(200,500);
//ui.setupUi(this);
}
QQPanel::~QQPanel()
{
}
void QQPanel::contextMenuEvent( QContextMenuEvent * event )
{
QMenu *pMenu = new QMenu(this);
QAction *pAddGroupAct = new QAction(tr("添加分组"),pMenu);
pMenu->addAction(pAddGroupAct);
connect(pAddGroupAct,SIGNAL(triggered (bool)),this,SLOT(addGroupSlot(bool)));
pMenu->popup(mapToGlobal(event->pos()));
}
void QQPanel::addGroupSlot( bool addgroup )
{
QString name = QInputDialog::getText(this,tr("输入分组名"),tr(""));
if (!name.isEmpty())
{
MyListView *pListView1 = new MyListView(this);
pListView1->setViewMode(QListView::ListMode);
pListView1->setStyleSheet("QListView{icon-size:40px}");
m_pBox->addItem(pListView1,name);
m_pListMap->insert(pair<MyListView*,QString>(pListView1,name));
}
//要确保每个MyListView钟的m_pListMap都是一致的,不然就会有错了。
//因为弹出的菜单进行转移的时候需要用到
map<MyListView*,QString>::iterator it = m_pListMap->begin();
for (it; it != m_pListMap->end(); ++it)
{
MyListView* pList = it->first;
pList->setListMap(m_pListMap);
}
}
![](http://img.blog.csdn.net/20130824202055156?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20130824202103468?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
由以上两个截图显示,我的好友和陌生人的个有5个图标
![](http://img.blog.csdn.net/20130824202111890?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20130824202117687?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
以上两个截图显示,把陌生人中图标5转移到我的好友里
![](http://img.blog.csdn.net/20130824202445421?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20130824202452109?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
以上两个截图,显示添加了一个分组,黑名单,因为我默认列表在创建时都有相同的5个图标
![](http://img.blog.csdn.net/20130824202754843?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20130824202802015?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20130824202808562?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpMjAwNTAxMDE5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
以上三个截图显示了把黑名单里的图标5转移到了我的好友分组里了
当然这个程序算是比较简单的。还不能真正的跟QQ的面板相比,还不能把所有的分组都收起来。以后再慢慢研究怎么实现了,
http://blog.csdn.net/hai200501019/article/details/10283553
1.可以删除列表中图标
2.可以像qq一样的,把某个分组下的图标转移到另外的分组
3.添加分组
代码里写了注释了,这里就不重复了,下面直接看代码吧。
自定义的数据模型
ListModel继承了QAbstractListModel,主要是实现要显示的数据结构。用的是model/view的三层结构,这样好拆分[cpp] view plain copy
struct ListItemData
{
QString iconPath;
QString Name;
};
class ListModel:public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject *parent = NULL);
~ListModel();
void init();
void addItem(ListItemData *pItem);
QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const ;
int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
void deleteItem(int index);
ListItemData* getItem(int index );
protected:
private:
vector<ListItemData*> m_ItemDataVec;
};
[cpp] view plain copy
<pre name="code" class="cpp">ListModel::ListModel( QObject *parent /*= NULL*/ ):QAbstractListModel(parent)
{
init();
}
ListModel::~ListModel()
{
}
QVariant ListModel::data( const QModelIndex & index, int role /*= Qt::DisplayRole */ ) const
{
if (index.row() > m_ItemDataVec.size())
{
return QVariant();
}
else
{
switch (role)
{
case Qt::DisplayRole:
{
return m_ItemDataVec[index.row()]->Name;
}
break;
case Qt::DecorationRole:
{
return QIcon(m_ItemDataVec[index.row()]->iconPath);
}
break;
case Qt::SizeHintRole:
{
return QSize(10,50);
}
}
}
return QVariant();
}
int ListModel::rowCount( const QModelIndex & parent /*= QModelIndex() */ ) const
{
return m_ItemDataVec.size();
}
void ListModel::init()
{
for (int i = 1; i < 26; ++i)
{
ListItemData *pItem = new ListItemData;
pItem->Name = QString::number(i);
pItem->iconPath = QString(":/QQPanel/Resources/%1.jpg").arg(i);
QFile Iconfile(pItem->iconPath);
if (Iconfile.exists())
{
m_ItemDataVec.push_back(pItem);
}
}
}
void ListModel::deleteItem( int index )
{
vector<ListItemData*>::iterator it = m_ItemDataVec.begin();
m_ItemDataVec.erase(it + index);
}
void ListModel::addItem( ListItemData *pItem )
{
if (pItem)
{
this->beginInsertRows(QModelIndex(),m_ItemDataVec.size(),m_ItemDataVec.size() + 1);
<span style="white-space:pre"> </span>m_ItemDataVec.push_back(pItem);
<span style="white-space:pre"> </span>this->endInsertRows();
}
}
ListItemData* ListModel::getItem( int index )
{
if (index > -1 && index < m_ItemDataVec.size())
{
return m_ItemDataVec[index];
}
}
</pre><br>
<br>
<pre></pre>
<h1><a name="t1"></a><br>
</h1>
<h1><a name="t2"></a>自定义的列表</h1>
<div>这个类才是重点,因为这里实现了删除和转移图标的几个重要的函数。</div>
<pre name="code" class="cpp">class MyListView:public QListView
{
Q_OBJECT
public:
MyListView(QWidget *parent = NULL);
~MyListView();
void setListMap(map<MyListView*,QString> *pListMap);
void addItem(ListItemData *pItem);
protected:
void contextMenuEvent ( QContextMenuEvent * event );
private slots:
void deleteItemSlot(bool bDelete);
void moveSlot(bool bMove);
private:
int m_hitIndex;
ListModel *m_pModel;
////记录分组和分组名字的映射关系,这个值跟QQPanel类中的映射组的值保持一致
//这里还有一个用处就是在弹出的菜单需要分组的名称
map<MyListView*,QString> *m_pListMap;
//记录每个菜单项对应的列表,才能知道要转移到那个分组
map<QAction*,MyListView*> m_ActionMap;
};</pre><br>
<pre name="code" class="cpp">MyListView::MyListView( QWidget *parent /*= NULL*/ ):QListView(parent)
{
m_hitIndex = -1;
m_pModel = new ListModel;
this->setModel(m_pModel);
m_pListMap = NULL;
}
MyListView::~MyListView()
{
}
void MyListView::contextMenuEvent( QContextMenuEvent * event )
{
int hitIndex = this->indexAt(event->pos()).column();
if (hitIndex > -1)
{
QMenu *pMenu = new QMenu(this);
QAction *pDeleteAct = new QAction(tr("删除"),pMenu);
pMenu->addAction(pDeleteAct);
connect(pDeleteAct,SIGNAL(triggered (bool)),this,SLOT(deleteItemSlot(bool)));
QMenu *pSubMenu = NULL;
map<MyListView*,QString>::iterator it = m_pListMap->begin();
for (it;it != m_pListMap->end();++it)
{
if (!pSubMenu)
{
pSubMenu = new QMenu(tr("转移联系人至") ,pMenu);
pMenu->addMenu(pSubMenu);
}
if (it->first != this)
{
QAction *pMoveAct = new QAction( it->second ,pMenu);
//记录菜单与分组的映射,在moveSlot()响应时需要用到。
m_ActionMap.insert(pair<QAction*,MyListView*>(pMoveAct,it->first));
pSubMenu->addAction(pMoveAct);
connect(pMoveAct,SIGNAL(triggered (bool)),this,SLOT(moveSlot(bool)));
}
}
pMenu->popup(mapToGlobal(event->pos()));
}
}
void MyListView::deleteItemSlot( bool bDelete )
{
int index = this->currentIndex().row();
if (index > -1)
{
m_pModel->deleteItem(index);
}
}
void MyListView::setListMap( map<MyListView*,QString> *pListMap )
{
m_pListMap = pListMap;
}
void MyListView::addItem( ListItemData *pItem )
{
m_pModel->addItem(pItem);
}
void MyListView::moveSlot( bool bMove )
{
QAction *pSender = qobject_cast<QAction*>(sender());
if (pSender)
{
//根据点击的菜单,找到相应的列表,然后才能把图标转移过去
MyListView *pList = m_ActionMap.find(pSender)->second;
if (pList)
{
int index = this->currentIndex().row();
ListItemData *pItem = m_pModel->getItem(index);
pList->addItem(pItem);
//添加到别的分组,就在原来的分组中删除掉了
m_pModel->deleteItem(index);
}
}
//操作完了要把这个临时的映射清空
m_ActionMap.clear();
}
</pre><br>
<h1><a name="t3"></a>自定义的主控件</h1>
[cpp] view plain copy
class QQPanel : public QWidget
{
Q_OBJECT
public:
QQPanel(QWidget *parent = 0, Qt::WFlags flags = 0);
~QQPanel();
protected:
void contextMenuEvent ( QContextMenuEvent * event );
protected slots:
void addGroupSlot(bool addgroup);
private:
QToolBox *m_pBox;
map<MyListView*,QString> *m_pListMap; //记录分组和分组名字的映射关系,好在转移图标时知道转移到那个分组
};
[cpp] view plain copy
QQPanel::QQPanel(QWidget *parent, Qt::WFlags flags)
: QWidget(parent, flags)
{
m_pBox = new QToolBox(this);
m_pListMap = new map<MyListView*,QString>();
MyListView *pListView = new MyListView(this);
pListView->setViewMode(QListView::ListMode);
pListView->setStyleSheet("QListView{icon-size:40px}");
m_pBox->addItem(pListView,tr("我的好友"));
m_pListMap->insert(pair<MyListView*,QString>(pListView,tr("我的好友")));
MyListView *pListView1 = new MyListView(this);
pListView1->setViewMode(QListView::ListMode);
pListView1->setStyleSheet("QListView{icon-size:40px}");
m_pBox->addItem(pListView1,tr("陌生人"));
m_pListMap->insert(pair<MyListView*,QString>(pListView1,tr("陌生人")));
pListView->setListMap(m_pListMap);
pListView1->setListMap(m_pListMap);
m_pBox->setFixedWidth(150);
m_pBox->setMinimumHeight(500);
this->setMinimumSize(200,500);
//ui.setupUi(this);
}
QQPanel::~QQPanel()
{
}
void QQPanel::contextMenuEvent( QContextMenuEvent * event )
{
QMenu *pMenu = new QMenu(this);
QAction *pAddGroupAct = new QAction(tr("添加分组"),pMenu);
pMenu->addAction(pAddGroupAct);
connect(pAddGroupAct,SIGNAL(triggered (bool)),this,SLOT(addGroupSlot(bool)));
pMenu->popup(mapToGlobal(event->pos()));
}
void QQPanel::addGroupSlot( bool addgroup )
{
QString name = QInputDialog::getText(this,tr("输入分组名"),tr(""));
if (!name.isEmpty())
{
MyListView *pListView1 = new MyListView(this);
pListView1->setViewMode(QListView::ListMode);
pListView1->setStyleSheet("QListView{icon-size:40px}");
m_pBox->addItem(pListView1,name);
m_pListMap->insert(pair<MyListView*,QString>(pListView1,name));
}
//要确保每个MyListView钟的m_pListMap都是一致的,不然就会有错了。
//因为弹出的菜单进行转移的时候需要用到
map<MyListView*,QString>::iterator it = m_pListMap->begin();
for (it; it != m_pListMap->end(); ++it)
{
MyListView* pList = it->first;
pList->setListMap(m_pListMap);
}
}
运行结果
由以上两个截图显示,我的好友和陌生人的个有5个图标
以上两个截图显示,把陌生人中图标5转移到我的好友里
以上两个截图,显示添加了一个分组,黑名单,因为我默认列表在创建时都有相同的5个图标
以上三个截图显示了把黑名单里的图标5转移到了我的好友分组里了
当然这个程序算是比较简单的。还不能真正的跟QQ的面板相比,还不能把所有的分组都收起来。以后再慢慢研究怎么实现了,
http://blog.csdn.net/hai200501019/article/details/10283553
相关文章推荐
- Qt同步线程(比较清楚,而且QMutex QMutexLocker QReadWriteLock QSemaphore QWaitCondition 每个都有例子)
- QTabWidget 实现类似QQ聊天窗口(拖动分离出新的窗口)
- Qt 状态机框架学习(没学会)
- Qt 智能指针学习(7种QT智能指针和4种std智能指针)
- qt 使用xlslib操作execl
- 使用Qt库时出现winsock2.h和windos.h包含顺序错误
- PyQt5 笔记(04):主窗口卡死问题
- Ogre3D嵌入Qt框架 之 小结
- 分享一个自己写的QT小游戏-玛丽奥医生
- Qt 学习之路 2(31):贪吃蛇游戏(1)
- 使用Qt开发俄罗斯方块游戏(1)
- QTableWidget 导出到表格
- QT5的软键盘输入法实现
- QT一些基本的操作(文件、图片等)
- QT5的软键盘输入法实现
- PyQt5 笔记(03):弹出窗口大全
- 使用 Qt InstallFramework 进行程序打包(一)
- PyQt5 笔记(02):嵌套布局
- PyQt5 笔记(01):嵌套布局
- QT 实现在一个模块中间显示一个红色提示框,3秒后消失