I have a JFace application and want to do some work on startup. So I overrode the open method of the window.
But now I have the problem that in the case of a failure I can't display an error message because the shell is null at this time. And I have no idea to solve this problem.
public class MyExample extends ApplicationWindow {
public MyExample() {
super(null);
}
public void run() {
setBlockOnOpen(true);
open();
Display.getCurrent().dispose();
}
#Override
public int open() {
// do some work
if (...) {
MessageDialog.openError(getShell(), "Error", "Error occured");
}
return super.open();
}
}
I would try:
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", "Message");
}
});
EDIT:
The static method Display.getDefault() returns the default Display thread or a new one is created if it did not already exist.
On the other hand, the static method Display.getCurrent() returns the Display instance from the currently running thread, or null if the currently running thread is not a user-interface thread for any display.
See more on the Java Documentation of the Display class.
You may also want to take a look at the difference between syncExec() and asyncExec().
Related
I am writing a program in Java and I want to add a loading page where it displays a progress bar and a label saying what is loading and the bar showing how far it is to completing, I have everything set up so it should work but it doesn't, I do not know what is wrong (I am new to java so please have mercy)
I have tried having a boolean that is set to false by default and is set to true only after "after-all-set-code" has been executed (I am using netbeans to create the GUI) and when I call the function to update the text/progress bar, if the boolean is still set to false it will wait a second and retry till the "all-set-code" changes it to true, but that doesn't seem to work.
This is inside my main class
public class Xtra {
public static loadingPage LP = new loadingPage();
public static void main(String[] args) {
// Show loading page
LP.main(args);
// Set loading
LP.setLoading(0, "Fetching Config...");
// Get Config
// Durring the "getConfig" function it calls the function "LP.setLoading()" multiple times to update the loading bar & text
Xtra.getConfig();
// Set loading
LP.setLoading(0, "Loading user data...");
}
}
This is my setLoading() class inside of loadingPage:
public void setLoading(int per, String txt) {
if ("".equals(txt)) {
setLoadingValue(per);
} else {
setLoadingText(txt);
setLoadingValue(per);
}
}
This is my setLoadingValue() function:
public void setLoadingValue(int x) {
// This will turn true when the after-all-set code runs
while (!loadingBarLoaded) {
// Try to wait a second,
// Do this so it doesn't take up too much CPU
try {
// Wait a second
TimeUnit.SECONDS.sleep(1);
// Print to console
System.out.println("Loading value not loaded,\nWaiting another second");
// If there is an error waiting
} catch (InterruptedException ex) {
// Alert user
System.out.println("You cannot sleep now, monsters are nearby");
}
}
// After loaded boolean turns true
// Alert user of change
System.out.println("Setting loading value to " + x + "%");
// Change value
loadingBar.setValue(x);
}
This is my setLoadingText() function:
public void setLoadingText(String txt) {
// This will turn true when the after-all-set code runs
while (!loadingTextLoaded) {
// Try to wait a second,
// Do this so it doesn't take up too much CPU
try {
// Wait a second
TimeUnit.SECONDS.sleep(1);
// Print to console
System.out.println("Loading text not loaded,\nWaiting another second");
// If there is an error waiting
} catch (InterruptedException ex) {
// Alert user
System.out.println("You cannot sleep now, monsters are nearby");
}
}
// After loaded boolean turns true
// Alert user of change
System.out.println("Setting loading text to \"" + txt + "\"");
// Change value
loadingText.setText(txt);
}
and my after-all-set-code is loadingTextLoaded = true & loadingBarLoaded = true depening on what is finished
It's supposed to update the text and the value of the progress bar but it isn't, it is outputting the Settings loading text to "..." to the console & the Setting loading value to ...% to the console as well, but not changing the actual value or text of the components.
What am I doing wrong?
Minimalistic problem: (Main file)
// Inside main function
// Show loading page
LP.main(args);
LP.loadingBar.setValue(50);
LP.loadingText.setText("Hello");
// nothing changes. (Both are public so I can call them from different files
This is what the loadingBar & loadingText vars and declared like
public javax.swing.JProgressBar loadingBar;
public javax.swing.JLabel loadingText;
loadingBarLoaded & loadingTextLoaded is declared like this
public boolean loadingTextLoaded = false;
public boolean loadingBarLoaded = false;
loadingTextLoaded & loadingBarLoaded and changed to true after all the generated code puts them into place inside the window after the all-set-code is ran (I am using NetBeans GUI builder so the code is generated)
Inside the loadingPage.java this is how it is laid out.
The main window and there is a panel covering the entire main window and it has three things inside of it, a label at the top that sits in the middle, it has the application name "Xtra" and below it the loadingText subtitle that small and says what is currently loading, below that, a progress bar loadingBar.
Here is a screen shot of the layout
Sorry if I am not following the unwritten coding rules, I only started with java like a week ago.
Learn about threading in swing. You have to use a SwingWorker and do the loading in the doInBackground() method and return progress there.
import javax.swing.*;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class Main {
private JProgressBar prog;
public static void main(String[] args) throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(()-> new Main().initGUI());
}
private void initGUI(){
JDialog dialog = new JDialog();
dialog.setTitle("Progress");
prog = new JProgressBar();
prog.setMaximum(100);
dialog.add(prog);
dialog.pack();
dialog.setVisible(true);
BackgroundWorker bw =new BackgroundWorker();
bw.execute();
}
private class BackgroundWorker extends SwingWorker<String, Integer>{
#Override
protected String doInBackground() throws Exception {
try{Thread.sleep(1000);}catch(Exception e){}
publish(10);
try{Thread.sleep(1000);}catch(Exception e){}
publish(20);
try{Thread.sleep(1000);}catch(Exception e){}
publish(30);
try{Thread.sleep(1000);}catch(Exception e){}
publish(70);
try{Thread.sleep(1000);}catch(Exception e){}
publish(100);
return "finished";
}
#Override
protected void process(List<Integer> chunks) {
prog.setValue(chunks.get(chunks.size()-1));
}
}
}
Important is, that you don't access any Swing Component from the doInBackground() method, as this method is not called from the Swing Event Dispatch Thread.
I fixed it, when the GUI builder created the page it used this code
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new loadingPage().setVisible(true);
}
});
I changed it to:
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
LP.setVisible(true);
}
});
and at the begining of the file I put this:
public static loadingPage LP = new loadingPage();
and it seemed to work.
I'm calling sendPhoneNumberHandler from #OnClick which mean normal to get NetworkOnMainThreadException.
To handle this problem I used #WorkerThread annotation that allow the method call to run outside the UI/Main Thread. In contrast I still got NetworkOnMainThreadException.
#WorkerThread
public void sendPhoneNumberHandler(final Context context, String phoneNumber) {
MonoApplication application = MonolApplication.get(phoneNumberMvpView.getContext());
rx.Observable<HeaderResponse> checkPhoneNumber =
application.getMonolService(Constants.monolAPIUrl).checkPhoneNumber(phoneNumber);
subscription = checkPhoneNumber.subscribe(new Observer<HeaderResponse>() {
#Override public void onCompleted() {
}
#Override public void onError(Throwable e) {
phoneNumberMvpView.onPhoneNumberSendResult(false, 0);
}
#Override public void onNext(HeaderResponse s) {
phoneNumberMvpView.onClearText();
phoneNumberMvpView.onPhoneNumberSendResult(true, s.getError());
}
});
}
Any clue ?
Using the annotation does not make that method run off the UI thread. It simply indicates that this method needs to be executed in a separate thread
I am working on a plugin for IntelliJ Idea 13. I do some changes in beforeDocumentSaving and I use document.setText:
public class AppendAction implements ApplicationComponent
{
#Override public void initComponent()
{
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter()
{
#Override public void beforeDocumentSaving(Document document)
{
document.setText(appendSomething(document.getText()));
}
});
}
}
This works great, my only problem is that when this plugin is run, and I want to undo the changes, I get to following error message:
Cannot Undo
Following files have changes that cannot be undone:
Any Idea? :-)
The answer is wrapping the document.setText into ApplicationManager.getApplication().runWriteAction and CommandProcessor.getInstance().runUndoTransparentAction.
I found an example TrailingSpacesStripper among intellij-community sources on githib:
https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java
public class AppendAction implements ApplicationComponent
{
#Override public void initComponent()
{
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter()
{
#Override public void beforeDocumentSaving(final Document document)
{
ApplicationManager.getApplication().runWriteAction(new DocumentRunnable(document, null)
{
#Override public void run()
{
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable()
{
#Override public void run()
{
document.setText(appendSomething(document.getText()));
}
});
}
});
}
});
}
}
You should wrap the change through the CommandProcessor API.
From IntelliJ IDEA Architectural Overview:
Any operations which modify the contents of the document must be wrapped in a command (CommandProcessor.getInstance().executeCommand()). executeCommand() calls can be nested, and the outermost executeCommand call is added to the undo stack. If multiple documents are modified within a command, undoing this command will by default show a confirmation dialog to the user.
I have a view that I want to react to what happens in the editor. Right now I have a button that I want it so that when clicked it updates the data in the view to some new set of information. Where do I start, I have my selection event but no idea on how to communicate between the two. I'm looking for a loose coupling solution.
I'm sure there are many ways of doing this, but I've used the JFace IPropertyChangeListener interface in the past for simple event propagation.
Make your view implement IPropertyChangeListener. Create a Singleton class that you can register your IPropertyChangeListener with, and send a PropertyChangeEvent to. Then in the constructor of your view, register it with your Singleton.
Now you can get hold of your Singleton in your editor and fire off an event that will get picked up in your view.
Example code for the Singleton:
public class PropertyChangeEventBus {
private static PropertyChangeEventBus s_instance = new PropertyChangeEventBus();
public static PropertyChangeEventBus instance()
{
return s_instance;
}
private Set<IPropertyChangeListener> m_listeners;
private PropertyChangeEventBus()
{
// use CopyOnWriteArraySet to prevent ConcurrentModificationExceptions
m_listeners = new CopyOnWriteArraySet<IPropertyChangeListener>();
}
public void addListener(IPropertyChangeListener listener)
{
m_listeners.add(listener);
}
public void removeListener(IPropertyChangeListener listener)
{
m_listeners.remove(listener);
}
public void fire(final PropertyChangeEvent event)
{
// run property change events in UI thread to prevent having to have lots of syncExecs in the listener methods
ViewUtils.syncExec(new Runnable()
{
#Override
public void run()
{
for (IPropertyChangeListener listener : m_listeners)
{
try
{
listener.propertyChange(event);
}
catch(Exception e)
{
//log it, present error message
}
}
}
});
}
}
Example Code for the View:
//The constructor
public MyView()
{
PropertyChangeEventBus.instance().addListener(this);
}
#Override
public void propertyChange(PropertyChangeEvent event)
{
if(event.getProperty().equals(SOME_CONSTANT))
{
// Refresh View
}
}
I would like to ask some questions regarding the use of threads. I have looked at a lot of posts and links suggested from those posts but still came up blank.
I have a NetBeans project that has a few classes. One of them is the Gui class that I use to just click a button and some processing gets performed. From the Gui I call an instance of another class that in turn calls other classes. One of these classes submits a Sparql query to a TDB backend database. All output is saved to files for now.
What I would like to do is to somehow make the class called from the Gui to run on another thread and also to be able to update an EditorPane and a TextArea on the Gui from one or more of the called classes. Up to now I have tried calling an instance of the Gui class and use a public method within but this does not work. I am calling the instance Gui with
Gui gui = new Gui();
gui.setEditorPaneText("File name is: " + fn);
and the method in the Gui class is
public void setEditorPaneText(final String string) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setString(string);
EditorPane.setText(getString());
EditorPane.repaint();
}
});
}
I tried running the debugger but the processing skips from the first line of the method to the last curly bracket without processing the code within. My Gui class has the following as a main method. The commented part was a previous version of the event queue that I changed while I was reading through the numerous posts on the issue.
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Gui().setVisible(true);
throw new UnsupportedOperationException("Not supported yet.");
}
});
}
The following is the previous code of the main method that I replaced after reading some of the posts on this issue.
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Gui().setVisible(true);
}
});
Any helpful information will be much appreciated. Thank you.
I think your main error is that you create two instances of your Gui class. You have the following snippet twice: new Gui(). Take a look at my example code below to see an example how to pass the Gui to your worker thread.
// This is handwritte-untested-uncompiled code to show you what I mean
public class Main {
public static void main(String[]args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Gui g = new Gui();
g.show(); // show() is equal to setVisible(true)
g.doBackendAction(); // Normally this would be invoked by a button or sthg. I was to lazy
}
});
}
}
public class Gui extends JFrame {
private JTextArea area;
public Gui() {
// Just some code to create the UI. Not sure if this actually does sthg right :P
area = new JTextArea();
setContentPane(area);
pack();
}
public void setTextAreaContent(final String string) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
area.setText(string);
this.repaint(); // Not 100% sure if we need this
}
});
}
public void doBackgroundWork() {
BackgroundWorker w = new BackgroundWorker(this);
new Thread(w).start(); // Start a worker thread
}
}
public class BackgroundWorker implements Runnable {
private Gui gui;
public BackgroundWorker(Gui gui) {
this.gui = gui; // we take the initial instance of Gui here as a parameter and store it for later
}
public void run() {
try { Thread.sleep(10 * 1000); } catch (InterruptedException e) {; }
this.gui.setTextAreaContent("Hello World!"); // calls back to the Gui to set the content
}
}