Создание асинхронного узла JTree несовместимо с библиотекой JGraph

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

Это работает фантастически, если я запускаю это Jtree независимо в какой-либо JPanel, оно даже работало в каком-то месте проекта, но меня попросили добавить это Jtree в какой-то новый компонент Swing.

В новой панели Swing он не заполняется полностью, он заполняет только те узлы, которые были вставлены (при запуске в течение нескольких миллисекунд) до того, как Jtree было отображено на экране. После рендеринга Jtree он не обновляется. Теперь интересная часть. Я также сделал прослушиватель мыши на узле, чтобы я мог создать новый узел, щелкнув правой кнопкой мыши функцию создания узла, и с этим новым узлом создается и добавляется в корневой узел Jtree.

Важно добавить, что я использовал метод newThread(){void run}).start() для создания потока для добавления узла в Jtree, потому что раньше я никогда не чувствовал необходимости в методе SwingUtilities.invokeLater. но теперь, если я использовал метод SwingUtilities.invokeLater, то главное окно также не открывается, оно просто останавливается во время запуска, я только что проверил, что SwingUtilities.invokeLater также отлично работает со старым компонентом и, конечно же, отлично работает независимо.

И я вызываю model.nodeStructureChanged(changedNode); после добавления узла, поэтому раньше он работал нормально.

Пожалуйста, помогите, код трудно извлечь, и код Jtree раньше работал нормально, может быть, какой-то компонент блокирует содержащие виджеты для асинхронного обновления?

EDIT Обновите, чтобы включить некоторый код, я использую класс Temp, предоставленный Ником: -

    public BasicGraphEditor(String appTitle, mxGraphComponent component)
{
    // Stores and updates the frame title
    this.appTitle = appTitle;

    // Stores a reference to the graph and creates the command history
    graphComponent = component;
    final mxGraph graph = graphComponent.getGraph();
    undoManager = createUndoManager();

    // Do not change the scale and translation after files have been loaded
    graph.setResetViewOnRootChange(false);

    // Updates the modified flag if the graph model changes
    graph.getModel().addListener(mxEvent.CHANGE, changeTracker);

    // Adds the command history to the model and view
    graph.getModel().addListener(mxEvent.UNDO, undoHandler);
    graph.getView().addListener(mxEvent.UNDO, undoHandler);

    // Keeps the selection in sync with the command history
    mxIEventListener undoHandler = new mxIEventListener()
    {
        @Override
        public void invoke(Object source, mxEventObject evt)
        {
            List<mxUndoableChange> changes = ((mxUndoableEdit) evt
                    .getProperty("edit")).getChanges();
            graph.setSelectionCells(graph
                    .getSelectionCellsForChanges(changes));
        }
    };

    undoManager.addListener(mxEvent.UNDO, undoHandler);
    undoManager.addListener(mxEvent.REDO, undoHandler);

    // Creates the graph outline component
    graphOutline = new mxGraphOutline(graphComponent);

    // Creates the library pane that contains the tabs with the palettes
    libraryPane = new JTabbedPane();

            /////////////////////////////////////////////////
            // Only change i have done here: start
            ////////////////////////////////////////////////
    Temp tempExplorer = new Temp();

    libraryPane.add("new Explorere", tempExplorer);

            /////////////////////////////////////////////////
            // Only change i have done here: End
            ////////////////////////////////////////////////

    // Creates the inner split pane that contains the library with the
    // palettes and the graph outline on the left side of the window
    JSplitPane inner = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
            libraryPane, graphOutline);
    inner.setDividerLocation(320);
    inner.setResizeWeight(1);
    inner.setDividerSize(6);
    inner.setBorder(null);

    // Creates the outer split pane that contains the inner split pane and
    // the graph component on the right side of the window
    JSplitPane outer = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, inner,
            graphComponent);
    outer.setOneTouchExpandable(true);
    outer.setDividerLocation(200);
    outer.setDividerSize(6);
    outer.setBorder(null);

    // Creates the status bar
    statusBar = createStatusBar();

    // Display some useful information about repaint events
    installRepaintListener();

    // Puts everything together
    setLayout(new BorderLayout());
    add(outer, BorderLayout.CENTER);
    add(statusBar, BorderLayout.SOUTH);
    installToolBar();

    // Installs rubberband selection and handling for some special
    // keystrokes such as F2, Control-C, -V, X, A etc.
    installHandlers();
    installListeners();
    updateTitle();
}

Приведенный выше класс взят из библиотеки Jgraph как https://github.com/jgraph/jgraphx. И я просто добавление компонента jtree, как указано выше, никаких других изменений. пожалуйста помоги.


person leoismyname    schedule 20.12.2012    source источник


Ответы (1)


Swing не является потокобезопасным, если это явно не указано. В документах JavaDocs для JTree прямо говорится это не потокобезопасно. Если вы обновите его в потоке вне EDT, нет никакой гарантии, что что-то будет работать. Поэтому, если вы хотите обновить JTree из другого потока, вам нужно использовать SwingUtilities.invokeLater(Runnable run);, чтобы поместить запрос в EDT.

Я бы рекомендовал иметь структуру данных для хранения информации JTree и использовать JTree только для взаимодействия с пользователем (а не для хранения данных).

ИЗМЕНИТЬ

Вот пример использования SwingUtilities.invokeLater() для обновления JTree в компонентной модели. Без публикации какого-либо кода это лучшее, с чем мне приходилось работать. Пожалуйста, попробуйте использовать это, чтобы воссоздать вашу проблему (добавляйте фрагменты вашего кода в этот пример, пока вы не сузите область проблемы).

import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;

public class Temp extends JPanel{
    JTree tree = new JTree();

    public Temp(){
        JScrollPane jsp = new JScrollPane(tree);

        // Creates the library pane that contains the tabs with the palettes
        JTabbedPane libraryPane = new JTabbedPane();

        libraryPane.add("new Explorere", jsp);

        // Creates the inner split pane that contains the library with the
        // palettes and the graph outline on the left side of the window
        JSplitPane inner = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
                libraryPane, new JPanel());
        inner.setDividerLocation(320);
        inner.setResizeWeight(1);
        inner.setDividerSize(6);
        inner.setBorder(null);

        // Creates the outer split pane that contains the inner split pane and
        // the graph component on the right side of the window
        JSplitPane outer = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, inner,
                new JPanel());
        outer.setOneTouchExpandable(true);
        outer.setDividerLocation(200);
        outer.setDividerSize(6);
        outer.setBorder(null);

        // Puts everything together
        setLayout(new BorderLayout());
        add(outer, BorderLayout.CENTER);
    }

    public static void main(String[] args) {
        final Temp temp = new Temp();
        SwingUtilities.invokeLater(new Runnable(){

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setContentPane(temp);
                frame.pack();
                frame.setVisible(true);
            }});

        Thread updater = new Thread(temp.new CustomThread());
        updater.start();
    }

    public class CustomThread implements Runnable{

        @Override
        public void run() {
            for(int i = 0; i < 1000; i++){
                updateTree("New Item "+ i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        public void updateTree(final String nodeToAdd){
            SwingUtilities.invokeLater(new Runnable(){

                @Override
                public void run() {
                    DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
                    DefaultMutableTreeNode child = new DefaultMutableTreeNode(nodeToAdd);
                    model.insertNodeInto(child, root,root.getChildCount());
                    tree.scrollPathToVisible(new TreePath(child.getPath()));
                }});

        }

    }
}
person Nick Rippe    schedule 20.12.2012
comment
почти такой же результат с SwingUtilities.invokeLater, он отлично работает как независимый компонент, но в тот момент, когда я интегрирую его, он не обновляет дерево. Спасибо за ваш ответ. - person leoismyname; 20.12.2012
comment
Он отлично работает с SwingUtilities.invokeAndWait() и отлично работает, если я вообще не использую поток, пожалуйста, помогите, почему, он не работает нормально с invokeLater. - person leoismyname; 20.12.2012
comment
@leoismyname - я добавил пример, показывающий, что ваша ситуация работает. Пожалуйста, поработайте с этим примером: если вы можете воссоздать проблему, используя этот код, и все еще не можете понять, как ее исправить, обновите свой вопрос, указав проблемную область. - person Nick Rippe; 20.12.2012
comment
@leoismyname - я не могу сказать вам, почему ваш код не работает, если я его не вижу. Я привел пример, показывающий, что он работает... но я мог только случайно предположить, почему ваш код не работает. - person Nick Rippe; 20.12.2012
comment
Уважаемый Ник, такая же проблема, этот пример также отлично работает независимо, но в тот момент, когда я встраиваюсь в родительский компонент, он не работает, теперь мой вопрос будет обновлен с некоторым кодом. - person leoismyname; 20.12.2012
comment
@leoismyname снова обновил мой код на основе обновлений вашего вопроса. Мне пришлось удалить ссылки на неопубликованные функции и классы, которых у меня нет, но то, что осталось, работает. Можете ли вы начать с обновленного кода и постепенно добавлять то, что я убрал, пока не увидите проблему? Затем напишите, какие строки вызвали проблему? - person Nick Rippe; 20.12.2012
comment
Ник, я знаю, что это будет работать нормально, реальная проблема в том, что он отлично работает независимо, но в тот момент, когда я интегрирую его в библиотеку JGraph, он не работает (т.е. новые узлы не добавляются). Я не делал никаких изменений в библиотеке Jgraph, кроме добавления Jtree в свою библиотекуPane. Может быть, они выполняют какую-то задачу в фоновом режиме, которая не позволяет асинхронно выполнять какие-либо обновления на jtree. - person leoismyname; 20.12.2012
comment
он работает со старой версией JGraph, это то, что я использую сейчас, там он работает нормально, но не работает с последней версией. Большое спасибо за вашу помощь. - person leoismyname; 20.12.2012
comment
@leoismyname, вы должны пойти мне навстречу — попробуйте устранить неполадки, следуя моим советам. Кроме того, похоже, что мы можем сузить круг до использования этой библиотеки JGraph; Вы должны сделать это явным в своем вопросе (изменить заголовок и указать его явно в первых нескольких строках). - person Nick Rippe; 20.12.2012
comment
@leoismyname вы приняли ответ - не хотите уточнить, в чем именно была проблема (связанная с jgraph или что-то еще)? - person kleopatra; 21.12.2012
comment
я удалил SwingUtilities.invokeLater отовсюду в проекте, и теперь он работает нормально, хотя, если я запускаю класс Temp, как указано выше, он все равно не работает. - person leoismyname; 21.12.2012