JGraphX ​​новая инициализация graph/graphComponent не работает должным образом и вызывает артефакты

Привет, я использую JGraphX ​​для создания какого-то приложения для редактирования графов на основе Java. Приложение в целом работает нормально и по назначению в нормальных условиях. В общем, у меня есть класс под названием Editor, который содержит все необходимые объявления для графа, как показано в примере кода ниже.

инициализация графа и переопределение некоторых его методов

public class Editor extends JFrame implements Serializable {
Handler handler;
JTabbedPane tabPane;
mxGraphComponent graphComponent;
EntityDataTable dataTable;

protected static mxGraph graph = new mxGraph() {

    // Overrides method to disallow edge label editing
    public boolean isCellEditable(Object cell) {
        if (cell instanceof mxCell) {
            mxCell c = (mxCell) cell;
            if (c.isEdge()) {
                return false;
            } else {
                return false;
            }
        }
        return false;
    }

    // Overrides method to disallow edge selection
    public boolean isCellSelectable(Object cell)
    {
        if (model.isEdge(cell))
        {
            return false;
        }

        return super.isCellSelectable(cell);
    }

    // Overrides method to provide a cell label in the display
    public String convertValueToString(Object cell) {
        if (cell instanceof mxCell) {
            Object value = ((mxCell) cell).getValue();

            if (value instanceof Element) {
                Element elt = (Element) value;

               // String tag = elt.getTagName();
                String tag =  elt.getAttribute("name");


                return tag;

            }
        }

        return super.convertValueToString(cell);
    }

    public String getToolTipForCell(Object cell){

        return "Double Click to Edit";
    }
};

...

ограничивает некоторые undoEvents

 protected mxEventSource.mxIEventListener undoHandler = new mxEventSource.mxIEventListener(){
 public void invoke(Object source, mxEventObject evt)
 {

     mxUndoableEdit evt1 = (mxUndoableEdit) evt.getProperty("edit");
     List<mxUndoableEdit.mxUndoableChange> changes = evt1.getChanges();

     Object[] temp = graph.getSelectionCellsForChanges(changes);


      boolean islegal = true;
      for (int i = 0; i < temp.length; i++)
      {
      mxCell cell = (mxCell)temp[i];
      String value = cell.getValue().toString();
      if (value.equals("subprocess")||value.equals("optional")||value.equals("parallel")||value.equals("synchronous")||value.equals("activating")||value.equals("deactivating")){
      //System.out.println("is not legal");
      islegal = false;
      }
      }
      for (int i = 0; i < changes.size(); i++){
          if (changes.get(i).toString().contains("mxValueChange")){
              islegal = false;
          }
      }

      graph.setSelectionCells(graph.getSelectionCellsForChanges(changes));

      if (islegal == true){
      undoManager.undoableEditHappened((mxUndoableEdit) evt
      .getProperty("edit"));
      }else{
     // System.out.println("illegal undo");
      }
  }};

...

 protected boolean modified = false;
protected mxGraphOutline graphOutline;
protected JPanel actionPane;

mxUndoManager undoManager;

public Editor() {
    handler = new Handler(this);
    dataTable = new EntityDataTable(handler);


    initGUI();
    initGraphSettings();
}

public Editor(SaveData saveData) {

    handler = new Handler(this);

    dataTable = new EntityDataTable(handler);

    initGUI();
    initGraphSettings();

    //erst alle entities erstellen und submitten, dann alle verbindungselemente zu den entities hinzufügen und nochmal submit

    //Load entities
    ArrayList<DataSaveElement> saveDataList = saveData.getSaveData(); for (int i = 0; i < saveDataList.size(); i++){

        System.out.println("Loaded "+saveDataList.get(i).getType()+" "+saveDataList.get(i).getName());

        if (saveDataList.get(i).getType().equals("Process")){
            ProcessPopUp temp = new ProcessPopUp(handler, this);
            temp.setGlobalID(saveDataList.get(i).getGlobalID());
            temp.setName(saveDataList.get(i).getName());
            temp.setDesc(saveDataList.get(i).getDescription());
            temp.setType(saveDataList.get(i).getType());

... много кода для перестроения, некоторые настройки графика, графический интерфейс и т. д. Внутри initGui() инициализируется мой mxGraphComponent

graphComponent = new mxGraphComponent(graph);

Поскольку визуализация графа является только одной частью приложения, а другие данные существуют в фоновом режиме, при сохранении сохраняются все значения данных, включая позиции вершин и т. д. Таким образом, при загрузке файла сохранения новое приложение создается с нуля, просто добавляя все сохраненные значения данных шаг за шагом. Когда я закрываю все Java-приложение, запускаю его снова и загружаю сохраненный файл, проблем нет. Проблема возникает при загрузке сохраненного файла, когда приложение все еще работает, например, e. грамм.

menuItem = new JMenuItem("Open...",
            new ImageIcon("images/middle.gif"));
    menuItem.addActionListener(new ActionListener() {
        @java.lang.Override
        public void actionPerformed(ActionEvent e) {
            LoadAndSaveManager manager = new LoadAndSaveManager();
            try {
                Object o = manager.load(new FileChooser(0).getSelectedFile().getAbsolutePath());
                SaveData saveData =(SaveData) o;

                Editor editorNew = new Editor(saveData);
                new MenuBar(editorNew);
                editorNew.setVisible(true);

                editor.dispose();

            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    });

    menu.add(menuItem);

Моя строка меню довольно проста и получает параметр als редактора. Поскольку создается новый редактор, который создает новый mxGraph, а также новый mxGraphComponent и, наконец, удаляет старый редактор, не должно быть никаких помех... по крайней мере, насколько я знаю. Однако, несмотря на наличие нового экземпляра редактора, который имеет свой собственный новый Graph и GraphComponent, старый все еще каким-то образом используется. Как показано на примерах изображений ниже.

Это будет сохранено, и приложение будет полностью закрыто. Две ноды и ссылка для сохранения:

два узла

При запуске приложения и загрузке сохраненных данных ничего не происходит.

Затем я начинаю новый и добавляю, например, три узла и две ссылки. Три узла и две ссылки:

Три узла

Теперь я загружаю ранее сохраненные данные. Я ожидаю, что окно закроется и появится новое окно с предыдущими данными. Это не вариант. Данные загружены, но старый график кажется каким-то образом активным, и все узлы и связи находятся на графике. Перепутаны данные:

смешанный

Если бы это была единственная проблема, я мог бы просто очистить график и впоследствии добавить все «загрузочные данные», однако каким-то образом GraphComponent также кажется сломанным. При перетаскивании узлов связи иногда нарушаются. Неработающие ссылки:

прервано

Из моих наблюдений до сих пор это, кажется, исправляется при выборе области (я думаю, что это заставляет graphComponent обновить ()). Выбор:

выбор

К сожалению, опубликовать весь код не так уж и сложно, поэтому я опубликовал некоторый код, который, как мне кажется, может иметь значение для решения проблемы. Если потребуется дополнительный код, я специально опубликую его позже.

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


Вот минимальный полный пример кода проблемы, касающейся проблемы помех graphComponents при объявлении нового.

public class Main {

Editor editor;

public Main() {
    editor = new Editor();
    new MenuBar(editor);
    editor.setVisible(true);
}

public static void main(String args[]) {
    new Main();
}}

public class Editor extends JFrame {

mxGraphComponent graphComponent;

protected static mxGraph graph = new mxGraph() {

    // Overrides method to disallow edge label editing
    public boolean isCellEditable(Object cell) {
        if (cell instanceof mxCell) {
            mxCell c = (mxCell) cell;
            if (c.isEdge()) {
                return false;
            } else {
                return false;
            }
        }
        return false;
    }

    // Overrides method to disallow edge selection
    public boolean isCellSelectable(Object cell)
    {
        if (model.isEdge(cell))
        {
            return false;
        }

        return super.isCellSelectable(cell);
    }

    // Overrides method to provide a cell label in the display
    public String convertValueToString(Object cell) {
        if (cell instanceof mxCell) {
            Object value = ((mxCell) cell).getValue();

            if (value instanceof Element) {
                Element elt = (Element) value;

                // String tag = elt.getTagName();
                String tag =  elt.getAttribute("name");


                return tag;

            }
        }

        return super.convertValueToString(cell);
    }

    public String getToolTipForCell(Object cell){

        return "Double Click to Edit";
    }
};

public Editor() {
    initGUI();
    initGraphSettings();
}

public Editor(ArrayList<SaveDataElement> saveData) {
    initGUI();
    initGraphSettings();

    //Load data
    addToGraph(saveData);
}

public void initGUI(){
    setExtendedState(JFrame.MAXIMIZED_BOTH);
    setSize(new Dimension(1200, 900));
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    graphComponent = new mxGraphComponent(graph);

    JPanel graphPanel = new JPanel(new BorderLayout());
    graphPanel.add(graphComponent);

    add(graphPanel);
}
public void initGraphSettings(){

    Map<String, Object> style = graph.getStylesheet().getDefaultEdgeStyle();
    style.put(mxConstants.STYLE_ALIGN, true);
    style.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM);

    graph.setCellsCloneable(false);
    graphComponent.setConnectable(false);
    graphComponent.getViewport().setBackground(Color.WHITE);

    new mxRubberband(graphComponent);
}

public mxGraph getGraph(){
    return graph;
}

public void addToGraph(ArrayList<SaveDataElement> saveData){
    for (int i = 0; i < saveData.size(); i++) {
        String name = saveData.get(i).getName();
        int vertPosX = saveData.get(i).getPosX();
        int vertPosY = saveData.get(i).getPosY();

        new AddGraphNode("node", name, "rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;", vertPosX, vertPosY);
    }

    Object[] cells = graph.getChildVertices(graph.getDefaultParent());
    Object startCell = null;
    Object endCell = null;

    for (int i = 0; i < saveData.size(); i++){
        for (int j = 0; j < cells.length; j++){
            if (((mxCell)cells[j]).getAttribute("name").equals(saveData.get(i).getName()))
                startCell = cells[j];

            for (int k = 0; k < saveData.get(i).getTargets().size(); k++){
                if (((mxCell)cells[j]).getAttribute("name").equals(saveData.get(i).getTargets().get(k))){
                    endCell = cells[j];
                    new AddGraphLink(startCell, endCell,"Link", "endArrow=classic;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;verticalAlign=top;verticalLabelPosition=bottom'");
                }
            }
        }
    }
}}

public class MenuBar extends JMenuBar {


MenuBar(Editor editor){

    JMenuBar menuBar = new JMenuBar();
    JMenuItem menuItem;
    JMenu menu = new JMenu("File");
    menuBar.add(menu);

    menuItem = new JMenuItem("Add");
    menuItem.addActionListener(new ActionListener() {
        @java.lang.Override
        public void actionPerformed(ActionEvent e) {
            //Setting up some data to create nodes and links
            ArrayList<SaveDataElement> saveData = new ArrayList<SaveDataElement>();

            ArrayList<String> targetsForTestX = new ArrayList<String>();
            targetsForTestX.add("Test Y");
            targetsForTestX.add("Test Z");
            saveData.add(new SaveDataElement("Test X", 200, 300, targetsForTestX));

            ArrayList<String> targetsForTestY = new ArrayList<String>();
            saveData.add(new SaveDataElement("Test Y", 300, 420, targetsForTestY));

            ArrayList<String> targetsForTestZ = new ArrayList<String>();
            saveData.add(new SaveDataElement("Test Z", 100, 420, targetsForTestZ));

            editor.addToGraph(saveData);

        }
    });
    menu.add(menuItem);

    menuItem = new JMenuItem("Load 1");
    menuItem.addActionListener(new ActionListener() {
        @java.lang.Override
        public void actionPerformed(ActionEvent e) {
            //Setting up some data to create nodes and links
            ArrayList<SaveDataElement> saveData = new ArrayList<SaveDataElement>();

            ArrayList<String> targetsForTest1 = new ArrayList<String>();
            targetsForTest1.add("Test 2");
            saveData.add(new SaveDataElement("Test 1", 40, 40, targetsForTest1));

            ArrayList<String> targetsForTest2 = new ArrayList<String>();
            saveData.add(new SaveDataElement("Test 2", 200, 40, targetsForTest2));


            Editor editorNew = new Editor(saveData);
            new MenuBar(editorNew);
            editorNew.setVisible(true);

            editor.dispose();
        }
    });

    menu.add(menuItem);

    editor.setJMenuBar(menuBar);
}}

public class SaveDataElement {
String name;
int posX, posY;
ArrayList<String> targets;

public SaveDataElement(String name, int posX, int posY, ArrayList<String> targets){
    this.name = name;
    this.posX = posX;

    this.posY = posY;
    this.targets = targets;
}

public String getName() {
    return name;
}

public int getPosX() {
    return posX;
}

public int getPosY() {
    return posY;
}

public ArrayList<String> getTargets() {
    return targets;
}}

public class AddGraphNode extends Editor {

public AddGraphNode(String tag, String name, String style, int vertPosX, int vertPoxY){

    this.getGraph().getModel().beginUpdate();
    Object parent = this.getGraph().getDefaultParent();

    Document doc = mxDomUtils.createDocument();

    Element entity = doc.createElement(tag);
    entity.setAttribute("name", name);

    try
    {

        Object v1 = this.getGraph().insertVertex(parent, "1",entity, vertPosX, vertPoxY, (int)(name.length()*8) ,
                40, style);

    }
    finally
    {
        this.getGraph().getModel().endUpdate();
    }
}}

public class AddGraphLink extends Editor{

public AddGraphLink(Object v1, Object v2, String relation, String style){

    this.getGraph().getModel().beginUpdate();
    Object parent = this.getGraph().getDefaultParent();

    try
    {
        this.getGraph().insertEdge(parent, null, relation, v1, v2,style);
    }
    finally
    {
        this.getGraph().getModel().endUpdate();
    }
}}

При использовании пункта меню «Добавить» некоторые узлы и связи добавляются к графику, а с помощью пункта меню «Загрузить 1» будет создан новый Редактор (создание нового графика и компонента графика). Однако добавленные узлы и ссылки по-прежнему присутствуют в новом компоненте.

Что касается визуально неработающих ссылок, о которых я упоминал выше, этого не происходит... Я буду исследовать это дальше. Несмотря на это, это также может быть связано с проблемой graphComponent.


person Patrick P.    schedule 07.12.2018    source источник
comment
К сожалению, опубликовать весь код не так уж и сложно, поэтому я опубликовал некоторый код, который, как мне кажется, может иметь значение для решения проблемы. Если потребуется дополнительный код, я специально опубликую его позже. -- при обращении за помощью в отладке вашего кода лучше всего создать и опубликовать допустимый минимально воспроизводимый пример программа. Пожалуйста, нажмите и прочитайте ссылку, поскольку она объяснит, что это такое и, что более важно, почему это обычно необходимо в этой ситуации. Удачи   -  person Hovercraft Full Of Eels    schedule 07.12.2018
comment
Предупреждение, однако - это может быть непросто сделать, так как может потребоваться изменить структуру вашей общей программы, поскольку ваши классы должны быть созданы как независимо тестируемые единицы. Это, однако, не так уж и плохо, несмотря ни на что.   -  person Hovercraft Full Of Eels    schedule 07.12.2018
comment
Я добавил минимальный пример кода проблемы, связанной с помехами graphComponent.   -  person Patrick P.    schedule 07.12.2018
comment
Вы можете указать это?   -  person Patrick P.    schedule 07.12.2018
comment
Хорошо, спасибо! Я принял ответ.   -  person Patrick P.    schedule 08.12.2018
comment
Пожалуйста, смотрите ответ и его изменения. Обратите внимание, что для создания этого ответа потребовалось некоторое время и немало усилий, поскольку мне пришлось сначала загрузить библиотеку, затем уговорить мою IDE принять ее, поскольку jar несовместим, а затем использовать отладчик IDE, чтобы помочь найти ошибка,....   -  person Hovercraft Full Of Eels    schedule 08.12.2018


Ответы (1)


Я вижу 3 основные проблемы в вашем коде:

  1. Недопустимое использование статического поля: protected static mxGraph graph = new mxGraph() {
  2. Недопустимое наследование: public class AddGraphLink extends Editor {
  3. И снова неправомерное наследование: class AddGraphNode extends Editor {

Если сделать поле графика статическим, изменения, внесенные в одну переменную, будут ощущаться во всех переменных, и это, вероятно, причина ваших так называемых "артефактов". И причина, по которой вы считаете, что вы должны сделать поле статическим, заключается в том, что два приведенных выше класса наследуются от редактора (опять же неуместно). Решение очевидно:

  1. Сделайте поле графика полем экземпляра, а не статическим, и
  2. Не используйте наследование там, где оно не принадлежит. Вместо этого ваши классы AddGraphXxxx не должны расширять редактор, а должны иметь внутри себя поля редактора, один из которых вы можете установить с помощью конструктора, а другой, чьи методы вы можете вызывать, например, что-то вроде этого:

// protected static mxGraph graph = new mxGraph() { //!!  **** NO ****
private mxGraph graph = new mxGraph() {             //!!  **** YES ****
     .....
     .....

public class AddGraphNode {
    public AddGraphNode(Editor editor, String tag, String name, String style, int vertPosX, int vertPoxY) {
        // **** note use of the editor parameter below ****
        editor.getGraph().getModel().beginUpdate();

        Object parent = editor.getGraph().getDefaultParent();
        Document doc = mxDomUtils.createDocument();
        Element entity = doc.createElement(tag);
        entity.setAttribute("name", name);
        try {
            // **** same here ****
            Object v1 = editor.getGraph().insertVertex(parent, "1", entity, vertPosX, vertPoxY,
                    (int) (name.length() * 8), 40, style);
        } finally {
            // **** and the same here ****
            editor.getGraph().getModel().endUpdate();
        }
    }
}

и вы должны создать этот экземпляр в редакторе, передав параметр this:

new AddGraphNode2(this, "node", name,
        "rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;",
        vertPosX, vertPosY);    

Вы должны внести аналогичные изменения в класс AddGraphLink.


Примечание: ознакомьтесь с Использование нескольких JFrames, хорошая/плохая практика?, чтобы понять, почему замена JFrames не лучшая программа. дизайн, и как вы можете изменить код, чтобы улучшить его структуру и взаимодействие с пользователем.

person Hovercraft Full Of Eels    schedule 07.12.2018
comment
Спасибо за ответ! Это решило проблему. - person Patrick P.; 08.12.2018