当前位置: 移动技术网 > IT编程>开发语言>C/C++ > Qt之股票组件-自选股--列表可以拖拽、右键常用菜单

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

2019年07月09日  | 移动技术网IT编程  | 我要评论

奥斯汀格里芬,爹地你敢不认我,上海九龙男子医院怎么走

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


如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网