QT传输函数控件设计12 自定义信号和槽
现在我们这么思考一下:
我们已经在每个NodeGraphicsItem里面定义了一个指针,指向了每个原始数据的NodeXmlItem,我们可以在itemChange函数中坐标修改的时候对其进行修改,但是问题来了,我们修改了值,但是只有当更新update的时候才会重新显示位置。我们在鼠标松开事件中使用了update,但是如果鼠标不松开,我们只是单纯地移动小圆点,能不能把实时移动的信息发给上层呢?
注意我们可以在View里定义一个鼠标移动事件,然后做判断,但是这样会带来各种灾难级的Bug,而且常常会伴随内存读取错误和内存泄漏。这里因为代码讲述没有太大的意义,我就不列代码了,而是详细说一下思路:
现在的xml文件的图示结构是这样的:
全局范围内有一个TsfunXmlGroup和一个TsfunXmlItem,然后我们令这个全局的TsfunXmlItem指向TsfunXmlGroup中的TsfunXmlItem列表中的某个实体。在这个TsfunXmlItem指向的实体中,里面有一个列表,附带所有节点信息。
然后是画图类的图示结构:
不再过多说明了。然后它们之间的联系是,tsfunGraphicsItem里面有一个指针,指向了TsfunXmlItem,而且里面的节点也都指向了TsfunXmlItem里面的节点:
注意图形项的Item都不是继承自QObject,所以无法定义信号槽,如果要多继承自QObject,可能会出现一些不好的Bug,所以我们就应该在TsfunXmlItem里面进行定义信号和槽。
当我们的NodeGraphicsItem调用itemChange事件的时候,会修改NodeXmlItem,于是我们在NodeXmlItem里面定义一个信号,在setNomalizedIntensity函数的最后设置发射该信号。然后在TsfunXmlItem里面定义槽,接收该信号。注意在创建的时候进行信号连接。之后我们再让TsfunXmlItem的槽中发射信号,该信号让tsfunGraphicsItem中自定义的槽接收。然后该槽重新发射信号,让TsfunView中自定义的槽接收,然后该槽进行update。
也就是说我们通过信号和槽的机制,让TsfunView能够实时接收到我们改变小圆点位置的信息。
然后update完以后,我们在update里面设置信号,让MainWindow来接收,然后修改VTK程序的传输函数。
关于信号和槽的具体设置方法大家可以去看别的博客或者书来学习,这里就不涉及了。
下面再给出修改VTK的代码:
vtkSmartPointer<vtkPiecewiseFunction> scalarTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
vtkSmartPointer<vtkColorTransferFunction> colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
#include "TsfunView.hpp"
extern TsfunView *tfViewGlobal;
我们把之前创建的TsfunView通过指针索引,让MainWindow可见,上面的四个模板类从局部改成到全局。
然后在主函数中进行信号连接:使用了View中自定义的信号
connect(tfViewGlobal,SIGNAL(updateRenderInView()),this,SLOT(updateImage()));
然后在槽(上节讲过的在MainWindow中自定义的槽)中写:
if (flagsforset) {
colorTF->RemoveAllPoints();
scalarTF->RemoveAllPoints();
for (int i = 0;i<tsfunxmlitem->nodeXmlItemList.count();i++) {
tsfunxmlitem->nodeXmlItemList[i];
double rangeForIntensity = range[1] - range[0];
double intensity = range[0]+(tsfunxmlitem->nodeXmlItemList[i].returnNormalizedIntensity())*rangeForIntensity;
double r = (tsfunxmlitem->nodeXmlItemList[i].returnEmission().red()) / 255.0;
double g = (tsfunxmlitem->nodeXmlItemList[i].returnEmission().green()) / 255.0;
double b = (tsfunxmlitem->nodeXmlItemList[i].returnEmission().blue()) / 255.0;
double op = tsfunxmlitem->nodeXmlItemList[i].returnOpacity();
colorTF->AddRGBPoint(intensity, r, g, b);
scalarTF->AddPoint(intensity, op);
}
volumeProperty->SetScalarOpacity(scalarTF);
volumeProperty->SetColor(colorTF);
volume->SetProperty(volumeProperty);
myQVTKWidget->update();
}
首先,flagsforset是判断有没有导入数据的flag,即我们之前所说的VTK打开文件以后,把该flag设置为1,这样就可以修改传输函数了。然后清除先前的节点,并在循环中加入新的节点。
现在一切都大功告成了。下一节做一个总结和收尾。