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

1、概述

代码写的久了,什么功能都想搞点儿模式。不知道是否是只有我一我的这么想的,作功能时不在是只为了完成功能,而是在完成功能的同时会去考虑可扩展性怎么样?哪块儿的代码能够复用?又或者哪里须要留更多的接口?这两个类之间是否是能够加一个协做类?总之各类想法都出来了。app

假设说有这样一种场景,咱们经过一个入口界面,上边有不少种按钮,当咱们点击上边的按钮时,程序就会生成一个小窗口。这些小窗口他们的外在行为基本一致,只是窗口里边展现的内容各不同。工具

总结一下应该是这样的组件化

  • 窗体能够经过标题栏进行拖拽
  • 鼠标移动到边界能够进行放大缩小
  • 鼠标选中时边框高量显示
  • 标题栏具备名称、关闭和各类各样的菜单
  • 不一样功能窗体的菜单内容不同,展现风格也可能彻底不一样
  • 标题栏和中心窗体也有交互
  • 连续点击一个按钮时,生成同类型的窗体不能彻底覆盖

用过富途牛牛的同窗应该对这个功能比较清楚一点,富途中的交易模块也是如此,工具箱里支持各类小窗体生成,而且都有各自丰富的内容。布局

这么多繁杂的窗体,富途是怎么管理的呢?测试

本篇文章就来仔细挖掘下这个功能,咱们也来实现一个小窗口管理功能。this

2、效果展现

如效果图所示,咱们以前所说的几个功能点都已经有了,而且在配色上比以前的demo程序也有改善,固然要投入使用仍是须要设计师同窗通过专业的指点。spa

3、功能类

为了实现这个窗口管理功能,思考的头都快要炸掉了,不是由于功能有多复杂,而是做为一个开发人员,我想的太多了,老是会出现一种幻觉,产品经理可能会提出这样那样的新需求,测试也可能会给你报一个莫名其妙的问题。设计

最最关键的仍是咱们要对本身写的代码有一个基本要求。结构必须合理、减小冗余和耦合,让其余人阅读代码时使用最小的成本,code

为了作这个窗口管理功能,我也是想了很久,老是想着设计的更合理一些,别人在使用时就会更简单一些,使用成本也会更小。

为此,我引入了好几个关键类,都是辅助管理窗口的,下面先来一张类图,这是我使用starUML画的,不是特别规范,主要是为了说明这些类的功能,以及他们之间的一个关系,图上都用注释写出了每一个类的大概做用。

从图上也能够看出来,类中就是一个简单的名字,我没有把类的接口都贴上去,一个是时间问题,另外一个我也觉着不是特别有必要,由于我自己对这个画图工具使用就不是特别深刻,因此这里就只提供了简单的版本。你们凑合看吧,对理解设计思路仍是有很大的帮助。

本篇文章主要讲的是小窗口管理,可是这里主要是经过讲述迷你报价小窗口来讲明怎么去管理炒鸡多的小窗口

从上边图中可知,关键类有如下这些

  1. ISmall:业务窗口接口类,负责和SmallWidget进行通讯
  2. MiniPriceSmall:迷你报价小窗口的中心窗口
  3. SmallWidget:小窗口模板类,这是全部小窗口的外壳类,包含了标题栏。迷你报价窗口就是构造了这么一个类,而后设置了一个中心窗口MiniPriceSmall。其余的业务窗口都是使用一样的模式
  4. ViewModePanel:迷你报价工具菜单按下时,弹出的视图列表,主要是为了修改显示模式。仔细观察效果展现中的gif图,菜单'M'被点击时,弹出了显示模式窗口,随后在显示模式窗口任意选项上单击后,迷你报价窗口上都出现了一行欢迎文本--感谢您的使用,您选择了模式:X
  5. SmallContext:小窗口管理上下文类,主要负责处理咱们定制好的外壳类SmallWidget和业务窗口MiniPriceSmall(其余更多业务窗口)之间的消息通讯
  6. SmallGroup:小窗口组管理者,主要负责磁力吸附这个功能,详情参看高仿富途牛牛-组件化(二)-磁力吸附这篇文章
  7. SmallFactory:小窗口工厂,负责构造各式各样的小窗口
  8. ToolBoxDialog:工具箱的实现没有专门去写文章讲解,须要了解的能够参看高仿富途牛牛-组件化(三)-界面美化这篇文章,第三小节对工具箱作了简单的讲解

4、设计上的考虑

首先,我本身作的是一个组件化的功能,我须要考虑的东西也是更多的,不只仅要把功能完成,更多的是要去考虑,别人用的时候代价怎么能够更小?能够复用的代码,我尽可能都提取出来,写成共性的东西,其余开发在作相应业务模块时,只须要考虑真的和他们有直接关系的地方。

这里咱们就拿迷你报价这个功能来讲事,看看怎么去设计是合理的,或者说目前看起来是合理的,可能后期根据需求,咱们的结构或许会进行必定的调整。

一、功能拆分

单独拿出一个迷你报价功能来讲,可能100%的人都会说很简单,这有什么难的,我刚参加工做那会儿百度百度也都能作出来。但是每每把一些简单的东西,抽象抽来,让程序变得更简单这件事情自己就是一件比较难的事情。

迷你报价这个窗口总的来讲能够分为上下两部分:上边标题栏和下面内容展现区域

标题栏

标题栏是每个小窗口都有的,所以这里咱们确定是要单独提出来做为一个公有的东西,当其余开发作自选股、小时钟、短线精灵这些小窗口时,标题栏的移动事件他们就根本不须要考虑了。

细心的同窗可能会发现了,标题栏上还有一些不认识的按钮,这些按钮时干什么的呢?

虽然标题栏已经被公有化,可是标题栏上的按钮可不是这么想的,他们的行为要根据底部内容窗口的类型决定,所以这里就有了必定的变化。

为了适应这个变化,我本身定义了一个接口类ISmall,主要是让内容窗口进行继承的,也就是说内容窗口须要重写这个接口类的一些接口,来完成运行时的一些设定。

好比说,迷你报价标题栏按钮显示有哪些?按钮显示时都有哪些功能?按钮点击时该有什么样的相应?按钮点击后弹出的面板点击被点击了,内容窗口作何处理等等。

内容展现

内容展现窗口就比较简单了,这里有2个选择,继承ISmall接口类或者不继承

  1. 继承-意味着内容窗口须要和外壳窗口进行消息通讯
  2. 不继承-咱们就只是展现数据的,不须要进行消息通讯

事实上大多数的内容窗口都是须要继承ISmall这个接口类的,纯展现而没有交互的功能几乎没有。

二、关键类

ISmall

ISmall接口类是毫无疑问的关键类,它是一个纯虚类,继承它的类都必须实现它的全部接口,以下代码所示

/**
* 简介: 小窗口上的回调事件,须要通知中心窗体
*/
struct ISmallCall
{
    virtual ViewMode ViewType(SmallToolType) = 0;
    virtual void GetItems(QVector<DataItem> &) = 0;
    virtual QSize GetSize() const = 0;
    virtual void Notify(const QString &) = 0;
};

GetSize()、GetItems()这些接口都是获取数据接口,外壳类SmallWidget,会根据当前中心窗口返回的定制数据去初始化本身。

好比说效果图中展现的显示模式面板,这个面板中的数据内容,就是迷你报价类MiniPriceSmall重写了getItems接口以后,给外壳类提供的数据,而且制定了事件触发时使用的显示模式为视图。

迷你报价类重写的数据准备接口以下

void MiniPriceSmall::GetItems(QVector<DataItem> & items)
{
    for (int i = 0; i < 9; ++i)
    {
        DataItem item;
        item.name = 'A' + i;
        item.type = item.name;
        item.img = "./image/mainWindow/tquant-btn_normal.png";
        items.append(item);
    }
}

目前接口类中的接口还不是特别完善,根据后续业务功能的变化,接口确定还会继续增多。

MiniPriceSmall

这个类是迷你报价的数据展现类,他继承自QWidget窗口类和ISmall接口类,界面搭建这里我就不说了,两个缘由:其一是这里的界面自己就是空的,其二也是比较关键的,中心窗口的内容是根据业务类型决定的,后期根据不一样开发负责的功能本身去搭建。一般状况下这里都是别人已经封装好了的控件,有必要的时候在使用一个简单外壳类包一层便可。

除过界面以外,就是ISmall的接口实现类了,一部分接口是给外部SmallWidget提供数据的,而另外一部分接口就是响应外部事件。

目前来讲,响应外部接口事件就只有一个接口Notify,比较重要,消息响应时根据参数来区分消息类型

SmallWidget

小窗口类,为全部小窗口的外壳类,提供了定制化的标题栏和边界缩放。功能实现不难,须要的可自行百度。

ViewModePanel

显示模式面板,主要是一些小窗口能够根据该面板的选项能够进行显示模式的重组,而且能够切换窗体的大小等等。效果展现中咱们只是修改了显示的问题,不过实现起来思路是同样的,最主要的是内容窗体已经接收到咱们传递的消息。

SmallContext

这是一个比较关键的类,当时想了好久,到底要不要加。

这个类主要负责的功能就是同步小窗体外壳类SmallWidget和内容窗体类之间的消息传递。

  • 当SmallWidget想要给内容窗体传递消息时,会经过ISmall接口类进行发送指令,由于内容窗体是继承了这个ISmall接口类的
  • 当内容窗体想给外壳发送消息时,是经过ISmall的接口调用来到达目的的,严格来讲实际上是外壳窗体主动跟内容窗体要的消息内容。

当SunPanel面板经过工厂类SmallFactory进行构造小窗口时,会把SmallWidget和构造好的内容窗体进行注册,并开始接收SmallWidget发送过来的事件请求。

void SmallContext::RegisterSmallWidget(SmallWidget * widget, ISmallCall * call)
{
    m_itemMap[widget] = call;

    connect(widget, &SmallWidget::ClickedButton, this, &SmallContext::HandleEvent);
}

当工具栏点击了'M'按钮时,SMallWidget就会给SmallContext发送事件请求,并附带相关参数,这里SmallWidget发送了显示模式选择事件,下面是SmallContext的处理方式

void SmallContext::HandleEvent(int type, const QPoint & globalPos)
{
    if (SmallWidget * widget = dynamic_cast<SmallWidget *>(sender()))
    {
        if (m_itemMap.contains(widget))
        {
            if (ISmallCall * call = m_itemMap[widget])
            {
                ViewMode view = call->ViewType(SmallToolType(type));

                if (view == VM_VIEW)
                {
                    ShowView(widget, call, globalPos);
                }
            }
        }
    }
}

SmallFactory

最后就是工厂类了,一看名字就知道,他是一个工厂,专门负责生产小窗口的,对工具箱来讲,他只知道给SubPanel发送显示一个小窗口这个指令,可是SubPanel本身其实也不会去真正的构造一个小窗口,缘由其实很简单,单个的构造一个小窗体其实没问题,可是当小窗体种类多起来时,须要维护的消息就随之增多。

重构以后的代码看起来像这样,当SubPanel类须要一个小窗口时,他只须要这么干就行

SmallWidget * smallWidget = m_pSmallFactory->CreateWidget(type, subContent);
subContent->AddSmallWidget(smallWidget);

smallWidget->move(m_pSmallFactory->GetPos(type, subContent));
smallWidget->show();

调用工程的CreateWidget方法,构造一个小窗口,而后加入到本身的布局中

这里还有一个比较关键的问题,就是窗口初始位置管理,咱们在工厂里维护了一个位置偏移量,当连续请求同一类型的小窗口时,咱们会给这个偏移量增长一个固定值,使得连续请求时,同一类型的小窗口获取到的位置是有规律的变化

void SmallFactory::UpdateOffset(bool change, QWidget * parent)
{
    if (change)
    {
        m_offset += QPoint(13, 13);
    }
    else
    {
        m_offset = QPoint(0, 0);
    }

    QRect r = parent->rect();
    if ( m_offset.y() > r.height() / 4 
        || m_offset.x() > r.width() / 4)
    {
        m_offset *= -1;
        m_offset.setX(m_offset.x() - 15);
    }
}

5、相关文章

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

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

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

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

若是您以为文章不错,不妨给个 打赏,写做不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!




很重要--转载声明

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

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

相关文章
相关标签/搜索