0

I want to update a JLabel with a constant rate of about 300ms. Currently I am using the following code section to do this:

class setLabel {

    PdfHandler PdfHandling = new PdfHandler(pdfName);//as the name implies, this object is to handle pdf's
    Iterator<String> textIterator=PdfHandling.getPageStringList(pageNr).iterator();//extracting text from a pdf word by word

    ActionListener task = new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            centerLabel.setText(textIterator.next());//update the JLabel cenetrLabel with the next string in the textIterator
        }
    };

    public void startTask(){
            new javax.swing.Timer(rate, task).start();
    }

}

The problem is that using this code leads to a non constant update of the JLabel (centerLabel). This means that the time between two "new" labels is not constant (which you can see with your bare eyes) and some strings seem to be skipped because of the too short time between the updates. I have already tried several different things like using invokeLater or sheduledExecutorService but all seemed to have the same problem. My question is, if this would be the right way to do this and if using the JLabel approach for this task was a bad idea from the beginning? If so, what would be a good approach to display strings one after another in a smooth controlled way (somewhat like a video).

Thank you very much, any help and suggestions are appreciated.

Edit:

I have tried to use the following code instead of the Iterator, althoug I don't know if this is faster:

class stringBuffer {
    private ArrayList<String> buf;
    private final int size;
    private int index;

    stringBuffer(ArrayList<String> list){
            buf = list;
            size = buf.size();
    }

    public String getNext(){
            if(index < size){
                return buf.get(index++);
            } else {
                return null;
            }
    }

}

The problem still remained so I tried to just access the list straight away

centerLabel.setText(list.get(index++));

but this still didn't solve the problem.

2nd Edit:

Here is a runnable example which demonstrates the problem:

import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

import java.awt.event.*;
import java.awt.BorderLayout;

class JLabelTest {
    public static void main(String[] args){
        BuildGui gui = new BuildGui();
    }
}

class BuildGui {
    JFrame topLevelFrame;
    JLabel centerLabel;
    JButton startButton;

    BuildGui(){
        initFrame();
        addElements();
        addActionListeners();
    }

    public void initFrame(){
        topLevelFrame = new JFrame("JFrame");
        topLevelFrame.setBounds(200, 200, 400, 200);
        topLevelFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        topLevelFrame.setVisible(true);
    }

    public void addElements(){
        centerLabel = new JLabel("Center");
        startButton = new JButton("Start");

        centerLabel.setHorizontalAlignment(JLabel.CENTER);
        centerLabel.setVerticalAlignment(JLabel.CENTER);

        topLevelFrame.add(startButton, BorderLayout.SOUTH);
        topLevelFrame.add(centerLabel,BorderLayout.CENTER);
    }

    public void addActionListeners(){

        startButton.addActionListener( new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                new setLabel().startTask();
                startButton.setEnabled(false);
            }
        });

    }


    class setLabel {

        private final String[] stringArray =     {"one","two","one","two","one","two","one","two"};//just something to display
        private final int size = stringArray.length;
        private final ArrayList<String> list = new ArrayList<String>(Arrays.asList(stringArray));
        private int index = 0;

        ActionListener task = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
            centerLabel.setText(list.get(index++));
            if(index >= size) index = 0;
            }
        };

        public void startTask(){
            new javax.swing.Timer(300, task).start();
        }

    }

}

3rd EDIT

I have got told that the previous example had several issues. So I tried to fix them, I hope this example is better

import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.EventQueue;

class JLabelTest2 extends JFrame {
    JLabel centerLabel;
    JButton startButton;

    public static void main(String[] args){
        EventQueue.invokeLater(() -> {
            JLabelTest2 gui = new JLabelTest2();
            gui.setVisible(true);
        });
    }

    JLabelTest2(){
        initUI();
    }

    public void initUI(){
        setBounds(200, 200, 400, 200);

        centerLabel = new JLabel("Center");
        startButton = new JButton("Start");

        centerLabel.setHorizontalAlignment(JLabel.CENTER);
        centerLabel.setVerticalAlignment(JLabel.CENTER);

        getContentPane().add(startButton, BorderLayout.SOUTH);
        getContentPane().add(centerLabel,BorderLayout.CENTER);

        setDefaultCloseOperation(EXIT_ON_CLOSE);

        startButton.addActionListener( new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                new setLabel().startTask();
                startButton.setEnabled(false);
            }
        });

        pack();

    }

    class setLabel {

        private final String[] stringArray = {"one","two","one","two","one","two","one","two"};
        private final int size = stringArray.length;
        private final ArrayList<String> list = new ArrayList<String>(Arrays.asList(stringArray));
        private int index = 0;

        ActionListener task = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                centerLabel.setText(list.get(index++));
                if(index >= size) index = 0;
            }
        };

        public void startTask(){
            new javax.swing.Timer(300, task).start();
        }

    }

}

But somehow I have still got the problem on my machine. I have not got a lot running and the issue doesn't change even if I close all running programs. Are there maybe still some problems in the code I don't see?

4th Edit

Adding centerLabel.repaint() didn't change the behavior. I still see the labels change but with a unstable frequency. Infos of the environment:

OS: Ubuntu 14.04 LTS, 64bit

Procesor: Intel Core™ i5-2430M CPU @ 2.40GHz × 4

Memory: 5,7 GiB

Java Infos:

java version "1.8.0_102"

Java(TM) SE Runtime Environment (build 1.8.0_102-b14)

Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

TheDude
  • 168
  • 10
  • Try replacing the code in the actionListener with a simple println"entered") and see if the timing is still arbitrary. It could be that the time it takes to read the next token (with your iterator) is significant compared to your rate. – FredK Jul 22 '17 at 18:58
  • Thanks for the quick reply. I have already tried that and it seems the timing is ok with println. I didn't guess that the time to read the next token could be critical. I rather thought this problem was related to something to do with swing itself. I will try to access the desired strings differently. – TheDude Jul 22 '17 at 19:26
  • *"with a constant rate"* - Well, this is your first problem, there is no guarantee that any update will be performed at a fixed rate, it only guarantees "at some time after", this is the same for `Thread.sleep`. This come down to a few factors, thread scheduling, load on the EDT, the `RepaintManager` algorithm. I'd like to see a runnable example which demonstrated the issue though – MadProgrammer Jul 22 '17 at 23:36
  • Thanks for the input. I thought it wouldn't be 100% exact, but i imagined only fluctuations in the ms range (which I hoped wouldn't be noticeable by looking at it), maybe this was just to naive. I have now added a runnable example. – TheDude Jul 23 '17 at 08:56
  • Your example doesn't display anything because you're not packing, and you're making the frame visible before the components are added, and because you're not constructing your UI in the EDT. Once that is fixed, it works fine, and I can't see any fluctuation. A change every 300ms shouldn't cause any problem, unless your machine does so many ither things in parallel that it can't keep up. – JB Nizet Jul 23 '17 at 08:58
  • Thank you for your help. I am just starting with java and swing, but if I compile this with "javac JLabelTest.java" and then run with "java JLabelTest" it does display, so I thought it was ok this way. I am eager to learn to do it the right way and will have a look into your helpful remarks. – TheDude Jul 23 '17 at 09:29
  • The code, as it is now, should be "OK", and when I compile and start it, I see the alternating `one` `two` strings printed. If this is **not** the case for you, then the reason must be hidden more deeply. You could do an arbitrary `centerLabel.repaint()` after setting the text, but this should **NOT** (!!!) be necessary. Which environment is this? (Operating system, Java version, ...) – Marco13 Jul 23 '17 at 12:56

0 Answers0