JUNG: добавление потомков в DelegateTree после его рисования

Я разрабатываю приложение Java и использую библиотеку JUNG. В моем приложении я сначала создаю DelegateTree и рисую его на экране:

public static GraphZoomScrollPane generateTree(Tree tree,
    GraphicalUserInterface gui) {

    /* Create a new tree */
    edu.uci.ics.jung.graph.Tree<Node, Edge> graphTree = new DelegateTree<Node, Edge>();

    /* Add all nodes and vertices to the tree */
    graphTree.addVertex(tree.getRoot());
    addChildren(tree.getRoot(), graphTree);

    /* Create the visualization */
    TreeLayout<Node, Edge> treeLayout = new TreeLayout<Node, Edge>(graphTree);
    VisualizationViewer<Node, Edge> vv = new VisualizationViewer<Node, Edge>(treeLayout);
    vv.setBackground(Color.WHITE);
    vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<Edge>());
    vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<Node, Edge>());
    vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Node>());
    vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);

    vv.addGraphMouseListener(new ClickNode(gui, vv));
    final DefaultModalGraphMouse<Node, Edge> graphMouse = new DefaultModalGraphMouse<Node, Edge>();
    graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
    vv.setGraphMouse(graphMouse);

    return new GraphZoomScrollPane(vv);
}

После этого пользователь может добавлять новых детей в листья моего дерева. Но когда я просто делаю

graphTree.addEdge(edge, parent, child);

а затем перерисовать VisualizationViewer, визуализация потеряла структуру «Дерево». Он просто добавляет дочерний элемент где-то над родителем и все остальные дочерние элементы этого нового дочернего элемента прямо поверх него.

Есть ли лучший способ динамически добавлять детей к листьям моего дерева? Или мне нужно использовать для перерисовки что-то другое, а не только vv.repaint()?

Любая помощь будет действительно оценена.

Пример того, что происходит:

http://www.dylankiss.be/JUNGExample.PNG

Начиная только с корня (OUTLOOK), после добавления 3 дочерних элементов (Leaf, Leaf, Leaf) с разными краями (солнечно, пасмурно, дождливо) они просто появляются друг над другом.

EDIT: это метод addChildren().

private static void addChildren(Node node, edu.uci.ics.jung.graph.Tree<Node, Edge> tree) {
    for (int i = 0; i < node.getChildren().size(); i++) {
        tree.addEdge(new Edge(node.getChildren().get(i).getParentValue()), node, node.getChildren().get(i));
        addChildren(node.getChildren().get(i), tree);
    }
}

EDIT 2: это часть AWT ActionListener, где я добавляю новых дочерних элементов в дерево.

while (there are still edges to be added) {
    value = name of new edge;
    child = new Node(this.m_node, value);
    this.m_node.addChild(child);
    graphTree.addEdge(new Edge(value), this.m_node, child);
}

person dylan202    schedule 06.04.2012    source источник


Ответы (1)


Публикация метода, который отвечает за добавление новых ребер, поможет здесь :)

Но на первый взгляд кажется, что вы добавляете 3 разных ребра между одними и теми же двумя узлами (OUTLOOK и Leaf). Я предполагаю, что вы делаете это (или эквивалент с экземплярами Node и Edge):

graphTree.addChild("sunny", "OUTLOOK", "Leaf");
graphTree.addChild("overcast", "OUTLOOK", "Leaf");
graphTree.addChild("rainy", "OUTLOOK", "Leaf");

В этом случае, поскольку графы JUNG сохраняют уникальность узлов, вы получаете только два узла и 3 разных ребра между ними. Когда JUNG попытается отобразить этот график, вы получите два узла и 3 перекрывающихся ребра, как вы использовали EdgeShape.Line.

Если вашей первоначальной целью действительно было установить 3 разных ребра между двумя узлами, попробуйте использовать разные формы ребер, чтобы избежать перекрытия и получить лучший рендеринг, например. EdgeShape.BentLine или что-то подобное.

Если вам нужно 3 разных узла, вам придется использовать 3 разных имени или 3 разных экземпляра узла, которые не равны.

Удачи :)

ИЗМЕНИТЬ:

Следуя вашему комментарию, я просмотрел исходники TreeLayout и обнаружил небольшую проблему, которая делает невозможным динамическое обновление макета.

Чтобы устранить проблему, используйте этот класс:

import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import java.awt.Point;
import java.util.Collection;

import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.util.TreeUtils;

public class DynamicTreeLayout<V, E>
    extends TreeLayout<V, E>
{
public DynamicTreeLayout(Forest<V, E> g) {
    this(g, DEFAULT_DISTX, DEFAULT_DISTY);
}

public DynamicTreeLayout(Forest<V, E> g, int distx) {
    this(g, distx, DEFAULT_DISTY);
}

public DynamicTreeLayout(Forest<V, E> g, int distx, int disty) {
    super(g, distx, disty);
}

protected void buildTree() {
    alreadyDone.clear(); // This was missing and prevented the layout to update positions

    this.m_currentPoint = new Point(20, 20);
    Collection<V> roots = TreeUtils.getRoots(graph);
    if (roots.size() > 0 && graph != null) {
        calculateDimensionX(roots);
        for (V v : roots) {
            calculateDimensionX(v);
            m_currentPoint.x += this.basePositions.get(v) / 2 + this.distX;
            buildTree(v, this.m_currentPoint.x);
        }
    }
}

private int calculateDimensionX(V v) {
    int localSize = 0;
    int childrenNum = graph.getSuccessors(v).size();

    if (childrenNum != 0) {
        for (V element : graph.getSuccessors(v)) {
            localSize += calculateDimensionX(element) + distX;
        }
    }
    localSize = Math.max(0, localSize - distX);
    basePositions.put(v, localSize);

    return localSize;
}

private int calculateDimensionX(Collection<V> roots) {
    int localSize = 0;
    for (V v : roots) {
        int childrenNum = graph.getSuccessors(v).size();

        if (childrenNum != 0) {
            for (V element : graph.getSuccessors(v)) {
                localSize += calculateDimensionX(element) + distX;
            }
        }
        localSize = Math.max(0, localSize - distX);
        basePositions.put(v, localSize);
    }

    return localSize;
}
}

Вам также нужно будет добавить следующее, если вы хотите, чтобы макет обновлялся, а средство просмотра перерисовывалось для каждой модификации вашего графика:

layout.setGraph(g);
vv.repaint();
person ecniv    schedule 06.04.2012
comment
Что делает метод addChildren(), так это проверяет, есть ли у моего объекта Node дочерние элементы, и если да, создает отдельные ребра (new Edge(...)) для каждого дочернего элемента, указывая от родительского узла к дочернему, а затем рекурсивно вызывает метод addChildren() для каждого дочернего элемента. Каждый добавляемый мной Node и Edge — это новые и очень разные объекты. Когда я использую существующее дерево и вызываю метод generateTree(), он красиво рисует мое дерево так, как я хочу. Это просто не работает, когда я добавляю узлы после того, как нарисовал их в первый раз. - person dylan202; 06.04.2012
comment
Я просмотрел класс TreeLayout, и есть небольшая ошибка, которая не позволяет ему обновлять позиции узлов при создании новых узлов. Смотрите мой обновленный первоначальный ответ. - person ecniv; 06.04.2012
comment
Большое спасибо! Добавление этого нового класса сработало как шарм. - person dylan202; 06.04.2012
comment
Дополнительный вопрос: когда я добавляю новых детей к родительскому узлу, они, кажется, добавляются случайным образом, а не в том порядке, в котором я их добавлял. Будет ли это иметь какое-то отношение к новому DynamicTreeLayout? - person dylan202; 17.04.2012