syntaxhighlighter.googlecode.com

sobota, 1 marca 2014

Swing - asynchroniczna aktualizacja JTable

Problem: chcemy asynchronicznie aktualizować JTable. Mamy zatem sytuację: tworzymy JTable. Wyświetlamy go. A teraz asynchronicznie, bez udziału użytkownika, dochodzą nowe wiersze do JTable.
Idea rozwiązania:
1. Wyświetlamy JTable
2. Asynchronicznie, w wątku systemowym, dodajemy wiersze do modelu JTable'a (tym modelem jest obiekt klasy dziedziczącej po DefaultTableModelu).

Oto kod:
/**
 * $Id$
 */
package pl.prv.wojsal.table;

import java.awt.BorderLayout;
import java.awt.Color;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;

/**
 * <pre>
 * <ul>
 * <li>Projekt: wsSwing
 * <li>Klasa: WsPacjenciViewJTableAsynchronizm
 * <li>Opis: Test na asynchroniczna aktualizacje tablemodela, a tym samym jTable.
 * <li>Utworzono: 1 mar 2014, 10:23:02
 * <li>author: (c) Wojciech Salata
 * </ul>
 * </pre>
 */

public class WsPacjenciViewJTableAsynchronizm {

    public static void main(String[] args) {
        WsPacjenciViewJTableAsynchronizm m = new WsPacjenciViewJTableAsynchronizm();
        m.run();
    }

    private WsPacjenciTableModel    dataModel;

    /**
     *
     */
    private void run() {
        JFrame frame = new JFrame();

        JPanel panel = new JPanel();
        panel.setBackground(Color.BLUE);
        panel.setLayout(new BorderLayout());
        this.dataModel = new WsPacjenciTableModel("lp", "Data", "Nazwisko", "Imię", "Status");
        JTable table = new JTable();
        table.setModel(dataModel);
        JScrollPane jsp = new JScrollPane(table);
        panel.add(jsp);
        frame.setTitle("Lista zarejestrowanych pacjentów");
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setBounds(500, 200, 700, 300);
        frame.setVisible(true);

        runAsynchronicznaAktualizacjaTableModelu();
    }

    /**
     * Asynchroniczne dodawanie wierszy do tabeli. Dodajemy wiersze do tablemodelu w watku systemowym.
     */
    private void runAsynchronicznaAktualizacjaTableModelu() {
        Thread th = new Thread(new Runnable() {

            int    lp    = 0;

            public void run() {
                while (true) {
                    System.out.println("Bede dodawal wiersz lp=" + this.lp);
                    WsPacjenciModel wiersz = WsPacjenciDaoMock.getNewModel(this.lp++);
                    dataModel.addWiersz(wiersz);
                    System.out.println("Dodalem wiersz lp=" + this.lp);
                    try {
                        Thread.sleep(3 * 1000l);
                    } catch (InterruptedException e) {
                        System.out.println(e.getLocalizedMessage());
                        e.printStackTrace();
                    }
                }
            }
        });
        th.start();
       
    }

}

=========================================================================
  /**
 * $Id$
 */
package pl.prv.wojsal.table;

import java.util.Date;

/**
 * <pre>
 * <ul>
 * <li>Projekt: wsSwing
 * <li>Klasa: WsPacjenciModel
 * <li>Opis: Model wiersza tabeli.
 * <li>Utworzono: 1 mar 2014, 10:58:52
 * <li>author: (c) Wojciech Salata
 * </ul>
 * </pre>
 */

public class WsPacjenciModel {

    private int            lp;
    private Date        data;
    private String    imie;
    private String    nazwisko;
    private boolean        status;
    public int getLp() {
        return lp;
    }
    public void setLp(int lp) {
        this.lp = lp;
    }
    public Date getData() {
        return data;
    }
    public void setData(Date data) {
        this.data = data;
    }

    public String getImie() {
        return imie;
    }

    public void setImie(String imie) {
        this.imie = imie;
    }

    public String getNazwisko() {
        return nazwisko;
    }

    public void setNazwisko(String nazwisko) {
        this.nazwisko = nazwisko;
    }
    public boolean isStatus() {
        return status;
    }
    public void setStatus(boolean status) {
        this.status = status;
    }

}
===================================================================

/**
 * $Id$
 */
package pl.prv.wojsal.table;

import java.util.Vector;

import javax.swing.table.DefaultTableModel;

/**
 * <pre>
 * <ul>
 * <li>Projekt: wsSwing
 * <li>Klasa: WsPacjenciTableModel
 * <li>Opis: Model tablea.
 * <li>Utworzono: 1 mar 2014, 10:29:24
 * <li>author: (c) Wojciech Salata
 * </ul>
 * </pre>
 */

public class WsPacjenciTableModel extends DefaultTableModel {

    /**
     *
     * @param nazwyKolumn
     */
    public WsPacjenciTableModel(String... nazwyKolumn) {
        super(nazwyKolumn, 0);
    }

    /**
     * @param wiersz
     */
    public void addWiersz(WsPacjenciModel wiersz) {
        Vector rowData = new Vector();
        rowData.add(wiersz.getLp());
        rowData.add(wiersz.getData());
        rowData.add(wiersz.getNazwisko());
        rowData.add(wiersz.getImie());
        rowData.add(wiersz.isStatus());
        this.insertRow(0, rowData);

    }
}
====================================================================

/**
 * $Id$
 */
package pl.prv.wojsal.table;

import java.util.Date;

/**
 * <pre>
 * <ul>
 * <li>Projekt: wsSwing
 * <li>Klasa: WsPacjenciDaoMock
 * <li>Opis: Klasa DAO - wersja mock, dla testow.
 * <li>Utworzono: 1 mar 2014, 22:19:14
 * <li>author: (c) Wojciech Salata
 * </ul>
 * </pre>
 */

public class WsPacjenciDaoMock {

    private static String[]    nazwiska    = { "Ochódzki", "Jarząbek", "Talar", "Kloss", "Maliniak", "Karwowski", "Paszczywół" };
    private static String[]    imiona        = { "Ryszard", "Wacław", "Andrzej", "Hans", "Roman", "Stefan", "Zenon", "Wojciech", "Donald" };

    /**
     * @param lp
     * @return
     */
    public static WsPacjenciModel getNewModel(int lp) {
        WsPacjenciModel model = new WsPacjenciModel();
        model.setData(new Date());
        model.setImie(imiona[lp % imiona.length]);
        model.setNazwisko(nazwiska[lp % nazwiska.length]);
        model.setLp(lp);
        model.setStatus((lp % 3) == 0);
        return model;
    }

}

niedziela, 2 lutego 2014

Swing - obsługa długo trwających akcji

Problem dotyczy obsługi akcji w Swingu, w których występuje długie przetwarzanie danych. Pojawia się problem blokady wątku Swinga - kontrolki Swinga w tym czasie "nie żyją".

Oto rozwiązanie problemu, przy pomocy klasy SwingWorker, dostępnej w Java SE (od wersji 6).
Idea jest następująca:
  1. Tworzymy obiekt SwingWorker
  2. nadpisujemy metodę doInBackground() - tu umieszczamy logikę, która może trwać dowolnie długo
  3. nadpisujemy metodę done() - tu dajemy kod modyfikujący kontrolki Swinga
  4. Całość uruchamimay przez metodę execute()


package pl.prv.wojsal.action;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;

import org.apache.log4j.Logger;

import pl.prv.wojsal.utils.UtCzas;
import pl.prv.wojsal.utils.UtLog;

/**
 * <pre>
 * <ul>
 * <li>Projekt: wsSwing
 * <li>Klasa:
 * <li>Opis: Obsluga akcji o dlugim czasie przetwarzania
 * <li>Utworzono: 02-02-2014, 14:24:12
 * <li>author: (c) Wojciech Salata
 * </ul>
 * </pre>
 */

public class ObslugaDlugiejAkcji {

    private static Logger    log    = Logger.getLogger(ObslugaDlugiejAkcji.class.getName());

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        UtLog.initLog4jForTests();
        ObslugaDlugiejAkcji m = new ObslugaDlugiejAkcji();
        m.run();
    }

    private JFrame    fr;

    /**
     *
     */
    private void run() {
        JPanel panel = new JPanel();
        final JLabel lb1 = new JLabel("Label chowany/pokazywany przez Bt1");
        panel.add(lb1);
        final JLabel lb2 = new JLabel("Label chowany/pokazywany przez Bt2");
        panel.add(lb2);

        JButton bt1 = new JButton("Button 1 (dluga akcja)");
        bt1.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                log.debug("Obsluga akcji bt1 - start");

                final ObslugaDlugiejAkcji oa1 = new ObslugaDlugiejAkcji();
                oa1.run();
                // UtCzas.sleep("To jest zle - to musi byc w doInBackground()");
                log.debug("Po oa1.run()");
                SwingWorker<Object, Object> sw = new SwingWorker<Object, Object>() {

                    @Override
                    protected Object doInBackground() throws Exception {
                        UtCzas.sleep("Czekamy na koniec akcji bt1");
                        log.debug("Koniec dlugiego przetwarzania bt1");
                        return null;
                    }

                    @Override
                    protected void done() {
                        boolean visible = lb1.isVisible();
                        lb1.setVisible(!visible);
                        oa1.dispose();
                        log.debug("Usuniete oa1");
                    }
                };
                sw.execute();

                log.debug("Obsluga akcji bt1 - koniec");
            }
        });
        panel.add(bt1);
        JButton bt2 = new JButton("Button 2");
        bt2.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                log.debug("Obsluga akcji bt2 - start");
                boolean visible = lb2.isVisible();
                lb2.setVisible(!visible);
                log.debug("Obsluga akcji bt2 - koniec");
            }
        });
        panel.add(bt2);
        this.fr = new JFrame();
        fr.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        fr.getContentPane().add(panel);
        fr.setSize(300, 200);
        fr.setVisible(true);
    }

    /**
     *
     */
    protected void dispose() {
        this.fr.dispose();
    }


}