实现基于节点的图形界面?

23
我希望实现一个节点界面,基本上是一个DAG,其中每个节点对其输入连接执行操作,并输出某些内容(您可以将其连接到另一个节点)。
一些示例应用:
作为第一个目标,我希望有一个只有2个节点的图形应用程序。一个“数字”节点简单地输出一个固定的数字,而一个“添加”节点则需要两个输入并输出它们的和。
截至目前,人们已经回答了如何在代码中表示数据的大致想法,例如Python风格的伪代码:
class Number:
    def __init__(self, value):
        self.value = value

    def eval(self):
        return self.value

class Add:
    def __init__(self, input1, input2):
        self.input1 = input1
        self.input2 = input2

    def eval(self):
        return self.input1.eval() + self.input2.eval()


a = Number(20)
b = Number(72)

adder = Add(a, b)
print adder.eval()

我该如何在这个程序中添加自定义GUI界面?类似于以下的样式,但不要像手绘一样简陋!

nodal UI mockup

我应该从哪里开始呢?目前我计划使用Objective-C/Cocoa编写,但我也非常愿意听取其他语言的建议。

7个回答

3
我会从建模一些基本接口开始(在OOP意义上,而不是GUI意义上)。我觉得你会有一个节点,它将接受一组输入和一个输出。你没有给出数据类型的广度指示,但你需要一些适当的方法来表示你的输入/输出。对于你的第一个目标,这可以是一个整数。
在某些通用的C风格的OOP语言中(希望它有意义):
class Node<T> {
    Node<T>[] inputs;
    T eval();
}

class AdderNode extends Node<int> {
    int eval() {
        int accum = 0;
        for (inputs : i)
            accum += i.eval();
        return i;
    }
}

class ConstNode<int I> extends Node<int> {
    int eval() { return I; }
}

AdderNode a;
a.inputs.add(ConstNode<2>());
a.inputs.add(ConstNode<3>());
a.eval();

你可以通过将int替换为某个抽象类、通用类型或接口来扩展此功能。当然,实际的实现会因实际使用的编程语言而异。

2
我会从建模有趣的操作开始。最终,你将把它们连接到用户界面上,但那只是方向盘和油门,而不是引擎。
你试图构建的东西与编程语言有很多共同点:变量、值、类型、表达式、求值等。许多比喻都是适用的,可能会提供一些指导。
如果你使用的是.NET 3.5,你可以使用表达式树,它允许你在运行时表示和编译代码表达式。
例如,为了建模你的第一个目标:
using System.Linq.Expressions;

ConstantExpression theNumber2 = Expression.Constant(2);
ConstantExpression theNumber3 = Expression.Constant(3);

BinaryExpression add2And3 = Expression.Add(theNumber2, theNumber3);

要调用这个表达式,我们需要用一个方法包装add2And3。这可以通过 lambda 表达式实现:
Expression<Func<int>> add2And3Lambda = Expression.Lambda<Func<int>>(add2And3);

"Func" 表示一个不带参数并返回 "int" 的方法。在 C# 中,由 "add2And3Lambda" 表示的代码将是:
() => 2 + 3

所以我们拥有的是一个表达式树,其根节点是一个方法。因为方法是可调用的,所以我们可以将该树编译成基础委托类型的实例:
Func<int> add2And3Func = add2And3Lambda.Compile();

现在我们可以调用我们构建的代码:
int theNumber5 = add2And3Func();

支持.NET语言中的每个表达式。

想象一下,您图中的每个节点都与一个表达式相关联。这可能让您了解表达式树的强大功能以及它们如何帮助您完成此任务。


1

所有节点系统都具有描述函数式编程语言的共同点。一个函数接受多个参数并返回单个结果,无论它是为了什么目的而设计的。以下是一些例子:

  • 图形:模糊(Image,Kernel,Radius)-> Image

  • 数学:加(Number,Number)-> Number

  • 关系型:过滤器(Table,Predicate)-> Table

基本上这归结为函数签名,例如Func<object[], object>(C#)。

您将面临如何使您的节点系统持久的问题。您想把节点的结果用作树中仅一个其他节点(树)的参数还是多个节点(图)的参数?

下面是一个树的示例,直接拥有子节点的参数:

Add(
  Multiply(
    Constant(5),
    Constant(4)
  ),
  Multiply(
    Constant(5),
    Constant(3)
  )
)

图的示例,将所有节点存储在列表中并仅使用引用:

A := Constant(5)
B := Constant(4)
C := Constant(3)

D := Func(Multiply, A, B)
E := Func(Multiply, A, C)

F := Func(Add, D, E)

1

1

我在寻找类似解决方案时偶然发现了这个帖子。 最近,我在github上发现了一个不错的项目https://github.com/nodebox/nodebox,看起来正是你所需要的。至少可以从该项目中提取和采用编辑器组件。

问候, Stephan


欢迎来到StackOverflow,感谢你想做出贡献。但是只有链接的答案并不受鼓励。请仔细阅读回答指南。一个好的经验法则是在没有链接的情况下查看你的答案,如果它提供了很少或者没有价值,那么请考虑扩展答案。 - JaredMcAteer

0

我在项目中实现了一个与你描述的 Execution Graph 相似的系统:GRSFramework

源代码可以在这里找到:here

目前,我正在努力发布此系统的更好、更精简的版本,该版本可以在项目 ExecutionGraph 中查看。
这对你也有帮助。

此外,Google 的 TensorFlow 库也实现了类似的系统:TensorFlow


0

也许 bwise 有一些感兴趣的东西?

这个页面 的下半部分展示了使用 bwise 创建一个乘法块并将两个数字作为输入的示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接