JGraph AWT EventQueue исключение при рисовании (с многопоточностью) - PullRequest
2 голосов
/ 14 июля 2011

Положение

У меня есть визуализация с использованием JGraph . График обновляется другим потоком в том, который создает экземпляр Visualization.

Ожидаемое поведение

График должен обновляться различными рабочими потоками. Функция, которую потоки вызывают для обновления графа, - syncronized, поэтому рабочие потоки не будут вызывать проблемы параллелизма между собой.

Фактическое поведение

Существует ( иногда ) исключение, которое выдается в потоке AWT-EventQueue при рисовании. Иногда это нулевой указатель, иногда это индекс вне границ. Вот трассировка стека:

Исключение в потоке "AWT-EventQueue-0" java.lang.NullPointerException на com.mxgraph.shape.mxConnectorShape.translatePoint (неизвестный источник) на com.mxgraph.shape.mxConnectorShape.paintShape (неизвестный источник) на com.mxgraph.canvas.mxGraphics2DCanvas.drawCell (неизвестный источник) at com.mxgraph.view.mxGraph.drawState (Неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawCell (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawChildren (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawCell (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawChildren (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawCell (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawChildren (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawCell (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawFromRootCell (неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.drawGraph (Неизвестный источник) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.paintComponent (неизвестный источник) в javax.swing.JComponent.paint (JComponent.java:1029) на com.mxgraph.swing.mxGraphComponent $ mxGraphControl.paint (неизвестный источник) в javax.swing.JComponent.paintChildren (JComponent.java:866) на javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JViewport.paint (JViewport.java:764) в javax.swing.JComponent.paintToOffscreen (JComponent.java:5138) в javax.swing.BufferStrategyPaintManager.paint (BufferStrategyPaintManager.java:302) в javax.swing.RepaintManager.paint (RepaintManager.java:1188) в javax.swing.JComponent._paintImmediately (JComponent.java:5086) в javax.swing.JComponent.paintImmediately (JComponent.java:4896) в javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:783) в javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:735) в javax.swing.RepaintManager.prePaintDirtyRegions (RepaintManager.java:677) на javax.swing.RepaintManager.access $ 700 (RepaintManager.java:58) в javax.swing.RepaintManager $ ProcessingRunnable.run (RepaintManager.java:1593) в java.awt.event.InvocationEvent.dispatch (InvocationEvent.java:226) в java.awt.EventQueue.dispatchEventImpl (EventQueue.java:647) в java.awt.EventQueue.access $ 000 (EventQueue.java:96) at java.awt.EventQueue $ 1.run (EventQueue.java:608) at java.awt.EventQueue $ 1.run (EventQueue.java:606) at java.security.AccessController.doPrivileged (собственный метод) в java.security.AccessControlContext $ 1.doIntersectionPrivilege (AccessControlContext.java:105) в java.awt.EventQueue.dispatchEvent (EventQueue.java:617) в java.awt.EventDispatchThread.pumpOneEventForFilters (EventDispatchThread.java:275) в java.awt.EventDispatchThread.pumpEventsForFilter (EventDispatchThread.java:200) в java.awt.EventDispatchThread.pumpEventsForHierarchy (EventDispatchThread.java:190) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:185) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:177)at java.awt.EventDispatchThread.run (EventDispatchThread.java:138)

Кажется, это не оказывает негативного влияния на работу приложения. Появляется новый AWT-EventQueue, который на каком-то этапе неизбежно столкнется с той же проблемой.

Причина

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

Вопрос

Как мне обойти эту проблему? Можно ли как-то синхронизировать метод рисования с методом, который обновляет график?

Код визуализации

package ui;

import javax.swing.JFrame;

import com.mxgraph.layout.mxOrganicLayout;
import com.mxgraph.layout.mxStackLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;

import core.Container;
import core.Node;
import core.WarehouseGraph;

public class Visualisation extends JFrame{
    private static final long serialVersionUID = 8356615097419123193L;
    private mxGraph graph = new mxGraph();
    Object parent = graph.getDefaultParent();
    mxOrganicLayout graphlayout = new mxOrganicLayout(graph);
    mxStackLayout containerLayout = new mxStackLayout(graph, true, 10);

    public Visualisation(WarehouseGraph model){
        super("Warehouse Simulator");

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(800, 600);

        graph.getModel().beginUpdate();
        try{
            for(Node node : model.getNodes().values()){
                mxCell cell = (mxCell)graph.insertVertex(parent, node.getId(), node.getId(), 0, 0, 60, 30);

                Object prev = null;
                for(Container container : node.getContainers()){
                    Object newCont = graph.insertVertex(cell, container.getId(), container.getId(), 0, 0, 60, 20);
                    if(prev != null) graph.insertEdge(cell, null, null, prev, newCont);
                    prev = newCont;
                }
            }

            for(Node node : model.getNodes().values()){
                for(Node toNode : node.getDownstreamNodes()){
                    Object fromCell = ((mxGraphModel)graph.getModel()).getCell(node.getId());
                    Object toCell = ((mxGraphModel)graph.getModel()).getCell(toNode.getId());
                    graph.insertEdge(parent, null, null, fromCell, toCell);
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            graph.getModel().endUpdate();
        }

        graphlayout.execute(parent);

        Object[] nodes = mxGraphModel.getChildVertices(graph.getModel(), parent);
        for(Object cell : nodes){
            containerLayout.execute(cell);
            graph.updateCellSize(cell);
        }


        mxGraphComponent graphComponent = new mxGraphComponent(graph);
        getContentPane().add(graphComponent);

        this.setVisible(true);
    }

    //CALLED FROM SYNCHRONIZED FUNCTION
    public void moveContainer(Container container, Node to){
        graph.getModel().beginUpdate();
        mxCell toCell = (mxCell)((mxGraphModel)graph.getModel()).getCell(to.getId());
        mxCell containerCell = (mxCell)((mxGraphModel)graph.getModel()).getCell(container.getId());
        mxCell fromCell = (mxCell)containerCell.getParent();

        try{
            Object[] edges = mxGraphModel.getEdges(graph.getModel(), containerCell);
            graph.removeCells(edges);

            containerCell.removeFromParent();

            graph.addCell(containerCell, toCell);

            Object[] containers = mxGraphModel.getChildVertices(graph.getModel(), toCell);
            if(containers.length >= 2)
                graph.insertEdge(toCell, null, null, containerCell, containers[containers.length-2]);
            containerLayout.execute(toCell);
            containerLayout.execute(fromCell);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            graph.getModel().endUpdate();
        }
    }
}

Код контроллера

package core;
import serialisation.JsonUtil;
import ui.Visualisation;

public class Controller{
    private WarehouseGraph graph;
    Visualisation viz;

    public static void main(String[] args){
        Controller.getInstance();
    }

    private Controller(){
        graph = JsonUtil.initFromJson(); //graph has many worker threads running which can call containerMoved
        viz = new Visualisation(graph);
    }

    private static class SingletonHolder{
        private static final Controller INSTANCE = new Controller();
    }

    public static Controller getInstance(){
        return SingletonHolder.INSTANCE;
    }

    public synchronized void containerMoved(Container container, Node from, Node to){
        viz.moveContainer(container, to);
    }
}

1 Ответ

4 голосов
/ 14 июля 2011

Не ответьте на ваш вопрос, просто обратите внимание на вашу концепцию.

  1. Можете ли вы показать нам свои public static void main(String[] args) {...} методы? Я надеюсь, что есть нечто, похожее на:

    public static void main (String [] args) { SwingUtilities.invokeLater (new Runnable () {

            public void run() {
                Visualisation vis = new Visualisation();
            }
        });
    }
    
  2. this.setSize(800, 600); должно быть this.setpreferredSize(new Dimension(800, 600));

  3. Затем добавьте this.pack(); перед последней строкой (this.setVisible(true);)

EDIT:

  1. Какой тип компонентов возвращает mxGraphComponent graphComponent = new mxGraphComponent(graph);, потому что если пользовательские компоненты основаны на JPanel, то

getContentPane().add(graphComponent); должно быть add(graphComponent);

  1. Все входные данные из потока / задачи backGround должны быть заключены в invokeLater () . Если он мерцает или замерзает, то вам нужно искать invokeAndWait(). Лично я ненавижу, что invokeAndWait() существует там, но для этого метода я не могу дать вам некоторые правильные предложения. Или:

  2. Если вы будете запускать свою задачу на некоторых периодических базах, вам придется искать Runnable (вывод должен быть заключен в invokeLater) или SwingWorker .

...