I'm calling repaint()
on a Swing component in a JFrame in a thread which is not the EDT (as opposed to a typical repaint
call in a Swing listener/adapter/actionmap). Technically, its being called in a callback from the javax.sound.midi Reciever
, but I don't think this is any different from the general case.
The component is slow to update. This toy code shows a JLabel
whose text should be immediately updated to the current time, then we repaint
, then we print to stdout the current time. On my system, the label and stdout times look proper -- the label repaint
happens a few millis earlier than the stdout. However, the label visually doesn't update for some time, seemingly a random amount up to even a half second. When it does update, it displays a sensible time, but an old one.
Performing the [update display time, repaint, print time] test from within the callback of a mouse listener (or key listener etc.) works fine, latency is low. Of course calling repaint
only schedules it to happen, but shouldn't the behaviour be the same, especially if wrapped in SwingUtilities.invokeLater
? Is there an event priority or simple threading fact that I'm missing? What component is taking long to repaint if not the JLabel
? In summary, how can I invoke repaint
from a non Swing, non-EDT thread?
Non working solutions:
- calling
paintComponent
directly as in https://stackoverflow.com/a/9004806/14495758 ; I shouldn't have to implement a customJFrame
, right? - calling
paintImmediately
as in repaint() in Java doesn't "re-paint" immediately? ; this results in the same behaviour, calls torepaint
are not being collected and called later - calling
repaint
on theJFrame
; this results in the same behaviour
import javax.sound.midi.*;import javax.swing.*;import java.lang.reflect.InvocationTargetException;
public class Main {
public static String time() {return (System.currentTimeMillis()/1000)+"."+(System.currentTimeMillis()%1000);}
public static void main(String[] args) {
JFrame w = new JFrame();
JLabel label = new JLabel("hey");
Transmitter tr = MidiUtil.firstTransmitterExcept("gervill","real time sequencer");//makes a MidiDevice and returns its transmitter
tr.setReceiver(new Receiver(){public void send(final MidiMessage message,final long t) {
try {
label.setText(time());
SwingUtilities.invokeAndWait(()->{label.paintImmediately(0,0,label.getWidth(),label.getHeight());});
}catch(final InterruptedException ie) {}catch(final InvocationTargetException ite) {}
System.out.println(time());
}public void close() {}});
w.add(label);
w.pack();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.setVisible(true);
}
}
Thank you in advance for reading.