Delay when coding in Java Swing - java

I am writing an GUI application using Java Swing. I just have 1 problem which is that I wrote a big function that gets executed when clicking a button. At first I am setting the value of a label to "generating report.." then just before the 'return' part of the function I set the value of the label "done generating report". What is actually happening is that the first setting of the label doesn't take effect. They both take effect after I am done with the function call so the end result just shows 'done generating report'.
Here is the java code I am using:
protected void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
// TODO add your handling code here:
try
{
jLabel6.setText("Generating Report");
jProgressBar1.setVisible(true);
generateReport(BillImportId.getText()); // This will take around 30 seconds to finish
}
catch (Exception e)
{
System.out.println("Exception occured: " + e);
}
}//GEN-LAST:event_jButton2ActionPerformed

Move the call to generateReport into the method doInBackground of a SwingWorker. Use the methods process and done to update your UI elements.
Check out also "Concurrency in Swing"

Related

How to detect a pressed key globally, in a window without focus in java? [duplicate]

I'm making a small program in Java using the Robot class. The program takes over the mouse. while in the course of debugging if it starts acting in a way that I don't want it's hard to quit the program, since I can't move the mouse over to the terminate button in eclipse, and I can't use hotkeys to hit it because the mouse is constant clicking in another window, giving that window focus instead.
What I'd like to do is just hook up a keylistener so that when I hit q I can quit the program, but the only way I know how to do this involves making a window, and that window needs focus to capture the input. Is there a way to listen for keyboard or mouse input from anywhere, regardless of what has focus?
There is a library that does the hard work for you:
https://github.com/kwhat/jnativehook
This is not a trivial problem and Java doesn't give you a way to do it elegantly. You can use a solution like banjollity suggested but even that won't work all the time if your errant mouse clicks open another fullsized window currently open in your taskbar for example.
The fact is, Java by default gives developers very little control over the OS. This is due to 2 main reasons: security (as citied by java documentation) and the fact that different operating systems handle events completely differently and making one unified model to represent all of these would probably not make a whole lot of sense.
So to answer your question, I imagine what you want is some kind of behaviour for your program where it listens for keypresses globally, not just in your application. Something like this will require that you access the functionality offered by your OS of choice, and to access it in Java you are going to need to do it through a Java Native Interface (JNI) layer.
So what you want to do is:
Implement a program in C that will listen for global keypresses on your OS, if this OS is Windows than look for documentation on windows hooks which is well docuemented by Microsoft and MSDN on the web and other places. If your OS is Linux or Mac OS X then you will need to listen for global keypresses using the X11 development libraries. This can be done on an ubunutu linux distro according to a Howto that I wrote at http://ubuntuforums.org/showthread.php?t=864566
Hook up your C code to your Java code through JNI. This step is actually the easier step. Follow the procedure that I use in my tutorial at http://ubuntuforums.org/showthread.php?t=864566 under both windows and linux as the procedure for hooking up your C code to your Java code will be identical on both OSes.
The important thing to remember is that its much easier to get your JNI code working if you first code and debug your C/C++ code and make sure that it is working. Then integrating it with Java is easy.
Had same problem. In my case, robot just controlled a single Windows App, that was maximized. I placed these lines at top of main loop driving the robot:
Color iconCenterColor = new Color(255,0,0); // if program icon is red
if (iconCenterColor.equals(robot.getPixelColor(10,15)))
throw new IllegalStateException("robot not interacting with the right app.");
To cancel the robot, just alt-tab to another app. Works great for a simple one app driving robot.
Start the program from a command line in a terminal and use Ctrl-C to terminate it.
(As mentioned by #MasterID and shown on JNativeHook's documentation for native keyboard input detection {main GitHub project here}),
This code should be enough to listen to any key without app focus (press and/or release):
>>Remember to add the jnativehook library in your project to be able to use all its utilities.<<
public class yourClass implements NativeKeyListener {//<-- Remember to add the jnativehook library
public void nativeKeyPressed(NativeKeyEvent e) {
System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}
public void nativeKeyReleased(NativeKeyEvent e) {
System.out.println("Key Released: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}
public void nativeKeyTyped(NativeKeyEvent e) {
System.out.println("Key Typed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}
public static void main(String args[]){
//Just put this into your main:
try {
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
System.err.println("There was a problem registering the native hook.");
System.err.println(ex.getMessage());
System.exit(1);
}
GlobalScreen.addNativeKeyListener(new yourClass());
//Remember to include this^ ^- Your class
}
}
For this particular problem, use the nativeKeyPressed method like this:
public void nativeKeyPressed(NativeKeyEvent e) {
System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
if (e.getKeyCode() == NativeKeyEvent.VC_Q){
System.exit(1);
}
}
Note that JNativeHook by default shows a lot of stuff in your console that you might not want, to change that, just add this right before the try-catch that you used in the main function as shown (this is also going to turn off warning and error messages, more info here):
//(From here)
Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.OFF);
logger.setUseParentHandlers(false);
//(To there-^)
try {
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
System.err.println("There was a problem registering the native hook.");
System.err.println(ex.getMessage());
System.exit(1);
}
Disclaimer: I know this question was solved years ago, I just hope someone finds this a little easier to find/use.
Have your program open a second window which displays underneath your main window but is maximised, then your errant mouse clicks will all be received by the maximised window, and it can receive your keyboard input.
Here's a pure Java way to do it to solve the problem you've described (not the KeyListener problem... the quit test early when using robot problem):
Throughout your test, compare the mouse position with one that your test has recently set it to. If it doesn't match, quit the test. Note: the important part of this code is the testPosition method. Here's code that I used recently:
public void testSomething() throws Exception {
try {
// snip
// you can even extract this into a method "clickAndTest" or something
robot.mouseMove(x2, y2);
click();
testPosition(x2, y2);
// snip
} catch (ExitEarlyException e) {
// handle early exit
}
}
private static void click() throws InterruptedException {
r.mousePress(InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(30 + rand.nextInt(50));
r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(30 + rand.nextInt(50));
}
private static void testPosition(int x2, int y2) throws ExitEarlyException {
Point p = MouseInfo.getPointerInfo().getLocation();
if(p.x != x2 || p.y != y2) throw new ExitEarlyException();
}

Is DefaultListModel over-riding the order of actions in my actionlistener?

I have a section of my JAVA program where you click a button and the actionListener should go through the following process;
Change the text on the button from "Start" to "Standby"
Add a label to a panel stating that a process has started
Execute a method (that sorts data and returns it via addelement to a defaultListModel JList, and finally
Change the text on the button from "Start" to "Complete"
as per below
uploadNotamButton.addActionListener((ActionEvent e) -> {
if(e.getSource()==uploadNotamButton)
uploadNotamButton.setText("STANDBY");
progressLabel.setText("Process Has Begun, standby...");
progressLabel.setVisible(true);
uploadNotams();
uploadNotamButton.setText("COMPLETE");
});
However, when I press the button, the button text does not change, the label does not show, but the method executes. Only when the method is complete, does the button text change to "Complete" (never showed "STANDBY") and the label stating "the process has begun, standby" displays (when the process is complete).
Is this a feature of defaultlistmodel that takes priority over everything or my coding inexperience?
Also, the data that gets analysed in the method, is displayed in the JList in one go, and not each element at a time. If the data was shown in the list as it was analysed, it would at least show that something was happening. Is this not possible with the defaultListModel?
Many Thanks in advance
PG
Is this a feature of defaultlistmodel that takes priority over everything or my coding inexperience?
This has nothing to do with DefaultListModel and all to do with Swing being single-threaded. Your long running process is being run on the Swing event thread, blocking this thread from doing its necessary actions, including drawing text and images on your GUI and interacting with users.
The solution is to use a background thread such as can be obtained through a SwingWorker, running your long-running code in this background thread, adding a PropertyChangeListener to the worker to be notified when it's done, and then respond to this notification.
For example (code not tested)
uploadNotamButton.addActionListener((ActionEvent e) -> {
// if(e.getSource()==uploadNotamButton)
uploadNotamButton.setText("STANDBY");
progressLabel.setText("Process Has Begun, standby...");
progressLabel.setVisible(true);
// create worker to do background work
SwingWorker<Void, Void> worker = new SwingWorker<>() {
#Override
protected Void doInBackground() throws Exception {
// this is all done within a background thread
uploadNotams(); // don't make any Swing calls from within this method
return null;
}
};
// get notified when the worker is done, and respond to it
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue == SwingWorker.StateValue.DONE) {
uploadNotamButton.setText("COMPLETE");
// the code below needs to be surrounded by a try/catch block
// and you'll need to handle any exceptions that might be caught
((SwingWorker) evt.getSource()).get();
}
}
});
worker.execute(); // run the worker
});

Can I make an automated java application ignore JOptionPanes?

There is a java swing application that I need to automate one of the functions of. It's quite simple - the user clicks a button in the swing application and starts an action.
I made a small java application that includes the java swing application as a .jar and calls the action behind the button (read).
The problem is - in case of an exception, the swing .jar shows JOptionPane, which halts the automated execution. Is it possible to somehow override this behavior without altering the original jar?
Application structure:
Main.java
import com.swingapp.ui
public static void main(String[] args){
Swingapp.read();
}
Then the read() function in the Swingapp library:
public void read(){
try{
//do a bunch of stuff...
} catch (Exception ex){
JOptionPane.showMessageDialog(null, ex.getMessage()); // In case of an exception, the swing application will show a message dialog. This halts the automated execution of my java task, I'd like to just skip this
}
When exception happens in above application, user is expected to click "OK". But running this as automated task, nobody there to click okay
Since a JOptionPane gains focus as soon as it opens (I think the most right button gets the focus, but it does not matter in your case) you can do the following:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent arg0) {
Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
while(c != null) {
if (c instanceof JOptionPane) {
try {
new Robot().keyPress(KeyEvent.VK_ENTER);
} catch (AWTException e) {
e.printStackTrace();
break;
}
}
c = c.getParent();
}
}
}, AWTEvent.FOCUS_EVENT_MASK);
It will traverse up to see if anything in the current hierarchy is an instance of JOptionPane. If so -> simulate that the user pressed Enter (Return) which will close the dialog even if the focus is in an input field.
I have following solution for you. You need to registrate a listener to monitor all window events. Use Toolkit.getDefaultToolkit().addAWTEventListener(). If you get a window opened event, try to check whether the window is a JDialog and whether the dialog's contentPane contains an instance of JOptionPane. If yes you need traverse the component tree, find the first button and click it.

How do I update the appearance of my JButton when it's clicked?

I'm working on a Java7 Swing "wizard" type of project that needs to validate a web address before continuing on to the next step of the wizard. The validation requires accessing a URL over the internet to verify that expected resources are available. In some cases, this can take a few seconds which can be long enough to confuse a user.
As a quick solution to this, I would like to disable the "next" button and change the display text while the validation is running so the user knows that the wizard is working and not hung up on anything. The problem is that when I add the code to modify the JButton, none of the changes happen until after the validation has completed. This is the case even if I change the button and call revalidate() before I execute the validation methods.
Here is an excerpt of what I've tried:
// create next button
next = new JButton("Next", new ImageIcon(getClass().getResource("/navigate_right.png")));
next.setActionCommand("MYACTION");
next.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Is this the event dispatch thread? "
+ javax.swing.SwingUtilities.isEventDispatchThread());
System.out.println("Changing button");
next.setEnabled(false);
next.setText("Testing Connection");
next.getRootPane().revalidate();
System.out.println("Validating Service");
boolean isValidLocation = ServiceValidator.serviceExists(locationField.getText());
// ...etc...
When I run this code, the lines "Changing button" and "Validating Service" both get printed before the actual button changes in the display. How can I get the JButton to change before System.out.println("Validating Service"); is printed?
The problem is that when I add the code to modify the JButton, none of the changes happen until after the validation has completed.
Your code is executing on the EDT, so you long running code prevents the GUI from repainting itself until the task is finished executing. You need to use a separate Thread for the long running task, maybe a SwingWorker. Read the section from the Swing tutorial on Concurrency for more information.

A proper way to output text immediately in Java Swing GUI

Such a piece of code:
private void log(String message) {
LogBox.append(message + "\n");
}
private void log(Exception e) {
log(e.getMessage());
}
private void ConvertButtonActionPerformed(java.awt.event.ActionEvent evt) {
String url = UrlBox.getText();
if (url.isEmpty()) {
log("Empty URL");
return;
}
LogBox.setText("");
try {
log("URL "+url+" accepted. Trying to download...");
String content = URLConnectionReader.getText(url);
log("Downloaded. Parsing the content...");
//
} catch (Exception e) {
log(e);
}
}
should output each message to the LogBox (JTextArea) immediately after each log call, but outputs URL ... accepted only when URLConnectionReader.getText(url) finishes.
There were several ways do do an immediate output:
Application.DoEvents in Visual Basic 6 and .NET
Application.ProcessMessages in Delphi
Is there some simple way to do an immediate output? I was studying questions about the DoEvents and how to do this in Java, but I think that starting to learn Java from multi-threading isn't a right approach.
Create a SwingWorker to do the download: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
The role of an ActionListener is just that: to listen for user action, and initiate a response to that action by the program.
Sometimes, the program's response is very quick, and only involves the GUI. For example, in a calculator app, you could have a listener attached to an "equals" button that calculates the result of the current expression and writes it to a textbox. This can all be done within the listener method (although you might want to separate behavior for testing).
If the response to an user action is to initiate some long-running process, like downloading and parsing the file, then you don't want to do this within the listener body, because it will freeze the UI. Instead, gather any information (in your case, the URL value) from within the listener, and spin up a SwingWorker to handle the program's response.
In my comment, I suggested moving everything after the getText() into a SwingWorker. This is because, to me, the response is "download a file if you have a valid URL, and log the progress." And as I see it, testing for an empty string is part of that response. If you want to leave the empty-string test inside the listener, that's fine, but imo it's less testable.
You must leave the call to getText() inside the body of the listener, because you are only allowed to access Swing components from the event dispatch thread. If you moved that call into the worker, then it might access the textbox at the same time the textbox is updating itself, resulting in corrupt data.
Read up on Concurrency.
You should probably use a SwingWorker for the long running task, then you can publish results to the GUI as they become available.

Categories

Resources