i am creating a GUI application and in the background I want to run an additional task. I will paste some code, to prevent pasting a mess of code that was generated by Swing, I will leave some parts out, assume that the window.java is working as intended.
window.java:
public class window {
frame = new JFrame();
JLabel lbl1 = new JLabel("Start Counter");
frame.add(lbl1);
Thread counter = new Thread(new counter());
counter.start();
}
counter.java
public class regCheck extends window implements Runnable
{
public void run()
{
int i = 0;
while (true)
{
window.lbl1.setText(i);
try {Thread.sleep(1000);}
catch (InterruptedException e) {e.printStackTrace();}
i++;
}
}
}
what I want this example to do is create a label within a window and count upwards until the program is closed.
The easy answer here is to say "pass in the Jlabel" however in reality I have multiple things that I need to change not just a label.
the line "window.lbl1.setText(i);" does not work here, it is just to illustrate what I want to achieve.
Use the MVC pattern. Create a model that has the counter with a setValue() method that fires a listener notification. You can extend java.util.Observable to make that easier to do. Add a getValue() method to retrieve the new count. Make the setter and getter synchronized for thread safety.
Now your thread can be passed an instance of the model and call setValue() to update the value in its run() method.
Finally, your view can be passed the same instance of the model and add a listener to it. To make it easier your view can implement java.util.Observer. In the listener update() callback within the view, call the model's getValue() and use the return as the argument to setText(). Since the listener update is not being invoked from the AWT event dispatcher thread, you have to call setText() using javax.swing.SwingUtilities.invokeLater() in order to meet the thread safety requirements of Swing.
it is actually architectural question, you can pass any arguments to other thread and this thread of course could modify different labels, but I prefer another variant:
you have window, which has its objects/controls and can manipulate them
you have separate thread which increase counter
this separate thread should notify main window object about changes and window object should change it's controls accordingly, for example change text in one or several controls
one simple variant is to have interface, like ICounterHandler with one method
void onCounterChanged(int newCounterValue);
counter thread should accept ICounterHandler in constructor, save it and call this method when needed, preferable asynchronous
of course there are many other variants, but you can start with this one
Related
I use SwingWorkers to fill several JTables with data from a mysql-database and use following code:
jT.setModel(DbUtils.resultSetToTableModel(results))
This works great. But right after each JTable-Model is set I need to execute different methods (methods may differ for each JTable) which e.g. put the row-count of a JTable into a JLabel or calculate sums of certain columns and put that sum into another JLabel...
My Problem:
I want the GUI to stay responsive (therefore the use of SwingWorkers to get the Data in the background), but I need to find a way to somehow "listen" to changes of the jTable (so I can execute the methods AFTER the Table is refreshed). I can't use the obvious solution to make different SwingWorker-classes for each JTable (which include the respective method of that JTable), because I need a general solution.
I already thought about putting each individual method into a TableModelListener and to add that to each respective JTable, so I tested following:
jT.getModel().addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
System.out.println("TableModelEvent: "+e);
// Probably I could add this jTable's method right here?
}
});
But I never get any Output...
I also tested the swingworker's .get()-method to wait for the set of the model (see first line of code) and then execute the method, but that lead to the same situation where I started from (GUI freezes and waits for execution of the swingworker... so no gain by using a swing worker..).
Any ideas?
The TableModelListener should work, and why might be the subject of another question, where you post your minimal example program, but one thing that likely will work in lieu of the TableModelListener is to use a PropertyChangeListener, add it to your SwingWorker before calling execute, and listen for changes to the SwingWorker state property. The new value you want to listener for is SwingWorker.StateValue.DONE.
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// TODO: code that you want to call when SwingWorker is done
// **including** calling get() on the SwingWorker itself
// so you can trap and respond to exceptions thrown during its run.
}
}
}
});
mySwingWorker.execute();
I'm making a small game and I've already implemented a save function in which the game is saved (by writing information to a new XML file). The saving takes a couple of seconds and I want to do the following: while the program is saving the game, I want to change the look of the JPanel, and when it is done saving, I want to go back to another page(show another JPanel).
I have the following code:
confirm.addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e){
String fileNaam = saveGame.getText();
//This method changes the look of the panel
changePanel();
//This method saves the game
model.saveGame(fileNaam);
//This method takes the user back to a previous page
controller.viewTeamPage();
}
});
What happens is that the game is saved and the user is taken back to the teampage, but the panel is never changed. The changePanel() method does work, so that is not the problem but it seems like it is never executed. I was wondering if somebody knows how I can fix this.
EDIT:
private void changePanel () {
panel.removeAll();
panel.repaint();
panel.revalidate();
}
This is the method to change the look of the panel, for now I just remove everything on the panel to keep it simple.
Also, the saving is not done in a separate Thread, is that something I should look at?
EDIT 2: I fixed it by using a thread to save the game and return to the teampage after the saving is done. See the code below.
confirm.addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e){
final String fileNaam = saveGame.getText();
changePanel();
Thread t = new Thread (new Runnable () {
#Override
public void run() {
model.saveGame(fileNaam);
controller.viewTeamPage();
}
});
t.start();
}
});
If you are changing the same panel and not intializing a new panel then the problem i think is that you need to call the panel.revalidate or panel.repaint i think. I made a demo for a Procedural generation project and i had to do this to make my panel change.
Call your save game method from a new thread but don't "join" or "try" to wait for this thread to finish from inside the method actionPerformed();
Make the call to controller.viewTeamPage() after the save game thread is done saving the game. One simple way of doing that would be passing the "controller" object to the constructor of your custom thread so you can make that call after saving the game.
The step 1 is very important in this case because all the calls you are making in the method actionPerformed() are being made in the UI thread, preventing the entire UI from refreshing until the method returns. Even calling repaint() alone, in changePanel(), wont be enough because it just "schedules" a refresh on you panel that will only happen after actionPerformed() returns. If you put the most time consuming call in a separate thread however, the actionPerformed() returns quickly allowing the UI to be refreshed while the game saving thread is doing its job.
I've been attempting to write a server program for a game and one issue that I am experiencing is writing to my 'log' (which is just a JTextArea's caption) from another class/thread. Basically, the setup is this:
MainWindow:
-JTextArea with the log.
Threads are started from MainWindow. They may need to output something, and basically what I am after is instead of printing whatever these threads need to output to System.out I can print them to my log, in my MainWindow (which is a JFrame). Is there a way to access the MainWindow from the threads? I don't really know how else to explain this.
Thanks
SwingUtilities.invokeLater() is made for that purpose.
final String labelText = "current label text";
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Write your GUI updater code here
// like this
labelOnTheGui.setText(labelText);
}
});
This is the only way you can manipulate the GUI starting the action from a different thread.
I have two basic questions.
I have a GUI project with Java Swing. When I put buttons on the frame and I double clicked them, I had the code of the actionPerformed, but it is blocked.
How can I put there a button and then use it on a actionListener?
My Project is about Server-client (multithread and sockets)
I call one method to reiceve one string that we can write on a JtextField and it stays on a while cicle with PrintWriter and a getOutputStream.
Something like:
do{
...
}while(thisstring!=null || thisstring!="exit")
So.. when I write something and press the button to send it, it stays on the cicle and the button blocks. How can I unblock the button to write something else?
Edit:
I understood the EDT problem, but I can't solve it.
I tried use the Timer but without success, something like that:
int delay = 1000; //milliseconds
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
//My action calling the Thread class with the while cicle that has the PrintWriter
}
};
new Timer(delay, listener).start();
How can I handle this to do the timer when I press the button?
How can I stay on the that cicle (read the comment line) to send the information by OutputStream every time that one user enter something on the text field?
I know that for example for a console application I use a BufferedReader and then I use the ReadLine() to wait for anything sent from the console, but with GUI interface it freezes all time..
There is a fundamental concept in Java GUI development surrounding which thread in which the developer implements user-interaction processing such as button clicks.
In short, you need to perform your processing outside of the thread that calls your action handling method. This single thread is known as the Event Dispatch Thread (EDT), and if you have logic that runs much more than a few milliseconds, it will prevent the UI from continuing to draw things like the button releasing, etc.
You'll want to move your long-running, socket code off the EDT. Doing so will allow the button to release and let the user interact with other controls (or even the same button).
To avoid duplicating other discussions on the topic, I direct you to this pretty good one. Additionally, this article gives a short overview of threading concepts in Swing.
Regards,
ScottH
According to your comment you have some naming issues there. You need a class that implements the ActionListener-interface like so: class YourListenerClass implements ActionListener, but you could also do that via an anonymous class like new ActionListener {
public void actionPerformed(ActionEvent e) {
//your code for your button here
}
});
when you set your ActionListener.
The crucial thing is that you need to name your method the correct way. It MUST be public void actionPerformed(ActionEvent e) and you definitely have to implement the ActionListener-interface.
The next thing is that you have to register your listener in your button like:
yourButton.addActionListener(new YourListenerClass);
or
insert an anonymous class like I showed to you before.
The 2nd thing sounds like an multithreading issue like I mentioned in my comment. I didnt follow scotth's link, but according to his description this might be a source you want to read to solve any further blocking issues.
EDIT:
Well, at first I didn't want to explain it, because it's quite a chunk of code, but as the problem persists I want to add something about SwingWorkers in my answer.
If you have long running code, it wont help to use a Timer as the code invoked by it will also be on the EDT as it's triggered by an event.
Instead of that you could use a SwingWorker to solve this. This needs some extra code, though.
Here's a simple approach you could follow:
public class WorkingHard{
SwingWorker<String, String> worker;
JButton yourButton = ...;
...
//do some cool stuff, as register those listeners!
...
public void actionPerformed(ActionEvent evt){
if(evt.getSource().equals(yourButton);
// Construct a new SwingWorker
worker = new SwingWorker<String, Void>(){
#Override
protected String doInBackground(){
//do your reading in this method, it will be executed in an own thread
String readText = "i will be read".
/*your reading algorithm, you could also call publish(...) to post your results,
e.g. likewise), then you also have to override process(...). this process will be
thread save, too*/
readText += ... ;
...
return readText;
}
#Override
protected void done(){
try {
//do sth. with your result, now thread safe.
someGuiElement.setText(get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
// Execute the SwingWorker; the GUI will not freeze
worker.execute();
}
}
If you want to know more about those workers... there several threads dealing about it, e.g. this one.
I'm creating an application in Swing using NetBeans. I would like to be able to manipulate some components during its startup (just once), after the window's made visible, for example update a progress bar. To this end, I have the app's main class, called MainWindow:
public class MainWindow extends JFrame
{
public MainWindow()
{
initComponents(); // NetBeans GUI builder-generated function for setting
// up the window components
}
public void Init()
{
loadLabel.setText("Loading....");
loadProgressBar.setValue(20);
doSomething();
loadProgressBar.setValue(40);
doSomething();
loadProgressBar.setValue(80);
doSomething();
loadProgressBar.setValue(100);
loadLabel.setVisible(false);
loadProgressBar.setVisible(false);
}
/* .... */
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
mainHandle = new MainWindow();
mainHandle.setVisible(true);
mainHandle.Init();
}
});
}
}
The problem is that the effect of the statements for updating the progress bar (or manipulating any other GUI component) within the Init() function can't be observed. If the Init() function is called from within main() as shown above, the window appears, but is empty, the Init() function executes and returns, only afterwards the window draws its contents but any changes made by Init() aren't visible because the window was empty and inactive the whole time. I also tried calling init from the windowOpened() AWT event, which executes after the window is fully drawn, but amazingly putting any statements for manipulating components there seems to have no effect, or rather they are put in a queue, and executed rapidly at some point in succession, so only the effect of the last one (hiding of the elements) can be observed. The only way I managed to get it working was to remove the whole invokeLater(new Runnable()...) mantra and put the new MainWindow(), setVisible(), Init() sequence directly in main(), which I guess is very ugly and breaks the concept of the gui running in a threaded manner. What is the right way to do this? Where do I put code to be executed first thing when the gui is ready to be manipulated, execute the statements once and return control to the main event loop?
I guess at the moment this is working in such a way, that while the Init() function is operating, any operations on the gui components are suspended (the drawing thread isn't separate and waits for Init() to finish before the manipulations are executed). Maybe I should make Init() a new thread... only how and what kind?
Thanks.
You could change the EventQueue.invokeLater() to invokeAndWait(), and move the call to init() out to a second EventQueue.invokeLater() call.
If (as looks to be the case) doSomething() takes a noticable amount of time, a better idea is to move the Init code into the body of a SwingWorker. This could be executed from the MainWindow() constructor or after the setVisible() call in main and is the idiomatic way to have a responsive GUI (in case the user gets bored waiting and wants to quit) and display some visible signs of progress.
See the process and publish methods for details on how to update the progress bar between doSomething() calls.
You may also want to look into ProgressMonitors for another alternative that would deal with the dialog box etc for you.
There are several things you can do:
For windows (such as JFrame or JDialog) you can attach WindowListener and do your manipulations in windowOpened method.
Override addNotify method and do your control manipulations there.
Attach HierarchyListener and do your manipulations whenever displayability of component changed.
Always make sure your do your component manipulations on EDT. Use SwingUtilities.invokeLater for simple UI updates or SwingWorker for long running tasks