Java Swingworker cannot work with swt? - java

I am doing a project that need to get page content from web page in Java, and sometimes, I need to execute some javascript on the web page and them get the modified content. So I choose to use SWT tool. Here is part of the code:
public void run(){
Display.getDefault().asyncExec(new Runnable(){
public void run(){
display = Display.getDefault();
shell = new Shell(display,SWT.CLOSE | SWT.MIN |SWT.TITLE);
shell.setText("Web Page");
shell.setSize(1024,768);
browser = new Browser(shell, SWT.NONE);
browser.setBounds(0, 0, 1010,700);
browser.addProgressListener(new ProgressListener() {
#Override
public void completed(ProgressEvent event) {
boolean success = browser.execute(script);
if(success || script.length()==0){
model.setHtml(browser.getText());
}
shell.dispose();
}
#Override
public void changed(ProgressEvent event) {
}
});
browser.setUrl(url);
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
});
}
And I am calling the the run() function within a class worker which extends from SwingWorker. The worker class is defined like this:
public class worker extends SwingWorker<Object,Integer>{
private ScrapingModel model;
private ScrapingView view;
private int[] indices;
public worker(ScrapingView v, ScrapingModel m, int[] ins){
model = m;
view = v;
indices = ins;
}
#Override
protected Object doInBackground() throws Exception {
int length = indices.length;
for(int i=0;i<indices.length;i++){
int index = indices[i];
//here call the run() function, sorry I skipped some code
publish((i+1)*100/length);
}
return null;
}
#Override
protected void process(List<Integer> chunks) {
for (Integer chunk : chunks) {
view.progressBar.setValue(chunk);
}
}
}
Here is the problem: the code wthin Display.getDefault().asyncExec never executes when I call the run() function in worker class. However, if I tried to call run() outside worker class, it could be executed. Any ideas?

Instead of wrapping the Runnable in an asyncExec
Display.getDefault().asyncExec(new Runnable(){
// swt code to open shell
});
Create a new Thread
Runnable r = new Runnable(){
// swt code to open shell
}
Thread t = new Thread(r);
th.start();

Related

Javafx button doesn't respond after first call (I call methode in task thread)

I have initialized parseBtn onAction but because the work in parseFunction will be more than one minute I created Task to do the work in new Thread(task).start() the function is working in background will but after finishing parseBtn doesn't respond any more.
I think because the work in another thread the button doesn't notify that work finished
#FXML private Button parseBtn;
public void initialize(){
parseBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new Thread(task).start();
}
});
}
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
//do something
return null;
}
};
the process inside the task is done but the button doesn't respond any more
I found the solution my mistake was that task can't be calling multiple time.
class parser extends Thread{
#Override
public void run() {
//work to do
}
}
public void initialize(){
parseBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
parser p = new parser();
p.start();
}
});
}

Drawing animation in a Nattable OverlayPainter

I am trying to render a loading animation on top of a nattable. I use the OverLayPainter mechanism to draw a "glasspane" and some text on top of the table, and this works perfect:
public class MessageOverlay
implements IOverlayPainter {
....
#Override
public void paintOverlay(final GC gc, final ILayer layer) {
this.currentGC = gc;
this.currentLayer = layer;
if (visible) {
currentGC.setAlpha(200);
currentGC.fillRectangle(0, 0, currentLayer.getWidth(), currentLayer
.getHeight());
drawMessage();
if (withLoadingAnimation) {
showAnimation = true;
}
} else {
showAnimation = false;
}
}
}
However, the paintOverlay method is not called regularely but rather everytime the table changes.
To be able to display a smoothe animation, I added a new thread
final Thread animatorThread = new Thread(new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
try {
Thread.sleep(1000 / fps);
} catch (final InterruptedException e) {
break;
}
display.asyncExec(new Runnable() {
#Override
public void run() {
if (showAnimation && !currentGC.isDisposed()) {
final Image currentImage = getNextImage();
final int posX = currentGC.getClipping().width / 2
- currentImage.getBounds().width;
final int posY = currentGC.getClipping().height / 2
- currentImage.getBounds().height;
currentGC.drawImage(currentImage, posX, posY);
}
}
});
}
}
});
animatorThread.start();
As you can see it tries to access the graphics context this.currentGC set in the paintOverlay method. My problem is, that currentGC within the animatorThread is always disposed.
How can I a.) ensure that the context is not disposed in the thread or b.) solve this in an alternative way?
Thanks for the help.
You could try to create a new GC with the current NatTable instance and if needed pass the config from the passed in GC instance. Then you are in charge of disposing the GC instance and should not have the risk of a disposed GC outside your thread.
A simple example could look like the following snippet that simply shows the pane for 1000ms and then removes it again. You need of course to change the logic to be more dynamic with regards to your loading operation then:
AtomicBoolean paneThreadStarted = new AtomicBoolean(false);
...
natTable.addOverlayPainter(new IOverlayPainter() {
#Override
public void paintOverlay(GC gc, ILayer layer) {
if (this.paneThreadStarted.compareAndSet(false, true)) {
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
GC currentGC = new GC(natTable);
currentGC.setForeground(GUIHelper.COLOR_WHITE);
currentGC.setBackground(GUIHelper.COLOR_BLACK);
currentGC.setAlpha(200);
currentGC.fillRectangle(0, 0, layer.getWidth(), layer.getHeight());
String load = "Loading data ...";
Point textExtent = currentGC.textExtent(load);
currentGC.drawText(load,
layer.getWidth() / 2 - textExtent.x / 2,
layer.getHeight() / 2 - textExtent.y / 2,
true);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentGC.dispose();
natTable.redraw();
}
});
}
}
});
This way you are able to show the pane again by changing the AtomicBoolean from the outside:
Button showPaneButton = new Button(buttonPanel, SWT.PUSH);
showPaneButton.setText("Show Pane");
showPaneButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
this.paneThreadStarted.set(false);
natTable.redraw();
}
});

Running a progress bar from another class doesnt work

I'm trying to update a progressbar from another class on different thread then the main one.
but its not working for me, I dont know why
so I've created a class called BarThread.java
class BarThread extends Thread {
ClassificationV4 classObj = new ClassificationV4();
JProgressBar progressBar;
public BarThread(JProgressBar bar) {
progressBar = bar;
}
public void run() {
int minimum = 0;
int maximum = classObj.getMaximumLength();
for (int i = minimum; i < maximum; i++) {
try {
int value = progressBar.getValue();
progressBar.setValue(value + 1);
Thread.sleep(classObj.getSleepTime());
} catch (InterruptedException ignoredException) {}
}
}
}
and here I created an anonymous thread to run the progress bar inside on of my methods
//create anonymous thread
new Thread() {
public void run() {
Thread stepper = new BarThread(jProgressBar1);
stepper.start();
}
};
Any help will be appreciated.
Swing is not thread safe. You have to update your JProgressBar in the dispatcher (main) thread.
In general Swing is not thread safe. All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread.
You can't update UI directly from non-UI thread. Only main thread can update view/UI. If your thread want to update UI, use activity.runOnUiThread()
Official document can found here https://developer.android.com/guide/components/processes-and-threads.html#Threads
This code may help you.
class BarThread extends Thread {
ClassificationV4 classObj = new ClassificationV4();
JProgressBar progressBar;
Activity activity;
public BarThread(Activity activity, JProgressBar bar) {
this.activity = activity;
this.progressBar = bar;
}
public void run() {
int minimum = 0;
int maximum = classObj.getMaximumLength();
for (int i = minimum; i < maximum; i++) {
try {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
int value = progressBar.getValue();
progressBar.setValue(value + 1);
}
});
Thread.sleep(classObj.getSleepTime());
} catch (InterruptedException ignoredException) {
}
}
}
}
Call from Anonymous thread
new Thread() {
public void run() {
Thread stepper = new BarThread(activity, jProgressBar1);
stepper.start();
}
};

Execute method every second

I want to execude some code every second in android, but I'd like to do is in one thread (main thread). So far I have this:
locationTimer = new Timer("locationTimer", false);
locationTimer.schedule(new LocationCheckerTask(this), 0, 1000);
public class LocationCheckerTask extends TimerTask {
private GeoWatcher watcher;
public LocationCheckerTask(Context context) {
watcher = new GeoWatcher(context);
}
#Override
public void run() {
// funky stuff
}
}
Unfortunately, Timer class runs it's tasks on another thread.
Why I want to do this in a single thread?
Code in run() method will be executing really fast, so I figured I don't need another thread for it. What I want to do is to construct separate threads in run() method based on condition calculated every second. So instead of having child thread constructing another threads, I'd like to do this on the main one.
You can do this with Handler
public class Job implements Runnable{
private Handler handler;
public Job () {
handler = new Handler(Looper.getMainLooper());
loop();
}
#Override
public void run() {
// funky stuff
loop();
}
private void loop() {
handler.postDelayed(this, 1000);
}
}
use runOnUiThread(Runnable) method of Activity to run the task in UI Thread
public class LocationCheckerTask extends TimerTask {
private GeoWatcher watcher;
public LocationCheckerTask(Context context) {
watcher = new GeoWatcher(context);
}
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// funky stuff
}
});
}
}
the Handler is a perfect candidate for such tasks (dont try to combine TimerTask + runOnUiThread - it is useless as it uses a Handler under the hood)
private Runnable fiveSecondRunnable = new Runnable() {
#Override
public void run() {
if (count5 < 0) {
switchT030Sec();
} else {
tvSec5.setText(""+count5);
Log.v("5sec set", "yes");
count5--;
man.postDelayed(this, 1000);
}
}
};
and start it by calling
man.post(fiveSecondRunnable);

Java swing - when cancel button clicked don't loop

I have a gui, that is having a Login prompt added.
while(notValidLogIn){
LoginPrompt.getDetails() //a static method that
}
Hwoever, the loginPrompt is a Jdialog, with a parent JFrame. How can I stop looping of cancel clicked, I could put System.exit(0) in cancel action performed. But don't want to stop everything, I want something like :
while(notValidLogIn && LoginPrompt.isNotCancelled()){
LoginPrompt.getDetails(); //a static method that creates an instance of login JDialog()
}
In a recent project I was working on, I've implemented an event based solution. The idea is JDialog notify to its parent JFrame how the login process went and this last one may or may not continue its execution. This way I have no loops and keep separate responsibilities: The schema would be something like this:
LoginEvent:
This is the event itself. Not that complicated:
class LoginEvent extends EventObject {
public static final int LOGIN_SUCCEEDED = 0;
public static final int LOGIN_FAILED = 1;
public static final int LOGIN_DIALOG_CLOSED = 2;
private int id;
public LoginEvent(Object source, int id) {
super(source);
this.id = id;
}
public int getId() {
return id;
}
}
LoginListener
An interface to handle these LoginEvents:
public interface LoginListener extends EventListener {
public void handleLoginEvent(LoginEvent evt);
}
Login Dialog
This class has to mantain a List with subscribed LoginListeners:
class LoginDialog {
List<LoginListener> listeners = new ArrayList<>();
JDialog dialog;
JButton accept;
JButton cancel;
public void show() {
//create and show GUI components
}
public void close() {
if(dialog != null) {
dialog.dispose();
}
}
...
public void addLoginListener(LoginListener loginEventListener) {
if(!listeners.contains(loginEventListener)) {
listeners.add(loginEventListener);
}
}
public void removeLoginListener(LoginListener loginEventListener) {
listeners.remove(loginEventListener);
}
public void dispatchLoginEvent(LoginEvent evt) {
for(LoginListener loginListener: listeners) {
loginListener.handleLoginEvent(evt);
}
}
}
Adding action listeners to accept and cancel buttons:
accept.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// validate login data
if(loginValid) {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_SUCCEEDED));
} else {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_FAILED));
}
}
});
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_DIALOG_CLOSED));
}
});
Subscribing a LoginListener
In your JFrame:
final LoginDialog dialog = new LoginDialog();
dialog.addLoginListener(new LoginListener() {
#Override
public void handleLoginEvent(LoginEvent evt) {
if(evt.getId() == LoginEvent.LOGIN_SUCCEEDED {
dialog.close();
//continue execution
return;
}
if(evt.getId() == LoginEvent.LOGIN_FAILED) {
JOptionPane.showMessageDialog(null, "Login failed!");
return;
}
if(evt.getId() == LoginEvent.CLOSE_LOGIN_DIALOG) {
dialog.close();
// do something when this dialog is closed
}
}
};
dialog.show();
while(notValidLogIn && LoginPrompt.isNotCancelled()){
LoginPrompt.getDetails(); //a static method that creates an instance of login JDialog()
}
If this loop is inside another thread other than the EDT(event dispatch thread), then you can use SwingUtilities.invokeAndWait(new Runnable()) function: invokeAndWait() blocks the current thread until the EDT is done executing the task given by it. This option is particularly used while we want to await an execution of a thread for taking confirmation from user or other input using JDialogue/JFileChooser etc
while(notValidLogIn && LoginPrompt.isNotCancelled()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
LoginPrompt.getDetails() ;
}
});
}
Note: re-stating for emphasizing: you should ensure that this loop is executing inside another Thread: such as using an extended class of Runnable, or by means of anonymous class:
new Thread()
{
// other code of your context
public void run()
{
while(notValidLogIn && LoginPrompt.isNotCancelled()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
LoginPrompt.getDetails() ;
}
});
}
}
}.start();

Categories

Resources