校园导航系统CampusNavigation(C++实现)

课程设计选题及要求

选题 6:校园导航系统

设计一个校园导航系统,为学校来访者提供各种信息查询服务,方便查找学校主要单位、校园景点之间最短距离,并能及时对该图进行更新,地点信息包括名称、代号、相关介绍等,及两个地点之间有直接路径的距离

基本功能与技术要求

  1. 数据文件管理功能:创建新文件、打开文件、保存文件。
  2. 计算与统计功能,完成记录中相关数据的统计;
    1. 能够打开已存在的数据表文件,计算任意两个地点之间的最短路径的长度及途经地点。
    2. 提供来访者从出发地到目的地的路线统计,以图示界面输出。
  3. 记录管理功能:记录的管理采用链表、数组等实现。
    1. 输入/添加/插入记录,并保存到数据文件中。
    2. 查询、显示记录,根据用户要求按所给地点查询单位或景点信息,以及到地点的问路情况等。
    3. 修改记录:能够打开已存在的数据表文件,并对确定的任意记录进行修改,在修改过程中,应显示记录信息,给出确认提示,并对更新的记录信息进行文件保存。
    4. 删除记录:能够打开已存在的数据表文件,可以删除数据表中的任一记录,要求具备逻辑删除(具有恢复功能)和物理删除功能,并对新的数据表信息进行文件保存。

设计思路

  1. 整体模式以及主要涉及的模块 整体设计采用 MVC 模式,及数据的储存处理与表现分离。使用 QT 框架提供基础的图形界面操作,整体分为三个模块,CNView前端部分负责数据的显示,CNModel数据的储存预处理。

  2. 模块功能的划分以及类的初步设计
    1. CNView 前端部分 主要结合 QT 框架,界面展示,地点展示,路径展示,以及提供对应的 GUI 工具实现文本的录入
    2. CNModel 后端部分

功能设计和模块划分

本次课程设计额

C++父类指针操作子类新函数的方式

父类指针无法直接调用子类的新函数,需要转换为子类的指针后方可调用。

C++编译器在编译的时候是做静态类型分析。也就是说,你的父类指针是否真的指向一个子类类型,编译器并不会做这个假设。所以你用父类指针去调用子类的函数是无法被识别的。这里提供一种不安全和一种安全的转换方式可以实现该功能:   假设我们有父类和子类的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base
{
public:
void virtual Func()
{
cout << "Base\n";
}
};

class Derived : public Base
{
public:
void Func()
{
cout << "Derived\n";
}

void NewFunc()
{
cout << "New func\n";
}
};
  1. 强制转换(不安全)
1
2
3
4
5
Base* b = new Derived();
b->Func();
// 不安全的转换
Derived* d = (Derived*)b;
d->NewFunc();

这里之所以说不安全是因为转换的时候无法得知是否转换成功。编译器强制把 d 当成 Derived 类型去使用。比如说 b 本来是真的指向 Base 而不是 Derived 类型那么强制转换后调用 Derived 的 NewFunc 可能会导致程序崩溃。

2.动态转换,dynamic_cast(安全)

1
2
3
4
5
6
7
8
Base* b = new Derived();
b->Func();
// 安全转换
Derived* d = dynamic_cast<Derived*>(b);
if (d != NULL)
{
d->NewFunc();
}

dynamic_cast 是在运行时去做转换而非编译时,所以它可以给出是否转换成功的信息。如果转换不成功则返回 NULL。所以可以判断转换结果是否为 NULL 来决定是否能使用该指针不会导致程序崩溃

dynamic_cast参考
dynamic_cast参考

第一天进度记录

  1. 完成后端 1/2(除去文件读写)
  2. string index 放着先不实现,所有都先按照鼠标事件和 xy 坐标来写
  3. 明早补充边的增加和删减 注意边增添的时候算直线距离
  4. 普通点的增加和删减

第 X 天进度记录

就是说如果槽的命名是这样的话:

1
void on_<widget name>_<signal name>(<signal parameters>);

就会自动将widget name中的信号signal name和这个槽void on_<widget name>_<signal name>(<signal parameters>)链接起来。

这是 QT 不需要 connect 语句就可以自动链接信号和槽的机制!

绘图相关

当发生一下情况时会产生绘制事件并调用 paintEvent()函数:

  1. 在窗口部件第一次显示时,系统会自动产生一个绘图事件,从而强制绘制这个窗口部件。

  2. 当重新调整窗口部件的大小时,系统也会产生一个绘制事件。

  3. 当窗口部件被其他窗口部件遮挡,然后又再次显示出来的时候,就会对那些隐藏的区域产生一个绘制事件。

同时可以调用 QWidget::update()或者 QWidget::repaint()来强制产生一个绘制事件。二者的区别是:

repaint()函数会强制产生一个即时的重绘事件,而 update()函数只是在 Qt 下一次处理事件时才调用一次绘制事件。

如果多次调用 update(),Qt 会把连续多次的绘制事件压缩成一个单一的绘制事件,这样可避免闪烁现象。

使用 repaint()