I'm actually new to Java and SWT, so my question might sound dumb.
I've looked over the intrnet (and stackoverflow) but all the answers and examples are too complex for a noob like me.
I have a class, call it Admin. When I click a button, it launches a method in another class(Handler).
In Admin I have a progress bar, that should increase with the
operation in Handler.
I don't know how much time the operations in
Handler takes, but I know I have 9 of them.
I would like, somehow,
to inform Admin when each method has finished.
What I have so far: (non relevant parts omitted)
Admin.java:
ProgressBar bar = new ProgressBar(this, SWT.SMOOTH);
Button btn = new Button(this, SWT.NONE);
btn.setText("Update");
btn.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
btn.setEnabled(false);
thread.start();
}
});
Handler handler = new Handler();
final Thread thread = new Thread(){
public void run() {
handler.run();
for(int i=0; i<=10; i++) {
final int value = i;
try {
Thread.sleep(800);
} catch (Exception e) { }
display.asyncExec(new Runnable(){
public void run() {
bar.setSelection(value);
}
});
}
}
};
Handler.java:
public class Handler implements Runnable{
void m1() {...}
void m2() {...}
...
void m9() {...}
public void run()
{
m1();
m2();
...
m9();
}
With this implementation, the call to handler.run() works fine, and the progress bar is filling, but of course they are not "corelated" in any way.
I will appreciate your wisdom!
Thank you very much!
The highly coupled way of solving this, is to have the Handler take a ProgressBar as input in the constructor, and after m1() finishes, it updates the progress to 1/9 and so on. A better (more decoupled) way to do it, is that you can "listen" to Handler, and that Handler fires an event with its progress after each method finishes. Then you can update in the Admin.java.
I will ask you to consider using ProgressMonitorDialog from JFace if adding this library to your application is not much of a problem. Your code then would look something like below:
ProgressMonitorDialog dialog = new ProgressMonitorDialog(parent);
dialog.run(true, true, new IRunnableWithProgress() {
#Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
monitor.beginTask("my work", 9);
m1();
monitor.worked(1);
m2();
monitor.worked(1);
...
m9();
monitor.done;
}
});
You can also send the monitor instance into your m* methods for even detailed reporting. Have a look at SubProgressMonitor for dividing each one of your initial 9 ticks further before sending them into each m* method.
But this means that your progress bar is a new dialog which might not be acceptable to you so in that case the first answer suits your needs.
Related
I'm writing a Java program that acts as both a server and a client. Leaving out the irrelevant bits it has three classes: Main, Server and Client. Main just sets up a menu and contains the main method. Server and Client hold the algorithms for the server and the client respectively.
What I'm trying to do is to call the algorithm from the server and client classes and their GUIs depending on the button pressed. The code to call the server currently looks like this:
serverButton = new JButton();
serverButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent e) {
server.showGUI();
server.run();
}
});
The problem is that server.run() runs continuously for quite a long while and is a lot of heavy lifting. This bugs out the GUI, which from my understanding is because I'm calling the method from the EDT.
How can I call this method from the main thread? Do I need to create a SwingWorker and leave it there until the end of server.run()?
How can I call this method from the main thread?
This is how it is usually done in Swing.
public class WhatEverServer {
private UserInterface userInterface;
[...]
private static void createAndShowGUI() {
if( GraphicsEnvironment.isHeadless() )
logger.log( Level.FATAL, "This system seems to be 'headless'. Aborting now." );
else {
userInterface = UserInterface.getInstance();
userInterface.createAndShowUI();
}
}
public static void main( String[] args ) {
// schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater( new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
public class UserInterface {
...
public void createAndShowUI() {
// make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() );
// create and set up the window.
JFrame frame = new JFrame( "Whatever Server" );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set UI components, i.e
// set main menu bar
frame.setJMenuBar( this.mainMenuBar );
// set layout
frame.getContentPane().setLayout( new BorderLayout() );
// add UI components
// display the window.
frame.pack();
frame.setVisible(true);
}
}
This bugs out the GUI, which from my understanding is because I'm
calling the method from the EDT.
Yes, since the action is triggered by an event, the actionPerformed() is invoked by (or on) the EDT. I don't know what you are doing in server.run(), but I suppose this should not end up on the EDT.
Do I need to create a SwingWorker and leave it there until the end of
server.run()?
I would use SwingWorker or SwingUtilities in that case. You can write an ActionHandler in this way, using two threads, one for doing some of the 'heavy lifting', one for setting up the UI :
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable {
public void run() {
...
// do some 'heavy lifting' here ...
SwingUtilities.invokeLater(new Runnable() {
public void run() {
server.setupUI();
}
)
...
// or do some 'heavy lifting' here
});
}
}
Make sure the server object reference is final and then invoke the method in a new thread in your actionPerformed method.
Runnable task = () -> {server.run();};
Thread thread = new Thread(task);
thread.start();
It depends on your requirement, if you want the user do not want anything to do until server returns, it is best to do it in a Busyindicator like :
public void actionPerformed( ActionEvent e )
{
BusyIndicator.showWhile(Display.getCurrent(), new Runnable()
{
#Override
public void run()
{
server.run();
}
});
}
This will show user a hour glass while the server run is going on and user is blocked from using UI.
Or
if you want the UI to be responsive, you need to call server.run() in a separate thread.
MyThread t = new MyThread()
{
public void run()
{
server.run();
}
}
t.start();
and it is good practice to add a listener to thread to notify completion of server response so UI can do its things.
t.addListener( new MyThreadListener()
{
public void serverDone()
{
Display.getDefault().asyncExec( new Runnable()
{
public void run()
{
}
});
}
});
Please note this is not complete code for thread listener, just for idea sake.
I'm new in java. I'm trying to understand thraeads and timers in java. It was big problem for me to undate ProgressBar from another thread. I read many posts and found answers. So can you say is this the best way to resolw my task. So, what I want is to run second thread and when it's done, stop progress bar and change text on the button. So I creat Frame with button and progress bar in Test class:
After I realize action listener for Button to run second thread:
public class Test extends javax.swing.JFrame {
RunBg thread = new RunBg();
....
// declaration for button, progressbar and main() method
....
private void jButton1MouseClicked(java.awt.event.MouseEvent evt) {
// needed to check is the thread running at first time else creat new object and rund hread again cause thread can be executed once
if(thread == null){
thread = new RunBg();
}
if (jProgressBar1.isIndeterminate()) {
jProgressBar1.setIndeterminate(false);
jButton1.setText("Run!");
thread.suspend();
} else {
jButton1.setText("Working...");
jProgressBar1.setIndeterminate(true);
// let us know is the second thread running or nope so we can know resume it or pause
if (thread.isAlive()) {
thread.resume();
} else {
thread.start();
}
timer.start();
}
}
/*
Then I creat timer to know is my thread still alive else change Progress bar and button
*/
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(!thread.isAlive()){
thread.stop();
jProgressBar1.setIndeterminate(false);
jButton1.setText("Запустить!");
timer.stop();
// thread.interrupt();
thread = null;
}
}
});
...
// end of class
}
And my second class for another thread named RunBG:
public class RunBg extends Thread{
int i = 0;
public void run(){
while(i<20000){
System.out.println(">>>"+i);
i++;
}
System.out.println("<<<Finished>>>");
}
}
Everythig works fine , but I have question. Is there a better way to realize my task or maybe I made some mistakes. Also I thik that my code will help another beginners who looking for help to understand how it's work.
May be it is a basic question. But I'm a little confused about it. In my design I've a thread that is started from a mouse click. Method looks something like this:
Runnable r = new Runnable(){
public void run(){
A.firstMethod();
A.SecondMethod();
B.FirstMethod();
B.SecondMethod();
B.ThirdMethod();
}
};
new Thread(r).start();
Here B.FirstMethod(), B.SecondMethod(), B.ThirdMethod(); are accessing same variables and do some database operations during execution (i.e. locking, reading, writing etc.). But, If mouse is clicked during the execution or before finishing all the tasks I often get error message from database. How can I handle such type of situations. Here I cannot force the user to wait just with a simple Progressbar.
To handle such situation, you can disable the button once the thread has started its execution, and enable it again once the thread is going to finish its execution.
The code will then look like following:
Runnable r = new Runnable()
{
public void run()
{
button.setEnabled(false);
A.FirstMethod();
A.SecondMethod();
B.FirstMethod();
B.SecondMethod();
B.ThirdMethod();
button.setEnabled(true);
}
};
new Thread(r).start();
Or when the button is clicked, you can use a boolean variable to check if the thread is in progress or not, and then decide as to what needs to be done like:
volatile boolean inProgress = false; // Instance Variable
Runnable r = new Runnable()
{
public void run()
{
inProgress = true;
A.FirstMethod();
A.SecondMethod();
B.FirstMethod();
B.SecondMethod();
B.ThirdMethod();
inProgress = false;
}
};
if(!inProgress)
new Thread(r).start();
else
Popup some message perhaps
I have a list of hex colors with a duration in milliseconds for each one. I would like to fill the screen with each color for its duration, then move on to the next color.
I tried to iterate over the colors to do the following:
myView.setBackgroundColor(Color.parseColor( theColor ));
SystemClock.sleep( theDuration );
myView.setBackgroundColor(Color.parseColor( nextColor ));
SystemClock.sleep( nextDuration );
etc...
which seemed obvious to me but doesn't do anything to the view when it's running, at least in my AVD. I'm learning that it's because Android only draws at predefined times. (I tried calling "Invalidate()" as well with no luck.)
What is the best way to display all the colors consecutively?
(I realize I shouldn't be calling sleep() either, so any suggestions for that would also be appreciated.)
Thanks.
new Thread() {
#Override
public void run() {
YourActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
myView.setBackgroundColor(Color.parseColor( theColor ));
}
Thread.sleep( theDuration);
YourActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
myView.setBackgroundColor(Color.parseColor( nextColor ));
}
Thread.sleep( nextDuration );
}
}.start();
Put this in a method and call it.
There are many ways to achieve what you want. One could be to use Handler and Runnable. Assuming you know how to get current color and duration, you could do:
declare Runnable as class variable
private Runnable runnable = null;
inside onCreate(), after you set initial view, initialise Handler
final Handler handler = new Handler();
initialise runnable and change the background in run() method
runnable = new Runnable() {
#Override
public void run() {
//change the color
myView.setBackgroundColor(currentColor);
//run it again in nextDuration miliseconds
handler.postDelayed(toggle, nextDuration);
}
};
//start runnable in theDuration miliseconds
handler.postDelayed(toggle, theDuration);
You could have arrays of colors and durations and cycle through them with index variable. This is assuming that myView is a valid view.
EDIT:
To those who downvoted, read the documentation for Handler:
When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it...
In other words, you are creating a handler in onCreate() of your activity, so it will be able to update your view.
This blogpost from adroid-developers website uses very similar construct as proposed above.
The Handler runs the update code as a part of your main thread, avoiding the overhead of a second thread and also making for easy access to the View hierarchy used for the user interface.
See here and here answered by CommonsWare and here - answered by Laith Alnagem.
I ended up creating a Runnable inside the my button click event handler. Everything (looping through the colors and durations plus "sleeping" based on the durations) is done in the Runnable run() method.
public void playOnClick(View v) {
Runnable runnable = new Runnable() {
public void run() {
...
}
Then I created a handler inside my UI Activity class that just changes the color of the background.
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
Bundle b = msg.getData();
String theColor = b.getString("color");
myView = (View) findViewById(R.id.bigView);
myView.setBackgroundColor(Color.parseColor(theColor));
}
};
Inside the run() method, he Runnable sends a message to the handler containing the background color using Bundle/Message objects:
b = new Bundle();
b.putString("color", theColor);
msg = new Message();
msg.setData(b);
handler.sendMessage(msg);
I have a problem while creating a JProgressBar which is set to indeterminate.
The following code is my implementation of the JProgressBar and is called/constructed from another class:
public class Progress implements Runnable
{
private JFrame frameProgress;
private JProgressBar progressBar;
public Progress(String title, String message)
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e)
{
GlobalVariables.LOGGING_logger.error("Error instatiating progress bar.",
e);
}
UIManager.put("ProgressBar.selectionForeground", Color.black);
UIManager.put("ProgressBar.selectionBackground", Color.black);
this.frameProgress = new JFrame(title);
this.frameProgress.setIconImage(GlobalVariables.GUI_icon.getImage());
this.frameProgress.setSize(300, 60);
this.frameProgress.setLocation(16, 16);
this.progressBar = new JProgressBar();
this.progressBar.setStringPainted(true);
this.progressBar.setString(message);
this.progressBar.setIndeterminate(true);
this.frameProgress.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frameProgress.add(this.progressBar);
this.frameProgress.setResizable(false);
this.frameProgress.setVisible(true);
}
public void start()
{
new Thread(this).start();
}
public void close()
{
this.frameProgress.dispose();
this.frameProgress = null;
this.progressBar = null;
}
#Override
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do nothing, because progress bar is indeterminate
}
});
}
}
The caller of this JProgressBar is the following code snippet:
Progress p = new Progress("bla", "blub");
p.start();
boolean successfull = xmlWriter.writeCommonSettingsFromGUI(this);
p.close();
And now i want, while the xmlWriter.writeCommonSettingsFromGUI(this); is doing something, that the JProgressBar is shown to the user and is working while the algorithm is running.
How can I achieve this? I don't know so much about threading and searched in many other forums, but I don't found any answer for my question.
Please help me and thank you in advance ;)
EDIT:
The Progress JFrame opens up with no content for that time, the algorithm is running.
You are probably facing concurrency issues with Swing. Assuming that the following code runs on the EDT (Event Dispatching Thread):
Progress p = new Progress("bla", "blub");
eventually, this will open a JFrame with a progress bar in it.
I would consider using a JDialog instead of a JFrame
I would not force the size of the JFrame, but rather call pack()
Then, still running on the EDT (and thus blocking all UI-events such as repaint, mouse clicks, etc...), you call p.start() which starts a new Thread() which will invoke run() which itself calls
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do nothing, because progress bar is indeterminate
}
});
This basically won't do anything except push an additional event on the EventQueue and it will run after all currently pending events. This event will run... "nothing" since your Runnable is just empty. The new Thread dies almost immediately. So all this code is useless.
Still pursuing on the EDT, you call boolean successfull = xmlWriter.writeCommonSettingsFromGUI(this); (btw, "successful" ends with only one 'l'). This will continue on blocking the EDT, preventing repaints from occurring and preventing the JProgressBar from painting itself. Eventually you will dispose the JFrame but since all this code is running on the EDT, the user will not see much of the progress bar and the UI will look frozen.
Consider reading the Swing tag wiki (especially the very last part with 3 important links).
Using a SwingWorker should help you out in this.