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

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. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時經過修改本文達到有利於轉載者的目的。

相關文章
相關標籤/搜索