0

I am working on a the GUI of a piece of code that I have been patching together. I am stuck at this part of the program where I would like a datafile the user chooses to be displayed in a JTable in a preview manner (i.e. the user should not be able to edit the data on the table).

With a button click from Experiment Parameters tab (see screenshot below), I create and run a "PreviewAction" which creates a new tab, and fills it up with the necessary components. Below is the code for DataPreviewAction. EDIT: I also posted a self-contained, minimal version of this that mimics the conditions in the real project, and exhibits the same behaviour.

import java.awt.BorderLayout;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

public class MyFrame extends JFrame {

private JPanel panel1;
private JTabbedPane tabs;
private JButton runButton;

public MyFrame() {
    tabs = new JTabbedPane();
    panel1 = new JPanel();
    runButton = new JButton("go!");
    runButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            runButtonActionPerformed(evt);
        }
    });
    panel1.add(runButton);
    tabs.addTab("first tab", panel1);
    this.add(tabs);
    pack();
}

public static void main(String args[]) {
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager
                .getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(MyFrame.class.getName()).log(
                java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(MyFrame.class.getName()).log(
                java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(MyFrame.class.getName()).log(
                java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(MyFrame.class.getName()).log(
                java.util.logging.Level.SEVERE, null, ex);
    }

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {

        public void run() {
            MyFrame frame = new MyFrame();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    });
}

private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {
    /*
     * Normally there is more stuff happening here but this much will do for
     * the sake of example
     */
    List<String[]> data = new LinkedList<String[]>();
    for (int i = 1; i < 1000; i++)
        data.add(new String[] { "entry1", "value1", "value2", "value3" });

    SwingUtilities.invokeLater(new DataPreviewAction(data, tabs));
}

public class DataPreviewAction implements Runnable {

    private JTabbedPane contentHolder;
    private List<String[]> data;

    public DataPreviewAction(List<String[]> data, JTabbedPane comp) {
        this.contentHolder = comp;
        this.data = data;
    }

    @Override
    public void run() {
        DefaultTableModel previewModel = new DefaultTableModel() {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };

        for (String[] datarow : data) {
            previewModel.addRow(Arrays.copyOf(datarow, datarow.length,
                    Object[].class));
        }

        JTable table = new JTable(previewModel);

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton("A button"));
        buttonPanel.add(new JLabel(
                "Some description for the awesome table below "));
        buttonPanel.add(new JButton("another button"));

        JScrollPane tablePanel = new JScrollPane(table);
        JPanel container = new JPanel();
        container.setLayout(new BorderLayout());
        container.add(buttonPanel, BorderLayout.NORTH);
        container.add(tablePanel, BorderLayout.CENTER);
        contentHolder.addTab("Preview", container);
        contentHolder.validate();
        contentHolder.repaint();
    }
}
}

There are at least two problems here:

  1. The JTable (or the JScrollPane) does not render at all
  2. The JScrollPane is not as wide as the frame itself, I have no idea why

I am not all that good in Swing so I might be missing something fundamental. I have checked that the datafile is read properly, and the data model contains the right amount of rows (1000+). SO the table should not be empty.

Suggestions?

screenshot of the problem

posdef
  • 6,498
  • 11
  • 46
  • 94

2 Answers2

6
JPanel buttonPanel = new JPanel();
buttonPanel.add(new JButton("A button"));
buttonPanel.add(new JLabel("Some description for the awesome table below "));
buttonPanel.add(new JButton("another button"));

JScrollPane tablePanel = new JScrollPane(table);
JPanel container = new JPanel();
container.add(buttonPanel,BorderLayout.NORTH);
container.add(tablePanel,BorderLayout.SOUTH);
contentHolder.addTab("Preview", container);
    //contentHolder.validate(); <- NO good
    //contentHolder.repaint();  <- --"---
}
  • JPanel uses FlowLayout (implemented in API, acceptiong only PreferredSize, by default isn't resizable), correct output as is demonstrated in attn image, you have to change default LayoutManager for JPanel to BorderLayout, then code lines

.

container.add(buttonPanel,BorderLayout.NORTH);
container.add(tablePanel,BorderLayout.SOUTH);
  • will expands JComponents and can be works as you expecting, but I think tablePanel should be placed in CENTER area

EDIT:

enter image description here

import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class MyFrame extends JFrame {

    private JPanel panel1;
    private JTabbedPane tabs;
    private JButton runButton;
    private JFrame frame = new JFrame();
    private String[] columnNames = {"Nama", "Nim", "IP", "Hapus Baris ke"};
    private Object[][] data = {
        {"igor", "B01_125-358", "1.124.01.125", true},
        {"lenka", "B21_002-242", "21.124.01.002", true},
        {"peter", "B99_001-358", "99.124.01.001", false},
        {"zuza", "B12_100-242", "12.124.01.100", true},
        {"jozo", "BUS_011-358", "99.124.01.011", false},
        {"nora", "B09_154-358", "9.124.01.154", false},
        {"xantipa", "B01_001-358", "1.124.01.001", false},};
    private DefaultTableModel model = new DefaultTableModel(data, columnNames) {
        private static final long serialVersionUID = 1L;

        @Override
        public boolean isCellEditable(int row, int column) {
            switch (column) {
                case 3:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public Class getColumnClass(int column) {
            return getValueAt(0, column).getClass();
        }
    };

    public MyFrame() {
        tabs = new JTabbedPane();
        panel1 = new JPanel();
        runButton = new JButton("go!");
        runButton.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                //
            }
        });
        panel1.add(runButton);
        tabs.addTab("first tab", panel1);
        JTable table = new JTable(model);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton("A button"));
        buttonPanel.add(new JLabel("Some description for the awesome table below "));
        buttonPanel.add(new JButton("another button"));
        JScrollPane tablePanel = new JScrollPane(table);
        JPanel container = new JPanel();
        container.setLayout(new BorderLayout());
        container.add(buttonPanel, BorderLayout.NORTH);
        container.add(tablePanel, BorderLayout.CENTER);
        tabs.addTab("Preview", container);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(tabs);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                MyFrame frame = new MyFrame();

            }
        });
    }
}

EDIT 2nd. e.g.

enter image description here

from code (included your idea about to fill data to model)

import java.awt.BorderLayout;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class MyFrame extends JFrame {

    private JPanel panel1;
    private JTabbedPane tabs;
    private JButton runButton;
    private JFrame frame = new JFrame();
    private String[] columnNames = {"Nama", "Nim", "IP", "Hapus Baris ke"};
    private Object[][] data = {
        {"igor", "B01_125-358", "1.124.01.125", "true"},
        {"lenka", "B21_002-242", "21.124.01.002", "true"},
        {"peter", "B99_001-358", "99.124.01.001", "false"},
        {"zuza", "B12_100-242", "12.124.01.100", "true"},
        {"jozo", "BUS_011-358", "99.124.01.011", "false"},
        {"nora", "B09_154-358", "9.124.01.154", "false"},
        {"xantipa", "B01_001-358", "1.124.01.001", "false"},};
    private DefaultTableModel model = new DefaultTableModel(data, columnNames) {
        private static final long serialVersionUID = 1L;

        @Override
        public boolean isCellEditable(int row, int column) {
            switch (column) {
                case 3:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public Class getColumnClass(int column) {
            return getValueAt(0, column).getClass();
        }
    };

    public MyFrame() {
        tabs = new JTabbedPane();
        panel1 = new JPanel();
        runButton = new JButton("go!");
        runButton.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                List<String[]> data = new LinkedList<String[]>();
                for (int i = 1; i < 10; i++) {
                    data.add(new String[]{"entry1", "value1", "value2", "value3"});
                }
                for (String[] datarow : data) {
                    model.addRow(Arrays.copyOf(datarow, datarow.length, Object[].class));
                }
            }
        });
        panel1.add(runButton);
        tabs.addTab("first tab", panel1);
        JTable table = new JTable(model);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton("A button"));
        buttonPanel.add(new JLabel("Some description for the awesome table below "));
        buttonPanel.add(new JButton("another button"));
        JScrollPane tablePanel = new JScrollPane(table);
        JPanel container = new JPanel();
        container.setLayout(new BorderLayout());
        container.add(buttonPanel, BorderLayout.NORTH);
        container.add(tablePanel, BorderLayout.CENTER);
        tabs.addTab("Preview", container);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(tabs);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                MyFrame frame = new MyFrame();

            }
        });
    }
}
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Changing `LayoutManager` helped with scaling the area for `ScrollPane`, but neither that nor moving the alignment to CENTER makes a difference for the rendering. – posdef Mar 07 '14 at 12:19
  • example for [SSCCE, MCVE, MCTRE](http://stackoverflow.com/a/10414459/714968), then something will be possible to suggest (JFrame???, JTabbedPane, setPreferredScrollableViewportSize), otherwise everything is left out of the MilkyWay – mKorbel Mar 07 '14 at 12:23
  • I dont understand what you mean, could you perhaps clarify? – posdef Mar 07 '14 at 12:27
  • yes, the example code above runs on my machine.. the imports are missing from the code above, I just realised.. I'll just add them – posdef Mar 07 '14 at 12:31
  • ignore my previous comment, what do you really want to do, 1. add a new tab to JTabbedPane or 2. change data for JTable – mKorbel Mar 07 '14 at 12:32
  • JTabbedPane created inside DataPreviewAction never is added to JFrame, is local variable without any effect(s) – mKorbel Mar 07 '14 at 12:34
  • add new tab to JTabbedPane, no change to data on JTable, just load a datamodel in to JTable and display it. – posdef Mar 07 '14 at 12:35
  • JTabbedPane is not created inside DataPreviewAction and the tab is indeed created, the contents of the tab however are not, see: [screenshot for SSCCE](http://i.imgur.com/j7I5VPz.png) – posdef Mar 07 '14 at 12:39
  • JTable with data is added to JTabbedPane (local variable in DataPreviewAction) and hasn't any link to private JTabbedPane tabs; created ad added to JFrame. – mKorbel Mar 07 '14 at 12:40
  • issue is in parameter DefaultTableModel model = new DefaultTableModel(data, columnNames) see my edit – mKorbel Mar 07 '14 at 12:52
  • So I need to have some data already in the table in order to add more!? That doesn't make any sense... If I don't have any arguments to the `DefaultTableModel` constructor then I should have a 0x0 table, then expand it with as many rows as I have, i.e. when the user clicks the "go!" button. Why is that not the case with my code? (BTW: shall we remove some of the less relevant comments above, to minimise clutter and confusion?) – posdef Mar 07 '14 at 13:22
  • I debug you code before, then I post result (best ilustration is in my 2nd. edit) – mKorbel Mar 07 '14 at 13:59
2

Following the footsteps of mKorbel I ended up doing some debugging. I am providing it here in case others run into the same problem.

It felt quite odd that the table looked OK when the underlying DataModel was supplied a data matrix upon initialisation

private DefaultTableModel model = new DefaultTableModel(data, columnNames)

but it would not show up properly when created with the empty constructor

private DefaultTableModel model = new DefaultTableModel()

and adding rows later with model.addRow(Object[] row);

I started look through the source code, and it turns out with the empty constructor the number of rows and columns for the model (private fields) is initiated to 0 and not updated properly afterwards. I noticed this while debugging since my tables had the dimension of 1370 x 0, which of course does not display properly.

Since I do not want to hardcode the number of rows/cols in advance the best course of action was to convert my "rows" to a matrix and provide the data to the model via constructor (much like mKorbel did). Here comes the fun part, if you want to supply the data then you need to supply the column names as well. THe fact that you have to have column names is counter-intuitive (IMHO), what happens if you dont have/need headers? The data is already in a table form, so I dont understand why column names is so important.

At any rate the following code renders the table at least:

String[] colNames = new String[data[1].length];
for(int i=0; i<colNames.length; i++)
    colNames[i] = "C" + i;

DefaultTableModel model = new DefaultTableModel(data,colNames){
    @Override
    public boolean isCellEditable(int row, int column){
        return false;
    }};

I am accepting this because it points to the origin of the problem, but I would not be able to pinpoint the problem without mKorbel's answer, so give the upvote to his/her answer :)

posdef
  • 6,498
  • 11
  • 46
  • 94