Java Timer unknown event - java

When I execute the following method in my program some components change when the timer runs out. For example a jTextArea I created changes in size without any events containing a construction to change its size. It doesn't matter if I first expand the jTextArea and then start the timer or vice versa.
//Show Debug Information for given Seconds with given Text
void giveUserInformation(String input, boolean function, int duration) {
//Debug information and label visibility handling
jLabelDebug.setVisible(true);
jLabelDebug.setText(input);
//Image
if (function)
jLabelDebug.setIcon(new javax.swing.ImageIcon(getClass().getResource("/resources/images/ok-icon.png")));
else
jLabelDebug.setIcon(new javax.swing.ImageIcon(getClass().getResource("/resources/images/Actions-edit-delete-icon.png")));
//Show duration
if (timerShowDurationRuns) {
timerShowDuration.cancel();
timerShowDuration = new Timer();
}
timerShowDurationRuns = true;
//fadeIn();
timerShowDuration.schedule(new TimerTask() {
#Override
public void run() {
jLabelDebug.setVisible(false);
timerShowDurationRuns = false;
//fadeOut();
}
}, duration * 1000);
setCursor(Cursor.getDefaultCursor());
}

When you create the JTextArea you should use code like:
JTextArea textArea = new JTextArea(5, 30);
Now the text area will be able to determine its preferred size and will remain fixed as you add text to it.
Then when you add it to the GUI you use code like:
frame.add( new JScrollPane( textArea ) );
Now as you add more data the text area size will remain fixed, but scroll bars will appear when required.

Related

Swing is very slow with long strings

I built a simple Java program that logs in a JTextArea component.
JTextArea _log = new JTextArea();
_log.setEditable(false);
JScrollPane scrollLog = new JScrollPane(_log);
scrollLog.setPreferredSize(getMaximumSize());
add(scrollLog);
The problem is that logging like this takes 15ms on average:
public void log(String info) {
_log.append(info + "\n");
}
This is far(!) slower than logging using System.out.println. Logging takes more time than the whole running time of the algorithm!
Why is the JTextArea is so slow? Is there a way to improve it?
EDIT 1:
I am using separate thread for the algorithm, and using SwingUtilities.invokeLater to update the log in the UI.
The algorithm tread finish his work after 130ms on average, but the JTextArea finish his appends after 6000ms on avarage.
EDIT 2:
I tried to test this by use setText of string that contains 2500 charaters. In that case the operation took 1000ms on average.
I tried to use another controller then JTextArea and I get same results.
Is it hard for Swing components to deal with large strings? What can I do about it?
EDIT 3:
I just test with this code:
public class Test extends JFrame {
public Test() {
final JTextArea log = new JTextArea();
log.setEditable(false);
log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
JScrollPane scrollLog = new JScrollPane(log);
scrollLog.setPreferredSize(getMaximumSize());
JButton start = new JButton("Start");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
for (int i = 0; i < 2500; i++) {
log.append("a\n");
}
long end = System.nanoTime();
System.out.println((end - start) / 1000000.0);
}
});
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2, 1));
panel.add(scrollLog);
panel.add(start);
add(panel);
}
public static void main(String[] args) {
Test frame = new Test();
frame.setSize(600,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The time of that for loop is 1870ms on avarage.
This is the only code that I ran (include the declaration of _log at the top of the question)
A JTextArea is not slow.
Far(!) away from System.out.println.
System.out.println() executes on a separate Thread.
The log takes more time then the hole running time of the algorithm!
So your algorithm is probably executing on the Event Dispatch Thread (EDT) which is the same Thread as the logic that appends text to the text area. So the text area can't repaint itself until the algorithm is finished.
The solution is to use a separate Thread for the long running algorithm.
Or maybe a better choice is to use a SwingWorker so you can run the algorithm and "publish" results to the text area.
Read the section from the Swing tutorial on Concurrency for more information and a working example of a SwingWorker.
Edit:
//log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
The above line is causing the problem. I get 125 for the first test and 45 when I keep clicking the button.
That property is not needed. The text is still displayed on the left side of the text pane. If you want right aligned text then you need to use a JTextPane and set the attributes of the text pane to be right aligned.
That is why you should always post an MCVE. There is no way we could have guessed from your original question that you were using that method.
Edit2:
Use the alignment feature of a JTextPane:
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
textPane.getStyledDocument().setParagraphAttributes(0, doc.getLength(), center, false);
Now any new text you add to the document should be center aligned. You can change this to right.

why am i appending twice in jtextfield?

I have looked over this code and i don't know what is wrong. I keep getting incorrect outputs when i enter a command (any input). Please look at the bottom part of my code.
public class gui {
private final static javax.swing.JFrame frame = new javax.swing.JFrame();
private final static javax.swing.JPanel panel = new javax.swing.JPanel();
public final static javax.swing.JTextArea outtextArea = new javax.swing.JTextArea("");
public final static javax.swing.JTextField intextArea = new javax.swing.JTextField();
public static void main(String[] args) {
java.awt.Font font = new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 15);
String command;
/* Optional */
frame.setTitle("Console");
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(javax.swing.JRootPane.FRAME); // COMMENT THIS OUT WHEN COMPLETE
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); //if exit command, dispose() first
frame.setBackground(java.awt.Color.BLACK);
/* size */
frame.setMinimumSize(new java.awt.Dimension(0b001011010100,0b000110000100));
frame.setLocation(0,0);
frame.setExtendedState(javax.swing.JFrame.MAXIMIZED_BOTH);
/* Sets panel */
panel.setBackground(java.awt.Color.PINK); // if sees pink, has error
panel.setLayout(new java.awt.BorderLayout());
panel.setSize(frame.getWidth(),frame.getHeight());
/* Sets text area */
//javax.swing.JScrollPane inscrollPane = new javax.swing.JScrollPane(intextArea);
intextArea.setHorizontalAlignment(javax.swing.JTextField.LEFT);
intextArea.setFont(font);
intextArea.setBackground(java.awt.Color.BLACK);
intextArea.setForeground(java.awt.Color.GREEN);
intextArea.setFocusable(true);
javax.swing.JScrollPane outscrollPane = new javax.swing.JScrollPane(outtextArea);
outtextArea.setRows(10);
outtextArea.setLineWrap(true);
outtextArea.setFont(font);
outtextArea.setBackground(java.awt.Color.BLUE);
outtextArea.setForeground(java.awt.Color.GREEN);
outtextArea.setEditable(false);
/* Sets all necessary components */
frame.add(panel);
panel.add(outscrollPane,java.awt.BorderLayout.CENTER);
// panel.add(inscrollPane,java.awt.BorderLayout.SOUTH);
panel.add(intextArea,java.awt.BorderLayout.SOUTH);
/* Adjusts components */
frame.pack();
frame.setVisible(true);
//every time a command is entered, it is sent to handler and
//textbox should be cleared
// THIS BELOW IS WHERE THE PROBLEM LIES/////////////////////////////
boolean keepGoing=true;
while(keepGoing){
command = intextArea.getText();
String refactored;
if(entering_a_command(command) && !command.equals("exit")){
refactored=command.substring(0,command.length()-1);
outtextArea.append(refactored+"\n");
intextArea.setText("");
}
else if(!command.equals("exit")){//no need to read before submission
outtextArea.append("");
command=intextArea.getText();
}
else{
outtextArea.append("EXITING\n");
keepGoing=false;
}
}
}
/*
Method is strictly for entering user input at appropriate time
*/
private static boolean entering_a_command(String temp){
//handler.print(temp);
return temp.contains("="); //key to submit user input
}
}
My input:
12345=
123456=
This is hell=
This is hello=
My EXPECTED output:
12345
123456
This is hell
This is hello
My ACTUAL output:
12345
12345
This is hell
This is hell
My problem:
When i enter an input the first time, it all checks out. When i enter an input the second time, an input that has greater length than the first, it is automatically submitted just as if i had pressed the trigger key (=).
The input box is the black box in the bottom. To submit an input, press '='
The problem is that you're abusing the threading model. You shouldn't be accessing UI components in a thread other than the UI thread - and having a tight loop like this is pretty much always a bad idea. You should read about the Swing threading model. From that tutorial:
Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors.
Instead, you should add an event listener to your text area. There are loads of options here, none of which is obviously ideal, unfortunately. Adding a key listener and handling keyTyped sounds good - but you get the event before the key ends up in the text area, which isn't ideal. Adding a document listener is a nice abstraction in that then it doesn't matter how the text is changed (e.g. programmatically) - but you can't mutate the document within a listener, so you can't clear it.
As a starting point, adding a key listener and handling keyReleased works well at least for the simple case. Get rid of your current loop (and the unconventionally named entering_a_command method) and replace them with:
intextArea.addKeyListener(new java.awt.event.KeyAdapter() {
#Override public void keyReleased(java.awt.event.KeyEvent e) {
String command = intextArea.getText();
if (!command.contains("=")) {
return;
}
command = command.substring(0, command.length() - 1);
if (command.equals("exit")) {
frame.setVisible(false);
frame.dispose();
return;
}
outtextArea.append(command + "\n");
intextArea.setText("");
}
});

How do I implement timer using JLabel on JPanel?

I would like to get a set variable of the object inst, then set it to a JLabel and set that to the current JLabel that's already on the panel. But I would like for the variable (inst.Time (which represents seconds)) of the object 'inst' to decrement by one and remove the current JLabel and add to the panel the updated, decremented inst.Time.
I would like to decrement by 1 second as countdown timer until it reaches 0 and come out of the method WaitAndEnterIntoWorkArea. Could someone please show how to do so with the given code? Help please. Thank you
int.Time is an integer
private int WaitAndEnterIntoWorkArea(Instruction inst) // 'inst' is an object of Instruction arrayList
{
int index = 0;
int nFirstYDirectionToMove = 1;
JLabel busy = new JLabel(String.valueOf(inst.Time) + "s"); //inst.Time is a time that was set for that particular 'inst' and set the busy JLabel to show time + s, e.g. 5s or 3s (s represents seconds)
busy.setFont(font3);
busy.setForeground(Color.RED);
if(inst.WorkArea.startsWith("anvil")) //If inst.WorkArea is set to "anvil" Go into the block of code
{
nFirstYDirectionToMove = 1; //Move over one (Not important)
//I would like to decrement inst.Time 1 at a time to 0 and add to JPanel every second
while(true)
{
synchronized("row1" + String.valueOf(index)){
if(row1[index].getText().equalsIgnoreCase("Open")) //If JLabel row1[index].getText() is already set as text "Open" execute the if statement;
{
//I would like to remove the current JLabel (row1[index]) and add the 1 second decremented JLabel to the panel
panel.remove(row1[index]); //Remove the JLabel of row1[indexOfArea] (row1 is an array of JLabels) from the panel
row1[index] = busy; //Set JLabel text of row1[index] to the busy JLabel //Set the 1 second decremented JLabel to the current JLabel
panel.add(row1[index]); //Add the JLabel with the new text label with something like e.g. 5s or 4s, etc. to the panel
revalidate();
repaint();
break;
}
}
}
}
If I'm not mistaken, simply calling busy.setText(String.valueOf(inst.Time) + "s"); should be all you need. But as always, I would highly recommend using JavaFX instead of Swing.
As for the timing try
Timer timer = new Timer();
timer.scheduleAtFixedRate(()-> busy.setText(...), 0, 1000);

Java / Swing : JTextArea in a JScrollPane, how to prevent auto-scroll?

here's a runnable piece of code that shows what my "problem" is.
I've got a JTextArea wrapped in a JScrollPane. When I change the text of the JTextArea, the JScrollPane scrolls automatically to the end of the text and I don't want that.
Here are my requirements:
the application should not scroll vertically automatically but...
the user should be able to scroll vertically
the user should not be able to scroll horizontally
the application should never scroll horizontally
the JTextArea must not be editable
(so even if there's more text than what can fit horizontally, neither the application nor the user should be able to scroll horizontally. While vertically, only the user should be able to scroll.)
I don't know how to "fix" this: should this be fixed using JTextArea or JScrollPane methods?
Note that AFAICT this is not a duplicate at all of: JTextPane prevents scrolling in the parent JScrollPane
Here's the kinda funny example, every 200 ms it puts new text in the JTextArea and you can see the JScrollPane always scrolling automatically to the end of the text.
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public final class TextInScrollPane extends JFrame {
private static final Random r = new Random( 42 );
public static void main( final String[] args ) {
final JFrame f = new JFrame();
f.setDefaultCloseOperation( EXIT_ON_CLOSE );
f.setLayout(new BorderLayout());
final JTextArea jta = new JTextArea( "Some text", 30, 30 );
jta.setEditable( false ); // This must not be editable
final JScrollPane jsp = new JScrollPane( jta );
jsp.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
jsp.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
f.add( jsp, BorderLayout.CENTER );
f.pack();
f.setLocationRelativeTo( null );
f.setVisible(true);
final Thread t = new Thread( new Runnable() {
public void run() {
while ( true ) {
try {Thread.sleep( 200 );} catch ( InterruptedException e ) {}
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < 50 + r.nextInt( 75 ); i++) {
for (int j = 0; j < r.nextInt(120); j++) {
sb.append( (char) 'a' + r.nextInt(26) );
}
sb.append( '\n' );
}
SwingUtilities.invokeLater( new Runnable() {
public void run() {
jta.setText( sb.toString() );
}
} );
}
}
});
t.start();
}
}
How to set AUTO-SCROLLING of JTextArea in Java GUI?
http://download.oracle.com/javase/1.5.0/docs/api/javax/swing/text/DefaultCaret.html#NEVER_UPDATE
Try:
JTextArea textArea = new JTextArea();
DefaultCaret caret = (DefaultCaret)textArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
This should prevent the caret from automatically making the document scroll to the bottom.
Answering my own question: I'm not exactly sure this is the best way to solve my issue, but setting the JTextArea's caret using setCaretPosition(0) seems to work fine:
jta.setText( sb.toString() );
jta.jta.setCaretPosition( 0 );
You have run into a very strange behaviour in the implementation of the Document classes. I use a DefaultStyledDocument in a JTextPane inside a JScrollPane.
Now, here is the wierd thing. If I update the document on the EventQueue (like you do by scheduling a runnable to run later) the scroll pane automatically scrolls to the end.
However, the document classes claim to be thread safe and actually updatable from another thread. If I make sure to update on another thread than the EventQueue everything works fine but the scroll pane does NOT scroll to the end.
I have no explanation as to why this is so, I haven't looked in the Swing source. I have been exploiting this "feature" since 2006 and it has been consistent so far :-)

Problem with Java GUI

I have made a java program with GUI. Now I want to add a component on the GUI where I can display whatever I want in the same way we display output through
System.out.println();
Which component I can add on the GUI and how to display content on that component.
You could define a PrintStream that prints to a JTextArea:
final JTextArea textArea = new JTextArea();
PrintStream printStream = new PrintStream( new OutputStream() {
#Override
public void write( final int b ) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append( "" + (char )b );
textArea.setCaretPosition( textArea.getText().length() );
}
});
}
} );
System.setOut(printStream);
For just one line you can use a JLabel and set its text property. How to use JLabel: http://www.leepoint.net/notes-java/GUI/components/10labels/jlabel.html
Or if you need to print multiple lines you can use a JTextArea-box.
Its also possible to draw/paint text ontop of the GUI-panel with Java2D and the Graphics object.
You can use a JTextArea and add text to it each time you print something. Call setEditable(false) so it's read-only. Add it to a JScrollPane so it's scrollable.
Or you could use a JList and add each line as a separate list item. It won't do word wrapping but if you're displaying something akin to an event log it'll look good for that purpose.

Categories

Resources