偶然发现Qt有个控件可以实现下拉列表,所以就试着实现一下类似QQ面板的下拉列表,这里主要实现几个功能:

1.可以删除列表中图标

2.可以像qq一样的,把某个分组下的图标转移到另外的分组

3.添加分组

代码里写了注释了,这里就不重复了,下面直接看代码吧。

自定义的数据模型

ListModel继承了QAbstractListModel,主要是实现要显示的数据结构。用的是model/view的三层结构,这样好拆分

  1. struct ListItemData
  2. {
  3. QString  iconPath;
  4. QString  Name;
  5. };
  6. class ListModel:public QAbstractListModel
  7. {
  8. Q_OBJECT
  9. public:
  10. ListModel(QObject *parent = NULL);
  11. ~ListModel();
  12. void init();
  13. void addItem(ListItemData *pItem);
  14. QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const ;
  15. int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
  16. void deleteItem(int index);
  17. ListItemData* getItem(int index );
  18. protected:
  19. private:
  20. vector<ListItemData*> m_ItemDataVec;
  21. };
  1. <pre name="code" class="cpp">ListModel::ListModel( QObject *parent /*= NULL*/ ):QAbstractListModel(parent)
  2. {
  3. init();
  4. }
  5. ListModel::~ListModel()
  6. {
  7. }
  8. QVariant ListModel::data( const QModelIndex & index, int role /*= Qt::DisplayRole */ ) const
  9. {
  10. if (index.row() > m_ItemDataVec.size())
  11. {
  12. return QVariant();
  13. }
  14. else
  15. {
  16. switch (role)
  17. {
  18. case Qt::DisplayRole:
  19. {
  20. return m_ItemDataVec[index.row()]->Name;
  21. }
  22. break;
  23. case Qt::DecorationRole:
  24. {
  25. return QIcon(m_ItemDataVec[index.row()]->iconPath);
  26. }
  27. break;
  28. case Qt::SizeHintRole:
  29. {
  30. return QSize(10,50);
  31. }
  32. }
  33. }
  34. return QVariant();
  35. }
  36. int ListModel::rowCount( const QModelIndex & parent /*= QModelIndex() */ ) const
  37. {
  38. return m_ItemDataVec.size();
  39. }
  40. void ListModel::init()
  41. {
  42. for (int i = 1; i < 26; ++i)
  43. {
  44. ListItemData *pItem = new ListItemData;
  45. pItem->Name = QString::number(i);
  46. pItem->iconPath = QString(":/QQPanel/Resources/%1.jpg").arg(i);
  47. QFile Iconfile(pItem->iconPath);
  48. if (Iconfile.exists())
  49. {
  50. m_ItemDataVec.push_back(pItem);
  51. }
  52. }
  53. }
  54. void ListModel::deleteItem( int index )
  55. {
  56. vector<ListItemData*>::iterator it = m_ItemDataVec.begin();
  57. m_ItemDataVec.erase(it + index);
  58. }
  59. void ListModel::addItem( ListItemData *pItem )
  60. {
  61. if (pItem)
  62. {
  63. this->beginInsertRows(QModelIndex(),m_ItemDataVec.size(),m_ItemDataVec.size() + 1);
  64. <span style="white-space:pre">      </span>m_ItemDataVec.push_back(pItem);
  65. <span style="white-space:pre">      </span>this->endInsertRows();
  66. }
  67. }
  68. ListItemData* ListModel::getItem( int index )
  69. {
  70. if (index > -1 && index < m_ItemDataVec.size())
  71. {
  72. return m_ItemDataVec[index];
  73. }
  74. }
  75. </pre><br>
  76. <br>
  77. <pre></pre>
  78. <h1><a name="t1"></a><br>
  79. </h1>
  80. <h1><a name="t2"></a>自定义的列表</h1>
  81. <div>这个类才是重点,因为这里实现了删除和转移图标的几个重要的函数。</div>
  82. <pre name="code" class="cpp">class MyListView:public QListView
  83. {
  84. Q_OBJECT
  85. public:
  86. MyListView(QWidget *parent = NULL);
  87. ~MyListView();
  88. void setListMap(map<MyListView*,QString> *pListMap);
  89. void addItem(ListItemData *pItem);
  90. protected:
  91. void contextMenuEvent ( QContextMenuEvent * event );
  92. private slots:
  93. void deleteItemSlot(bool bDelete);
  94. void moveSlot(bool bMove);
  95. private:
  96. int  m_hitIndex;
  97. ListModel   *m_pModel;
  98. ////记录分组和分组名字的映射关系,这个值跟QQPanel类中的映射组的值保持一致
  99. //这里还有一个用处就是在弹出的菜单需要分组的名称
  100. map<MyListView*,QString> *m_pListMap;
  101. //记录每个菜单项对应的列表,才能知道要转移到那个分组
  102. map<QAction*,MyListView*> m_ActionMap;
  103. };</pre><br>
  104. <pre name="code" class="cpp">MyListView::MyListView( QWidget *parent /*= NULL*/ ):QListView(parent)
  105. {
  106. m_hitIndex = -1;
  107. m_pModel = new ListModel;
  108. this->setModel(m_pModel);
  109. m_pListMap = NULL;
  110. }
  111. MyListView::~MyListView()
  112. {
  113. }
  114. void MyListView::contextMenuEvent( QContextMenuEvent * event )
  115. {
  116. int hitIndex = this->indexAt(event->pos()).column();
  117. if (hitIndex > -1)
  118. {
  119. QMenu *pMenu = new QMenu(this);
  120. QAction *pDeleteAct = new QAction(tr("删除"),pMenu);
  121. pMenu->addAction(pDeleteAct);
  122. connect(pDeleteAct,SIGNAL(triggered (bool)),this,SLOT(deleteItemSlot(bool)));
  123. QMenu *pSubMenu = NULL;
  124. map<MyListView*,QString>::iterator it = m_pListMap->begin();
  125. for (it;it != m_pListMap->end();++it)
  126. {
  127. if (!pSubMenu)
  128. {
  129. pSubMenu = new QMenu(tr("转移联系人至") ,pMenu);
  130. pMenu->addMenu(pSubMenu);
  131. }
  132. if (it->first != this)
  133. {
  134. QAction *pMoveAct = new QAction( it->second ,pMenu);
  135. //记录菜单与分组的映射,在moveSlot()响应时需要用到。
  136. m_ActionMap.insert(pair<QAction*,MyListView*>(pMoveAct,it->first));
  137. pSubMenu->addAction(pMoveAct);
  138. connect(pMoveAct,SIGNAL(triggered (bool)),this,SLOT(moveSlot(bool)));
  139. }
  140. }
  141. pMenu->popup(mapToGlobal(event->pos()));
  142. }
  143. }
  144. void MyListView::deleteItemSlot( bool bDelete )
  145. {
  146. int index = this->currentIndex().row();
  147. if (index > -1)
  148. {
  149. m_pModel->deleteItem(index);
  150. }
  151. }
  152. void MyListView::setListMap( map<MyListView*,QString> *pListMap )
  153. {
  154. m_pListMap = pListMap;
  155. }
  156. void MyListView::addItem( ListItemData *pItem )
  157. {
  158. m_pModel->addItem(pItem);
  159. }
  160. void MyListView::moveSlot( bool bMove )
  161. {
  162. QAction *pSender = qobject_cast<QAction*>(sender());
  163. if (pSender)
  164. {
  165. //根据点击的菜单,找到相应的列表,然后才能把图标转移过去
  166. MyListView *pList = m_ActionMap.find(pSender)->second;
  167. if (pList)
  168. {
  169. int index = this->currentIndex().row();
  170. ListItemData *pItem = m_pModel->getItem(index);
  171. pList->addItem(pItem);
  172. //添加到别的分组,就在原来的分组中删除掉了
  173. m_pModel->deleteItem(index);
  174. }
  175. }
  176. //操作完了要把这个临时的映射清空
  177. m_ActionMap.clear();
  178. }
  179. </pre><br>
  180. <h1><a name="t3"></a>自定义的主控件</h1>
  1. class QQPanel : public QWidget
  2. {
  3. Q_OBJECT
  4. public:
  5. QQPanel(QWidget *parent = 0, Qt::WFlags flags = 0);
  6. ~QQPanel();
  7. protected:
  8. void contextMenuEvent ( QContextMenuEvent * event );
  9. protected slots:
  10. void addGroupSlot(bool addgroup);
  11. private:
  12. QToolBox    *m_pBox;
  13. map<MyListView*,QString> *m_pListMap;    //记录分组和分组名字的映射关系,好在转移图标时知道转移到那个分组
  14. };
  1. QQPanel::QQPanel(QWidget *parent, Qt::WFlags flags)
  2. : QWidget(parent, flags)
  3. {
  4. m_pBox = new QToolBox(this);
  5. m_pListMap = new map<MyListView*,QString>();
  6. MyListView *pListView = new MyListView(this);
  7. pListView->setViewMode(QListView::ListMode);
  8. pListView->setStyleSheet("QListView{icon-size:40px}");
  9. m_pBox->addItem(pListView,tr("我的好友"));
  10. m_pListMap->insert(pair<MyListView*,QString>(pListView,tr("我的好友")));
  11. MyListView *pListView1 = new MyListView(this);
  12. pListView1->setViewMode(QListView::ListMode);
  13. pListView1->setStyleSheet("QListView{icon-size:40px}");
  14. m_pBox->addItem(pListView1,tr("陌生人"));
  15. m_pListMap->insert(pair<MyListView*,QString>(pListView1,tr("陌生人")));
  16. pListView->setListMap(m_pListMap);
  17. pListView1->setListMap(m_pListMap);
  18. m_pBox->setFixedWidth(150);
  19. m_pBox->setMinimumHeight(500);
  20. this->setMinimumSize(200,500);
  21. //ui.setupUi(this);
  22. }
  23. QQPanel::~QQPanel()
  24. {
  25. }
  26. void QQPanel::contextMenuEvent( QContextMenuEvent * event )
  27. {
  28. QMenu *pMenu = new QMenu(this);
  29. QAction *pAddGroupAct = new QAction(tr("添加分组"),pMenu);
  30. pMenu->addAction(pAddGroupAct);
  31. connect(pAddGroupAct,SIGNAL(triggered (bool)),this,SLOT(addGroupSlot(bool)));
  32. pMenu->popup(mapToGlobal(event->pos()));
  33. }
  34. void QQPanel::addGroupSlot( bool addgroup )
  35. {
  36. QString name = QInputDialog::getText(this,tr("输入分组名"),tr(""));
  37. if (!name.isEmpty())
  38. {
  39. MyListView *pListView1 = new MyListView(this);
  40. pListView1->setViewMode(QListView::ListMode);
  41. pListView1->setStyleSheet("QListView{icon-size:40px}");
  42. m_pBox->addItem(pListView1,name);
  43. m_pListMap->insert(pair<MyListView*,QString>(pListView1,name));
  44. }
  45. //要确保每个MyListView钟的m_pListMap都是一致的,不然就会有错了。
  46. //因为弹出的菜单进行转移的时候需要用到
  47. map<MyListView*,QString>::iterator it = m_pListMap->begin();
  48. for (it; it != m_pListMap->end(); ++it)
  49. {
  50. MyListView* pList = it->first;
  51. pList->setListMap(m_pListMap);
  52. }
  53. }

运行结果

 
由以上两个截图显示,我的好友和陌生人的个有5个图标
 
 
 
以上两个截图显示,把陌生人中图标5转移到我的好友里
 
 
以上两个截图,显示添加了一个分组,黑名单,因为我默认列表在创建时都有相同的5个图标
 
 

以上三个截图显示了把黑名单里的图标5转移到了我的好友分组里了

当然这个程序算是比较简单的。还不能真正的跟QQ的面板相比,还不能把所有的分组都收起来。以后再慢慢研究怎么实现了,

http://blog.csdn.net/hai200501019/article/details/10283553

Qt实现QQ好友下拉列表(用QListView实现,所以还得定义它的Model)的更多相关文章

  1. Qt实现 QQ好友列表QToolBox

    简述 QToolBox类提供了一个列(选项卡式的)部件条目. QToolBox可以在一个tab列上显示另外一个,并且当前的item显示在当前的tab下面.每个tab都在tab列中有一个索引位置.tab ...

  2. 基于Qt的相似QQ好友列表抽屉效果的实现

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/shuideyidi/article/details/30619167     前段时间在忙毕业设计, ...

  3. Qt实现QQ界面

    1.Qt实现QQ界面是通过QToolBox类来实现的,基本结构是:QToolBox里面装QGroupBox,然后QGroupBox里面装QToolButton,设置好相关属性即可 2.定义类继承QTo ...

  4. js实现打开网页自动弹出添加QQ好友邀请窗口

    我们有时进一些网面或专题页面会自动弹出一个加为好友的对话框了,在研究了很久之后发现可以直接使用js来实现,下面我们一起来看js实现打开网页自动弹出添加QQ好友邀请窗口的方法. 第一步.JS脚本 这个是 ...

  5. 微信sdk分享,苹果手机分享到qq好友和qq空间没有反应

    最近线上程序苹果手机进行微信分享时,分享到qq好友和qq空间,无法调用分享程序,从微信跳转到qq后就没有反应了,但是安卓手机分享就没事? 解决:调用微信sdk分享时,分享的url(link)的参数不能 ...

  6. iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)

    iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一) 一.项目结构和plist文件 二.实现代码 1.说明: 主控制器直接继承UITableViewController // ...

  7. java模仿qq好友面板的布局(BoxLayout问题)

    .............. JLabel ll = new JLabel(dlg.getNameText() + ":" + dlg.getIPText(), ii[index] ...

  8. 使用Javascript无限添加QQ好友原理解析

    做QQ营销的朋友都知道,QQ加好友是有诸多限制的,IP限制,次数限制,二维码限制,人数限制,使用软件自动加好友会遇到各种各样的问题,很多软件通过模拟人工添加QQ号码,在添加几个之后就会遇到腾讯规则限制 ...

  9. [iOS基础控件 - 6.9.3] QQ好友列表Demo TableView

    A.需求 1.使用plist数据,展示类似QQ好友列表的分组.组内成员显示缩进功能 2.组名使用Header,展示箭头图标.组名.组内人数和上线人数 3.点击组名,伸展.缩回好友组   code so ...

随机推荐

  1. 各浏览器对 window.open() 的窗口特征 sFeatures 参数支持程度存在差异

    标准参考 无. 问题描述 使用 window.open 方法可以弹出一个新窗口,其中 open 方法的 sFeatures 参数选项在各浏览器中支持程度不一,这有可能导致同样的代码使各浏览器中弹出窗口 ...

  2. REF CURSOR和CURSOR

    REF CURSOR DECLARE TYPE TY_EMP_CUR IS REF CURSOR; V_Emp_Cur TY_EMP_CUR; V_Id EMP.ID%TYPE; BEGIN OPEN ...

  3. 一致性算法--Raft

    分布式一致性算法--Raft 前面一篇文章讲了Paxos协议,这篇文章讲它的姊妹篇Raft协议,相对于Paxos协议,Raft协议更为简单,也更容易工程实现.有关Raft协议和工程实现可以参考这个链接 ...

  4. 转: seajs手册与文档之 -- 配置选项

    config alias preload debug map base charset timeout noConflict config 可以使用 config 方法来配置seajs. seajs. ...

  5. 《Java并发编程实战》第二章 线程安全性 读书笔记

    一.什么是线程安全性 编写线程安全的代码 核心在于要对状态訪问操作进行管理. 共享,可变的状态的訪问 - 前者表示多个线程訪问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与 ...

  6. likely() and unlikely()

    likely() and unlikely() http://www.cnblogs.com/yangzd/archive/2010/09/27/1837202.html 在linux中判断语句经常会 ...

  7. BZOJ 1103: [POI2007]大都市meg( 树链剖分 )

    早上数学考挂了...欲哭无泪啊下午去写半个小时政治然后就又可以来刷题了.. 树链剖分 , 为什么跑得这么慢... ------------------------------------------- ...

  8. BZOJ 3282: Tree( LCT )

    LCT.. -------------------------------------------------------------------------------- #include<c ...

  9. ContentProvider的一些总结

    ContentProvider中的URI, The URI that identifies the provider   一个特定的uri对应着唯一一个内容提供者, 谷歌官方文档里的说明, Query ...

  10. 深入理解GCD ( 二 )

    转自@nixzhu的GitHub主页(译者:Riven.@nixzhu),原文<Grand Central Dispatch In-Depth: Part 2/2> 欢迎来到GCD深入理解 ...