Problems with JTable using fixed rows

Discussion in 'Java' started by Felix Natter, Nov 2, 2009.

  1. Felix Natter

    Felix Natter Guest

    hi,

    I have a special table setup in order to get fixed header and footer
    rows and a scrollable middle part: Three JTable (header, data, footer)
    in a vertical BoxLayout which share a common TableColumnModel.

    Now I have two problems with this:

    1. resizing doesn't always work (probably related to 2.)

    2. the model's getValueAt method is constantly being called, even
    if nothing in the JTable, not even the view, changes. This causes
    really high CPU load. This does not occur when I comment out the header
    and footer JTables.

    My setup is like this (Alignment means "data"):

    dataModel = new AlignmentDataModel(gs.getAlignment());
    columnModel = new AlignmentColumnModel(gs.getAlignment());
    columnModel.setColumnSelectionAllowed(false);

    headerTable = new JTable(new AlignmentHeaderFooterDataModel(), columnModel);
    headerTable.setVisible(true);
    headerTable.setRowHeight(22);
    headerTable.setRowSelectionAllowed(false);
    headerTable.setColumnSelectionAllowed(false);
    headerTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);

    alignmentTable = new JTable(dataModel, columnModel);
    // create columns and call alignmentTable.addColumn(col)
    // ...


    alignmentTable.setVisible(true);
    // java6... alignmentTable.setFillsViewportHeight(true);
    alignmentTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    alignmentTable.setRowHeight(22);
    alignmentTable.setRowSelectionAllowed(false);
    alignmentTable.setColumnSelectionAllowed(false);

    footerTable = new JTable(new AlignmentHeaderFooterDataModel(), columnModel);
    footerTable.setVisible(true);
    footerTable.setRowHeight(22);
    footerTable.setRowSelectionAllowed(false);
    footerTable.setColumnSelectionAllowed(false);
    footerTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);

    alignmentTable.setAutoCreateRowSorter(true);

    Do you have any idea what could be causing this? The multiple JTables
    with a common TableColumnModel seems to be a standard solution...

    Thanks in advance!
    --
    Felix Natter
    Felix Natter, Nov 2, 2009
    #1
    1. Advertising

  2. Felix Natter

    markspace Guest

    Felix Natter wrote:
    > hi,
    >
    > I have a special table setup in order to get fixed header and footer
    > rows and a scrollable middle part: Three JTable (header, data, footer)
    > in a vertical BoxLayout which share a common TableColumnModel.



    Ideally, you should post a tiny example which does the same thing as
    your program, i.e., throws the exception. It should be an SSCCE:

    http://sscce.org/

    I realize this may be difficult to construct if you have a lot of custom
    code, but slimming your code down will have two advantages. One, you
    might spot the error yourself. And two, we'll have a much better chance
    of finding it for you if we have an example in front of us.

    One row, maybe one or two columns, plus the header and footer, in a
    simple JPanel in a simple JFrame, then the exception should happen. If
    you can get that in less than 200 lines or so, we have a good chance of
    helping you.
    markspace, Nov 3, 2009
    #2
    1. Advertising

  3. Felix Natter

    Noel Guest

    On Nov 3, 8:17 am, markspace <> wrote:
    > Felix Natter wrote:
    > > hi,

    >
    > > I have a special table setup in order to get fixed header and footer
    > > rows and a scrollable middle part: Three JTable (header, data, footer)
    > > in a vertical BoxLayout which share a common TableColumnModel.

    >
    > Ideally, you should post a tiny example which does the same thing as
    > your program, i.e., throws the exception.  It should be an SSCCE:
    > [truncated]


    I've taken the liberty of creating an SSCCE for Felix Natter, as the
    problem intrigues me. I haven't figured it out, either.

    ---------- begins ----------

    package cljp;

    import java.awt.HeadlessException;
    import javax.swing.*;
    import javax.swing.table.*;

    public class AppFrame extends JFrame {

    public static void main(String[] args) {
    JFrame frame = new AppFrame();
    frame.setVisible(true);
    }

    public AppFrame() throws HeadlessException {
    super("FixedRowTable");
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setSize(500, 350);
    initLayout();
    }

    private void initLayout() {
    Box container = new Box(BoxLayout.Y_AXIS);

    TableModel dataModel = new MyTableModel();
    TableModel headerModel = new HeaderFooterModel();
    TableModel footerModel = new HeaderFooterModel();

    JTable table = new JTable(dataModel);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    table.setAutoCreateRowSorter(true);

    JTable headerTable = new JTable(headerModel,
    table.getColumnModel());
    headerTable.setRowSelectionAllowed(false);

    JTable footer = new JTable(footerModel,
    table.getColumnModel());
    footer.setRowSelectionAllowed(false);

    container.add(headerTable);
    container.add(new JScrollPane(table));
    container.add(footer);
    getContentPane().add(container);
    }

    class MyTableModel extends AbstractTableModel {

    public int getRowCount() {
    return 30;
    }

    public int getColumnCount() {
    return 3;
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
    return "(" + columnIndex + ", " + rowIndex + ")";
    }
    }

    class HeaderFooterModel extends MyTableModel {

    @Override
    public int getRowCount() {
    return 1;
    }
    }
    }

    ---------- ends ----------


    If the order in which the tables are arranged in the layout is
    changed, from header-table-footer to header-footer-table,

    container.add(headerTable);
    container.add(footer);
    container.add(new JScrollPane(table));

    the continuous calling of getValue does not occur.
    Noel, Nov 3, 2009
    #3
  4. In article
    <>,
    Noel <> wrote:

    > On Nov 3, 8:17 am, markspace <> wrote:
    > > Felix Natter wrote:
    > > > hi,

    > >
    > > > I have a special table setup in order to get fixed header and footer
    > > > rows and a scrollable middle part: Three JTable (header, data, footer)
    > > > in a vertical BoxLayout which share a common TableColumnModel.

    > >
    > > Ideally, you should post a tiny example which does the same thing as
    > > your program, i.e., throws the exception.  It should be an SSCCE:
    > > [truncated]

    >
    > I've taken the liberty of creating an SSCCE for Felix Natter, as the
    > problem intrigues me. I haven't figured it out, either.
    >
    > ---------- begins ----------

    [...]
    > JTable footer = new JTable(footerModel,
    > table.getColumnModel());


    JTable footer = new JTable(footerModel,
    new JTable(dataModel).getColumnModel());

    [...]
    > ---------- ends ----------
    >
    > If the order in which the tables are arranged in the layout is
    > changed, from header-table-footer to header-footer-table,
    >
    > container.add(headerTable);
    > container.add(footer);
    > container.add(new JScrollPane(table));
    >
    > the continuous calling of getValue does not occur.


    I'm not sure why, but giving the footer it's own TableColumnModel also
    prevents the continual calling of getValueAt(). It's no solution, but it
    may suggest the underlying cause.

    --
    John B. Matthews
    trashgod at gmail dot com
    <http://sites.google.com/site/drjohnbmatthews>
    John B. Matthews, Nov 4, 2009
    #4
  5. Felix Natter wrote:
    > The problem is definitely that the data table is wrapped in a
    > JScrollPane, and, as Noel pointed out, that the footer JTable
    > is _below_ the data table.


    My theory:

    It's a matter of sharing the TCM in conjunction with the layout
    procedure and the fact that two nested validation roots are being used.

    JTable and JTableHeader register themself as TCMListener and call
    revalidate whenever column margins change. JTable acts as layout manager
    and changes the column margins.

    To prevent endless loops, revalidate doesn't do anything if the
    validation root is already known as being invalid to the repaint
    manager. By having more validation roots, one can break this rule
    (revalidate the component which is under the 'other' validation root).

    If the JScrollPane (= validation root) gets validated first, it will be
    removed from the list of invalid components. If then the footerTable
    gets validated, it sets the column widths. The TCM notifies the
    dataTable about the changes and dataTable calls revalidate. This causes
    the JScrollPane (as it is the validation root of the JTable) to be added
    to the list of invalid components again.

    If footerTable gets validated first, it sets the column widths and the
    TCM also notifies the dataTable about the changes. But in this case the
    following revalidate doesn't do anything because the JScrollPane is
    already on the list of invalid components.

    Bye
    Michael
    Michael Rauscher, Nov 4, 2009
    #5
  6. Felix Natter

    Felix Natter Guest

    Michael Rauscher <> writes:

    > Felix Natter wrote:
    >> The problem is definitely that the data table is wrapped in a
    >> JScrollPane, and, as Noel pointed out, that the footer JTable
    >> is _below_ the data table.

    >
    > My theory:
    >
    > It's a matter of sharing the TCM in conjunction with the layout procedure
    > and the fact that two nested validation roots are being used.
    >
    > JTable and JTableHeader register themself as TCMListener and call
    > revalidate whenever column margins change. JTable acts as layout manager
    > and changes the column margins.
    >
    > To prevent endless loops, revalidate doesn't do anything if the validation
    > root is already known as being invalid to the repaint manager. By having
    > more validation roots, one can break this rule (revalidate the component
    > which is under the 'other' validation root).
    >
    > If the JScrollPane (= validation root) gets validated first, it will be
    > removed from the list of invalid components. If then the footerTable gets
    > validated, it sets the column widths. The TCM notifies the dataTable about
    > the changes and dataTable calls revalidate. This causes the JScrollPane (as
    > it is the validation root of the JTable) to be added to the list of invalid
    > components again.
    >
    > If footerTable gets validated first, it sets the column widths and the TCM
    > also notifies the dataTable about the changes. But in this case the
    > following revalidate doesn't do anything because the JScrollPane is already
    > on the list of invalid components.


    hello Michael,

    thanks for the analysis, this sounds feasible. Can you think of a way
    to get arounds this, like modifying the validation order?

    Is there a way to get the source that was responsible for marking
    a component dirty? Then we could override the corresponding method...

    How about subclassing the TCM so that it only emits a message to the
    listeners when something (like columns swapped or pixel widths) actually
    changed or the surrounding BoxLayout changed?

    Can somebody think of another workaround?

    I'd rather not like to rewrite all of this using JXTable (which _might_
    work better)...

    Thanks a lot!
    --
    Felix Natter
    Felix Natter, Nov 4, 2009
    #6
  7. Felix Natter

    Noel Guest

    On Nov 4, 1:29 pm, Felix Natter <-brs.de>
    wrote:
    > Michael Rauscher <> writes:
    >
    > > My theory:

    >
    > > It's a matter of sharing the TCM in conjunction with the layout procedure
    > > and the fact that two nested validation roots are being used.

    >
    > > JTable and JTableHeader register themself as TCMListener and call
    > > revalidate whenever column margins change. JTable acts as layout manager
    > > and changes the column margins.

    >
    > > To prevent endless loops, revalidate doesn't do anything if the validation
    > > root is already known as being invalid to the repaint manager. By having
    > > more validation roots, one can break this rule (revalidate the component
    > > which is under the 'other' validation root).

    >
    > > [truncated]

    >
    > hello Michael,
    >
    > [...]
    >
    > How about subclassing the TCM so that it only emits a message to the
    > listeners when something (like columns swapped or pixel widths) actually
    > changed or the surrounding BoxLayout changed?


    That actually sounds considerably more complex than reimplementing
    with JXTable.

    > Can somebody think of another workaround?


    I thought to use GridBagLayout instead, adding the footer table to the
    content pane as the second component, and adjusting
    GridBagConstraints.gridy values to have the table appear below the
    scrollable table:

    --------- start ----------

    private void initLayout() {
    //Box container = new Box(BoxLayout.Y_AXIS);
    Container container = getContentPane();
    container.setLayout(new GridBagLayout());

    TableModel dataModel = new MyTableModel();
    TableModel headerModel = new HeaderFooterModel();
    TableModel footerModel = new HeaderFooterModel();

    JTable table = new JTable(dataModel);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    table.setAutoCreateRowSorter(true);

    JTable header = new JTable(headerModel,
    table.getColumnModel());
    header.setRowSelectionAllowed(false);

    JTable footer = new JTable(footerModel,
    table.getColumnModel());
    footer.setRowSelectionAllowed(false);

    GridBagConstraints cons = new GridBagConstraints();
    cons.gridx = 0;
    cons.fill = GridBagConstraints.BOTH;

    cons.gridy = 0;
    container.add(header, cons);

    cons.gridy = 2;
    container.add(footer, cons);

    cons.gridy = 1;
    cons.weightx = 1;
    cons.weighty = 1;
    container.add(new JScrollPane(table), cons);
    //getContentPane().add(container);

    ---------- end ----------

    I don't know how portable this technique is. It's functioning on
    Windows XP with JRE 1.6. It solves the getValue spin for me, but
    resizing still does not work.
    Noel, Nov 4, 2009
    #7
  8. Felix Natter

    Noel Guest

    On Nov 4, 2:10 pm, Noel <> wrote:
    >
    > I don't know how portable this technique is.  It's functioning on
    > Windows XP with JRE 1.6.  It solves the getValue spin for me, but
    > resizing still does not work.


    That would be a Sun JRE 1.6.
    Noel, Nov 4, 2009
    #8
  9. Felix Natter wrote:
    > Can you think of a way
    > to get arounds this, like modifying the validation order?


    If my theory is true, the following should work

    tblSP = new JScrollPane(dataTable) {
    public boolean isValidateRoot() {
    return false;
    }
    };


    Bye
    Michael
    Michael Rauscher, Nov 4, 2009
    #9
  10. In article <hcsunv$u5s$03$-online.com>,
    Michael Rauscher <> wrote:

    > Felix Natter wrote:
    > > Can you think of a way to get arounds this, like modifying the
    > > validation order?

    >
    > If my theory is true, the following should work
    >
    > tblSP = new JScrollPane(dataTable) {
    > public boolean isValidateRoot() {
    > return false;
    > }
    > };


    Indeed, it works perfectly in Noel's example. Thanks!

    On review, I see that JRootPane, JScrollPane and JSplitPane all return
    true for isValidateRoot(). In contrast, JTextField's isValidateRoot()
    returns false when it's contained within a JViewport, perhaps for a
    similar reason.

    --
    John B. Matthews
    trashgod at gmail dot com
    <http://sites.google.com/site/drjohnbmatthews>
    John B. Matthews, Nov 5, 2009
    #10
  11. Felix Natter

    Felix Natter Guest

    "John B. Matthews" <> writes:

    > In article <hcsunv$u5s$03$-online.com>,
    > Michael Rauscher <> wrote:
    >
    >> Felix Natter wrote:
    >> > Can you think of a way to get arounds this, like modifying the
    >> > validation order?

    >>
    >> If my theory is true, the following should work
    >>
    >> tblSP = new JScrollPane(dataTable) {
    >> public boolean isValidateRoot() {
    >> return false;
    >> }
    >> };

    >
    > Indeed, it works perfectly in Noel's example. Thanks!


    First of all thanks a lot for all the helpful replies, this
    solution works perfectly :)

    However, in order to make resizing columns work, the following is
    necessary:
    headerTable.setTableHeader(dataTable.getTableHeader());
    (I currently don't know why)

    However, when using the above line, the sorting method provided
    by Java 1.6 will not work any more. Luckily, the following solution
    for java 1.5 works perfectly:
    http://stackoverflow.com/questions/591610/jtable-sorting-rows-in-java-1-5

    Now everything works: column resizing, sorting, fixed rows, no update
    problems :)

    Thanks again!
    --
    Felix Natter
    Felix Natter, Nov 5, 2009
    #11
  12. Felix Natter wrote:
    > "John B. Matthews" <> writes:
    >
    >> In article <hcsunv$u5s$03$-online.com>,
    >> Michael Rauscher <> wrote:
    >>> tblSP = new JScrollPane(dataTable) {
    >>> public boolean isValidateRoot() {
    >>> return false;
    >>> }
    >>> };

    >> Indeed, it works perfectly in Noel's example. Thanks!


    In fact, it works for your SSCCE, too.

    > However, in order to make resizing columns work, the following is
    > necessary:
    > headerTable.setTableHeader(dataTable.getTableHeader());
    > (I currently don't know why)


    If you apply it to your SSCCE, resizing and sorting will work as expected.

    Bye
    Michael
    Michael Rauscher, Nov 6, 2009
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tivo Escobar
    Replies:
    1
    Views:
    7,268
    manusa
    Apr 12, 2007
  2. farseer
    Replies:
    1
    Views:
    2,315
    farseer
    Jun 28, 2005
  3. johnp
    Replies:
    4
    Views:
    3,655
    Toby Inkster
    May 23, 2005
  4. Tintin92
    Replies:
    1
    Views:
    1,672
    Andrew Thompson
    Feb 14, 2007
  5. Haircuts Are Important
    Replies:
    3
    Views:
    297
    Haircuts Are Important
    Jun 4, 2013
Loading...

Share This Page