Java + Swing: writing code to combine change events

I have this data stream, roughly:

DataGenerator -> DataFormatter -> UI 

DataGenerator is what quickly generates data; DataFormatter is what formats it for display; and the user interface is just a bunch of Swing elements.

I want to make my DataGenerator something like this:

 class DataGenerator { final private PropertyChangeSupport pcs; ... public void addPropertyChangeListener(PropertyChangeListener pcl) { this.pcs.addPropertyChangeListener(pcl); } public void removePropertyChangeListener(PropertyChangeListener pcl) { this.pcs.removePropertyChangeListener(pcl); } } 

and just call this.pcs.firePropertyChange(...) when my data generator has new data; then I can just do dataGenerator.addPropertyListener(listener) , where the listener is responsible for redirecting the change to the DataFormatter and then to the user interface.

The problem with this approach is that there are thousands of changes to the DataGenerator per second (from 10,000 to 60,000 per second depending on my situation), and the computational cost of formatting it for the user interface is quite high, which makes it unnecessary to load my processor; in fact, everything that interests me visually is not more than 10-20 changes per second.

Is it possible to use a similar approach, but combine change events before they get into the DataFormatter? If I get several update events on the same topic, I just need to display the latest version and skip all the previous ones.

+6
source share
5 answers

Two ideas:

  • A collection of PropertyChangeEvent s. Extend PropertyChangeSupport , overwrite public void firePropertyChange(PropertyChangeEvent evt) , fire only if the last event has been fired more than 50 ms (or for some time that seems appropriate) back. (In fact, you should overwrite each fire* method, or at least the one that you use in your script, to prevent the creation of a PropertyChangeEvent .)
  • Discard all close events. 60,000 events per second is a fairly large number. In this situation, I would interview. This is a conceptual change in MVP where the facilitator knows if he is in an active state and needs to interrogate. With this approach, you do not generate thousands of useless events; you can even imagine the highest frames per second, no matter how much data is available. Or you can set the presenter at a fixed speed, let it sleep between updates for a certain time, or let it adapt to other circumstances (for example, CPU usage).

I would be inclined to the second approach.

+4
source

It looks like your DataGenerator doing a lot of work without a GUI in the EDT stream. I would recommend that your DataGenerator extend SwingWorker and thereby do the work in the background thread implemented in doInBackground . SwingWorker can then publish intermediate results in the GUI, while you have a process method that receives the last few published snippets in the EDT and updates your GUI.

The SwingWorkers process method does merge published fragments, so it won’t run once for each published subtotal.

If you need only the latest EDT result, you can use this code that only cares about the last fragment in the list:

  @Override protected void process(List<Integer> chunks) { // get the *last* chunk, skip the others doSomethingWith( chunks.get(chunks.size() - 1) ); } 

Learn more about SwingWorker: tasks with intermediate results .

+3
source

You can use an ArrayBlockingQueue size 1, in which you will push your data using the offer() function (means that the queue is full, it does nothing)

Then create a ScheduledThreadPoolExecutor that periodically examines the queue.

This way you lose the connection between generation and display.

Generator β†’ Queue β†’ Format / Display

+1
source

Another option is to simply add a listener to your generator, but instead of responding directly to the change, you simply start the timer. This way your listener will look (in the form of pseudo-code, since I'm too lazy to run my IDE or look for exact method signatures)

 Timer timer = new Timer( 100, new ActionListener(){//update the UI in this listener}; public void propertyChange( PropertyChangeEvent event ){ if ( timer.isRunning() ){ timer.restart(); } else { timer.start(); } } 

This will work if your data generator does not continue to generate data all the time or if you want to receive intermediate updates. In this case, you can simply delete the timer.restart() call or select any other sentences in this thread (polling mechanism or SwingWorker )

+1
source

If you are writing your own small change listener with a lock flag and a timer, you can:

 syncronized onChangeRequest() { if (flag) { flag = false; startTimer(); } } timerEvent() { notify all your listeners; } 

I believe that in fact there is a great concurrency blocking flag that can be used, but I can’t remember for a lifetime that it is called!

0
source

Source: https://habr.com/ru/post/907988/


All Articles