I have 2 classes .java
The main :
public class Controller extends javax.swing.JFrame
{
public static void updateProgressBar(int i) {
jProgressBar1.setValue(i);
jProgressBar1.repaint();
}
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
Controller app = new Controller();
app.setVisible(true);
app.setResizable(false);
}
});
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
ChildModel model = new ChildModel();
Thread t1 = new Thread(model);
t1.start();
}
private javax.swing.JProgressBar jProgressBar1; //Initialized with Netbeans builder
}
My ChildModel (ChildModel.java) computes some code (that takes around 10-20 sec) and I want to show the progress on the father class (Controller.java).
Here is my ChildModel :
public class ChildModel implements Runnable
{
public ChildModel(){ /* Something */ }
public void complexMath()
{
//Lots of logic here
Controller.updateProgression(purcent);
}
#Override
public void run() {
complexMath();
}
}
The problem is obviously my static void updateProgressBar that cannot modify a non-static variable. How can I accomplish this ?
The jProgressBar1 variable is an instance variable, so you can't access it from a static method. And the method shouldn't be static: you want to update the progress in the controller, and not in all the Controller instances.
Pass a reference to the controller to the ChildModel, and use this reference from the ChildModel in order to update the progress bar. Also remember that all Swing interactions must be done in the EDT, and not in a background thread. SO the code should look like this:
public class Controller extends javax.swing.JFrame
{
public void updateProgressBar(int i) {
jProgressBar1.setValue(i);
// no need for repaint. The progress bar knows it must be repainted
// when its value changes
}
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
Controller app = new Controller();
app.setVisible(true);
app.setResizable(false);
}
});
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
ChildModel model = new ChildModel(this);
Thread t1 = new Thread(model);
t1.start();
}
private javax.swing.JProgressBar jProgressBar1; //Initialized with Netbeans builder
}
public class ChildModel implements Runnable
{
private Controller controller;
public ChildModel(Controller controller){
this.controller = controller;
}
public void complexMath()
{
//Lots of logic here
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
controller.updateProgression(percent);
}
});
}
#Override
public void run() {
complexMath();
}
}
Swing has its own concurrency mechanisms to deal with updating components. Here you could use
a Swing Timer and update the JProgressBar. Rather than have ChildModel implement Runnable, you could use a Timer as a class member variable and pass in your instance jProgressBar1, enabling you to call setValue when required.
Related
I created two "jFrame form" with netbeans. I can open the both same time. This codes run but no effect. I try to setSelect a checkbox in form2 with a button in form1. Somehow, I can't effect another thread. Could you please fix it for me. Thanks. (sorry my bad English, I am learning English, too)
This is form1.java (I removed some automatic codes)
package test;
import javax.swing.SwingUtilities;
public class form1 extends javax.swing.JFrame {
public form1() {
initComponents();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
new form2().setVisible(true);
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
final form2 click = new form2();
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
click.jCheckBox1.setSelected(true);
}
}
);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new form1().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
// End of variables declaration
}
This is form2.java (I removed some automatic codes)
package test;
public class form2 extends javax.swing.JFrame {
public form2() {
initComponents();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new form2().setVisible(true);
}
});
}
// Variables declaration - do not modify
public javax.swing.JCheckBox jCheckBox1;
// End of variables declaration
}
In your jButton2ActionPerformed method, you're creating a new JFrame which is never shown. You're not manipulating the JFrame you displayed in jButton1ActionPerformed.
Try this:
package test;
import javax.swing.SwingUtilities;
public class form1 extends javax.swing.JFrame {
private form2 click = null; // this will hold the second JFrame (the one with the checkbox)
public form1() {
initComponents();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
if (click == null) { // only create the second JFrame once
click = new form2(); // this stores the second JFrame with the name "click"
click.setVisible(true);
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
if (click != null) { // don't do anything if the second JFrame isn't displayed yet
click.jCheckBox1.setSelected(true);
}
}
}
);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new form1().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
// End of variables declaration
}
BTW, it is bad form and very confusing for class names to start with a lower case letter. Rename form1 to Form1 and form2 to Form2.
I managed to send an update to my observer (textarea)
when I logged textarea.getText() the result is my updated message.
but the GUI is not updating (still blank). What is the problem?
my observer
public void update(Observable o, Object data){
jTextArea2.setText((String)data);
}
my observable
public class Login extends javax.swing.JFrame
{
private static final long serialVersionUID = 2736805471773039923L;
final ObjectObservable observable;
public Login()
{
observable = new ObjectObservable();
JFrame calender = new Kalender();
observable.addObserver((Observer) calender);
initComponents();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)
{
observable.changeData(jTextField1.getText());
}
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Login().setVisible(true);
}
});
}
}
i'm doing a class that does a sort of "file explorer",
in the constructor i create the frame ,panel ecc.. but than i want to say to the main program that calls this class that the user has finish the selection, i know i can call a static method that is in the main from this class,but i want to make a action listener because i want to use this class for different programs
For Example if FileEx is my class:
public class FileEx()
{
public FileEx()
{
//program that do something
if(done == true)
//here i want to call the action
}
public void addActionListener(ActionListener ac) //i don't know if it's correct
//but i want something like this
{
}
}
public static void main(String[] args)
{
FileEx fileex = new FileEx();
fileex.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e)
{
//when done is true i want this block of code to be called
}
});
}
ActionListeners will only work when added to components that allow them to be added and that notify listeners with them such as JButtons, JMenuItems, JComboBoxes and such. We have no idea what type of class FileEx is or why it should accept an ActionListener and a little more information would be qutie helpful. If you want to notify another object that an event occurs, such as that a calculation is done, use another type of listener such as a PropertyChangeListener. Alternatively you could do the processing in a modal JDialog window, which will notify the calling window that it is done performing its duties by returning code flow to the calling window.
For example, please look at my answers to similar questions:
Drawing with paintComponent after value of Jbutton changed in another class
JTextField data in different frames, with data stored in global variable?
Loop making program freeze
EDIT
For example, if you wanted your FileEx to allow other classes to listen for changes to a String called selection (the so-called "bound" property) you could create it to look something like:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class TestFileEx {
public static void main(String[] args) {
final FileEx fileEx = new FileEx();
fileEx.addPropertyChangeListener(FileEx.SELECTION, new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
// TODO code to call when fileEx has changed selections
String fileExSelection = evt.getNewValue().toString();
// or
String fileExSelection2 = fileEx.getSelection();
}
});
}
}
and
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
publicclass FileEx {
public static final String SELECTION = "selection";
private SwingPropertyChangeSupport propertyChangeSupport = new SwingPropertyChangeSupport(
this);
private String selection;
public void someMethodThatChangesSelection() {
}
public String getSelection() {
return selection;
}
public void setSelection(String selection) {
String oldValue = this.selection;
String newValue = selection;
this.selection = selection;
// notify the listeners of change
propertyChangeSupport.firePropertyChange(SELECTION, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
public void rem(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
}
Here is the code based on your example which adds actionlisteners and calls them:
public class FileEx()
{
private final List<ActionListener> listeners = new ArrayList<>();
public FileEx()
{
//program that do something
if(done == true) {
notifyListeners();
}
}
public void addActionListener(ActionListener ac)
{
listeners.add(ac);
}
private void notifyListeners()
{
for (final ActionListener listener: listeners)
{
listener.actionPerformed(null);//You can create event if you want.
}
}
}
public static void main(String[] args)
{
FileEx fileex = new FileEx();
fileex.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e)
{
//when done is true i want this block of code to be called
}
});
}
I have an application that after successfull login (on a JFrame), starts to create the main frame (class MainUI that extends from JFrame). That MainUI class contains a JTabbedPane (which each tab is a class that extends from JPanel) and, on setVisible method, creates and shows each tab.
I want to add on the login form, after successfull login, a Spinner image to indicate that the MainUI is being created.
After display the Spinner image, I invoke the creation of the MainUI and call the setVisible method on EventQueue.invokeLater(); but the Spinner image is not updated. If I use new Thread(runner).start(); is updated, but I get a lot of Component creation must be done on Event Dispatch Thread
Some code of Login.java:
buttonLogin.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
login();
}
});
private void login()
{
//check DB and permissions
//if all is ok
lMsj.setIcon(spinner);
new Thread(new Runnable() {
public void run() {
showMainUI(usr);
}
}).start();
}
private void showMainUI(final Usuario usr)
{
Runnable runner = new Runnable() {
public void run() {
final MainUI mui = new MainUI();
mui.setVisible(true);
dispose();
}
};
EventQueue.invokeLater(runner);
}
and some code of MainUI.java
public MainUI()
{
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
setMinimumSize(new Dimension(1280, 960));
createComponents();
}
});
}
private void initComponents()
{
//..
// menuItem = new ...
// ...
}
#Override
public void setVisible(boolean value)
{
//..
if (Security.get().isAllowed("tab1")){
addTab1();
}
//..
}
private void addTab1(){
//..
getTabbedPane().addTab("Tab1", new Tab1());
//..
}
How I can fix this, so that the image is updated and the user interface is created in the "background"?
I have two classes in same package. i have declared a static variable in one class and want to access that variable in another class.
Here is my code in which i have declared the static variable
public class wampusGUI extends javax.swing.JFrame {
static String userCommand;
public wampusGUI() {
initComponents();
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
userCommand = commandText.getText();
}
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
wampusGUI w = new wampusGUI();
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
Here is the code in which i want to access variable
public class Game {
private wampusGUI gui;
public Game(wampusGUI w) {
world = new World();
world.start();
gui = w;
}
public void play() {
gui.setTextArea(welcome());
gui.setTextArea(describe());
for (;;) {
String s = userCommand; // here value should come should
System.out.println(userCommand);
Command c = Command.create(s);
String r = c.perform(world);
// is game over?
if (r == null) {
break;
}
System.out.println(r);
}
System.out.println("Game over");
}
}
However, i can pass the variable from first class as a argument. but the problem is that, when i will run program the value is going null first time, which i dont want. i want when i enter value in textfield then it should go to another class.
Thank you.
Looking at your code, it seems you want to show dialogs to your user with a certain text
gui.setTextArea(welcome());
gui.setTextArea(describe());
and sometimes, that dialog should capture user input which is handled afterwards.
Those setTextArea calls are not what you want to use. The user will never see the welcome message as it will immediately be replaced by the describe message.
Make sure you do not block the Event Dispatch Thread (EDT) or nothing will be shown at all. I do not know what your Command class will do, but I see an infinite loop on the Event Dispatch Thread which is never a good thing. Take a look at the Concurrency in Swing tutorial for more information
Thanks to that for loop, the user will simply not be capable to input any command as the EDT is busy handling your loop. What you need is a blocking call allowing the user to provide input (not blocking the EDT, but just blocking the execution of your code). The static methods in the JOptionPane class are perfectly suited for this (e.g. the JOptionPane#showInputDialog). These methods also have a mechanism to pass the user input back to the calling code without any static variables, which solves your problem.
I suggest that you use a listener of one sort or another to allow the Game object to listen for and respond to changes in the state of the GUI object. There are several ways to do this, but one of the most elegant and useful I've found is to use Swing's own innate PropertyChangeSupport to allow you to use PropertyChangeListeners. All Swing components will allow you to add a PropertyChangeListener to it. And so I suggest that you do this, that you have Game add one to your WampusGUI class (which should be capitalized) object like so:
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
// ....
}
This will allow Game to listen for changes in the gui's state.
You'll then want to make the gui's userCommand String a "bound property" which means giving it a setter method that will fire the property change support notifying all listeners of change. I would do this like so:
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
// ....
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
Then you would only change this String's value via this setter method:
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
The Game's property change listener would then respond like so:
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
One of the beauties of this technique is that the observed class, the GUI, doesn't have to have any knowledge about the observer class (the Game). A small runnable example of this is like so:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
private String userCommand;
private JTextArea displayTextArea = new JTextArea(10, 30);
private JTextField commandText = new JTextField(10);
public WampusGUI() {
initComponents();
}
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
private void initComponents() {
displayTextArea.setEditable(false);
displayTextArea.setFocusable(false);
JButton enterButton = new JButton("Enter Command");
enterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
enterButtonActionPerformed(evt);
}
});
JPanel commandPanel = new JPanel();
commandPanel.add(commandText);
commandPanel.add(Box.createHorizontalStrut(15));
commandPanel.add(enterButton);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(new JScrollPane(displayTextArea));
mainPanel.add(commandPanel, BorderLayout.SOUTH);
add(mainPanel);
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
WampusGUI w = new WampusGUI();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.pack();
w.setLocationRelativeTo(null);
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
class Game {
private WampusGUI gui;
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
}
public void play() {
gui.setTextArea("Welcome!\n");
gui.setTextArea("Please enjoy the game!\n");
}
public void play(String userCommand) {
// here we can do what we want with the String. For instance we can display it in the gui:
gui.setTextArea("User entered: " + userCommand + "\n");
}
}
I agree with Jon Skeet that this is not a good solution...
But in case u want an dirty solution to ur problem then u can try this:
public class wampusGUI extends javax.swing.JFrame
{
private static wampusGUI myInstance;
public wampusGUI( )
{
myInstance = this;
initComponents();
}
public static void getUserCommand()
{
if(myInstance!=null)
{
return myInstance.commandText.getText();
}
else
{
return null;
}
}
......
......
}
in the other class use:
public void play()
{
.....
//String s = userCommand; // here value should come should
String s = wampusGUI.getUserCommand();
.....
}
This kind of code is there in some of our legacy projects... and I hate this.