Apologies for the bad title. I am working on a game that uses a JTextPane as the main text view. I use the following code to print to the pane:
public void write(String txt, MessageType t) {
try {
for (char c : txt.toCharArray()) {
text.getDocument().insertString(text.getDocument().getLength(), String.valueOf(c), t.getAttributes());
Utils.pause(30);
}
text.getDocument().insertString(text.getDocument().getLength(), "\n", t.getAttributes());
}
catch (Exception e) { e.printStackTrace(); }
}
That code writes each character one at a time, sleeping for 30 milliseconds between each writing. It then writes a new line character. Here's the pause() method if you're interested:
public static void pause(int millis) {
try { Thread.sleep(millis); }
catch (InterruptedException e) { e.printStackTrace(); }
}
Most of the time, this works fine. It writes each character with some space in between, giving the impression of typing. However, there is one instance where it does not work.
I have a JTextField that handles input. I added this KeyListener to it:
input.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
CommandParser.getInstance().parse(input.getText());
input.setText("");
}
}
});
At the end of the parse() method of CommandParser, if a command isn't found, this line runs:
Game.getInstance().getGUI().write("Invalid command.", GUI.MessageType.BAD);
However, for that line only, the program waits a second, then prints everything at once, instead of printing one character every 30 milliseconds. Why is this? Does this have to do with inner classes?
This is because your action is happening on the EDT thread. The GUI will not update at all until you return from that method. You need to use something like SwingWorker or SwingTimer to farm the updates out to another thread.
Related
tl, dr;
I have a GUI thread that creates an object of another class (the seconds class has implemented Runnable, but here we don't execute the run() method, instead, we call a normal method) and calls a method. In that method, the first thread (current thread) is called again (to show sth on the LCD), then sends some data to the Internet, and waits 3 seconds for the server response. The problem is that the information is printed after 3 seconds. I know about the stack and program counter, but I wonder if there is another option that I can do my job.
I have the main method, which runs 3 threads (for short, I just write the requisite code. Tell me to add more, if needed):
public static void main(String[] args) throws UnknownHostException, InterruptedException {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GUI.getInstance().setVisible(true); //GUI is singleton, using swing and JFrame
} catch (Exception e) {
e.printStackTrace();
}
}
});
MQTTConnection.getInstance().tryToConnect(); //It's the connection class, which has a thread (the thread is handled by a library that keeps the connection alive. I have no threads there) and is a singleton too.
Thread t1 = new Thread(new SendDataThread()); //A thread which sends some data every 20 seconds.
t1.start();
}
And in SendDataThread, I have a function that creates some random data and sends them (using the MQTTConnection class).
This is the SendDataThread:
public class SendDataThread implements Runnable {
public void sendLog() {
boolean serverOnline = false;
StringBuilder data = new StringBuilder();
data.append(createData());
GUI.getInstance().printNeutral(data.toString()); //Prints BLACK on a `JTextPane`
if(MQTTConnection.getInstance().publishLog(MQTTConnection.getInstance().MQTT_PUBLISH_ESP_SEND_LOG, data.toString())) //This line has a 3 second timeout. If the server doesn't respond, it will return false. I've added the 3 seconds timeout too. Please continue reading.
serverOnline = true;
if(serverOnline)
GUI.getInstance().printOK("Server Submitted"); //Prints in GREEN
else
GUI.getInstance().printProblem("Check your connection!"); //Prints in RED
GUI.getInstance().printNeutral("-------------------------------------------------");
}
#Override
public void run() {
while(true) {
sendLog();
try {
Thread.sleep(20000); //sleeps 20 about seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//.....
}
And this is the 3 seconds timeout method, in MQTTConnection:
boolean publishLog(String topic, String data){
mqtt_responds = false;
publish(topic, data);
System.out.println("MQTT is connected");
long lastTime = System.currentTimeMillis();
while(System.currentTimeMillis() - lastTime < callback_timeout) {
if(mqtt_responds){
mqtt_responds = false;
System.out.println("Server submitted");
return true;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Timeout");
return false;
}
Till now, everything work right. The problem starts where I have a button in the GUI class, which the user can manually send random logs:
JButton sendLogBtn = new JButton("Send Log");
sendLogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SendDataThread sdt = new SendDataThread();
sdt.sendLog();
}
});
sendLogBtn.setBounds(10, 331, 89, 23);
panel.add(sendLogBtn);
This button creates an object of SendDataThread and calls the sendLog() method. The issue happens here: after sendLog() is called, sendLog(), calls this GUI thread again:
--> GUI.getInstance().printNeutral(data.toString()); //Prints BLACK on a `JTextPane`
But the log is printed after 3 seconds (After the sendLog() method has finished working, the timeout!)
How can I fix this?
In the button's actionPerformed you are calling sendLog. sendLog does exactly what you said, ie reports some logs and waits about 3 seconds (assuming callback_timeout is about equal to 3000).
To fix this, you need to make sure that the 3sec blocking is not on the EDT and also to make sure that the logs are instead posted on the EDT.
As a quick workaround you can do:
sendLogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Thread(() -> new SendDataThread().sendLog()).start();
}
});
and then, as always, post your logs in the EDT like for example:
SwingUtilities.invokeLater(() -> GUI.getInstance().printNeutral(...));
AND
SwingUtilities.invokeLater(() -> GUI.getInstance().printProblem(...));
AND
SwingUtilities.invokeLater(() -> GUI.getInstance().printOk(...));
As for the question in your comment, I don't really understand what you are asking, but I should say that (as far as I know) the EDT is a Thread where all the Swing code is (and should be) posted on for execution. This way the Swing code does not have to be synchronized, because all GUI related stuff is executed sequentially (on the EDT). AWT for example was not intended to be single threaded as far as I know. Swing is however single threaded.
Runnable updateSeekbar=new Runnable() {
#Override
public void run() {
try{
while (true) {
if (!isPlayerDead) {
Log.d("Threads", "Thread is running successfully.");
int progress=mediaPlayer.getCurrentPosition();
seekBar.setProgress(progress);
Log.d("Seekbar",seekBar.getProgress()+"");
Log.d("MediaProgress",mediaPlayer.getCurrentPosition()+"");
String s=modifyTime(mediaPlayer.getCurrentPosition() / 1000 / 60) + ":" + modifyTime((mediaPlayer.getCurrentPosition() / 1000) % 60);
progressTime.setText(s);
}
}
}
catch (Exception e){
e.printStackTrace();
}
}
};
Executor executor=new Executor() {
#Override
public void execute(Runnable command) {
Thread thread=new Thread(command);
thread.start();
}
};
executor.execute(updateSeekbar);
Actually the problem is that the thread dies before the mediaplayer can send updated position. I have made the thread in an endless while loop but still it dies. How can I make it run infinitely till the activity gets destroyed.
The thread only runs for a couple of seconds and then dies. I want it to run infinitely till the activity gets destroyed. All suggestions are welcome.
when mediaPlayer isn't in proper state and you try to call some improper method then some Exception may be thrown, and you are catching it OUTside while(true) loop. try to move try{}catch inside while(true), then your Thread will run infinitely
#Override
public void run() {
while (true) {
try{
if (!isPlayerDead) {
// current code
}
}
catch (Exception e){
e.printStackTrace();
}
SystemClock.sleep(20); // some bonus line
} // end of while
}
btw. give some rest for UI between iterations, e.g. by putting SystemClock.sleep(20); after every calculation (last line before closing bracket). you don't need so often progress refreshing, in current code it may happen even few times more often than system is capable to draw (in most often 60Hz case)
I am writing a simple text game and I've dacided to move from displaying
in cmd to displaying in a custom window, that constists of one TextArea used
for output and user input. So I need a method that waits for the String that
user writes and then returns it.
In Swing i would do something like this (in a Window class):
public String nextToken() {
synchronized (nextToken) {
while (nextToken.isEmpty())
try {
nextToken.wait();
} catch (InterruptedException e) {
print(e.getLocalizedMessage());
}
String tmp = nextToken.remove(0);
lastToken = text.getText();
newLine();
return tmp;
}
}
And the ActionListener for hitting enter:
public void actionPerformed(ActionEvent arg0) {
synchronized (nextToken) {
nextToken.add(text.getText().substring(lastToken.length(), text.getText().length()));
nextToken.notify();
} }; };
But when I create a Window using JavaFX and then try to use this method the window freezes.
What to do so that the window would display correctly and would wait for user input?
I'd be thankful for any advice
Sounds like you're waiting for input on the main thread, which will block it thus your window freezes. Try putting the waiting code (nextToken method) in a separate thread.
I tried this for many hours.. I have a thread that changes a JTextField of my UI, which completely destroys the UI. The Thread (lets call it Thread A) is generated by an ActionListener. The .setText() function call is in a extra thread (B) created by Thread A. Thread B is the Parameter of SwingUtilitis.invokeAll() and/or SwingUtilities.invokeAndWait(). I tried them both. Here's some code to make it more clear.
This is my ActionListener which creates Thread A - shortened of course:
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == window.getBtn_Search()) {
Refresher refresh = new Refresher();
refresh.start();
}
}
This is my Thread A, which later puts Thread B into the EDT Queue:
public class Refresher extends Thread implements Runnable {
private int counter = 0;
private UI window = null;
private int defRefresh = 0;
#Override
public void run() {
while(true){
-bazillion lines of code-
do {
try {
Refresher.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(window.canceled()) break;
UI.updateCounter(window.getLbl_Status(), (Configuration.getRefreshTime()-counter));
counter++;
} while (counter <= Configuration.getRefreshTime());
- more code-
}
}
}
The UI.updateCounter(...) will queue Thread B into the EDT.
public static void updateCounter(final JLabel label, final int i) {
try {
SwingUtilities.invokeAndWait(
new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
}
);
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Now when the last function gets called, everything gets messed up. I tried different stuff for hours and nothing worked. I also tried using SwingWorker, but the some or nothing at all happened.
The invokeAndWait() tried allows to post a Runnable task to be executed on the EDT, but it blocks the current thread and waits until the EDT is done executing the task.
But there is deadlock potential in invokeAndWait(), as there is in any code that creates a thread interdependency.
If the calling code holds some lock (explicitly or implicitly) that the code called
through invokeAndWait() requires, then the EDT code will wait for the non-
EDT code to release the lock, which cannot happen because the non-EDT code
is waiting for the EDT code to complete, and the application will hang.
As we can see here, modifying the JLabel component passed by the waiting non-
EDT code.
Instead we can use
invokeLater() takes
care of creating and queuing a special event that contains the Runnable. This event is processed on the EDT in the order it was received, just like any other event.
When its time comes, it is dispatched by running the Runnable’s run() method.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
});
OR
isEventDispatchThread() that returns true if the calling code is currently being executed on the EDT, false otherwise.
Runnable code= new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
}
);
if (SwingUtilities.isEventDispatchThread()) {
code.run();
} else {
SwingUtilities.invokeLater(code);
}
In general, labels are not very good at displaying text which change: their width change, and the layout with it.
Using a read-only JTextField, perhaps with proper changes in style, could be a better solution.
I think the intermediate JPanels you've created may count as validation roots. Therefore the revalidate() that automagically happens when you call setText() does not cause any layout changes higher than the level of the JPanel parent.
I don't think you actually need the panels, since a JLabel can contain both an Icon and text. See the tutorial.
So my advice is to remove the panels or, if they serve a purpose, make sure isValidateRoot() on the panels returns false.
When changing the label's text you should at least call repaint()/revalidate() on the label's topmost container, triggering a relayout, assuming the label calls invalidate()/revalidate() correctly on text change.
I've already browsed many threads on this topic, but nothing seems to fit my specific situation.
I have a swing application which analyzes QR-Codes, extracts the found loginNames and makes DB calls to fetch data for that user. To make sure the capture of the QR-Codes can be canceled and my application is still accessible while capturing, I used a SwingWorker for this purpose. Everything works fine so far, I've included a PropertyChangeListener so the application knows when my SwingWorker successfully read a code. But since I don't want to have the PropertyChangeListener as a nested class within my mainClass (to keep it well structured), I've created a new class for it outside. Now I want to return to my main class from this PropertyChangeListener class to switch to the appropriate panel which displays the fetched data. I have different codes which can be read, so depending on the code I have different panels to switch to (so I can't do a static switch to the same panel over and over again). So how can I delegate the PropertyChangeListener to give the control back to my EDT ?
I've tried using wait() and notify() to let my EDT know the SwingWorker finished. But obviously wait() blocks my EDT and the use of SwingWorker is pointless.
I hope I could explain my problem in enough detail, and some of you have a good idea to deal with this.
For any code snippets please ask, I'll then add the necessary ones. But since my project is a bit more complex I'll just post what is asked for.
Thanks in advance for any help :)
EDIT: Here is a code excerpt to illustrate what my SwingWorker is doing.
SwingWorker class:
public class CodeDetector extends SwingWorker<byte[], String> {
String s; // read String
byte[] completeCode; // byte[] which is returned by doInBackground()
BufferedImage resizedImg;
IplImage img;
JLabel labelForStream;
JLabel result;
FrameGrabber grabber = new VideoInputFrameGrabber(); // using JavaCV.
public CodeDetector(JLabel labelForStream, JLabel result) {
this.labelForStream = labelForStream;
this.resultLabel = result;
}
#Override
protected byte[] doInBackground() throws Exception {
try {
grabber.start(); //
while (true) {
// End if current thread was canceled.
if (Thread.currentThread().isInterrupted()) {
return null;
}
// Grab each image, save it, scan for code and display it.
img = grabber.grab();
resizedImg = // resizing image to fit labelForStream.
// save resizedImg to file
// read barcode from saved file
if (isBadgeCode(tmp) || isDeviceCode(tmp)) {
s = tmp;
} else {
continue;
}
break;
} catch (NotFoundException e) {
// Code could not be encoded yet.
}
...
// end worker after timeout
// show image on window
if (img != null) {
labelForStream.setIcon(new ImageIcon(resizedImg));
}
}
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage() + " - " + e.getStackTrace() + " - " + e.getClass());
}
return s != null ? s.getBytes() : null;
}
#Override
protected void done() {
try {
completeCode = get();
if (completeCode != null) {
String code = new String(completeCode);
if (isOtherCode(code)) {
resultLabel.setText(code);
} else if (isUsernameCode(code)) {
// Cut userName from read code (if previously verified) and set label text.
resultLabel.setText(verify(code, true) ? code.split(":")[0] : null);
}
} else {
resultLabel.setText(null);
}
resultLabel.setVisible(true);
resultLabel.updateUI();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
return;
} catch (Exception e) {
e.printStackTrace();
}
}
As this SwingWorker doesn't have references to any panels, even though the done()-method is done in EDT, I need to somehow notify my mainClass that a code has been read successfully and that it can change the panl now according to the specific code.
Hope this clears things up a bit.
I think that you misunderstood, for what reasons SwingWorker is there, please read SwingWorker
tutorial, where implementations quite guarentee that output from methods:
done()
process()
publish()
setProgress()
should be done on EDT
The simple swing worker answer is to override the done() method. That is executed on the EDT - SwingWorker takes care of that for you.
You can do it yourself, using SwingUtilities.invokeLater.
For the way you pose your question, I suspect you don't have a full grasp of the threading issues and how you switch between threads. So a good review of the tutorial (if you haven't already) may be in order.
This is wrong:
protected byte[] doInBackground() throws Exception {
// ....
if (img != null) {
labelForStream.setIcon(new ImageIcon(resizedImg));
}
// ....
}
As this shows you making critical Swing calls from within the doInBackground method, something that should never be done. Instead consider publishing the Image or the ImageIcon, and setting the JLabel's Icon from the process method override.
As I note in my comment, sometimes its a good idea to use a PropertyChangeListener with your SwingWorker if it decreases code coupling. This is one reason that SwingWorker has its own PropertyChangeSupport and its own state enum.