• 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. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


  • 相关阅读:
    可能出现jdk版本的bug和eclipse修改项目jdk的版本具体步骤
    神奇的bug,解决方法就是试试...
    eclipse安装完对应的sts插件后按alt+/却不能提示属性值
    Debug it (双头DP)
    A/B Matrix (贪心+棋盘图)
    map(lowerbond等等)
    A AD 2020(前缀和,空间压缩,模拟,3维数组)
    Path Queries (并查集+ 把图/树 分成很多块来看)
    Bin Packing Problem(线段树维护最大值,map元素查找的应用)
    Lowbit(势能线段树)
  • 原文地址:https://www.cnblogs.com/swarmbees/p/11154841.html
Copyright © 2020-2023  润新知