原文连接:Qt之股票组件-自选股--列表能够拖拽、右键经常使用菜单服务器
上一篇文章Qt之股票组件-股票检索--支持搜索结果预览、鼠标、键盘操做讲述了股票检索功能,这篇文章咱们来看看自选股列表的实现。ide
若是有须要的朋友能够加我好友,有偿提供源码、或者也能够进一步提供功能定制函数
封装的控件,或者demo都是没有样式的,因此看着会比较丑一些,不过加样式也是分分钟。。。这里咱能够先看功能,须要便可定制工具
本篇文章的自选股和大多数炒股软件同样,每一条自选都是支持拖拽的,拖拽时鼠标会跟随一个拖拽映像,而且鼠标移动时,会有拖拽提示,告知咱们鼠标释放时拖拽项将会被插入到哪一个位置。除过拖拽以外,自选股列表还支持右键菜单,都是同样经常使用的操做。组件化
右键菜单包括置顶、置低、删除、下移一项、上移一项等布局
本篇文章中不包括的功能也能够提供定制,需求合理便可。测试
下面来具体说一说这个功能的实现思路,会公开大多数核心代码,有须要的同窗能够根据思路自行完善整个代码。动画
以下效果图所示,是自选股使用上的一个展现效果,具备以下功能this
若是觉着demo比较丑的话,能够看财联社-产品展现这篇文章中的效果图
接下来就是咱们这篇文章的重头戏了,也是比较复杂的一个内容。
自选股列表我选择的是使用QListWidget来实现,而后每个item上在放一个Widget便可,Widget就是咱们定制窗体内容,
这里咱们主要讲解几个比较重要的核心内容
初始化StockList,实际上自选股列表应该从服务器拉取,咱们这里做为demo测试,所以就本身模拟了5条数据进行插入。
//已选个股列表 d_ptr->m_pStockList = new StockList; connect(d_ptr->m_pStockList, &StockList::RowClicked, this, [this](const QString & symbol){ emit RowClicked(symbol); }); //测试数据 正常状况下 应该是列表本身拉取 OptionalMarketItem item; for (int i = 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); }
往StockList中添加item项时,咱们首先须要构造一个标准的QListWidgetItem结构,而后把咱们本身定制的ListItem放到这个标准item结构上。
QListWidgetItem * StockList::AddItem(const OptionalMarketItem & data) { ListItem * itemWidget = new ListItem; itemWidget->SetData(data); QListWidgetItem * item = new QListWidgetItem; addItem(item); item->setSizeHint(QSize(0, 50)); setItemWidget(item, itemWidget); return item; }
ListItem就是一个普通的QWidget,上边排列了一些QLabel,用于显示咱们的股票数据。
ListItem界面构造就不过多解释了,惟一须要说明的就是,咱们股票数据发送变化时,界面上会有红绿色框的动画提示,这里须要调用两行代码来实现从新获取控件qss代码,并刷洗界面。
this->style()->unpolish(this); this->style()->polish(this);
本篇文章和上一篇文章的右键菜单实现方式同样,都是参考我很早之前写的Qt之自定义QLineEdit右键菜单这篇文章,实现默认的contextMenuEvent函数便可。
右键菜单已经说的不少了,这里就一笔带过了,须要的同窗能够本身快速的瞅一眼,应该比较容易理解。
void StockList::contextMenuEvent(QContextMenuEvent * event) { if (d_ptr->m_AllowMenu == false) { return; } if (d_ptr->m_ContextMenu == nullptr) { d_ptr->m_ContextMenu = new QMenu(this); d_ptr->m_ContextMenu->setObjectName(QStringLiteral("StockListMenu")); d_ptr->m_ContextMenu->setFixedWidth(100); QAction * delAct = new QAction(QStringLiteral("删除自选股"), d_ptr->m_ContextMenu); QAction * topAct = new QAction(QStringLiteral("置顶"), d_ptr->m_ContextMenu); QAction * bottomAct = new QAction(QStringLiteral("置底"), d_ptr->m_ContextMenu); QAction * upAct = new QAction(QStringLiteral("上移一位"), d_ptr->m_ContextMenu); QAction * downAct = new QAction(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个菜单,虽然看起来功能相差不少,可是其实处理逻辑基本都是同样的,先是一个内容结构排序,而后进行刷新数据到界面上。
为了节省篇幅,我这里就只介绍置顶一只股票的操做
置顶的逻辑看起来是这样的
void StockList::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 = new ListItem; topWidget->SetData(itemWidget->GetData()); setItemWidget(newItem, topWidget); if (itemWidget) { itemWidget->close(); itemWidget = nullptr; } setCurrentItem(newItem); StorageData(); }
拖拽Item应该算是一个比较难一点儿功能,好在Qt已经为咱们实现了一套QDrag事件的回调方法,也比较好使,以下图所示,重写以下4个方法,基本的拖拽事件就能完成了。
可是这里我么有选择默认的这个回调函数来实现这个功能,其中最大的缘由就是,他们的可定制性太局限了。
我这里采起的是本身模拟鼠标拖拽功能,同太重写以下几个函数来达到个人目的
virtual void mousePressEvent(QMouseEvent * event) override; virtual void mouseMoveEvent(QMouseEvent * event) override; virtual void mouseReleaseEvent(QMouseEvent * event) override; virtual void enterEvent(QEvent * event) override; virtual void leaveEvent(QEvent * event) override;
上边只是粗略的描述了这几个函数的功能, 由于函数实现体都比较长,所以这里我也是选择几个关键点来作以说明。
a、move函数
产生拖拽时,移动鼠标,咱们须要处理不少事件,好比
一、初始化水平表示线和拖拽项映像
if (d_ptr->m_ShotLine == nullptr) { InitShotLine(); } if (d_ptr->m_ShotPicture == nullptr) { InitShotLabel(); }
二、拖拽时修改鼠标状态
根据拖拽启动后,鼠标是否还在当前拖拽项上,设置鼠标的状态。
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 = new QListWidgetItem; ListItem * itemWidget = new ListItem; itemWidget->SetData(oldWidget->GetData()); insertItem(insertPos, newItem); newItem->setSizeHint(QSize(0, 50)); setItemWidget(newItem, itemWidget); setCurrentItem(newItem); oldWidget->deleteLater(); }
全量刷新数据。在原来的列表上刷新数据
当原始列表行数不够时,构造新的行
当原始列表函数多时,移除末尾多的行
void StockList::Update_p(OptionalMarketItemVector data) { d_ptr->m_bOnceLoad = true; disconnect(this, &QListWidget::currentItemChanged, this, &StockList::CurrentItemChanged); int i = 0; for (auto iter = data.begin(); iter != data.end(); ++iter, ++i) { bool success = 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); delete item; } } if (d_ptr->m_LeftPress == false) { RecoveryCurrentItem(); } connect(this, &QListWidget::currentItemChanged, this, &StockList::CurrentItemChanged); }
以上讲解都是针对自选股列表的实现,内容差很少就这些了,若是有疑问欢迎提出
Qt之股票组件-股票检索--支持搜索结果预览、鼠标、键盘操做
高仿富途牛牛-组件化(六)-炒鸡牛逼的布局记忆功能(序列化和反序列化)
![]() |
![]() |
很重要--转载声明