Реализация графического интерфейса на основе узлов? - PullRequest
22 голосов
/ 14 марта 2009

Я хотел бы реализовать нодальный интерфейс, в основном DAG , где каждый узел выполняет операцию со своими входными соединениями и выводит что-то (что вы можете подключить к другому узлу)

Некоторые примеры приложений:


В качестве первой цели я хотел бы иметь графическое приложение только с двумя узлами. «Число», которое просто выводит фиксированное число, и узел «Добавить», который принимает два входа и выводит сумму двух.

Поскольку до сих пор люди отвечали, у меня есть приблизительное представление о том, как представлять данные в коде, например, в 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()

Как бы я обернулся вокруг этого пользовательского графического интерфейса? Что-то вроде следующего, но чуть менее нарисованное от руки!

nodal UI mockup

С чего бы мне начать? В настоящее время я планирую написать его в Objective-C / Cocoa, хотя я более чем открыт для предложений по другим языкам.

Ответы [ 7 ]

4 голосов
/ 14 марта 2009

Я бы начал с моделирования некоторых базовых интерфейсов (в смысле ООП, а не в смысле ГИП). Мне кажется, у вас будет узел, который будет принимать набор входных данных и один выход. Вы не указали, насколько широки типы данных, но вам понадобится какой-нибудь подходящий способ представления ваших входов / выходов. Для вашей первой цели это может быть целое число.

В некотором родном языке ООП в стиле C (надеюсь, это имеет смысл):

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 голосов
/ 14 марта 2009

Я бы начал с моделирования интересных операций. В конечном итоге вы подключите их к пользовательскому интерфейсу, но это руль и педаль газа, а не двигатель.

То, что вы пытаетесь построить, имеет много общего с языками программирования: переменными, значениями, типами, выражениями, оценкой и т. Д. Многие из метафор применимы и могут служить руководством.

Если вы используете .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. В C # код, представленный add2And3Lambda, будет:

() => 2 + 3

Итак, у нас есть дерево выражений, корнем которого является метод. Поскольку метод callable , мы можем скомпилировать дерево в экземпляр базового типа делегата:

Func<int> add2And3Func = add2And3Lambda.Compile();

Теперь мы можем вызвать созданный нами код:

int theNumber5 = add2And3Func();

Поддерживается каждое выражение, доступное для языков .NET.

Представьте, что с каждым узлом в вашем графике связан Expression. Это может дать вам представление о силе деревьев выражений и о том, как они могут помочь вам в этой задаче.

1 голос
/ 23 мая 2009

Я нашел некоторую полезную информацию о реализации такого интерфейса в Какао:

1 голос
/ 14 марта 2009

Все эти системы узлов имеют общее описание функционального языка программирования. Функция принимает несколько параметров и возвращает один результат, независимо от того, для какой цели он был разработан. Некоторые примеры:

  • Графика: размытие (изображение, ядро, радиус) -> изображение

  • Математика: Добавить (число, номер) -> Число

  • Реляционный: фильтр (таблица, предикат) -> таблица

В основном это сводится к сигнатуре функции, такой как 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)
0 голосов
/ 29 октября 2017

Я наткнулся на эту тему во время поиска аналогичного решения. Недавно я нашел хороший проект на github https://github.com/nodebox/nodebox который, кажется, именно то, что вы ищете. По крайней мере, один может извлечь и принять компоненты редактора из проекта.

С уважением, Stephan

0 голосов
/ 13 марта 2017

Я реализовал График выполнения, как вы описали в этом проекте: GRSFramework

Исходный код можно найти здесь .

В настоящее время я работаю над выпуском улучшенной, очищенной версии этой системы в проекте ExecutionGraph .
Это может быть интересно и вам.

Также есть библиотека TensorFlow от Google, в которой реализована аналогичная система TensorFlow

0 голосов
/ 14 мая 2009

Может быть bwise есть что-то интересное?

В нижней части на этой странице показан пример использования bwise для создания блока умножения, который принимает в качестве входных данных два числа.

...