zoukankan      html  css  js  c++  java
  • Qt之股票组件-自选股--列表可以拖拽、右键常用菜单

    原文链接:Qt之股票组件-自选股--列表可以拖拽、右键常用菜单

    一、开头嘴一嘴

    上一篇文章Qt之股票组件-股票检索--支持搜索结果预览、鼠标、键盘操作讲述了股票检索功能,这篇文章我们来看看自选股列表的实现。

    如果有需要的朋友可以加我好友,有偿提供源码、或者也可以进一步提供功能定制

    封装的控件,或者demo都是没有样式的,所以看着会比较丑一些,不过加样式也是分分钟。。。这里咱可以先看功能,需要即可定制

    本篇文章的自选股和大多数炒股软件一样,每一条自选都是支持拖拽的,拖拽时鼠标会跟随一个拖拽映像,并且鼠标移动时,会有拖拽提示,告知我们鼠标释放时拖拽项将会被插入到哪个位置。除过拖拽之外,自选股列表还支持右键菜单,都是一样常用的操作。

    右键菜单包括置顶、置低、删除、下移一项、上移一项等

    本篇文章中不包括的功能也可以提供定制,需求合理即可。

    下面来具体说一说这个功能的实现思路,会公开大多数核心代码,有需要的同学可以根据思路自行完善整个代码。

    二、效果展示

    如下效果图所示,是自选股使用上的一个展示效果,具有如下功能

    1. 搜索编辑框,支持股票代码和股票名称搜索
    2. 搜索预览框支持鼠标hover,并且可以使用键盘上下键进行当前项切换,单机时支持切换自选股
    3. 自选股列表,支持拖拽,拖拽时会有拖拽项映像,并示意将要拖拽到哪个位置
    4. 支持右键菜单,可以对某一项进行移动,删除等操作

    如果觉着demo比较丑的话,可以看财联社-产品展示这篇文章中的效果图

    三、自选股列表

    接下来就是我们这篇文章的重头戏了,也是比较复杂的一个内容。

    自选股列表我选择的是使用QListWidget来实现,然后每一个item上在放一个Widget即可,Widget就是我们定制窗体内容,

    这里我们主要讲解几个比较重要的核心内容

    1、列表初始化

    初始化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);
    }
    

    2、添加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);
    

    3、右键菜单

    本篇文章和上一篇文章的右键菜单实现方式一样,都是参考我很早以前写的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个菜单,虽然看起来功能相差很多,但是其实处理逻辑基本都是一样的,先是一个内容结构排序,然后进行刷新数据到界面上。

    为了节省篇幅,我这里就只介绍置顶一只股票的操作

    置顶的逻辑看起来是这样的

    1. 移除当前项
    2. 并且把当前项item插入到新位置
    3. 构造一个新的Widget,设置给item
    4. 把新位置的item设置为当前选中项
    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();
    }
    
    

    4、拖拽Item

    拖拽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;
    
    1. 鼠标按下时,主要是记录了一些内容状态,方便在鼠标移动时去做判断,并决定是否启用鼠标拖拽功能
    2. 鼠标移动就比较复杂了,进行了各种对比,还需要移动被拖拽项的映像位置,移动那一根水平线的位置
    3. 鼠标释放时,调整整个列表的内容
    4. 鼠标进入窗体时,显示水平标识线
    5. 鼠标离开窗体时,隐藏水平标识线

    上边只是粗略的描述了这几个函数的功能, 因为函数实现体都比较长,因此这里我也是选择几个关键点来做以说明。

    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 = 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();
    }
    

    5、刷新数据

    全量刷新数据。在原来的列表上刷新数据

    当原始列表行数不够时,构造新的行

    当原始列表函数多时,移除末尾多的行

    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之自定义QLineEdit右键菜单

    Qt之股票组件-股票检索--支持搜索结果预览、鼠标、键盘操作

    高仿富途牛牛-组件化(一)-支持页签拖拽、增删、小工具

    高仿富途牛牛-组件化(二)-磁力吸附

    高仿富途牛牛-组件化(三)-界面美化

    高仿富途牛牛-组件化(四)-优秀的时钟

    高仿富途牛牛-组件化(五)-如何去管理炒鸡多的小窗口

    高仿富途牛牛-组件化(六)-炒鸡牛逼的布局记忆功能(序列化和反序列化)

    如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!




    很重要--转载声明

    1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

    2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


  • 相关阅读:
    PTA —— 基础编程题目集 —— 函数题 —— 61 简单输出整数 (10 分)
    PTA —— 基础编程题目集 —— 函数题 —— 61 简单输出整数 (10 分)
    练习2.13 不用库函数,写一个高效计算ln N的C函数
    练习2.13 不用库函数,写一个高效计算ln N的C函数
    练习2.13 不用库函数,写一个高效计算ln N的C函数
    迷宫问题 POJ 3984
    UVA 820 Internet Bandwidth (因特网带宽)(最大流)
    UVA 1001 Say Cheese(奶酪里的老鼠)(flod)
    UVA 11105 Semiprime Hnumbers(H半素数)
    UVA 557 Burger(汉堡)(dp+概率)
  • 原文地址:https://www.cnblogs.com/swarmbees/p/11154841.html
Copyright © 2011-2022 走看看