1263 words
6 minutes
Nest开发笔记-NestUI v0.1.0
2024-11-30

在本篇博客中,我将介绍NestUI模块的设计与实现,并说明如何集成第三方UI库SARibbon,如何设计MainWindowDockWidgetDockWidgetManager等类

项目仓库

Jerryym
/
Nest
Waiting for api.github.com...
00K
0K
0K
Waiting...

NestUI模块概述#

模块职责#

  • 为Nest框架提供基于Qt的UI组件
  • 负责对于框架中的UI进行管理,如:对于停靠窗口的管理(DockWidgetManager)

集成SARibbon#

  • SARibbon

    • 是一个Qt下的Ribbon界面控件,提供了类似微软Office系列软件的操作界面
    • SARibbon适用于大型软件、工业软件、复杂软件的ui
    • SARibbon在设计时参考了MFC Ribbon接口的命名风格
    • SARibbon的界面样式参考了微软Office系列软件以及WPS软件的Ribbon界面,并结合了两者的优点
    • SARibbon是一个可定义多种主题风格的Ribbon控件,它能通过qss快速的定义出自己想要的主题风格
  • 集成SARibbon

    在NestUI中,我将SARibbon库作为第三方构件来支撑框架中的UI。

    • 使用命令git submodule add https://github.com/Jerryym/SARibbon.git NestUI/vendor/SARibbon将SARibbon作为子模块添加到NestUI项目中,并通过命令git submodule update --init --recursive保证子模块被正确初始化和更新

MainWindow#

  • 设计思路

    • 设计一个结构体用于存储主窗口属性:窗口宽度、窗口高度和窗口标题

    • 设计一个名为MainWindow的类,此类继承于SARibbonMainWindow,使其能够使用SARibbon风格的UI

      NOTE

      后续根据开发和应用的实际需求,可能会把MainWindow设计成纯虚类。

  • 代码

    //MainWindow.h
    namespace NestUI {
    
        /// @brief 窗口属性
        typedef struct NESTUI_API sWindowProp
        {
            unsigned int m_nWidth;  //窗口宽
            unsigned int m_nHeight;  //窗口高
            QString m_STitle;   //窗口标题
    
            sWindowProp(unsigned int width = 1600, unsigned int height = 900,
              const QString& title = "Nest")
              : m_nWidth(width), m_nHeight(height), m_STitle(title)
            {}
        }sWindowProp;
    
        /// @brief 主窗口
        class NESTUI_API MainWindow : public SARibbonMainWindow
        {
            Q_OBJECT
        public:
            MainWindow(QWidget* parent = nullptr, const sWindowProp& sProp = sWindowProp());
            virtual ~MainWindow() {}
    
        public:
            /// @brief 获取主窗口宽度
            /// @return 
            unsigned int GetWidth() const { return m_sProp.m_nWidth; }
            /// @brief 获取主窗口高度
            /// @return 
            unsigned int GetHeigth() const { return m_sProp.m_nHeight; }
            /// @brief 获取主窗口标题名
            /// @return 
            QString GetTitle() const { return m_sProp.m_STitle; }
    
        private:
            /// @brief 主窗口属性
            sWindowProp m_sProp;
        };
    
    }
    
    //MainWindow.cpp
    namespace NestUI {
    
        MainWindow::MainWindow(QWidget* parent, const sWindowProp& sProp)
          : SARibbonMainWindow(parent), m_sProp(sProp)
        {
            //设置窗口标题
            setWindowTitle(sProp.m_STitle);
            //初始化窗口大小
            resize(QSize(sProp.m_nWidth, sProp.m_nHeight));
        }
    
    }
    

DockWidget#

  • 设计思路

    • 由于整个Nest框架是基于Qt开发的,所以主窗口类一定要能满足Qt相关特性(e.g:信号和槽机制)
    • 支持窗口设计,如设置窗口初始大小,窗口标题,窗口图标等
    • 支持可扩展,能够根据不同应用场景进行对应的继承扩展
  • 实现

    • 设计一个名为 DockWidget的类,此类继承于 QDockWidget,使其满足Qt停靠窗口相关功能和特性。
    • 设计一个名为 clearWidget方法,用于清空DockWidget内容。
    • 设计一个名为 refreshWidget方法,用于刷新DockWidget内容。
  • 代码

    //DockWidget.h
    namespace NestUI {
    
      class NESTUI_API DockWidget : public QDockWidget
      {
          Q_OBJECT
      public:
          DockWidget(QWidget* parent = nullptr);
          ~DockWidget();
    
      public:
          /// @brief 清空DockWidget内容
          virtual void clearWidget() = 0;
          /// @brief 刷新DockWidget
          virtual void refreshWidget() = 0;
      };
    
    }
    

DockWidgetManager#

在框架中,我希望有一个对象能管理应用程序主窗口中的所有停靠窗口,且能够提供相关增删改查的方法。

  • 功能设计

    • 这个类的对象在应用程序主窗口生命周期内当前仅存在一个,因此我们可以使用单例模式设计这个类。
    • 提供相关增删改查的方法。
  • 实现

    • 设计一个名为 DockWidgetManager的类,使用单例模式设计。
    • 在类中定义一个 QMap类型的成员变量,实现DockWidget对应的窗口标题与对应的DockWidget对象对应。
  • 代码

    //DockWidgetManager.h
    namespace NestUI {
    
        /// @brief DockWidget窗口管理类
        class NESTUI_API DockWidgetManager
        {
        public:
            /// @brief 获取当前DockManager
            /// @return 
            static DockWidgetManager* GetDockManager(QMainWindow* mainWindow);
    
            /// @brief 添加DockWidget
            /// @param STitle DockWidget对应的窗口标题
            /// @param dockWidget 
            void AddDockWidget(const QString& STitle, QDockWidget* dockWidget, Qt::DockWidgetArea area);
    
            /// @brief 获取DockWidget
            /// @param STitle STitle DockWidget对应的窗口标题
            /// @return 
            QDockWidget* GetDockWidget(const QString& STitle);
    
            /// @brief 移除DockWidget
            /// @param STitle DockWidget对应的窗口标题
            void RemoveDockWidget(const QString& STitle);
    
            /// @brief 获取当前主窗口中的dockWidget个数
            /// @return 
            int GetDockWidgetNum() const { return m_DockWidgetMap.size(); }
    
        protected:
            DockWidgetManager(QMainWindow* mainWindow);
            ~DockWidgetManager();
    
        private:
            /// @brief DockWidget窗口管理单例: 程序运行时,每个主窗口当且只能有一个DockManager存在
            static DockWidgetManager* s_Instance;
            QMainWindow* m_mainWindow; //应用程序主窗口
            //dockWidget Map
            QMap<QString, QDockWidget*> m_DockWidgetMap;
        };
    
    }
    
    //DockWidgetManager.cpp
    namespace NestUI {
    
        DockWidgetManager* DockWidgetManager::s_Instance = nullptr;
    
        DockWidgetManager::DockWidgetManager(QMainWindow* mainWindow)
        {
            m_mainWindow = mainWindow;
        }
    
        DockWidgetManager::~DockWidgetManager()
        {
            m_mainWindow = nullptr;
        }
    
        DockWidgetManager* DockWidgetManager::GetDockManager(QMainWindow* mainWindow)
        {
            // FIXED: 在此处插入 return 语句
            s_Instance = new DockWidgetManager(mainWindow);
            return s_Instance;
        }
    
        void DockWidgetManager::AddDockWidget(const QString& STitle, QDockWidget* dockWidget, Qt::DockWidgetArea area)
        {
            m_DockWidgetMap.insert(STitle, dockWidget);
            m_mainWindow->addDockWidget(area, dockWidget);
        }
    
        QDockWidget* DockWidgetManager::GetDockWidget(const QString& STitle)
        {
            if (m_DockWidgetMap.contains(STitle) == true)
            {
                return m_DockWidgetMap[STitle];
            }
            return nullptr;
        }
    
        void DockWidgetManager::RemoveDockWidget(const QString& STitle)
        {
            if (m_DockWidgetMap.contains(STitle) == true)
            {
                m_mainWindow->removeDockWidget(m_DockWidgetMap[STitle]);
                m_DockWidgetMap.remove(STitle);
            }
        }
    }
    

测试#

为了检测当前程序设计的可用性,在Example项目中,修改了main.cpp文件中的代码:

int main(int argc, char* argv[])
{
    NestApp::Application app(argc, argv);

    NestUI::MainWindow* pMainWindow = new NestUI::MainWindow;
    app.SetMainWindow(pMainWindow);
    app.Run();

    return app.exec();
}
  • 运行效果 NestUI测试 从图上可以看到,最简单的MainWindow成功显示,大功告成~

Reference#

  1. SARibbon
Nest开发笔记-NestUI v0.1.0
https://jerryym.github.io/posts/nest/nestui/nest开发笔记-nestui-v010/
Author
_进击のJerry_
Published at
2024-11-30