第2章:主框架搭建

好,咱们正式开始动手了。这一章,我们要把EPS软件的骨架搭起来。

说白了,就是创建一个Qt主窗口,把菜单栏、工具栏、状态栏这些标配安排上,再搞定中心部件和配置文件。嗯,这些听起来基础,但基础的东西往往最容易被忽视。我在项目里见过太多人上来就写业务逻辑,结果后面发现窗口结构不合理,改起来那叫一个痛苦。

2.1 创建Qt主窗口

我个人习惯,新建一个Qt项目时,先想清楚主窗口要继承哪个类。对于EPS这种专业软件,QMainWindow是首选。为什么?因为它自带菜单栏、工具栏、状态栏和停靠窗口的支持,省去我们很多手工活。

来看代码:

// MainWindow.h
#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    void setupUI();
    void createActions();
    void createMenus();
    void createToolBars();
    void createStatusBar();
    void readSettings();
};

注意那个Q_OBJECT宏,新手容易漏掉。漏掉之后信号槽就用不了,编译器也不会报错,运行时才崩溃。我曾经因为这个排查了整整一个下午,后来养成了习惯——写类第一件事就是检查这个宏。

构造函数里,我一般这样写:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setupUI();
    readSettings();
}

先初始化界面,再读取配置。顺序别搞反了。你想想看,如果先读配置,界面还没创建,配置往哪放?

2.2 菜单栏与工具栏设计

菜单栏是用户操作的第一入口。EPS软件功能多,菜单分类要清晰。我通常这样分:

菜单 功能 示例动作
文件 项目文件操作 新建、打开、保存、导出
编辑 数据编辑 撤销、重做、复制、粘贴
视图 界面布局 工具栏、状态栏、全屏
工具 专业功能 参数设置、计算、分析
帮助 文档与关于 用户手册、版本信息

创建菜单的代码很直观:

void MainWindow::createMenus()
{
    // 文件菜单
    QMenu *fileMenu = menuBar()->addMenu(tr("文件(&F)"));
    fileMenu->addAction(tr("新建项目(&N)"), this, &MainWindow::newProject, QKeySequence::New);
    fileMenu->addAction(tr("打开项目(&O)"), this, &MainWindow::openProject, QKeySequence::Open);
    fileMenu->addSeparator();
    fileMenu->addAction(tr("保存(&S)"), this, &MainWindow::saveProject, QKeySequence::Save);
    fileMenu->addAction(tr("另存为..."), this, &MainWindow::saveAsProject, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S));
    fileMenu->addSeparator();
    fileMenu->addAction(tr("退出(&X)"), this, &QWidget::close, QKeySequence::Quit);
}

工具栏呢?说白了就是菜单的快捷入口。常用的操作才放工具栏,别什么都往上堆。我见过一个项目,工具栏塞了30多个按钮,用户找功能比翻菜单还费劲。

void MainWindow::createToolBars()
{
    QToolBar *fileToolBar = addToolBar(tr("文件"));
    fileToolBar->setObjectName("FileToolBar");
    fileToolBar->addAction(tr("新建"));
    fileToolBar->addAction(tr("打开"));
    fileToolBar->addAction(tr("保存"));
    
    // 设置图标
    fileToolBar->setIconSize(QSize(24, 24));
    fileToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
}
小技巧:给工具栏设置objectName,这样保存窗口状态时能记住用户自定义的布局。用户拖拽调整后,下次打开还是老样子,体验会好很多。

2.3 状态栏与中心部件

状态栏,很多人觉得就是个摆设。其实不然。EPS软件处理数据量大,用户需要实时知道当前状态。我一般放三样东西:

  • 左侧:当前操作提示(比如"正在加载项目...")
  • 中间:进度信息(比如"处理中:45/100")
  • 右侧:永久信息(比如版本号、坐标系)
void MainWindow::createStatusBar()
{
    statusBar()->showMessage(tr("就绪"), 3000);
    
    // 添加永久部件
    QLabel *versionLabel = new QLabel(tr("v1.0.0"));
    versionLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    statusBar()->addPermanentWidget(versionLabel);
    
    QLabel *coordLabel = new QLabel(tr("坐标系: WGS84"));
    coordLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    statusBar()->addPermanentWidget(coordLabel);
}

中心部件,这是主窗口的核心区域。EPS软件通常需要多视图协作,我推荐用QSplitter配合QMdiArea

void MainWindow::setupCentralWidget()
{
    // 创建分割器
    QSplitter *splitter = new QSplitter(Qt::Horizontal, this);
    
    // 左侧:项目树
    QTreeWidget *projectTree = new QTreeWidget();
    projectTree->setHeaderLabel(tr("项目结构"));
    
    // 右侧:多文档区域
    QMdiArea *mdiArea = new QMdiArea();
    mdiArea->setViewMode(QMdiArea::TabbedView);
    
    splitter->addWidget(projectTree);
    splitter->addWidget(mdiArea);
    splitter->setStretchFactor(0, 1);  // 左侧占1份
    splitter->setStretchFactor(1, 3);  // 右侧占3份
    
    setCentralWidget(splitter);
}
注意:分割器的拉伸因子一定要设置。不设置的话,窗口缩放时两侧比例会乱掉。我曾经有个项目,用户反馈说"窗口一拉大,左边树就没了",查了半天就是忘了设setStretchFactor

2.4 应用程序设置与配置文件

配置文件,说白了就是让软件记住用户的偏好。窗口大小、上次打开的文件、工具栏布局,这些都应该保存下来。

Qt提供了QSettings,用起来很方便:

void MainWindow::readSettings()
{
    QSettings settings("EPS_Studio", "EPSApp");
    
    // 恢复窗口位置和大小
    QByteArray geometry = settings.value("geometry").toByteArray();
    if (!geometry.isEmpty()) {
        restoreGeometry(geometry);
    } else {
        resize(1200, 800);  // 默认大小
    }
    
    // 恢复窗口状态(工具栏、停靠窗口等)
    QByteArray state = settings.value("windowState").toByteArray();
    if (!state.isEmpty()) {
        restoreState(state);
    }
    
    // 恢复最近打开的文件
    lastFilePath = settings.value("lastFilePath").toString();
}

void MainWindow::writeSettings()
{
    QSettings settings("EPS_Studio", "EPSApp");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("windowState", saveState());
    settings.setValue("lastFilePath", lastFilePath);
}

什么时候保存?我建议在关闭事件里做:

void MainWindow::closeEvent(QCloseEvent *event)
{
    writeSettings();
    QMainWindow::closeEvent(event);
}
核心要点:
  • 配置文件路径:Windows在注册表或AppData,Linux在~/.config
  • 保存时机:关闭时保存,启动时恢复
  • 异常处理:读取失败时用默认值,别让软件崩溃
  • 版本兼容:配置格式变化时,要能处理旧版本的数据

嗯,到这里,主框架的骨架就搭好了。你想想看,有了这个基础,后面往里面填功能模块就轻松多了。下一章,咱们开始搞核心的数据模型,那才是EPS软件的真正灵魂。