I have a class called GUI which basically creates a latout using Swing. In that class i have a method called "log" which is supposed to add a new line to a textarea in the layout.
The problem is that whenever i call the function from outside of the GUI class, nothing happens. If i call the method from within the class it adds a line to the textarea as it's supposed to do.
I have set the method and all the variables it calls to public static, and i don't get any errors. It just doesn't do anything when i call the method from the outside.
Any ideas?
Edit:
Here's the method within the GUI class:
public static void log(String inputString) {
logConsole.append(inputString + "\r\n");
}
At the bottom of the class swing declared the textarea, and i just modified it to be public static instead of private.
public static javax.swing.JTextArea logConsole;
Can't post more code, hope this is at least a little bit helpful? :/
It's most likely a concurrency issue with Swing. Since Swing is single-threaded, Swing components need to be modified in the Event Dispatch Thread (i.e. EDT). For more information, see Concurrency in Swing.
EDIT -
If this is indeed a concurrency issue, then one quick workaround would be to use SwingUtilities. In particular, isEventDispatchThread() and invokeLater(...). For instance,
if(!SwingUtilities.isEventDispatchThread()){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
GUI.log("foo"); // modify textarea in EDT
}
});
}
else{
// your problem lies elsewhere
}
Related
I have some initialization code that needs to be run when my Java Swing application starts, without any events being triggered.
My initialization code is in the myInits() function. myInits() does all sorts of thing: reads config from a file, sets the background and some labels, spawns network threads, initializes variables.
From googling and my own knowledge i have figured out different ways to achieve this(please correct me if any of the assumptions stated below are wrong):
Run myInits() when the Window_Opened event is triggered.
This makes sure the GUI is painted and myInits() can access any component and change it. Unfortunately I can't use this method because my application starts hidden and Window_Opened doesn't get triggered.
Put myInits() inside the JFrame constructor:
public class MyFrame extends javax.swing.JFrame {
private MyFrame(){
initComponents(); // <= Auto generated by NetBeans GUI Builder
myInits();
}
}
I suppose there's nothing wrong with this methoed because initComponents() itself does all kinds of GUI manipulation. I used this method and it worked just fine.
But today I changed MyFrame into a Singleton and i got a java.lang.ExceptionInInitializerError. Because myInits() calls MyFrame.getInstance() and by putting it in the constructor I'm technically calling getInstance() inside another getInstance(). I tried making getInstance() synchronized but it didn't work.
Make myInits() public and call it from main() :
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ChatFrame().myInits();
}
});
}
I personally don't like this method because i don't want myInits() to be public. Also because I like number 4 better.
invokeLater myInits() in the constructor.
private myFrame(){
initComponents();
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
myInits();
}
});
}
This is what i myself came up with. Because the instantiation itself is being done in an invokeLater() method(see number 3), I figured I could be sure that myInits() will run after new myFrame(); has finished and i can safely call MyFrame.getInstance().
To sum it up:
1) Are all of these methods correct?
2) Is any of these (or other possible methods i may have not mentioned) considered the best practice?
3) Which one is the best practice for a Singleton JFrame?
4) Why when i synchronized my getInstance() method i still got the java.lang.ExceptionInInitializerError?
In my opinion, if your initialization logic in not ui specific, it should be in main. Extract a separate class out of myinit, and call it in main.
You may also make it singleton using enum, and use it, so that it gets initialized upon first use/ application load.
I have created a JFrame with a textArea called 'outputTextArea' and I want to print the results from a database query in the textArea. However, the variable outputTextarea is not static and therefore I can't call the method setText() in the main method to print the db resultset in the textArea.
I would like to know how I can make this variable (private javax.swing.JTextArea outputTextArea;) static, because NetBeans won't let me edit this variable because it was generated by NetBeans when I dragged and dropped the textArea.
I had the same problem.
In Netbeans IDE 8.0.2:
1) In the design tab
2) Click on the textarea
3) go to properties -> code
4) Variable Modifiers -> add static.
It worked for me.
Just add an accessor method to your class that adjusts the field. For instance:
public void setTextAreaText(String newText) {
outputTextArea.setText(newText);
}
Then anyone with a reference to your class can adjust the text in the text area. Just be sure to call that method from the Event Dispatch Thread. This is usually achieved with SwingUtilities.invokeLater
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myClassReference.setTextAreaText("Hello, World");
}
});
See the documentation on Event Dispatch Thread if this sort of thing is new to you. It's important to get threading correct when using Swing.
If you just want to edit codes. Open code with another editor just like notepad or something. And if you remove GEN-BEGIN:initComponents just before the auto generated code you can edit code through netbeans also.
I have a JFrame application with some variables and a number of SWING widgets. In it I create an instance of another class and pass the JFrame to the child in the constructor. From the child, I can reference the variables, but not the widgets. Why?
// My JFrame
public class Prot2Prom extends JFrame {
// My Child
public Prot2Prom() {
super( "Protocol To PROM" );
Child child = new Child(this);
In the Child class my constructor does
Prot2Prom frame = null;
public Child(Prot2Prom gui) {
frame = gui;
}
The following works:
frame.<parent variable>=x;
The following does not:
frame.textArea.append("Hello");
The textArea cannot be resolved. There were all added with "new". Why can't I see them?
Some notes and recommendations:
This has nothing to do with "widgets" or Swing and all to do with visibility of variables. I'm guessing that textArea is not a public field of the Prot2Prom class.
If variables are public outside classes can "see" them, access them, modify them.
A possible solution is to in fact make the variables that you want other classes to see, public.
In general you really don't want to do this.
Instead much better is to give a class public methods that allow other classes to call and by doing so alter the original class's behavior. In other words, your Swing GUI classes should adhere to good OOPs principles just as any Java class should.
Later you'll want to read up on the MVC or Model, View, Control design pattern as a way of separating out behaviors of your code into separate logical entities, which can make your code much more flexible and powerful.
Edit 1
Regarding your comment:
The "widgets" are all created by WindowsBuilder Pro. I am trying to use the textArea to create my Eclipse Console for a stand alone (jar) application. How can I print to it from a class instantiated by the Frame?
You'll want to give the class that holds the textArea variable a public method:
public void appendTextAreaText(String text) {
textArea.append(text);
}
Then your other classes can append text to the JTextArea. Why is this important? One reason is that if the class that holds textArea will at some times not want to allow other classes the ability to append to this widget, it can have the logic to control this in the method. Thus it gives much more control over the widget to the class that holds it.
e.g.,
public void appendTextAreaText(String text) {
if (allowTextAreaAppend) { // a class boolean field
textArea.append(text);
}
}
As an aside, I also recommend that you put the code generation tool to the side and instead create your Swing GUI's by hand for a bit until you get a firm grasp of Swing and Java fundamentals. This will make your future use of the Swing code generation tool much better and productive.
The textArea cannot be resolved
This message indicates that there is no member class variable called textArea in Prot2Prom. This is possibly a typo. Perhaps the variable is called textarea or defined only locally in the constructor scope.
To work your class would look something like this
public class Prot2Prom extends JFrame {
JTextArea textArea = new JTextArea();
...
A better approach to updating text in a parent component is to create a method to Prot2Prom like so:
public void addText(String text) {
textArea.append(text);
}
This provides more control over how text is added to the JTextArea.
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.
I'm having a problem with my JButton ActionListener. I have a doTheCleaning() method defined in another class which when called makes series of changes to my GUI.
public void doTheCleaning(){
//change image icon
//had thread.sleep here
//insert to text area
//had thread.sleep here
//etc
}
Then in another class, I instantiated the class containing my doTheCleaning() method and had my ActionListener written with my actionperformed() method for my jbutton written like this:
public void actionPerformed(ActionEvent e){
//some code
//newClass.doTheCleaning();
}
I know how to do the rest like addActionListener() and stuff so no need to question about that. My concern is that all the changes in my GUI that is performed when doTheCleaning() method is called applies only after the button is clicked. When this happens, the succession between the changes that happened in my labels and textarea were not shown. The code works fine if i called it directly in my tester class but calling it inside the actionperformed method shows only the final state of my GUI. I need to show which element changed first, then what's next, and so on.
How could I achieve it when I need these changes to occur only when I click the JButton?
**I'm not so good with doing GUI in java yet. iIhope you guys understood my point without me giving my code. but I could if necessary. Thanks.
Do not perform any intensive operations within EDT, otherwise the GUI will be unresponsive and you might not see the GUI updates. Best choice you can use is SwingWorker:
Override doInBackground(), and put any long operations inside this method so that it will be run on a separate thread rather than the EDT.
For any GUI creation or changing states of GUI components within doInBackground(), use publish(V... chunks) to send data to process(List<V> chunks). You need to override process(List<V> chunks). Also note that process(List<V> chunks) is executed on EDT.
After doInBackground() returns, done() executes on EDT and you can override it to use it for any GUI updates. You can also retrieve the value returned from doInBackground() by using get().
Note that SwingWorker<T,V> is generic, and you need to specify the types. T is the type of object returned from doInBackground() and get(), while V is the type of elements you passed to process(List<V> chunks) via publish(V... chunks).
execute() method starts the swing worker by invoking doInBackground() first.
For more on this, please read Concurrency in Swing.