I'm trying to update the main gui in a java swing application, so there is a runnable thread that keeps the main gui visible, but the problem is it is called in main, and main is a static function. I would like to say Element.SetTtext. But all calls that I want to update are not static. How can I update the lables,..etc in the Main GUI then?
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
new AGC().setVisible(true);
// code to update labels here
}
});
}
What I understood from your question is that you think static means non-changeable. This is not true with Java. In Java objects and components that never change are characterized as final.
Keep your main simple and small and make your loops and changes in doThings();
Here is a Timer in order to update the text of the JLabel:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Foo extends JFrame {
public Foo() {
jLabel1 = new JLabel("label 1");
jPanel1 = new JPanel();
jPanel1.add(jLabel1);
add(jPanel1);
pack();
// code to update whatever you like here
doThings();
}
private void doThings() {
// code to update whatever you like here
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
jLabel1.setText("foo " + (j++));
}
};
Timer timer = new Timer(500, actionListener);
timer.start();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Foo().setVisible(true);
}
});
}
private JLabel jLabel1;
private JPanel jPanel1;
private int j = 0;
}
little more clarity is required , when do u want to update the labels ? is it based on an event ?
You can always keep a global variable of the component you want to update and access it from the event handlers.
Can you please update your question with the code , so that it gives a better clarity ?
Related
I am currently practicing OOP with Java.
I have created a GUI project via WindowBuilder with Eclipse IDE and below is the result.
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Example window = new Example();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Example() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JProgressBar progressBar = new JProgressBar();
frame.getContentPane().add(progressBar, BorderLayout.CENTER);
}
What I am trying to do is to connect the JProgressBar to another class that has the actual task, to show the progress.
For example, if the other class contains the following code:
int i = 0;
while(i <= 100) {
progressBar.setValue(i);
i++;
}
how should I change the progressBar.setValue(i); part?
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay particular attention to the Concurrency in Swing section.
Here's the simplest working example I could create. As you can see in the picture, I caught the JProgressBar in the middle.
Each time you press the button, the progress bar will count from 0 to 100, one unit every 100 milliseconds.
In order to access the progress bar, you have to make it a class field or variable. You can then access the class field with a setter. Getters and setters are a basic Java concept. You can see another example of a plain Java getter/setter class in my JProgressBarModel class.
I used a Swing Timer to add a delay to the updating of the progress bar so you can see the bar update and simulate an actual long-running task. The actual work takes place in the WorkListener class. Because the code is inside an ActionListener, the Swing update of the progress bar takes place on the Event Dispatch Thread.
Here's the complete runnable code. I made all the additional classes inner classes so I could post the code as one block.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class JProgressBarExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JProgressBarExample());
}
private JProgressBar progressBar;
private final JProgressBarModel model;
public JProgressBarExample() {
this.model = new JProgressBarModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Progress Bar Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
progressBar = new JProgressBar();
panel.add(progressBar);
JButton button = new JButton("Start Process");
button.addActionListener(event -> {
model.setIndex(0);
setValue();
Timer timer = new Timer(100, new WorkListener(this, model));
timer.start();
});
panel.add(button);
return panel;
}
public void setValue() {
progressBar.setValue(model.getIndex());
}
public class WorkListener implements ActionListener {
private final JProgressBarExample view;
private final JProgressBarModel model;
public WorkListener(JProgressBarExample view, JProgressBarModel model) {
this.view = view;
this.model = model;
}
#Override
public void actionPerformed(ActionEvent event) {
Timer timer = (Timer) event.getSource();
int index = model.getIndex() + 1;
model.setIndex(index);
view.setValue();
if (index >= 100) {
timer.stop();
}
}
}
public class JProgressBarModel {
private int index;
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
}
One option is to do it similar to the frame part. You Example class has a field variable that could be directly accessible to your other code.
A better way would be to have a private field for the JProgressBar and a getProgressBar() method.
But currently you are using a method variable that is forgotten when initialize() returns.
my question is: how do I get the object of my CustomPanel, so that I am able to access its fields (because in my real programm I have some more fields in there) and also am able to delete it from my ArrayList?
I don't know how I have to implement an ActionListener in the Class Window, to somehow get the Object in my Arraylist, which containes the button that got pressed.
Also I am wondering if I am somehow able to implement an ActionListener in the Class CustomPanel which can influence the behaviour of the Object which is an instance of my Class Window.
I have kind of the following code:
public class Window extends JFrame{
ArrayList<CustomPanel> aLCustomPanel = new ArrayList();
JPanel jp = new JPanel();
public Window() {
for(int i=0;i<5;i++){
aLCustomPanel.add(new CustomPanel());
//here I could put the code from the 1 edit - see below
jp.add(aLCustomPanel.get(i));
}
this.add(jp);
}
public static void main(String args[]){
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Window().setVisible(true);
}
});
}
}
class CustomPanel extends JPanel {
private JButton button;
public CustomPanel(){
button = new JButton("button");
this.add(button);
}
public JButton getButton(){
return this.button;
}
}
my Code is much longer and weirder, so I tried to extract the (for this question) importing things.
Thanks for any help in advance!
edit:
for example: I would like to delete the object from the ArrayList, of which the button got pressed.
//imagine this comment in above code
aLCustomPanel.get(aLCustomPanel.size()-1).getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button_IwantToDeleteYou(e); //here I want to remove the panel, containing the button that got pressed from the above ArrayList, which is located in Class Window
}
});
edit2:
added a missing bracket and fixed some mistakes, code should be ok now.
Your code contained a few "gaps", i.e. missing code, which I filled in, as follows:
Added calls to [JFrame] methods setDefaultCloseOperation() and pack() and setLocationByPlatform(). I suggest you refer to the javadoc for those methods in order to understand what they do.
I set a layout manager for jp class member variable in your Window class.
Yes, you need to register an ActionListener with the JButton in class CustomPanel and that listener should reside in your Window class - the one that extends JFrame.
Here is my rewrite of your code. Note that I changed the name of class Window to CusPanel so as to distinguish between your class and java.awt.Window class. Not that it makes a difference, I just prefer not to use names of classes from the JDK.
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class CusPanel extends JFrame implements ActionListener {
private static final int COUNT = 5;
private ArrayList<CustomPanel> aLCustomPanel = new ArrayList<>();
private JPanel jp = new JPanel(new GridLayout(0, COUNT));
public CusPanel() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
for (int i = 0; i < COUNT; i++) {
aLCustomPanel.add(new CustomPanel(this));
// here I could put the code from the 1 edit - see below
jp.add(aLCustomPanel.get(i));
}
this.add(jp);
pack();
setLocationByPlatform(true);
}
public void actionPerformed(ActionEvent actionEvent) {
Object source = actionEvent.getSource();
if (source instanceof JButton) {
JButton button = (JButton) source;
Container parent = button.getParent();
jp.remove(parent);
jp.invalidate();
jp.repaint();
pack();
// aLCustomPanel.remove(parent); <- optional
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new CusPanel().setVisible(true);
}
});
}
}
class CustomPanel extends JPanel {
private JButton button;
public CustomPanel(ActionListener parent) {
button = new JButton("button");
button.addActionListener(parent);
this.add(button);
}
public JButton getButton() {
return this.button;
}
}
Note that after removing a CustomPanel, the GUI components need to be laid out again and the JFrame should also be resized accordingly. Hence in the actionPerformed() method, I call invalidate(), then repaint() and then pack(). I also think that if you remove a CustomPanel from the GUI, you should also remove it from the ArrayList, but hey, I still don't understand why you want to do this although I obviously don't know the whole story behind you wanting to do this in the first place.
Of-course, since each button (and each CustomPanel) looks exactly the same, you can't really know which button was removed. Again, I assume you see the big picture whereas I don't.
I have written a Java XML Parser as an Applet. It is looking and functioning well enough in this form.
My Question, Is if I want to run this without a browser, how Would I properly wrap it to run as an executable?
GUI.java
--------------
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GUI extends JPanel implements ActionListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
private Parser xmlEditor;
private String startTimeValue;
private String endTimeValue;
public GUI(){
init();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new GUI();
}
});
}
public void init() {
this.setXmlEditor(new Parser("C:\\Users\\Administrator\\workspace\\XMLParser\\src\\test.xml"));
add(new Label("Start Time"));
startTimeValue = xmlEditor.getStartTimeValue();
endTimeValue = xmlEditor.getEndTimeValue();
startTime = new TextField(startTimeValue);
add(new Label("End Time"));
endTime = new TextField(endTimeValue);
save = new Button("save");
save.addActionListener(this);
add(startTime);
add(endTime);
add(save);
}
public void actionPerformed(ActionEvent e)
{
System.out.println(endTime.getText());
xmlEditor.updateStartTimeValue(startTime.getText());
xmlEditor.updateEndTimeValue(endTime.getText());
System.out.println(e);
System.exit(0);
}
public Parser getXmlEditor() {
return xmlEditor;
}
public void setXmlEditor(Parser xmlEditor) {
this.xmlEditor = xmlEditor;
}
TextField startTime, endTime;
Button save;
}
While trying things with Swing and JFRame etc, I am not getting properly layout, or am opening multiple windows. Can anyone provide assistance? The second Panel Keeps replacing the First. Id like to really try to learn how to place multiple components inside an executable jar is the goal.
SwingPaintDemo.java
import java.awt.Label;
import java.awt.TextField;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
public class SwingPaintDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
Parser myParser = new Parser("C:\\Users\\Administrator\\workspace\\XMLParser\\src\\test.xml");
JPanel top = new JPanel();
top.add(new Label("Start Time"));
TextField startTimeField = new TextField(myParser.getStartTimeValue());
top.add(startTimeField);
f.getContentPane().add(top);
JPanel bottom = new JPanel();
bottom.add(new Label("End Time"));
TextField endTimeField = new TextField(myParser.getEndTimeValue());
bottom.add(endTimeField);
f.getContentPane().add(bottom);
f.pack();
}
}
JFrame uses a BorderLayout by default, where as a JPanel uses a FlowLayout
Instead of rebuilding the UI in the JFrame, simply add an instance of GUI to it, since you've already defined the functionality in a JPanel, this makes it easily reusable.
public class SwingPaintDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new GUI());
f.pack();
f.setVisible(true);
}
}
FYI: You should never reference src in any path element, src won't exist once the program is built and packaged. This is also doubly concerning for applets, as applets run in a tight security model, which prevents them from accessing the file system by default.
Instead, you should be using Class#getResource or Class#getResourceAsStream, depending on your needs.
this.setXmlEditor(new Parser(getClass().getResource("/test.xml")));
for example. You may need to change your Parser to accept either a URL and/or InputStream as well
What is the correct way of disposing a frame which is created inside a Runnable object?
The code below returns a null pointer exception when the endDialog is called before the LoadingRunnable has completed its constructor.
How can the endDialog be executed after the constructor has finished?
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LoadingRunnable implements Runnable
{
private JFrame jFrame;
#Override
public void run()
{
jFrame = new JFrame("Window");
JPanel jPanel = new JPanel();
JLabel label = new JLabel("Loading...");
jPanel.add(label);
jFrame.setContentPane(jPanel);
jFrame.pack();
jFrame.setVisible(true);
}
public void endDialog()
{
jFrame.setVisible(false);
jFrame.dispose();
}
public static void main(String args[])
{
LoadingRunnable l = new LoadingRunnable();
SwingUtilities.invokeLater(l);
//work done here
l.endDialog();
}
};
You have a concurrency problem here because SwingUtilities.invokeLater() schedules your runnable class execution in the Event Dispatch Thread asynchronously while your main thread's flow still running, causing a NPE.
The correct way to dispose a frame is through events, just as Swing is designed to be used. For instance by clicking the "X" (close) button or by dispatching a WindowEvent:
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
You may want to take a look to this question too: Optional way to close a dialog window
In addition
If you just want to show something during your application start up, then you can use SplashScreen API instead of JFrame. See How to Create a Splash Screen for further details.
Based on your previous question and this new one, I'd suggest you read the whole Concurrency in Swing tutorial to understand about common concurrency problems in Swing and how to deal with them.
Ok found how:
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Loading
{
private JFrame jFrame;
public void startDialog()
{
jFrame = new JFrame("Window");
JPanel jPanel = new JPanel();
JLabel label = new JLabel("Loading...");
jPanel.add(label);
jFrame.setContentPane(jPanel);
jFrame.pack();
jFrame.setVisible(true);
}
public void endDialog()
{
jFrame.setVisible(false);
jFrame.dispose();
}
public static void main(String args[])
{
final Loading l = new Loading();
for (int i = 0; i < 200; i++)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
l.startDialog();
}
});
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
l.endDialog();
}
});
}
}
};
Good day,
I've read a few other stack overflow postings and other tutorials, but I can't get my GUI to update correctly after a button starts a long process. I've attached the full code of the problem that I am having. Notice if you run the code, the JList gets updated all at once at the end instead of every iteration of the for loop.
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class theframe extends JFrame implements ActionListener
{
private JList list;
private DefaultListModel listmodel;
private JButton start;
public theframe()
{
listmodel = new DefaultListModel();
list = new JList(listmodel);
start = new JButton("Start");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500,500);
setVisible(true);
list.setPreferredSize(new Dimension(200,200));
start.addActionListener(this);
JPanel p = new JPanel();
p.add(start);
p.add(list);
this.add(p);
}
public static void main(String[] args)
{
theframe frame = new theframe();
}
#Override
public void actionPerformed(ActionEvent arg0)
{
if(arg0.getSource() == start)
{
for(int i=0;i<10;i++)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
// the JList should update one by one
listmodel.addElement("Start pushed ");
}
});
try
{
//This thread sleep simulates a long job
Thread.sleep(300);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
Any help would be greatly appreciated.
Your problem here is the fact that you are calling the invokeLater method already from the EDT (Event Dispatching Thread).
The method actionPerformed is called from the EDT so what happens is that the sleep call just stops the EDT itself: you can imagine that this is not how it should work, no EDT running means no GUI updates.
Since it's a time consuming task you should implement it in a Thread/Runnable so that you can execute it in parallel and then call the invokeLater from this other thread.
Something like:
class LongProcess extends Thread {
public void run() {
for (int i = 0; i < 10; ++i) {
SwingUtilities.invokeLater(...);
Thread.sleep(300);
}
}
}
void actionPerformed(ActionEvent e) {
LongProcess process = new LongProcess();
process.start();
}
The actionPerformed method will be called on the Event Dispatch Thread. Calling Thread.sleep on the EDT stops it from updating your GUI. Since your GUI cannot update, your JList will not repaint itself when items are added to it until after your loop exits.
You should probably be using a SwingWorker. (SwingWorker tutorial.)