GtProject:GT 图形用户界面库的 C++ 项目管理控件
GtProject 旨在为用户提供一个类似 Microsoft Project 的控件,用于安排任务的进度。
引言
GT 是一个紧凑、高效且可定制的图形用户界面 (GUI) 库,适用于 Windows 环境(未来还将添加 Linux 和 Mac)。大部分代码是平台无关的,仅直接依赖于操作系统图形、线程和剪贴板 API(这些功能已封装)。随着 GUI 系统的不断增长,你可能会问为什么要创建另一个系统。GT 的创建是为了解决一些主要竞争对手的不足之处,并且非常紧凑(GT 目前包含约 90,000 行代码,包括注释)。由于代码行数如此之少,GT 可以直接嵌入到你的应用程序解决方案或项目文件中。其主要竞争对手是 MFC、QT、GIMP Toolkit、wxWidgets、Fox Toolkit。GT 在设计和功能上最接近 QT。有关 GT 的更多信息,请阅读以下文章:
GtProject
是 GT 库的一个新添加项。作为一名工程师,我一直在我的应用程序中需要项目或任务管理功能。GtProject
旨在为用户提供一个类似 Microsoft Project 的控件,用于安排任务的进度。
GtProject 设计概述
代码库的主要组件如下:
GtProjectView
是用于查看GtProjectModel
对象的主要视口控件。它包含一个用于控制模型的菜单、一个用于查看任务的树视图、一个用于标记时间的日期标尺控件,以及一个用于显示甘特图的视口。GtProjectModel
是包含所有正在建模的任务的类。它能够使用 HPC Template Library (HTL) 序列化引擎对其自身进行序列化。有关 HTL 的更多信息,请参阅以下文章。原生存储格式是 XDL。GtProjectTask
是主要任务对象。它提供任务的开始和结束日期,可以包含子任务,并与其他任务之间存在约束。GtProjectConstraint
是两个任务之间的约束。下面显示了四种类型。在所有情况下,任务 A 都是约束的所有者。总的来说,它们将任务约束网络变成了一个有向无环图 (DAG)。GtEndToBegin
- 从任务 A 的结束到任务 B 的开始GtBeginToEnd
- 从任务 A 的开始到任务 B 的结束GtBeginToBegin
- 从任务 A 的开始到任务 B 的开始GtEndToEnd
- 从任务 A 的结束到任务 B 的结束
GtProjectView
看起来如下:
Using the Code
使用 GtProject
非常简单。在任何使用 GT 作为用户界面的应用程序中,将以下代码插入到打算作为电子表格父级的 GtDialog
的 InitializeControls()
方法中。设置控件大小并将其添加到该对话框的子控件集合中。
//in header file
GtProjectView * m_ptrProject;
//in InitializeControls()
m_ptrProject = new GtProjectView(this);
rectNew.xMin = 0; rectNew.xMax = 900; rectNew.yMin = 0; rectNew.yMax = 650;
m_ptrProject->Set_objFrame(rectNew);
this->AddSubWidget(m_ptrProject);
与 GT 的其他控件一样,GtProjectView
是可嵌套的。因此,如果你想将其插入到选项卡页面、框架等中,也可以这样做。只需相应地设置父对象指针和 AddSubWidget
方法。例如,将项目查看器插入到框架中看起来是这样的:
//in InitializeControls()
m_ptrProject = new GtProjectView(m_ptrProjFrame);
rectNew.xMin = 0; rectNew.xMax = 900; rectNew.yMin = 0; rectNew.yMax = 650;
m_ptrProject->Set_objFrame(rectNew);
m_ptrProjFrame->AddSubWidget(m_ptrProject);
控件的菜单是**项目**和**任务**。**项目**菜单包含所有文档控件功能,如**新建**、**打开**、**保存**、**另存为**、**关闭**。**任务**菜单包含操作单个任务所需的所有命令。菜单项是**属性**、**添加任务**、**添加子任务**、**删除任务**和**删除所有任务**。添加和删除是显而易见的。任务属性会打开所选任务的属性编辑器,如下所示:
要编辑日期,可以直接在编辑框中输入,或者通过单击相应日期的“sel”按钮启动日期选择器。这将启动日历视图日期选择对话框(如下所示)。编辑器的下半部分用于创建和删除任务之间的约束。要添加约束,请单击**添加**按钮,在列表视图中选择它,然后单击“**Sel Target**”来选择与之相关的任务。单击**保存**以保存对约束的更改,然后再选择另一个进行编辑。
GtProject 如何工作(如何绘制甘特图)
GtProject
的核心是显示甘特图信息的可视化引擎。有必要讨论一下它的实现方式,以了解 GtProject 的工作原理。查看器的甘特图部分有三个主要组件:视口、日期标尺和水平滚动条。日期标尺负责在视口顶部显示时间的标记。它连接到水平滚动条,其限制根据日期标尺左上角的组合框设置为天、月或年的数量。根据当前的水平滚动位置,它确定可见的第一天、月或年。下面展示了基于月份计算的代码片段作为示例。
//set at timeline and cursor start
iYearStart = m_dtStart.Get_intYear();
iMonthStart = m_dtStart.Get_intMonth();
//advance to viewport start, iCursor is from the horizontal scrollbar position
for (i = 0; i < iCursor; i++)
{
iMonthStart++;
if (iMonthStart > 12)
{
iMonthStart = 1;
iYearStart++;
}
}
m_dtViewStart.Set_intYear(iYearStart);
m_dtViewStart.Set_intMonth(iMonthStart);
一旦确定了视口起始位置,标尺的 OnPaint()
算法就会在视口顶部以固定的间隔标记时间。Months
的增量被设定为每 150 像素一个月。接下来就是正确地在视口中绘制任务。为了实现这一点,首先要确定一个任务是否可见以供绘制。这是通过查询树视图来查看表示该任务的节点是否可见来完成的。如果任务可见,代码会获取该树节点的框架。该节点的 Y
值会被保留下来,以便与任务正确对齐。接下来,使用日期标尺中的一个实用函数 GetDatePos()
来计算该节点的 x
值。
//get the rect from the tree view node, then calculate task position in viewport
GtRectI rectDraw = ptrNode->m_objFrame;
rectDraw.xMin = m_dateRuler->GetDatePos(ptrTask->Get_objStart());
rectDraw.xMax = m_dateRuler->GetDatePos(ptrTask->Get_objEnd());
rectDraw.yMin += 5;
rectDraw.yMax -= 5;
ptrTask->Set_objDrawFrame(rectDraw);
GetDatePos
的工作方式类似于日期标尺的绘图算法,它从视口开始将时间推进到请求的日期时间,并返回该日期的 x 坐标。如果日期时间超出视口范围,则返回相应的限制,以便任务的绘图矩形不会绘制在视口外部。应该注意的是,时间尺度的渲染由 GtDateRuler
完成,而任务和约束的渲染由 GtProjectView
完成。
未来发展
GtProject
的目标是为用户提供一个封装的项目管理控件。现在,基础项目、任务、约束和甘特图查看器已投入运行,作者将开始使 GtProject
更像 Microsoft。第一个任务将是实现 Microsoft Project 的自动调平功能,该功能会自动移动开始日期以遵守任务之间的约束。接下来,将把资源和利用率计算添加到任务中。
历史
- 2017 年 3 月 2 日:初始版本
作者:Anthony Daniels