[software development] librecad源码阅读,使用cmake如何成功执行并开启应用的主窗口?
Tofloor
poster avatar
deepin
2023-12-16 00:18
Author

关于Librecad的源码,我这里主要是抓住它的主要使用功能来进行阅读,且根据其Qmake的构建方式并通过Cmake方式来实现。

我们首先看下librecad的启动界面和主窗口是如何进行显示的,以便通过Cmake方式进行项目重构配置。

一.libreCAD源码的主界面显示的实现

不管是qmake构建,还是cmake方式构建项目,我们在Qt Creator这个IDE创建首个应用时,应用会主要提供3个代码文件,比如heard头文件中会提供mainwindow.h,Source 文件中会有main.cpp、mainwindow.cpp.

image.png

其中,mainwindow.h和mainwindow.cpp这两个文件主要是为Qt 应用提供主界面的UI组件的布局编写,main.cpp则会在主函数main()方法中去执行并启动程序,并让mainwindow.cpp中实现的UI和事件能够显示、正常运行。

同样,librecad的程序界面显示和执行,也离不开上述几个文件的基本实现过程,我们看下librecad源码的主界面被启动和显示的方式。

image.png

从上图的librecad源码中,我们可以知道qc_applicationwindow这个class类是主界面显示类,main这个类是执行类,qc_applicationwindow类可在在main.cpp中的main()主函数方法中通过appwin.show进行整个应用的主窗口显示。

并且,我们还可以通过qc_applicationwindow.h文件,找到qc_applicationwindow类与mainwindow类之间的继承关系,如下图:

image.png

如上,qc_applicationwindow这个类是继承于mainwindowx,也就是说qc_applicationwindow是mainwindowx的子类,它继承了mainwindowx的所有函数方法实现,自然也能成为主界面类,并通过show()方法显示qt应用的主窗口。

二.使用Cmake方式,来重新整理libreCAD源码,并实现主界面窗口的显示

首先,我们需要在Qt Creator中创建一个不带ui文件的Cmake方式构建的新项目,这个创建过程我就不再陈述太多,你们可以自己在网上找视频熟悉。

创建好cmake项目之后,我们打开项目第一个文件夹中的CmakeLists.txt文件,并了解其配置信息和重新设置配置。

&.如下图,新建cmake项目的CmakeLists.txt文件的配置信息

image.png

&.在上一张图中,我们如果对librecad源码进行cmake配置,肯定不会向上面很简单就实现,需要在项目目录中增加一些文件夹、第三方源码库、自己编写的库等等。

(1).在项目dpcad这个总文件夹中增加一个src文件夹,cmake相关配置信息如下:

image.png

(2).在src文件夹中继续添加一个application文件夹执行程序文件main,CmakeLists.txt配置信息如下:

image.png

(3).在application中cmake中继续配置添加mainwindow和qc_applicationwindow这两个类,如下图:

image.png

这两个类,我也采取了librecad源码的实现方式,让qc_applicationwindow作为mainwindow的子类,并在main.cpp文件中通过show()方法实主窗口的显示。

mainwindow.h文件代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);

    void sortWidgetsByTitle(QList& list);
    void sortWidgetsByTitle(QList& list);

    ~MainWindow();

public slots:
    void toggleRightDockArea(bool state);
    void toggleLeftDockArea(bool state);
    void toggleTopDockArea(bool state);
    void toggleBottomDockArea(bool state);
    void toggleFloatingDockArea(bool state);

};
#endif // MAINWINDOW_H

mainwindow.cpp文件中的代码


#include "mainwindow.h"

#include 
#include 

namespace Sorting{
    bool byWindowTitle(QWidget* left, QWidget* right){
        return left->windowTitle() < right->windowTitle();
    }
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
}

MainWindow::~MainWindow()
{
}

void MainWindow::sortWidgetsByTitle(QList& list){
    std::sort(list.begin(), list.end(), Sorting::byWindowTitle);
}

void MainWindow::sortWidgetsByTitle(QList& list){
    std::sort(list.begin(), list.end(), Sorting::byWindowTitle);
}

void MainWindow::toggleLeftDockArea(bool state){
    foreach (QDockWidget* dw, findChildren()) {
        if(dockWidgetArea(dw) == Qt::LeftDockWidgetArea && !dw->isFloating()){
            dw->setVisible(state);
        }
    }
}

void MainWindow::toggleRightDockArea(bool state){
    foreach (QDockWidget* dw, findChildren()) {
        if(dockWidgetArea(dw) == Qt::RightDockWidgetArea && !dw->isFloating()){
            dw->setVisible(state);
        }
    }
}

void MainWindow::toggleTopDockArea(bool state){
    foreach (QDockWidget* dw, findChildren()) {
        if(dockWidgetArea(dw) == Qt::TopDockWidgetArea && !dw->isFloating()){
            dw->setVisible(state);
        }
    }
}

void MainWindow::toggleBottomDockArea(bool state){
    foreach (QDockWidget* dw, findChildren()) {
        if(dockWidgetArea(dw) == Qt::BottomDockWidgetArea && !dw->isFloating()){
            dw->setVisible(state);
        }
    }
}

void MainWindow::toggleFloatingDockArea(bool state){
    foreach (QDockWidget* dw, findChildren()) {
        if(dw->isFloating()){
            dw->setVisible(state);
        }
    }
}

qc_applicationwinow.h文件中的代码

#ifndef QC_APPLICATIONWINDOW_H
#define QC_APPLICATIONWINDOW_H

#include 

#include "mainwindow.h"
#include "lc_actiongroupmanager.h"
#include "lc_actionfactory.h"
struct DockArea{
    QAction* left{nullptr};
    QAction* right{nullptr};
    QAction* top{nullptr};
    QAction* bottom{nullptr};
    QAction* floating{nullptr};
};


class QC_ApplicationWindow: public MainWindow
{
    Q_OBJECT

    public:
        ~QC_ApplicationWindow();

    public:
        /**
         * 单例application window的访问方法
         */
        static std::unique_ptr& getAppWindow();

    private:
        //单例模式
        QC_ApplicationWindow(QWidget* parent = nullptr);
        //单例对象QC_ApplicationWindow,不可复制、不可赋值给其他对象
        QC_ApplicationWindow(const QC_ApplicationWindow&) = delete;
        QC_ApplicationWindow& operator = (const QC_ApplicationWindow&) = delete;
        QC_ApplicationWindow(QC_ApplicationWindow&&) = delete;
        QC_ApplicationWindow& operator = (QC_ApplicationWindow&&) = delete;

        QMap a_map;

        LC_ActionGroupManager* ag_manager {nullptr};
        LC_ActionFactory* a_factory{nullptr};
};

#endif // QC_APPLICATIONWINDOW_H

qc_applicationwindow.cpp文件的代码:

#include "qc_applicationwindow.h"


QC_ApplicationWindow::QC_ApplicationWindow(QWidget* parent){

}

std::unique_ptr& QC_ApplicationWindow::getAppWindow(){
    static std::unique_ptr instance = std::unique_ptr(new QC_ApplicationWindow);
    Q_ASSERT(instance != nullptr);
    return instance;
}

QC_ApplicationWindow::~QC_ApplicationWindow(){

}

src目录中的main.cpp代码内容,如下:

#include 
#include 
#include 
#include "qc_applicationwindow.h"


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //MainWindow w;
    //w.show();
    QSettings settings;
    int windowWidth = settings.value("WindowWidth", 1024).toInt();
    int windowHeight = settings.value("WindowHeight", 1024).toInt();
//    int windowX = settings.value("WindowX", 100).toInt();
//    int windowY = settings.value("WindowY", 100).toInt();
    //获取屏幕尺寸
    QDesktopWidget* desktop = a.desktop();
    //获取屏幕宽度
    int windowX = desktop->width();
    //获取屏幕高度
    int windowY = desktop->height();

    QC_ApplicationWindow& appWin = *QC_ApplicationWindow::getAppWindow();
    //初始化主窗口大小
    appWin.resize(windowWidth, windowHeight);

    //计算主窗口中心位置
    int x = (windowX - appWin.width()) / 2;
    int y = (windowY - appWin.height()) / 2;
    //主窗口在系统桌面的位置为“居中”
    appWin.move(x, y);

    appWin.show();
    return a.exec();
}

完成以上cmake配置信息后,我们可以启动项目并执行,可以显示出应用的主窗口。


最后,librecad源代码中程序执行原理实际上并不是非常复杂,仍基于qt最基本的main函数,从这个出发口我们也可以很快找到主界面qc_applicationwindow类,后续可以在这个主界面类中添加应用的菜单栏、工具栏、工具集等等,希望有更多的程序爱好者能够一起关注librecad源代码的阅读。

Reply Favorite View the author
All Replies
wlly-lzh
deepin
2023-12-16 03:34
#1

在gitee上面开个仓库吧,把代码给大家看看。

Reply View the author
deepin
2023-12-16 04:45
#2
wlly-lzh

在gitee上面开个仓库吧,把代码给大家看看。

之前在gitee申请过,但是暂时没回复,还是先完善自己的项目再说,内容比较多。

Reply View the author