JProgressBar в JTable не обновляется

Почему индикаторы выполнения в следующем коде не обновляются и как мне их получить?

import java.awt.event.{ActionListener, ActionEvent}
import javax.swing.table.{TableCellRenderer, AbstractTableModel}
import javax.swing.{Timer, JTable, JProgressBar, JFrame}

object TestProgressBar {
  val frame = new JFrame()
  val model = new TestModel()
  val table = new JTable(model)

  class TestModel extends AbstractTableModel {
    def getColumnCount = 4

    def getRowCount = 4

    def getValueAt(y: Int, x: Int) = "%d,%d".format(x, y)

    override def setValueAt(value: Any, row: Int, col: Int) {
      if (col == 0) {
        model.fireTableCellUpdated(row, col);
      }
    }
  }

  class TestCellRenderer extends TableCellRenderer with ActionListener {
    val progressBar = new JProgressBar()
    val timer = new Timer(100, this)

    progressBar.setIndeterminate(true)
    timer.start()

    override def getTableCellRendererComponent(tbl: JTable, value: AnyRef,
                                               isSelected: Boolean,
                                               hasFocus: Boolean,
                                               row: Int, column: Int) = {
      progressBar
    }

    override def actionPerformed(e: ActionEvent) {
      val model = table.getModel
      for (row <- 0 to model.getRowCount) {
        model.setValueAt(0, row, 0)
      }
    }
  }

  def main(args: Array[String]) {
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)

    table.getColumnModel.getColumn(0).setCellRenderer(new TestCellRenderer())

    frame.add(table)

    frame.pack()
    frame.setVisible(true)
  }
}

person Simon Morgan    schedule 07.05.2012    source источник


Ответы (3)


Как отмечает @Robin, средство визуализации вызывается только при обновлении модели. Вам необходимо периодически менять значение в нужных строках. В этом примере используется javax.swing.Timer.

private class TestCellRenderer implements TableCellRenderer, ActionListener {

    JProgressBar bar = new JProgressBar();

    Timer timer = new Timer(100, this);

    public TestCellRenderer() {
        bar.setIndeterminate(true);
        timer.start();
    }

    @Override
    public Component getTableCellRendererComponent(JTable table,
        Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        return bar;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        TableModel model = table.getModel();
        for (int row = 0; row < model.getRowCount(); row++) {
            table.getModel().setValueAt(0, row, 0);
        }
    }
}

Вы также можете реализовать setValueAt(), так как реализация AbstractTableModel пуста:

@Override
public void setValueAt(Object aValue, int row, int col) {
    if (col == 1) {
        // update your internal model and notify listeners
        this.fireTableCellUpdated(row, col);
    }
}

Приложение: Для справки, вот соответствующая реализация Java, дополняющая Scala.

введите здесь описание изображения

Код:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

/**
* @see http://stackoverflow.com/a/10491290/230513
*/
public class TestProgressBar extends JPanel {

    private JTable table = new JTable(new TestModel());
    public TestProgressBar() {
        table.getColumnModel().getColumn(0).setCellRenderer(new TestCellRenderer());
        table.setPreferredScrollableViewportSize(new Dimension(320, 120));
        this.add(new JScrollPane(table));
    }

    private class TestModel extends AbstractTableModel {

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

        @Override
        public int getColumnCount() {
            return 4;
        }

        @Override
        public Object getValueAt(int row, int col) {
            return String.valueOf(row) + ", " + String.valueOf(col);
        }

        @Override
        public void setValueAt(Object aValue, int row, int col) {
            // update internal model and notify listeners
            fireTableCellUpdated(row, col);
        }
    }

    private class TestCellRenderer implements TableCellRenderer, ActionListener {

        JProgressBar bar = new JProgressBar();
        Timer timer = new Timer(100, this);

        public TestCellRenderer() {
            bar.setIndeterminate(true);
            timer.start();
        }

        @Override
        public Component getTableCellRendererComponent(JTable table,
            Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            return bar;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            TableModel model = table.getModel();
            for (int row = 0; row < model.getRowCount(); row++) {
                table.getModel().setValueAt(0, row, 0);
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("TestProgressBar");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestProgressBar().display();
            }
        });
    }
}
person trashgod    schedule 08.05.2012
comment
Это не имеет никакого значения; индикаторы выполнения все еще заморожены. - person Simon Morgan; 08.05.2012
comment
Он работает на Java; вы можете обновить пример Scala в своем вопросе, чтобы показать свою реализацию setValueAt(). В чем разница между override def и def? - person trashgod; 08.05.2012
comment
Я обновил свой пример. Ключевое слово override похоже на аннотацию @Override в Java, за исключением того, что оно обязательно во избежание двусмысленности. - person Simon Morgan; 09.05.2012
comment
Но не override def getValueAt? Обработчик действий вашего таймера обновляет столбец 0, в то время как ваш setValueAt() срабатывает только в столбце 1. - person trashgod; 09.05.2012
comment
getValue должен быть реализацией, а не переопределяемым. Я обновил свой пример, код, который я запускал, имел правильную проверку столбца (и я даже пробовал ее вообще без нее), а индикаторы выполнения все еще не обновляются. - person Simon Morgan; 09.05.2012
comment
Не могли бы вы разместить свой код где-нибудь, чтобы я мог сравнить его с моей версией Scala? - person Simon Morgan; 09.05.2012
comment
Ну, так как вы помогли мне изучить Scala; Я даже преобразовал в AbstractTableModel. См. также Начальные темы. - person trashgod; 09.05.2012
comment
По крайней мере, в Java 1.8.0_31 на Ubuntu Trusty полосы заморожены. Также пробовал разные варианты внешнего вида с тем же результатом: 27921327/TestProgressBar_050.png.html - person Daniel Alder; 06.08.2015
comment
Нет, именно так выглядит setIndeterminate(true); попробуйте setIndeterminate(false) и увеличивайте значение с каждым тиком. - person trashgod; 07.08.2015

Не уверен насчет Scala, но в Swing компонент, возвращаемый TableRenderer, просто используется для создания «штампа». Таким образом, фактический компонент не содержится в JTable, только его штамп. Это означает, что если вы впоследствии обновите компонент, это не отразится в файле `JTable.

Основное преимущество заключается в том, что вы можете повторно использовать свой компонент в средстве визуализации. Например. для рендеринга String вам понадобится только один экземпляр JLabel, в котором вы обновите текст и позволите рендереру вернуть его.

Раздел рендереры в учебнике по таблицам Swing объясняет это в Подробнее

person Robin    schedule 07.05.2012
comment
Так есть ли способ обойти проблему? - person Simon Morgan; 08.05.2012
comment
+1 за концепцию; это должно работать так же в Scala. Я думаю, что ему просто нужно что-то, чтобы управлять им, например . - person trashgod; 08.05.2012

Кажется, я решил проблему, переопределив методы isDisplayable и repaint JProgressBar.

import javax.swing.table.{TableCellRenderer, AbstractTableModel}
import javax.swing.{JTable, JProgressBar, JFrame}

object TestProgressBar {
  val frame = new JFrame()
  val model = new TestModel()
  val table = new JTable(model)

  class TestModel extends AbstractTableModel {
    def getColumnCount = 4

    def getRowCount = 4

    def getValueAt(y: Int, x: Int) = "%d,%d".format(x, y)
  }

  class TestCellRenderer extends TableCellRenderer {
    val progressBar = new JProgressBar() {
      override def isDisplayable = true
      override def repaint() { table.repaint() }
    }

    progressBar.setIndeterminate(true)

    override def getTableCellRendererComponent(tbl: JTable, value: AnyRef,
                                               isSelected: Boolean,
                                               hasFocus: Boolean,
                                               row: Int, column: Int) = {
      progressBar
    }
  }

  def main(args: Array[String]) {
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)

    table.getColumnModel.getColumn(0).setCellRenderer(new TestCellRenderer())

    frame.add(table)

    frame.pack()
    frame.setVisible(true)
  }
}
person Simon Morgan    schedule 09.05.2012
comment
Этого не требуется, если setValueAt() вызывает fireTableCellUpdated(); Я надеялся увидеть, как вы называете EventQueue.invokeLater(new Runnable() {…}). - person trashgod; 10.05.2012
comment
Если я что-то упустил, это кажется оптимальным решением, потому что оно избавляет от необходимости запускать еще один поток. Я вызываю invokeLater следующим образом: gist.github.com/2658997 - person Simon Morgan; 11.05.2012
comment
Ограничение этого подхода заключается в том, что он излишне связывает модель и представление. Timer предназначен только для демонстрационных целей; на практике доступность новых данных будет управлять изменением состояния выполнения. +1 за ссылку. - person trashgod; 11.05.2012