Подтвердить что ты не робот

Реализация графического интерфейса на основе 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, хотя я более чем открыт для предложений для других языков.

4b9b3361

Ответ 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