0

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:

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.

Adam
  • 161
  • 7
  • Firstly you shouldn't be modifying the labels text out side of the EDT. `repaint` may re-schedule a paint pass on the EDT, depending on it's own determination of what needs to be done. Generally I'd not consider trying to get one-to-one update between any thread and the EDT. A `SwingWorker` may be a better choice, but might need some tweaking to get it to work reasonably well – MadProgrammer Oct 21 '20 at 22:53
  • @MadProgrammer yes, updating on the EDT is something I missed. Thanks for the suggestion to use `SwingWorker`, but that fails too (polling in `doInBackground` and using `process` to update the ui) -- the issue seems to be deeper in Swing. Currently looking in the debugger, hopefully that reveals the lag. – Adam Oct 22 '20 at 01:16
  • I think, one of the things you need to appreciate is, `repaint` isn't immediate, it's scheduled. So, if you're updating the UI a lot, you could just be starving the thread – MadProgrammer Oct 22 '20 at 01:46
  • 1
    A [mre] that compiles and demonstrates the problem (or a simpler but similar one) would be helpful. Note, midi's have their own set of issues so a different example would be best. – WJS Oct 22 '20 at 12:57

0 Answers0