Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Populating huge table in SWT/JFace RCP application

How would you go about displaying huge amount of rows in SWT table? Huge is something above 20K rows, 20 columns. Don't ask me why I need to show that much data, it's not the point. The point is how to make it work as fast as possible so that end user won't get bored waiting. Each row displays an instance of some object, columns are its properties (some). I thought to use JFace content/label provider pattern, but afraid it will be even slower than hitting the table directly with the data. This is how it goes:

    Display.getDefault().asyncExec(new Runnable() {
       public void run() {
           List<MyObject> objects = model.getViewData();
           for(MyObject object: objects){
           TableItem item = new TableItem(table, SWT.NULL);
           item.setImage(0, img1);
           item.setBackground(color1);
               item.setText(0, object.getProperty0());
               item.setText(1, object.getProperty1());
               item.setText(2, object.getProperty2());
               ..... 
            }
       });

Drawing 20k records on my computer takes about 20 sec. The biggest performance problem I've seen in Windows are caused by incredible amount of native windowing messages sent by SWT when new table item created and populated with text. I've found great workaround for this - hide table before populating, and then show it when done. Just calling table.setVisible(false) before the loop and table.setVisible(true) after the loop does wonders - the speed goes up six-seven times!

I'd like to speed it up even more. What would you suggest ? Also, I wonder how this trick hiding the widget would work on non-windows implementations of SWT (aka Linux) ?

like image 813
Dima Avatar asked Nov 13 '09 00:11

Dima


3 Answers

SWT can do that for you. When you use the SWT.VIRTUAL style flag, items are only created when scrolled into view. Here's how to do it:

  1. Create the table with style SWT.VIRTUAL
  2. Set the row count using Table#setItemCount()
  3. Add a SWT.SetData Listener that fills the TableItems on demand.

Here's a code snippet:

public static void main( String[] args ) {
    Display display = new Display();
    Shell shell = new Shell( display );
    shell.setLayout( new FillLayout() );
    final Table table = new Table( shell, SWT.VIRTUAL );
    table.setItemCount( 10000 );
    table.addListener( SWT.SetData, new Listener() {
        public void handleEvent( Event event ) {
            TableItem item = (TableItem)event.item;
            item.setText( "Item " + table.indexOf( item ) );
        }
    } );
    shell.setSize( 300, 500 );
    shell.open();
    while( !shell.isDisposed() ) {
        if( !display.readAndDispatch() ) {
            display.sleep();
        }
    }
    display.dispose();
}
like image 137
ralfstx Avatar answered Nov 15 '22 14:11

ralfstx


You want to do lazy-loading on the table display. Basically, you keep all these objects offscreen in memory, then create only a handful of actual GUI table rows. As the user scrolls you re-render these rows with the objects at that scroll location.

See this article (EDIT: oops I meant this article) for a JFace example.

like image 21
Benjamin Cox Avatar answered Nov 15 '22 12:11

Benjamin Cox


1 - use the setText(String[]) instead of setText(int, String) one call instead of several.

2 - use myTable.setRedraw(false) before and myTable.setRedraw(true) after the process to stop all redrawing opérations during loading data.

it's simpler and can improve performance !!

good luck.

on my side using this I load 2500 lines of 20 column in less than 300ms..... on a standard today PC.

like image 21
YPE Avatar answered Nov 15 '22 14:11

YPE