How do I run Swing UI on two threads - java

I have a scenario where I need my swing UI to run on two different threads. I have a laptop where I will my application will run. There is a Button on clicking which an presentation should start at the other Screen that is attached to my laptop.
Now I have made a class presentation which is extending SwingWorker and reads the images from a folder and displays it on screen.
class Presenatation extends SwingWorker<Integer, Integer> {
#Override
protected Integer doInBackground() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
start(outputFolder, screenVO);/*Creates a JFrame to be displayed
on new screen and sets a JPanel to it. Reads the file images sets it into
JLabels every 2 seconds and updates it to Japnel*/
}
});
return null;
}
Inside my start method I have the code to read images and show them on the UI
What I feel is this approach is wrong since my SwingWorker shouldn't be calling invokeLater in doInBackground()
From what little knowledge I have, it should be something like this:
#Override
protected Void doInBackground() throws Exception
{
return null;
}
#Override
protected void process(List<Integer> chunks
{
}
I am not able to decide which part should be placed where ?
I have the following things to do :
Start a new Frame to be displayed on a new screen
Load Images into the frame every 2 seconds reading the image from a folder
Extending Presentation class to SwingWorker, is this approach Correct ? Because externally I have an Executor object in whose exec() I am passing the object of Presentation
Please help me !

Indeed, you should not call invokeLater from doInbackground, nor spawning a new thread. Note that SwingWorker is a Runnable, so it can be submitted to an Executor.
But you have to call the publish method every time you have a new image ready to display. Behind the scene the SwingWorker will invoke its process method on the Event Dispatch thread.
So, you have to override process to do the actual widgets update.

Take this answer with a grain of salt because many will disagree and say this is a terrible practice. However, no one, so far as I've seen, can precisely say WHY this is bad. So, until then, I maintain my opinion:
I think it is perfectly fine to call invokeLater() and invokeAndWait() within doInBackground().
There, I said it.
I asked basically the same question last month and I since then I've been experimenting with replacing process() and publish() with invokeLater(). In my experiments, I've not run into any threading or synchronization issues. And the approach is a lot easier than publish() and process().
Why do I say this?
First, by calling invokeLater, you are throwing whatever code you run to the EDT. So, there's no logical reason why that code should break.
Second, process() and publish() were written with very, very specific goals in mind (i.e., to provide a way for you to send tabular results so you can update your JTable or JList in real-time). I've never, not once, used process() or publish() in the way it was obviously written to be used (and I work with a lot of tabular data). In order to get publish() and process() to do what I want (90% of the time update an indeterminate JProgressBar and 9% of the time update a determinate JProgressBar), I usually end up writing some hackish way of publishing and processing my data. It's confusing, difficult to read, and difficult to manage change requests.
However, invokeLater() from within doInBackground() is easy to read and, again, I haven't heard anyone say why it would be unsafe to do. And, as in the case presented in the question I linked above, if you need to pause execution for user feedback, I don't see there being any other option than to invokeAndWait().
My honest opinion is that publish() and process() weren't very well written. SwingWorker is amazing once you learn the nuances, but those methods do not cover the needs of most people using SwingWorker and their need to update the user about progress. At least, not in my experience.
EDIT: Specifically regarding your situation, what I would do is:
Create a constructor for your SwingWorker and initialize a dialog there, but make it a class member of the SwingWorker so you can update it. For example:
class Task extends SwingWorker {
IndeterminateLoadingDialog ild;
public Task _Task() {
JDialog dialog = new JDialog(// parent frame);
ild = new IndeterminateLoadingDialog(this);
dialog.add(ild);
dialog.pack();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setLocationRelativeTo(dialog.getParent());
dialog.setVisible(true);
}
My IndeterminateLoadingDialog class is a JPanel that just has a JProgressBar and a JLabel. I can change the text through a method that interfaces with the JLabel.
Anyways, I always initialize whatever GUI components I need for the SwingWorker in the constructor. Just make sure new Task().execute() is called from the EDT.
If I need to update my IndeterminateLoadingDialog, I will just use invokeLater() to change the text directly.
Further, I close the dialog in the done() method.
Hope this helps you.

Some background
Swing (and all other GUI frameworks) are not multi-threaded (because of a long list of valid reasons).
Therefore a good rule of thumb is to create and operate any GUI components from the EDT thread only. There are edge cases (such as creating a new JFrame from a thread) which might work, but basically it is a bad idea to do anything with the GUI from a different thread than EDT.
You not only should but have to call invokeLater or invokeAndWait from any non-EDT thread. This is the only way to ensure that your thread is getting suspended, and the submitted Runnable is getting executed from the context of the EDT thread.
Some solutions
Basically, you definitely not need two EDT threads. In fact it is a bad idea, you have one keyboard, and one mouse creating one set of UI events, therefore two EDT threads make no use.
In fact, you don't even need a swingworker to switch pictures on the second display. SwingWorkers are great for off-loading long-running non-gui operations from the EDT thread (i.e. executing a database operation which takes 20 seconds to complete). In your case, loading a new picture is not a rocket science :)
Do the followings:
discard all swingworkers and other stuff
when you press the button, open the new presentation window
in the presentation window create a timer which fires in every 2 seconds
when the timer fires, load the new picture and throw that to the window
It is quite simple in real. Consider this example:
package a;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class PresentationStart extends JFrame {
public static void main(final String[] args) {
new PresentationStart();
}
public PresentationStart() {
super("Start here");
final JButton button=new JButton("Start");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
new PresentationView();
}
});
add(button);
pack();
setVisible(true);
}
}
And the viewer:
package a;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class PresentationView extends JFrame {
public PresentationView() {
super("View");
final JLabel picture=new JLabel("Picture comes here");
add(picture);
pack();
setVisible(true);
final List<String> pictures=new ArrayList<String>();
pictures.add("http://storage3d.com/storage/2008.10/49d7c6aeed760176755a7570b55db587.jpg");
pictures.add("http://storage3d.com/storage/2008.10/49d7c6aeed760176755a7570b55db587.jpg");
pictures.add("http://www.alragdkw.com/wp-content/uploads/2016/01/fruit-Banans.jpg");
final Timer timer=new Timer(2000,new ActionListener() {
int index=0;
#Override
public void actionPerformed(final ActionEvent e) {
// Load a new picture
try {
picture.setIcon(new ImageIcon(ImageIO.read(new URL(pictures.get(index)))));
} catch (final Exception ex) {
ex.printStackTrace();
}
index++;
if (index>=pictures.size()) {
index=0;
}
}
});
timer.start();
}
}

Make your presentation as a standalone java program, and start it with Runtime.exec(). It will create a separate window.

Related

Thread.sleep() not working as it should [duplicate]

public class TestFrame extends JFrame
{
public TestFrame()
{
setBounds(10, 10, 500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(3);
}
public static void main(String[] args) throws InterruptedException
{
TestFrame tf = new TestFrame();
tf.add(new JButton("test1"));
tf.setVisible(true);
Thread.sleep(2000);
tf.getContentPane().removeAll();
tf.add(new JButton("test2"));
System.out.print("Show test");
}
}
I want the program show JButton("test2") after 2 seconds.
Then I add thread.sleep(2000) after test1.
But I don't know why the program stops at showing the test1 JButton,
not showing test2 JButton and the "show test" message can sucess print out
Short answer, don't.
Swing is a single threaded framework, this means that any thing that blocks the Event Dispatching Thread will prevent it from updating the UI or processing any new events (making your UI look like it's hung, cause it has).
Sure, you could use a Thread, but Swing is also not thread safe. This means that ALL modifications to the UI MUST be made from within the context of the Event Dispatching Thread. While there are ways to overcome this, the easiest way is to simply use a Swing Timer.
Take a closer look at How to use Swing Timers and Concurrency in Swing for more details
You should also take a look at Initial Threads.
When updating the UI, it may be required to call revaldiate and repaint after you have added the new components to force the UI to update to re-layout it's contents.

What is the difference and efficiency between main() and run()?

So I'm very new to the JSwing world, having not covered much in my AP Computer Science class I finished last year. So, I am familiar with inheritance and all the core parts of the Java language, but I can't see the difference between the two methods main() and run().
My code here works just fine for right now:
Main Class:
import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
private JFrame frame;
public Main() {
frame = new JFrame("SoundCombine"); //Create frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Close the frame when |x| button is pressed
//Add components to the frame
JButton button = new JButton();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Run Class:
public class Run {
public static void main(String args[]){
new Main();
}
}
However, I've also seen this method of starting JSwing applications:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
What is the big difference between the two? What sort of problems would I encounter down the road by using either one of the two. Although there are many different ways to write the same program, I want to know the conventional way of going about this.
Thanks!
You're asking what's the difference between an orange and a buffalo -- these are two completely different things.
The main method is the entry point of all Java programs, even those without main methods such as applets where there actually is a main method, but it's hidden from view.
The run method you speak of is a method that needs to be part of any class that implements a Runnable interface. This is not the entry point for programs to start.
Your example above, that is to feed a Runnable into the SwingUtilities.invokeLater(...) method is a way to guarantee that the code within the Runnable's run method is called on the Swing event thread, something that you want to do when starting Swing (not JSwing) applications for thread safety purposes, since if you don't do this, there are risks of errors (actually exceptions) being thrown.
Swing applications still need a main method regardless of whether you queue them on the Swing event thread or not. It's not "either use main or run", since again, they are totally different animals.
Just because some code "works for now" is no guarantee that it will work later. Threading exceptions are notorious for occurring intermittently and when least desired.

How can I safely pass user GUI input data to a SwingWorker and work with that data?

I'm confused about how to properly and safely pass data from GUI elements on the event-dispatch-thread (EDT) to a SwingWorker that needs to interact with that data. I've followed the many examples on stackoverflow and around the web; no matter which way I try to write, it seems like I'm breaking the oath to never touch GUI elements from a different thread.
I'll try to illustrate my confusion with pseudocode.
I've got a JFrame with some swing elements to take input from user. User clicks a JButton, the JButton disables so user can't click it twice, and their input is passed to the SwingWorker in a background thread:
import javax.swing.*;
import java.awt.event.*;
class GUI implements Runnable{
JFrame jFrame;
JTextField userInputTextField;
JButton beginLongTaskButton;
GUI(){
//initialize the GUI elements, add them to JFrame, JPanel, etc.
this.beginLongTaskButton = new JButton("Begin Task");
this.beginLongTaskButton.addActionListener(a -> beginLongTask());//lambda sure is nice
}
void beginLongTask(){
this.beginLongTaskButton.setEnabled(false);
this.beginLongTaskButton.setText("Doing your long task...");
LongTaskWorker longTaskWorker = new LongTaskWorker(this.userInputTextField);//isn't this bad?
longTask.execute();
}
#Override
public void run(){
this.jFrame.setVisible(true);//blah blah
}
}
At this moment, the EDT should be just be sitting pretty, except for one problem: the SwingWorker was given some of the GUI elements via its constructor:
import javax.swing.*;
class LongTaskWorker extends SwingWorker<Void, Void>{
JTextField userInputTextField;
public LongTaskWorker(final JTextField userInputTextField){
this.userInputTextField = userInputTextField;//the text field on EDT I'm not supposed to touch
}
#Override
protected Void doInBackground() throws Exception{
//read lots of stuff from JTextField in this Thread (the wrong thread)?
//write? lots of stuff to JTextField in this Thread (the wrong thread)?
//do lots of other stuff to JTextField?
return null;
}
}
I've seen many people do something similar to this. I think even the JavaDoc SwingWorker example does it this way. But doesn't it mean I'm messing with objects on a different thread when I'm not supposed to? And does reading (not changing) the value of GUI components still violate the cross-threading rule? If my SwingWorker must frequently read data from GUI elements, won't that slow down the EDT?
Maybe the right thing to do is extract the data I need from the GUI elements within the EDT, then pass that extracted data to the SwingWorker? As opposed to directly passing the GUI element to the SwingWorker.
I think what you actually might want is a Model for your View. That is: The View as name suggests is your GUI and its elements - the JTextFields. Your Model would be a backing Map of the 81 Values. Now if you want to compute something on it, you'd pass the backing Map to that method (SwingWorker). If something changes the backing Model, an Event should trigger the GUI to refresh (load values from Model). If the user changes a value in the GUI, that change should be made through a controller. It in turn changes the Model and initiates the Change-Event.
This way you also can avoid feedback-loops.
You can read about this concept looking for "MVC" or "Model View Controller".

JInternalFrame won't appear

I have a java class called GameUpdater which extends JInternalFrame.
It used to extend JFrame when I ran the class as a program by itself, but I changed it to JInternalFrame to become part of a larger application - now accessible from a menu button.
The function being called when I press this menu button is as follows:
private void update(){
GameUpdater gu = new GameUpdater();
desktop.add(gu); //add to JDesktopPane
gu.setSize(400, 300);
gu.setVisible(true);
gu.readMatches();//this function takes ages
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
gu.setMatch("DONE"); //now it shows the frame, way too late
}
The method gu.readMatches() takes a long time to execute, so it periodically updates content in the JInternalFrame to display its progress. However the frame is not being shown until this update function is complete!
It's like setVisible(true) is waiting until the end of the function...
It worked absolutely fine when it was a JFrame. Is there any weird property of a JInternalFrame that would cause this?
Cheers
It sounds like you're executing a time consuming process inside the Event Dispatching Thread (EDT), this will prevent the event queue from process (amongst other things) repaint requests.
This will cause you program to appear as if it has "hung".
You need to off load this task to a background thread.
Have a read through Concurrency in Swing, especially the section on Worker Threads and SwingWorker
The problem is that you are blocking your EDT this can be taken care of by simply creating a new Thread/Runnable thar calls gu.readMatches(); the method:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
gu.readMatches(); //gu will have to be declared `final`
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
}
});
ofcourse though you might want to implement a JProgressBar so the user can keep track of of how far the reading is.

Lag before JFrame event handlers are added?

I'm working on a simple Java swing project. This is the code of the main class (name changed):
public class MainProg
{
private static MainProg program;
//mainWin is a JFrame
private MainWindow mainWin;
//Event handler class which extends MouseAdapter
private TrayManager trayMgr;
public static void main(String[] args)
{
program = new MainProg();
}
public MainProg()
{
mainWin = new MainWindow();
trayMgr = new TrayManager();
mainWin.startBtn.addMouseListener(trayMgr);
mainWin.setVisible(true);
}
}
As is clear, when the program starts, in main() it creates a new instance of the MainProg class, which then calls the constructor. In the constructor, it creates a new instance of the JFrame mainWin. It then attaches an event handler to a button on mainWin.
In the event handler class trayMgr, the only method is mouseClicked() which does nothing
except a System.out.println('Clicked');
The issue is, when I run this program in Netbeans, the JFrame is shown right away, but I seem to have to click the button 2-3 times before the message is printed in the console.
Is this just something specific to Netbeans, or do I have to change something to make the event handler be set before the window is made visible?
Your threading issue is not likely one that is causing your current problem, but there's the theoretic potential for problems, and I've seen some real problems associated with some of the more touchy look and feels. Quite simply you should queue your code that starts your GUI onto the Swing event thread. You do this by doing:
public void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(
public void run() {
program = new MainProg();
}
));
}
Someone else recommended using invokeAndWait(...) instead of invokeLater(...) but this can be risky especially if you inadvertently make this call from within the Swing event thread itself. For your situation you're better off using invokeLater(...).
But again, I think the main problem with the code you have shown was inappropriate use of MouseListener where an ActionListener should have been used. Learning to code any GUI library can be quite tricky, and for that reason, you can't assume anything. Check out the tutorials and learn from the experts. Also if you are considering coding Swing for the long haul, consider ditching the NetBean's code-generation utilities and learn first to code Swing by hand. You won't regret doing this.
Since you asked, the code I posted here is a Java SSCCE on a different topic. invokeLater is a way of running computations on the EDT. (There is also invokeAndWait, which would work fine here, but under some other conditions can cause a deadlock.)
In fact this example is perhaps a bit over-conservative. Some references say you can run Swing from the main thread the call to show() or setVisible(). However I have a program that misbehaves under Java 7 when I try that.

Categories