All my sample programs create a big JFrame which contains all the code for its components, instead of creating separate classes for components and then adding these components to the big JFrame. This big JFrame is run or displayed by instantiating it inside an anonymous Runnable as shown below.
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new StopWatch(); // Or any other JFrame
}
});
}
Class StopWatch implements ActionListener. Would it make any difference if I make it class StopWatch implements ActionListener, Runnable and then pass it as a runnable to the invokeLater method ? Is there is any other way of doing what the above code does ?
Related
I'm developing a cli-based custom web crawler in Java. Since it needs to have a user-friendly graphical interface for showing the result, I should add a swing frame to that involving some trees, labels, tables and so on.
That graphical interface is one of its switches, and must be started just in case user calls it. Thus, I have to start this interface in a new thread so that the application can proceed with other tasks and update components inside of GUI frame if needs.
My GUI class is some thing like:
public class Frame extends JFrame {
......
public static JLabel urlLabel;
......
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
And, I fork it from my main class like this:
.....
if(cmd.gui){
Frame.run();
Frame.urlLabel.setText("New Url");
}
......
Unfortunately, the text of label doesn't change. I already tested repaint(), revalidate() and such other functions like these, but, nothing turned up.
I tested getText() in order to make sure it is possible to access urlLabel from main class, and it worked (I could retrieved text of label).
I was wondering how I can sort out this issue? (Basically, I need to start a swing frame in a different thread and control its components from the main thread)
Thanks in advance.
If you use invokeLater(), your Runnable will be started in the EventThread after the current operation in this thread is finished. If your label is not updated, it might be that your EventThread is busy doing something else - e.g. crawling the web.
You definitely need to make sure that your crawling work is done in another thread (start a new one, don't use the one that runs anyway, since this is most probably the EventThread). Then you might use invokeLater() in this other Thread to change the label in the EventThread.
Hint: You can check if you are in the EventThread by using SwingUtilities.isEventDispatchThread().
Remember that your data/models will be used by different threads and that this might cause problems when the data is changed in your worker thread while your GUI is trying to display it.
Thank you guys for helping.
Finally, I could overcome this obstacle by using SwingUtilities.invokeLater for updating the label's text.
I mention the approach here, since someone else might need it:
The main class:
public class Frame extends JFrame {
......
private static JLabel urlLabel;
......
public JLabel getU(){
return urlLabel;
}
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
The GUI class:
if(cmd.gui){
Frame().run();
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
gui.getU().setText("New Url");
}
});
}
.....
Just a question about this manner:
Since I need to update some labels and tree nodes a couple of times during crawling, wanted to know if starting a new Runnable for each of those would be overload? If yes, how can I manage that?
---UPDATE---
According to the #xander's comment, it is possible to use lambda instead of Runnable. I think lambda doesn't have overload as much as a new object does.
I've seen similar questions answered but could not find an answer to my question. I have a Main Class, which has it's own JFrame. However, I've created a different Class where I've created another JFrame that prompts the user for some data. The Main Class is the main app. The secondary class is supposed to pop up before the main class GUI runs. I've created 2 different packages for each one of the Classes.
So, I'm trying to call an Object of the secondary Class from Main Class but the interface does not appear. I do not get any errors in the code and the App runs as if the Object of secondary Class is not being called at all. I am new to Java and would appreciate some lights on this.
My code is as follows:
Main Class
public class TempConverter extends javax.swing.JFrame {
public TempConverter() {
initComponents();
}
// More code
public static void main(String args[]) {
DemoUserData test = new DemoUserData();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
test.setVisible(true);
new TempConverter().setVisible(true);
}
});
}
Secondary Class
public class DemoUserData extends javax.swing.JPanel {
public DemoUserData() {
initComponents();
}
}
Your JFrame is the main window. Before it is shown at the very early start a splash screen maybe shown, normally a small rectange with a logo.
It however seems, you want some input dialog, like say a login. That cannot be a JPanel, but must be a top-level window: JFrame or JDialog. Or one of the JOptionPane dialogs (asking string input, or whatevever).
Maybe you should make a JFrame for your current JPanel, run that.
.
DemoUserDataFrame test = new DemoUserDataFrame(this);
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
test.setVisible(true);
}
});
public class DemoUserDataFrame extends JFrame {
//private final JFrame tempConverter;
public DemoUserDataFrame(final JFrame tempConverter) {
//this.tempConverter = tempConverter;
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
tempConverter.setVisible(true);
}
});
}
...
}
In the above, closing test, will make the main JFrame visible.
In order to have a better overview, have the classes not refer one to another, you might look into the Model-View-Controller concept. Then there is one global "Controller" class as intermediator for all business logic. It holds the data (Model), and so on.
actually i have called the swing worker from a frame (Suppose) A.. in the swing worker class in do-in-Background method i have certain db queries and i am calling frame B too.. in the done() method however i want to dispose the frame A.. how can i do that..? i cannot write dispose() in frame A class because that results in disposing of frame before the new frame(frame B) is visible... Please help!!
class frameA extends JFrame{
public frameA(){
//done some operations..
SwingWorker worker=new Worker();
worker.execute();
}
public static void main(string[] args){
new frameA();
}
}
and in worker class
class Worker extends SwingWorker<Void, String> {
public Worker() {
super();
}
//Executed on the Event Dispatch Thread after the doInBackground method is finished
#Override
protected void done() {
//want to dispose the frameA here..
}
#Override
protected Void doInBackground() throws Exception {
// some db queries
new frameB().setVisible(true);
// call to frameb
}
The done() method of the SwingWorker is usually overridden to display the final result. Upon
completion of doInBackground() , the SwingWorker automaticlly invokes
done() in the EDT. So put your frame's invisible and visible code in this function.
The doInBackground() is not meant to do any GUI rendering task. You can invoke publish(V) from doInBackground() function which in turn invokes The process(V) method to run inside the EDT and performing GUI rendering task.
So a sample solution would be:
class Worker extends SwingWorker<Void, String> {
JFrame frameA;
public Worker(JFrame frameA) {
this.frameA = frameA;
}
#Override
protected void done() {
frameA.dispose();
new frameB().setVisible(true);
}
//other code
}
Now, create you SwingWorker instance by passing the target frame to it's constructor: new Worker(frame); For your context you probably could make use of this
However, you should not really design your application to be dependent on multiple JFrame. There are reasons for not to use multiple JFrame window. For more, see The Use of Multiple JFrames, Good/Bad Practice?. A general work around with use case where multiple frame would be needed is explained here.
So the program I am making uses 2 threads: One for the GUI and one to do the work.
I want updates from the work thread/class to print out on JTextArea in GUI class.
Everything I tried didn't seem to work. I added lines to print out text on the console right after lines to add text to the JTextArea to make sure it had got to the line but everytime console got text but no changes happened to JTextArea in the GUI.
public static void consoleText(String consoleUpdate){
GUI.console.append(consoleUpdate);
}
I tried this in the work class but nothing happened.
Anyone know how to fix my problem?
Edit:
MAIN.JAVA
public class main {
public static void main(String[] args) {
Thread t1 = new Thread(new GUI());
t1.start();
}
GUI.JAVA
public class GUI extends JFrame implements Runnable{
public static JTextArea console;
private final static String newline = "\n";
public void run(){
GUI go = new GUI();
go.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
go.setSize(350, 340);
go.setVisible(true);
}
public GUI(){
setLayout(new FlowLayout());
console = new JTextArea(ConsoleContents, 15, 30);
add(console);
}
WORK.JAVA
...{
consoleText("\nI want this text on the JText Area");
}
public static void consoleText(String consoleUpdate){
GUI.console.append(consoleUpdate);
}
First, as has been said, your GUI should only run on the Event dispatch thread.
As it is written, your GUI class does two things : it's a frame, and a runnable, and both
are used completely independently. As a matter of fact, calling "run" on a your GUI object creates another, unrelated GUI object. That's probably the reason why you see nothing.
So I suggest making your main the following:
... main(...) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUI gui= new GUI();
gui.setVisible(true); // and other stuff
}
});
}
(I would also suggest getting rid of all "static" fields BTW. It's probably the source
of your problems, along with the weird place of the "run" method).
Now, your "consoleText" method, which I assume you call from another thread, should not
modify the text directly, but call SwingUtilities.invokeLater() to do so :
public void consoleText(final String consoleUpdate){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
console.append(consoleUpdate);
}
});
}
(the "final" declaration is important, as it allows the Runnable to use the consoleUpdate variable).
Let's say I have one thread running (I'm creating many instances of this thread), and inside that I instantiate a JForm. While being inside that JForm, I call another JForm. Think of it as a multiple step registration process. When I'm inside the second form, will the previously created thread still be inside the run()? Or is the new JForm creating a new thread? I want to keep the first thread alive and access a shared resource through out the lifetime of it.
class Form1 extends JForm{
public void jButton1ActionPerformed(..){
///show Form2
}
}
class A extends Thread{
public void run() {
//show Form1
}
}
class Main {
public static void main(String args[]){
new A().start();
new A().start();
new A().start();
}
Thanks.
When you create and run your A thread, you will simply show the Form and continue executing that Thread. Separately, on the single, dedicated Swing Thread (started automatically for you) the users click will be caught and handled, resulting in a call to jButton1ActionPerformed. That code block will execute inside the Swing thread.
Hope that helps. Note that you can name your threads and always use Thread.currentThread().getName() to help you understand further what is going on in your code.
If you want to create and show a Swing component from within a non-EDT thread, you must place the Swing code in a Runnable and queue it on the event thread like so:
class A extends Thread{
public void run() {
//show Form1
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Form1 form1 = new Form1();
form1.setVisible(true);
}
});
}
}
So regardless of how many "A" objects you create, and thus separate new threads you create, all Swing code will be running on the same one thread.