本帖最后由 天洑软件 于 2019-3-27 17:57 编辑
在众多工程应用软件中,一个模型中查看的数据会多种多样,比如:三维模型视图,受力图表等。然后,在查看过程中,用户可能还需要同时对各种数据进行对比查看等。在以前的软件中,多视口的实现通过多选项卡(多窗口)模式来实现的,如下图,这在软件操作过程中给用户带来了极大的不变,对于多模型来说,更是痛苦至极。
目前多视口的界面大体如下:
其中,左上为工程的整体三维视图,右上为计算域三维网格,左下为切片、矢量图等后处理结果,右下为部分边界网格。 这仅是列举的一小部分,这对于用户来说,查看不同的数据,对比分析也更加直观。而却各个视口独立于其他视口,只与当前的工程有关。用户可以在不同的视口中做不同的操作,不影响其他视口。 实现思路: 多视口中,每个视口都“贴”在一个父窗口上。当视口被分割成两个视口时,首先创建一个父窗口,用来“装”原视口和新创建出来的视口,然后再将创建的父窗口“贴”于原视口的位置。流程如下表示:
实现流程: 通过上述流程,不难发现,这与数据结构中的二叉树的数据结构类似,因此,采用动态数组来描述多视口数据。父窗口采用Qt中的QSplitter类,视口为相应的窗口类。 1. 定义数据元素类型: struct ViewCell { Direction direction; // 方向 double splitFraction; // 分割比例 QWidget* view; ViewCell() : direction(NONE), splitFraction(0.5), view(0) {} }; 其中,direction和splitFraction当为父窗口时有效。 2. 定义数据结构 QVector<ViewCell> m_Cells; 3. 分割算法: 由于二叉树的特性,每分割一次,数组的变为 (2*location)+2+1个,原视口变为 2*location+1,其中,location为当前窗口的序号(序号按照二叉树的前序序号排列)。然后将location位置的元素类型更改为父窗口内容,将2*location+1位置的元素类型更改为原视口内容,伪代码如下: int split(int location, Direction direction, double fraction) { ViewCell cell = m_Cells[location]; cell.direction = direction; cell.splitFraction = fraction; m_Cells.resize(2 * location + 2 + 1); int child_location = (2 * location + 1); ViewCell& child = m_Cells[child_location]; child.view = cell.view; cell.view = NULL;// 默认为空,在创建窗口函数中自动创建 m_Cells[location] = cell; return child_location; } 4. 创建窗口: 当二叉树的数据完成后,接下来就需要创建窗口,刷新等操作了。可以按照二叉树的前序遍历进行迭代了。伪代码如下: QWidget* createWidget(int index, QWidget* parentWdg) { Direction direction = m_Cells[index].direction; QWidget* widget = m_Cells[index].view; switch (direction) { case NONE: { if (!widget) { widget = new QWidget(this); } frame->setParent(parentWdg); m_Cells[index].view = widget; return widget; } case VERTICAL: case HORIZONTAL: { QSplitter* splitter = qobject_cast<QSplitter*>(widget); if (!splitter) { splitter = new QSplitter(parentWdg); } m_Cells[index].view = splitter; splitter->setParent(parentWdg); splitter->setOrientation(direction); // 采用前序遍历,迭代 splitter->insertWidget(0, createWidget(2 * index + 1, splitter)); splitter->insertWidget(1, createWidget(2 * index + 2, splitter)); return splitter; } } return NULL; } 到此,多视口的大体实现已经完成。
|