Node Editor中有许多窗口,它们都是 主窗口 QMainWindow
的子窗口。
Parameter Widget: 当点击某个节点时,会显示这个节点的信息,以及可以调整的参数。 当前选中了 Comment 节点,在 Parameter Widget上可以显示、修改它的标题。同时 Comment节点也会联动。
ListWidget: 节点列表。拖到 GraphicsView 时,可以动态创建一个节点。
Graphics View: Graphics Scene 的视口。
这些窗口的父子关系如下,每一个方块代表一个类。
现在,需要判断 Comment 节点是否被选中。 如果选中,就根据 Comment里的属性字段,动态创建一个 QWidget
,并添加到 Parameter Widget中。
Comment节点,其实是一个 QGraphicsItem
对象。判断QGraphicsItem
是否被选中,非常简单,只要重写一个虚函数即可:
QVariant DmGraphicsComment::itemChange(GraphicsItemChange change, const QVariant& value)
{
/* 如果 GraphicsItem “被选中”这个状态发生了改变... */
if (change == QGraphicsItem::ItemSelectedChange)
{
if (!m_isSelected)
{
m_isSelected = true;
/* 做出相应的举动 */
Self::OnSelected();
}
else
{
m_isSelected = false;
Self::OnUnselected();
}
}
return Super::itemChange(change, value);
}
如何根据属性动态生成不同的QWidget
对象,需要反射的支持,这个我们后来再说。我们假定下面代码的dynamicUi
变量是已经生成好的,那么,现在的重点变为,怎么在当前的窗口树下,找到parameterWidget,然后把dynamicUi
挂在它的上面,作为一个子窗口。
void DmGraphicsComment::OnSelected()
{
/* 要如何实现这个函数? */
QWidget* parameterWidget = FindWindowByObjectName("ParameterWidget");
assert(parameterWidget && "Parameter widget is nullptr");
/* 假定这个函数已经实现 */
QWidget* dynamicUi = GenerateDynamicUi();
QLayout* layout = parameterWidget->layout();
layout->addWidget(dynamicUi );
}
方法非常简单,只有三步:
- 给各窗口设置object name,比如MainWindow 的 object name 为
MainWindow
, ParameterWidget 的 object name 为ParameterWidget
- 找到名叫MainWindow的顶层窗口
- 在 MainWindow的子窗口里寻找名叫
ParameterWidget
的子窗口
QWidget* DmGraphicsComment::FindWindowByObjectName(const QString& name)
{
/* 拿到 Graphics View 窗口 */
QGraphicsView* graphicsView = Super::scene()->views()[0];
assert(graphicsView && "Graphics view of the item is null");
/* 遍历所有父窗口,直到找到名叫 MainWindow 的父窗口。 线性查找。 */
QWidget* parentWidget = graphicsView->parentWidget();
assert(parentWidget && "Parent widget of the graphics view is null");
while (parentWidget && parentWidget->objectName() != "MainWindow")
{
parentWidget = parentWidget->parentWidget();
}
/* 调用 Qt Api,查找子窗口 */
QWidget* parameterWidget = parentWidget->findChild<QWidget*>(name);
/* 返回结果 */
return parameterWidget;
}