欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
  Qt之股票组件-自选股--列表可以拖拽、右键常用菜单
 
  目录
 
  一、开头嘴一嘴
 
  二、效果展示
 
  三、自选股列表
 
  1、列表初始化
 
  2、添加Item
 
  3、右键菜单
 
  4、拖拽Item
 
  5、刷新数据
 
  四、相关文章
 
  原文链接:Qt之股票组件-自选股--列表可以拖拽、右键常用菜单
 
  一、开头嘴一嘴
 
  上一篇文章Qt之股票组件-股票检索--支持搜索结果预览、鼠标、键盘操作讲述了股票检索功能,这篇文章我们来看看自选股列表的实现。
 
  如果有需要的朋友可以加我好友,有偿提供源码、或者也可以进一步提供功能定制
 
  封装的控件,或者demo都是没有样式的,所以看着会比较丑一些,不过加样式也是分分钟。。。这里咱可以先看功能,需要即可定制
 
  本篇文章的自选股和大多数炒股软件一样,每一条自选都是支持拖拽的,拖拽时鼠标会跟随一个拖拽映像,并且鼠标移动时,会有拖拽提示,告知我们鼠标释放时拖拽项将会被插入到哪个位置。除过拖拽之外,自选股列表还支持右键菜单,都是一样常用的操作。
 
  右键菜单包括置顶、置低、删除、下移一项、上移一项等
 
  本篇文章中不包括的功能也可以提供定制,需求合理即可。
 
  下面来具体说一说这个功能的实现思路,会公开大多数核心代码,有需要的同学可以根据思路自行完善整个代码。
 
  二、效果展示
 
  如下效果图所示,是自选股使用上的一个展示效果,具有如下功能
 
  搜索编辑框,支持股票代码和股票名称搜索
 
  搜索预览框支持鼠标hover,并且可以使用键盘上下键进行当前项切换,单机时支持切换自选股
 
  自选股列表,支持拖拽,拖拽时会有拖拽项映像,并示意将要拖拽到哪个位置
 
  支持右键菜单,可以对某一项进行移动,删除等操作
 
  如果觉着demo比较丑的话,可以看财联社-产品展示这篇文章中的效果图
 
  三、自选股列表
 
  接下来就是我们这篇文章的重头戏了,也是比较复杂的一个内容。
 
  自选股列表我选择的是使用QListWidget来实现,然后每一个item上在放一个Widget即可,Widget就是我们定制窗体内容,
 
  这里我们主要讲解几个比较重要的核心内容
 
  1、列表初始化
 
  初始化StockList,实际上自选股列表应该从服务器拉取,我们这里作为demo测试,因此就自己模拟了5条数据进行插入。
 
  //已选个股列表
 
  d_ptr->m_pStockList=newStockList;
 
  connect(d_ptr->m_pStockList,&StockList::RowClicked,this,[this](constQString&symbol){
 
  emitRowClicked(symbol);
 
  });
 
  //测试数据正常情况下应该是列表自己拉取
 
  OptionalMarketItemitem;
 
  for(inti=1;i<=5;++i)
 
  {
 
  item.wstrSymbol=QString("0h000%1").arg(i).toStdWString();
 
  item.wstrName=QString("%1%1%1").arg(i).toStdWString();
 
  item.wstrIndustryName=QString("pingyin%1").arg(i).toStdWString();
 
  d_ptr->m_pStockList->AddItem(item);
 
  }
 
  2、添加Item
 
  往StockList中添加item项时,我们首先需要构造一个标准的QListWidgetItem结构,然后把我们自己定制的ListItem放到这个标准item结构上。
 
  QListWidgetItem*StockList::AddItem(constOptionalMarketItem&data)
 
  {
 
  ListItem*itemWidget=newListItem;
 
  itemWidget->SetData(data);
 
  QListWidgetItem*item=newQListWidgetItem;
 
  addItem(item);
 
  item->setSizeHint(QSize(0,50));
 
  setItemWidget(item,itemWidget);
 
  returnitem;
 
  }
 
  ListItem就是一个普通的QWidget,上边排列了一些QLabel,用于显示我们的股票数据。
 
  ListItem界面构造就不过多解释了,唯一需要说明的就是,我们股票数据发送变化时,界面上会有红绿色框的动画提示,这里需要调用两行代码来实现重新获取控件qss代码,并刷洗界面。
 
  this->style()->unpolish(this);
 
  this->style()->polish(this);
 
  3、右键菜单
 
  本篇文章和上一篇文章的右键菜单实现方式一样,都是参考我很早以前写的Qt之自定义QLineEdit右键菜单这篇文章,实现默认的contextMenuEvent函数即可。
 
  右键菜单已经说的很多了,这里就一笔带过了,需要的同学可以自己快速的瞅一眼,应该比较容易理解。
 
  voidStockList::contextMenuEvent(QContextMenuEvent*event)
 
  {
 
  if(d_ptr->m_AllowMenu==false)
 
  {
 
  return;
 
  }
 
  if(d_ptr->m_ContextMenu==nullptr)
 
  {
 
  d_ptr->m_ContextMenu=newQMenu(this);
 
  d_ptr->m_ContextMenu->setObjectName(QStringLiteral("StockListMenu"));
 
  d_ptr->m_ContextMenu->setFixedWidth(100);
 
  QAction*delAct=newQAction(QStringLiteral("删除自选股"),d_ptr->m_ContextMenu);
 
  QAction*topAct=newQAction(QStringLiteral("置顶"),d_ptr->m_ContextMenu);
 
  QAction*bottomAct=newQAction(QStringLiteral("置底"),d_ptr->m_ContextMenu);
 
  QAction*upAct=newQAction(QStringLiteral("上移一位"),d_ptr->m_ContextMenu);
 
  QAction*downAct=newQAction(QStringLiteral("下移一位"),d_ptr->m_ContextMenu);
 
  connect(delAct,&QAction::triggered,this,&StockList::DeleteSotck);
 
  connect(topAct,&QAction::triggered,this,&StockList::TopSotck);
 
  connect(bottomAct,&QAction::triggered,this,&StockList::BottomSotck);
 
  connect(upAct,&QAction::triggered,this,&StockList::UpSotck);
 
  connect(downAct,&QAction::triggered,this,&StockList::DownSotck);
 
  d_ptr->m_ContextMenu->addAction(delAct);
 
  d_ptr->m_ContextMenu->addAction(topAct);
 
  d_ptr->m_ContextMenu->addAction(bottomAct);
 
  d_ptr->m_ContextMenu->addAction(upAct);
 
  d_ptr->m_ContextMenu->addAction(downAct);
 
  }
 
  d_ptr->m_ContextMenu->exec(mapToGlobal(event->pos()));
 
  QListWidget::contextMenuEvent(event);
 
  }
 
  以上5个菜单,虽然看起来功能相差很多,但是其实处理逻辑基本都是一样的,先是一个内容结构排序,然后进行刷新数据到界面上。
 
  为了节省篇幅,我这里就只介绍置顶一只股票的操作
 
  置顶的逻辑看起来是这样的
 
  移除当前项
 
  并且把当前项item插入到新位置
 
  构造一个新的Widget,设置给item
 
  把新位置的item设置为当前选中项
 
  上传最新列表到数据中心,或者服务器
 
  voidStockList::TopSotck()
 
  {
 
  QListWidgetItem*item=currentItem();
 
  if(item==nullptr)
 
  {
 
  return;
 
  }
 
  if(row(item)==0)
 
  {
 
  return;
 
  }
 
  ListItem*itemWidget=ItemWidget(item);
 
  QListWidgetItem*newItem=takeItem(row(item));
 
  insertItem(0,newItem);
 
  ListItem*topWidget=newListItem;
 
  topWidget->SetData(itemWidget->GetData());
 
  setItemWidget(newItem,topWidget);
 
  if(itemWidget)
 
  {
 
  itemWidget->close();
 
  itemWidget=nullptr;
 
  }
 
  setCurrentItem(newItem);
 
  StorageData();
 
  }
 
  4、拖拽Item
 
  拖拽Item应该算是一个比较难一点儿功能,好在Qt已经为我们实现了一套QDrag事件的回调方法,也比较好使,如下图所示,重写如下4个方法,基本的拖拽事件就能完成了。
 
  但是这里我么有选择默认的这个回调函数来实现这个功能,其中最大的原因就是,他们的可定制性太局限了。
 
  我这里采取的是自己模拟鼠标拖拽功能,同过重写如下几个函数来达到我的目的
 
  virtualvoidmousePressEvent(QMouseEvent*event)override;
 
  virtualvoidmouseMoveEvent(QMouseEvent*event)override;
 
  virtualvoidmouseReleaseEvent(QMouseEvent*event)override;
 
  virtualvoidenterEvent(QEvent*event)override;
 
  virtualvoidleaveEvent(QEvent*event)override;
 
  鼠标按下时,主要是记录了一些内容状态,方便在鼠标移动时去做判断,并决定是否启用鼠标拖拽功能
 
  鼠标移动就比较复杂了,进行了各种对比,还需要移动被拖拽项的映像位置,移动那一根水平线的位置
 
  鼠标释放时,调整整个列表的内容
 
  鼠标进入窗体时,显示水平标识线
 
  鼠标离开窗体时,隐藏水平标识线
 
  上边只是粗略的描述了这几个函数的功能,因为函数实现体都比较长,因此这里我也是选择几个关键点来做以说明。
 
  a、move函数
 
  产生拖拽时,移动鼠标,我们需要处理很多事件,比如
 
  1、初始化水平表示线和拖拽项映像
 
  if(d_ptr->m_ShotLine==nullptr)
 
  {
 
  InitShotLine();
 
  }
 
  if(d_ptr->m_ShotPicture==nullptr)
 
  {
 
  InitShotLabel();
 
  }
 
  2、拖拽时修改鼠标状态
 
  根据拖拽启动后,鼠标是否还在当前拖拽项上,设置鼠标的状态。
 
  if(ListItem*newWidget=ItemWidget(d_ptr->dragItem))
 
  {
 
  d_ptr->m_ShotPicture->move(QCursor::pos()-d_ptr->dragItemPos);
 
  d_ptr->m_DragRect=visualItemRect(d_ptr->dragItem);
 
  if(d_ptr->m_DragRect.contains(event->pos())||event->pos().isNull())
 
  {
 
  if((event->pos()-d_ptr->startPos).manhattanLength()>5)
 
  {
 
  setCursor(Qt::ForbiddenCursor);
 
  }
 
  }
 
  else
 
  {
 
  setCursor(Qt::ArrowCursor);
 
  }
 
  if(d_ptr->m_ShotPicture->isHidden())
 
  {
 
  d_ptr->m_ShotPicture->show();
 
  }
 
  }
 
  b、release函数
 
  鼠标释放时,把拖拽项移动到新的位置
 
  if(ListItem*oldWidget=ItemWidget(d_ptr->dragItem))
 
  {
 
  QListWidgetItem*newItem=newQListWidgetItem;
 
  ListItem*itemWidget=newListItem;
 
  itemWidget->SetData(oldWidget->GetData());
 
  insertItem(insertPos,newItem);
 
  newItem->setSizeHint(QSize(0,50));
 
  setItemWidget(newItem,itemWidget);
 
  setCurrentItem(newItem);
 
  oldWidget->deleteLater();
 
  }
 
  5、刷新数据
 
  全量刷新数据。在原来的列表上刷新数据
 
  当原始列表行数不够时,构造新的行
 
  当原始列表函数多时,移除末尾多的行
 
  voidStockList::Update_p(OptionalMarketItemVectordata)
 
  {
 
  d_ptr->m_bOnceLoad=true;
 
  disconnect(this,&QListWidget::currentItemChanged,this,&StockList::CurrentItemChanged);
 
  inti=0;
 
  for(autoiter=data.begin();iter!=data.end();++iter,++i)
 
  {
 
  boolsuccess=false;
 
  if(QListWidgetItem*item=this->item(i))
 
  {
 
  if(ListItem*itemWidget=ItemWidget(item))
 
  {
 
  itemWidget->SetData(*iter);
 
  success=true;
 
  }
 
  }
 
  if(!success)
 
  {
 
  AddItem(*iter);
 
  }
 
  }
 
  if(i<this->count())
 
  {
 
  QListWidgetItem*item=nullptr;
 
  while(item=this->item(i))
 
  {
 
  if(ListItem*itemWidget=ItemWidget(item))
 
  {
 
  itemWidget->close();
 
  itemWidget=nullptr;
 
  }
 
  item=takeItem(i);
 
  deleteitem;
 
  }
 
  }
 
  if(d_ptr->m_LeftPress==false)
 
  {
 
  RecoveryCurrentItem();
 
  }
 
  connect(this,&QListWidget::currentItemChanged,this,&StockList::CurrentItemChanged);
 
  }
 
  以上讲解都是针对自选股列表的实现,内容差不多就这些了,如果有疑问欢迎提出
 
  四、相关文章
 
  财联社-产品展示
 
  Qt之自定义QLineEdit右键菜单
 
  Qt之股票组件-股票检索--支持搜索结果预览、鼠标、键盘操作

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h56755.shtml