Изменить Java JTable AbstractTableModel из внешнего источника данных (например, данные, считанные из сокета)

Я пытаюсь изменить TableDemo.java из https://docs.oracle.com/javase/tutorial/uiswing/components/table.html, чтобы модель таблицы можно было обновлять из внешнего источника данных (например, данные, считанные из сокета). Единственное решение, которое я смог придумать, заключалось в изменении метода createAndShowGUI() со статического на нестатический. Однако я не знаю, является ли это рекомендуемым решением для этого варианта использования. Мой рабочий код вставлен сюда, но я был бы признателен за любые предложения по улучшению или альтернативным подходам.

package components;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import java.awt.Dimension;
import java.awt.GridLayout;

@SuppressWarnings("serial")
public class DynamicTableDemo extends JPanel {

    // Add a MyTableModel member with a getter so you can modify table model data
    public final MyTableModel myTableModel;

    public DynamicTableDemo() {
        super(new GridLayout(1,0));

        myTableModel = new MyTableModel();

        JTable table = new JTable(myTableModel);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Add the scroll pane to this panel.
        add(scrollPane);
    }

    public MyTableModel getMyTableModel() {
        return myTableModel;
    }

    class MyTableModel extends AbstractTableModel {
        private String[] columnNames = {"Stuff"};
        private Object[][] data = {
                {new Integer(0)},
                {new Integer(0)},
                {new Integer(0)},
                {new Integer(0)},
                {new Integer(0)}
        };

        public int getColumnCount() {
            return columnNames.length;
        }

        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        /*
         * FIXME:
         * Multiple markers at this line
         * - overrides javax.swing.table.AbstractTableModel.getColumnClass
         * - Type safety: The return type Class for getColumnClass(int) from the type DynamicTableDemo.MyTableModel
         *   needs unchecked conversion to conform to Class<?> from the type AbstractTableModel
         * - Class is a raw type. References to generic type Class<T> should be parameterized
         */
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }

    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     * 
     * NOTE: Typically this is a static method but I removed static
     * so the underlying table model can be modified.
     */
    private void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("DynamicTableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        this.setOpaque(true); //content panes must be opaque
        frame.setContentPane(this);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {

        DynamicTableDemo dynamicTableDemo = new DynamicTableDemo();

        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                dynamicTableDemo.createAndShowGUI();
            }
        });

        int count = 0; // Change table data with an incrementing count
        while (true) {
            try {
                final int count2 = count; // Copy to a final variable for Runnable, feels a bit kludgey...
                /*
                 * In full application, blocking read of a value from a socket here.
                 * Note that myTableModel is updated from the EDT, but the socket read
                 * and sleep below will not be from the EDT.
                 */
                javax.swing.SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        dynamicTableDemo.getMyTableModel().setValueAt(new Integer(count2),
                                count2 % dynamicTableDemo.getMyTableModel().getRowCount(), 0);
                    }
                });
                count++;
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            }
        }

    }
}

person user3130600    schedule 01.03.2021    source источник
comment
Логика обновления TableModel должна быть в классе DynamicTableDemo, а не в методе main(). Все, что делает метод main(), — это создает и отображает исходный графический интерфейс. Таким образом, вместо создания цикла while в методе main() вы, вероятно, запустите `SwingWorker (в классе DynamicTableDemo) для чтения данных из сокета, а затем вы сможете опубликовать данные при его получении. Прочтите раздел руководства по Swing, посвященный задачам с промежуточными результатами для основного примера.   -  person camickr    schedule 01.03.2021
comment
Спасибо за советы. Я сразу перешел к учебнику по компоненту Table, но я должен был также прочитать учебник Concurrency in Swing. Мне также нужно освежить в памяти внутренние классы и статические вложенные классы, так как я до сих пор не до конца понимаю примеры Swing. В любом случае, я опубликую свою обновленную версию для дальнейшего использования и на случай, если у кого-то появятся дополнительные предложения по улучшению.   -  person user3130600    schedule 06.03.2021


Ответы (1)


Обновленная версия на основе комментариев:

package components;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;

@SuppressWarnings("serial")
public class DynamicTableDemo extends JPanel {

    // Make the JTable a field, so inner class TableCellTask can access it
    private final JTable table;
    
    public DynamicTableDemo() {
        super(new GridLayout(1,0));

        table = new JTable(new MyTableModel());
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Add the scroll pane to this panel.
        add(scrollPane);
        
        new TableCellTask().execute();
    }

    private static class TableCell {
        private final Object value;
        private final int row, col;
        TableCell(Object value, int row, int col) {
            this.value = value;
            this.row = row;
            this.col = col;
        }
    }

    private class TableCellTask extends SwingWorker<Void, TableCell> {
        @Override
        protected Void doInBackground() {
            int count = 0; // Change table data with an incrementing count
            while (!isCancelled()) {
                /*
                 * In full application, blocking read of a value from a socket here.
                 * Note that myTableModel is updated from the EDT, but the socket read
                 * and sleep below will not be from the EDT.
                 */
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
                publish(new TableCell(count, count % table.getModel().getRowCount(), 0));
                count++;
            }
            return null;
        }

        @Override
        protected void process(List<TableCell> tableCells) {
            for (TableCell tableCell : tableCells) {
                table.getModel().setValueAt(tableCell.value, tableCell.row, tableCell.col);
            }
        }
    }
    
    class MyTableModel extends AbstractTableModel {
        private String[] columnNames = {"Stuff"};
        private Object[][] data = {
                {new Integer(0)},
                {new Integer(0)},
                {new Integer(0)},
                {new Integer(0)},
                {new Integer(0)}
        };

        public int getColumnCount() {
            return columnNames.length;
        }

        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        public Class<?> getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }

    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     * 
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("DynamicTableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        DynamicTableDemo newContentPane = new DynamicTableDemo();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {

        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });

    }
}
person user3130600    schedule 06.03.2021