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.
Related
import javax.swing.*;
import java.awt.*;
public class Grid extends JFrame{
public Grid(){
super("Pathfinding Algorithms");
setContentPane(new drawGrid());
setSize(1920,1080);
setExtendedState(JFrame.MAXIMIZED_BOTH);
setUndecorated(true);
setVisible(true);
}
class drawGrid extends JPanel {
public void paintComponent(Graphics g){
g.setColor(Color.BLACK);
g.drawLine(0,50,1920,50);
}
}
public static void main(String[] args){
new Grid();
}
}
For some reason, nothing is being displayed whenever I run this code. I receive no errors and I get no output messages.
How can I fix this?
Follow a tutorial to learn the basics of Swing. Oracle provides one free-of-cost.
There you will find this example code to compare to your code.
In that example code you’ll find the main method makes this call:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
Swing thread
Every Swing app has a thread dedicated to the GUI, the event dispatching thread (EDT) thread mentioned here in the Comments. Drawing, tracking the user's inputs with mouse and keyboard, responding to window dragging/resizing, and all other on-screen work must be performed on that thread dedicated to Swing.
In contrast, you code is running on the main thread.
That invokeLater call seen above is a way to get your GUI creation code to run on the Swing thread. So you could modify your code like this:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Grid() ;
}
});
However, I suggest you study that tutorial and other examples for better ways of how to work with Swing. For example, it is generally best to avoid doing work unnecessarily in constructors as seen in your code snippet. And pay attention to issues listed in Comment by Andrew Thompson.
By the way, Swing is fully supported but is in maintenance-mode.
You may want to consider the alternative, JavaFX (OpenJFX). JavaFX is being actively developed, with a release every six months synchronized to Java releases. Development is led by the Gluon company in cooperation with Oracle Corp. as a sub-project on the OpenJDK project.
Same threading rules apply to JavaFX, where a thread is dedicated to the GUI. Never access or manipulate a GUI widget in either Swing or JavaFX/OpenJFX from another thread.
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.
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.
In many Swing snippets given here as answers, there is a call to SwingUtilities#invokeLater from the main method:
public class MyOneClassUiApp {
private constructUi() {
// Some Ui related Code
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyOneClassUiApp().constructUi();
}
});
}
}
But according to the Threads and Swing article, it is safe to construct UI from main thread:
A few methods are thread-safe: In the Swing API documentation,
thread-safe methods are marked with this text:
This method is thread safe, although most Swing methods are not.
An application's GUI can often be constructed and shown in the main
thread: The following typical code is safe, as long as no components
(Swing or otherwise) have been realized:
public class MyApplication {
public static void main(String[] args) {
JFrame f = new JFrame("Labels");
// Add components to
// the frame here...
f.pack();
f.show();
// Don't do any more GUI work here...
}
}
So, is there a real (thread safety) reason to construct the UI in main through SwingUtilities#invokeLater, or this is just a habit, to remember do it in other cases?
"The Swing single-thread rule: Swing components and models should be created, modified, and queried only from the event-dispatching thread."—Java Concurrency in Practice, also discussed here and here. If you don't follow this rule, then you can't reliably construct, modify or query any component or model that may have assumed that you did follow the rule. A program may appear to work correctly, only to fail mysteriously in a different environment. As violations may be obscure, verify correct usage by using one of the approaches mentioned here.
I think that using SwingUtiltities.invokeLater() is just an easier way to execute some code asynchronously. Sometimes it is required for certain application: for example you can create 2 separate windows simultaneously. Nothing more.
It's safe to create your Swing UI in the main method because how would other components be displayed before you set up your UI? As long as you haven't thrown some stuff on the screen already you'll be fine. In other words, this would be bad:
public class MyApplication
{
public static void main(String[] args)
{
JFrame f = new JFrame("Labels");
// Add components to
// the frame here...
f.pack();
f.show();
// now make another frame:
JFrame f2 = new JFrame("Labels2");
// Add components to the 2nd frame here...
f2.pack();
f2.show();
}
}
If you did the above you'd have JFrame f up and running then you'd be adding Swing UI components off the Event Dispatch Thread (EDT). invokeLater runs the code on the EDT - it won't hurt to use it if you want extra peace of mind.
i just started learning swings. And thought of trying out a simple program, but i can't run it.
import java.awt.*;
import javax.swing.*;
class MyDrawPanel extends JPanel
{
public void paintComponent(Graphics g)
{
g.setColor(Color.orange);
g.fillRect(20,50,100,100);
}
}
I am getting the following error:
Exception in thread "main" java.lang.NoSuchMethodError: main
My Question: Do we need to have a main method in every class we want to run? Can't JVM run any class which doesnt have a main method. Here i don't require a main class i think, cuz this paintComponent method should be called by the system, right?
P.S: I am using plain vanilla cmd to compile and run.
java is rather simple, when you give it a class file it will load it and try execute a program. Java programs are defined to start in the "public static void main(String... args)" method. So a class file missing this function has no valid entry point for a program.
To make java call your paintComponent() method you have to add an instance of your class to a toplevel container like JFrame or for web applications an JApplet. (Applets don't use a main method as they are executed as part of a web page and not a standalone app.)
Example:
import javax.swing.*
public class MyDrawPanel{
public static void main(String... args)
{
JFrame frame = new JFrame(200,200);//A window with 200x200 pixel
MyDrawPanel mdp = new MyDrawPanel();//Panel instance
frame.add(mdp);//Add the panel to the window
frame.setVisible(true);//Display all
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//exit when the window is closed
}
}
As vodkhang said, you need a "main" method. Make sure it looks just like this:
public static void main(String[] args)
{
// your code here.
// this example will use your panel:
// create a new MyDrawPanel
MyDrawPanel panel = new MyDrawPanel();
// create a frame to put it in
JFrame f = new JFrame("Test Frame");
f.getContentPane().add(panel);
// make sure closing the frame ends this application
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// show the frame
f.setSize(100,100);
f.setVisible(true);
}
Yes, every Java program that you want to run needs a main method with exactly this signature:
public static void main(String[] args)
You can run java code from within other systems (like web servers and etc without a "main") but to simply run it, the main is the entry point. Put it where-ever you want to start a program running.
When running, make sure you get the class name right to help it find your main method. In your case, if you are running java by hand in the same directory as your MyDrawPanel.class file you would do this:
java -cp . MyDrawPanel
If you are running from inside a developer tool, then it will provide a way to run the class you are looking at.
Do we need to have a main method in
every class we want to run?
You need a class with a main method to start a JVM.
Can't JVM run any class which doesnt have a main method.
Not initially.
Here i don't require a main class i think, cuz this paintComponent method should be called by the system, right?
Wrong. It's true that the paintComponent() method will eventually be called by "the system", specifically the Swing Event Dispatch Thread. But that needs to be started first, which happens implicitly when you create a window and make it visible. And that in turn can only happen in a main method.
You need a main method in the class that you want to run the program for. It is mandatory. How can the JVM know which method they should call to start if you have multiple methods. They can guess but most of the time, the guess may go wrong. So, provide a simple main method will help