Реализация графического интерфейса на основе node?
Я хотел бы реализовать узловой интерфейс, в основном DAG, где каждый node выполняет операцию по его входным соединениям и выводит то, что вы можете подключить к другому node)
Некоторые примеры приложений:
В качестве первой цели я хотел бы иметь графическое приложение с двумя узлами. "Число", которое просто выводит фиксированное число, и "Добавить" node, который принимает два входа и выводит сумму этих двух.
Как люди ответили так далеко, у меня есть приблизительное представление о том, как представлять данные в коде, например, в Python'y, выглядящем псевдокодом:
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, хотя я более чем открыт для предложений для других языков.
Ответы
Ответ 1
Я бы начал с моделирования некоторых базовых интерфейсов (в смысле ООП, а не в смысле GUI). Кажется, у вас будет Node, который будет принимать коллекцию входов и один вывод. Вы не указали каких широких типов данных, но вам нужен какой-то подходящий метод представления ваших входов/выходов. Для вашей первой цели это может быть целым числом.
В некотором родовом языке O-стиля O (надеюсь, что это имеет смысл):
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
с помощью метода. Это делается с помощью выражения лямбда:
Expression<Func<int>> add2And3Lambda = Expression.Lambda<Func<int>>(add2And3);
Func<int>
представляет собой метод, который не принимает параметров и возвращает int
. В С# код, представленный add2And3Lambda
, будет:
() => 2 + 3
Итак, у нас есть дерево выражений, корнем которого является метод. Поскольку метод является вызываемым, мы можем скомпилировать дерево в экземпляр базового типа делегата:
Func<int> add2And3Func = add2And3Lambda.Compile();
Теперь мы можем вызвать код, который мы построили:
int theNumber5 = add2And3Func();
Поддерживается каждое выражение, доступное для языков .NET.
Представьте, что каждый node в вашем графе имеет связанный с ним Expression
. Это может дать вам представление о силе деревьев выражений и о том, как они могут помочь вам в решении этой задачи.
Ответ 3
Все эти системы node имеют общее представление о том, что они описывают функциональный язык программирования. Функция принимает несколько параметров и возвращает один результат, независимо от того, для какой цели он был разработан. Некоторые примеры:
-
Графика: размытие (изображение, ядро, радиус) → Изображение
-
Math: Добавить (Number, Number) → Number
-
Реляционная: фильтр (таблица, предикат) → Таблица
В основном это сводится к сигнатуре функции, например Func<object[], object>
(С#).
Вы столкнетесь с вопросом о том, как сделать вашу систему node постоянной. Вы хотите сделать результат node применимым как параметр только одним другим node (деревом) или несколькими узлами (графом)?
Пример дерева, непосредственно имеют параметры дочерние узлы:
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)
Ответ 4
Я нашел полезную информацию о реализации такого интерфейса в Cocoa:
Ответ 5
Возможно, bwise имеет что-то интересное?
В нижней половине на этой странице показан пример использования bwise для создания блока умножения, в котором вводятся два числа.
Ответ 6
Я реализовал график выполнения, как вы описываете в этом проекте:
GRSFramework
Исходный код можно найти здесь.
В настоящее время я работаю над выпуском лучшей, очищенной версии этой системы в проекте ExecutionGraph.
Это может вас заинтересовать.
Тогда есть также библиотека TensorFlow от Google, в которой реализована аналогичная система TensorFlow
Ответ 7
Я наткнулся на эту тему, исследуя аналогичное решение.
Недавно я нашел хороший проект на github https://github.com/nodebox/nodebox
который, кажется, именно то, что вы ищете. По крайней мере, можно было извлечь и принять компоненты редактора из проекта.
С уважением,
Stephan