【问题标题】:JTree async node creation is not consistent with JGraph libraryJTree异步节点创建与JGraph库不一致
【发布时间】:2012-12-20 15:51:13
【问题描述】:

我正在创建一个带有 rootNode 的 Jtree,然后创建另一个异步更新根节点的线程。

如果我在某个 JPanel 中独立运行这个 Jtree,它的效果非常好,它甚至可以在项目的某个地方工作,但是我被要求在某个新的 swing 组件中拥有这个 Jtree。

在新的 Swing 面板中,它不会完全填充,它只会填充在 Jtree 呈现在屏幕上之前插入的节点(开始时几毫秒)。一旦 Jtree 被渲染,它就不会被更新。 现在有趣的部分我还在节点上创建了一个鼠标监听器,这样我就可以通过右键单击创建节点函数来创建一个新节点,并创建一个新节点并将其添加到 Jtree 根节点上。

要添加的重要一点是我使用 newThread(){void run}).start() 方法来创建一个线程以在 Jtree 上添加节点,因为我以前从未觉得需要 SwingUtilities.invokeLater 方法。但是现在如果我使用 SwingUtilities.invokeLater 方法,而不是主窗口也没有打开它,它只是在启动期间停止,我只是检查了 SwingUtilities.invokeLater 也可以与旧组件一起正常工作,当然也可以独立工作。

我确实调用了 model.nodeStructureChanged(changedNode);添加节点后,这就是它之前工作正常的原因。

请帮助, 代码难以提取,Jtree 代码之前运行良好,可能是某些组件阻止了包含小部件以异步刷新自身?

编辑 更新以包含一些代码,我正在使用 Nick 提供的 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组件,没有其他更改。 请帮忙。

【问题讨论】:

    标签: java swing jtree event-dispatch-thread jgraph


    【解决方案1】:

    除非明确说明,否则 Swing 不是线程安全的。在the JavaDocs for 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()));
                    }});
    
            }
    
        }
    }
    

    【讨论】:

    • 与 SwingUtilities.invokeLater 的结果几乎相同,它作为一个独立的组件可以正常工作,但是我整合它的那一刻不会刷新树。谢谢您的回复。
    • 它可以与 SwingUtilities.invokeAndWait() 一起正常工作,如果我根本不使用线程也可以正常工作,请协助为什么,它不能与 invokeLater 一起正常工作。
    • @leoismyname - 我添加了一个例子来展示你的情况。请使用此示例:如果您可以使用此代码重新创建问题但仍然无法弄清楚如何解决它,请使用问题区域更新您的问题。
    • @leoismyname - 如果我看不到它,我无法告诉你为什么你的代码不起作用。我提供了一个示例,表明它确实有效......但我只能随机猜测您的代码为什么不起作用。
    • 亲爱的尼克同样的问题,这个例子也可以独立工作,但是当我集成到父组件中它不起作用时,现在会用一些代码更新我的问题。
    猜你喜欢
    • 2011-11-05
    • 2015-07-25
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    • 2018-10-01
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多