Delay processing until GUI input - java

Following is the code to input proxy settings from user.
public static void setProxy()
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new proxy().setVisible(true);
}
});
// Control should halt here till user gives input as it assigns value to the variables
String host = prox;
String port = prt;
System.out.println("Using proxy: " + host + ":" + port);

You're not doing it correctly. The main method of a GUI application should do only one thing: start the GUI. The rest of the logic will be triggered by events fired by the user interacting with the GUI.
So, assuming your GUI displays a frame containing 2 text fields to enter the host and the port and a button to proceed, you should have, in your GUI, an action listener on the button:
proceedButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String host = hostTextField.getText();
String port = portTextField.getText();
doSomethingWithHostAndPort(host, port);
}
});
If doSomethingWithHostAndPort() does domething long, then it should do it in a separate thread, to avoid freezing the GUI.

You need to provide more information if you want a precise answer. But I'm going to make a few assumptions, you have a button which is pressed after information is entered, that button has an on click event attached to it, that is where you add your variable assignments and start the processing.

Related

Cannot successfully create instance of a JFrame class outside of main

I am writing a simple encrypted instant messenger, which involves two projects: A server and client. I got these two working very well, but decided to add a prompt on the client side to ask for a server IP Address and port number. Upon the user pressing a button, I have added an action listener for it to create an instance of the instant messenger class, which extends JFrame.
When this is done from inside this action listener (or outside in a separate method, I have tried) it creates the client JFrame, but none of the components exist inside. The client connects to the server as normal.
When the client is created in the main method, however, the program works as normal.
Here, below, is the difference between what works and what will not work:
This does not work:
okay.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
try{
port = Integer.parseInt(portField.getText());
}catch(NumberFormatException e){
return;
}
serverIP = serverIPField.getText();
dispose();
Client c = new Client(serverIP, port);
c.startRunning();
}
});
However, this does work (same class):
public static void main(String args[]){
Client c = new Client(serverIP, port);
c.startRunning();
}
All I have done, is change where the Client class is created, from inside a class constructor (called Prompt, which creates a JFrame to ask for the IP and port) to the main method. I have tried creating an instance of the Client from a new class, with the main method in, which seems to work fine.
As I stated before, the JFrame is created and is opening fine, it even connectes successfully to the server, but none of the components exist (just a blank white area). This is strange as this is not the case when it is created inside the main method.
I have little idea as to why this is happening, so if somebody could explain why, that would be brilliant.
Thanks.
Edit: I appreciate that I may have not added enough information. Here is the Client constructor:
public Client(String host, int port){
super("Instant Messenger - Client");
this.port = port;
serverIP = host;
initComponents();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2);
setVisible(true);
keys = new EncryptionKeys(1024);
}
Start running method:
public void startRunning(){
try{
connectToServer();
setupStreams();
exchangeKeys();
whileChatting();
}catch(EOFException eofEx){
showMessage("\nClient Terminated connection");
}catch(IOException ioEx){
showMessage("\nCould not connect to server.");
}finally{
closeDown();
}
}
And the while chatting method that is called by startRunning():
private void whileChatting() throws IOException{
ableToType(true);
do{
try{
message = (String) input.readObject();
if(!keysSent){
if(message.substring(0, 1).equals("n")){
try{
keys.nForeign = new BigInteger(message.substring(1, message.length()));
}catch(NumberFormatException nfEx){
showMessage("Error sending keys");
}
}
else if(message.substring(0, 1).equals("e")){
try{
keys.eForeign = new BigInteger(message.substring(1, message.length()));
keysSent = true;
}catch(NumberFormatException nfEx){
showMessage("Error sending keys");
}
}
continue;
}
showEncryptedMessage(message);
}catch(ClassNotFoundException cnfEx){
showMessage("\nUser Sending error");
}
}while(!message.equals("SERVER - END"));
}
The dispose() closes the JFrame for the prompt to the user. The class Prompt is solely used to get information from the user regarding IP address and port, which is then closed, so that the Client can be opened.
All code to manage Swing components (eg painting, listeners, etc...) occurs on a single thread: the Event Dispatch Thread (EDT). The startRunning method calls whileChatting, which contains a do/while loop that doesn't immediatedly exit. What happens in the two Scenarios:
When the Client is created in main, it is created on the main Thread. This allows the while loop to operate on the Main thread, allowing the the EDT to run in parallel. (Note: you should create your GUI on the EDT using SwingUtilities)
When the Client is created on the EDT (in actionPerformed), the non-terminating loop will occur on the EDT, effectively locking it up and not allowing it to perform any tasks until it terminates (in other words, the components in the JFrame cannot paint, update, or react until the EDT is free)
If you wish to perform a long running task, then you should consider using a Thread.

Blocking main-thread after gui-event

I've been making a program that reads from a file, identifies common "posts" in the file, and makes a summary of these. My problem is that the GUI-event that allows the user to specify the name and search-term of the post, does not interrupt the running of the program, like I want it to.
I can make it stop, but then the GUI will not be correctly displayed. I have tried some solutions, which will be specified at the bottom of the post.
EDIT: removed codedump and added something resembeling an SSCCE:
class SSCCE{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
new Gui();
}
});
}
}
class Gui implements ActionListener{
boolean runn=true;
JFrame wind2;
JTextField nameF, searchtermF;
JButton done;
Gui(){
runEx();
}
public void runEx(){
int i =0;
while(runn){
if(i==10) break;
System.out.println("Open window and record information given! One at the time!!!");
System.out.println(" ");
giveName("test");
i++;
}
}
public void giveName(String s){
JLabel nameL = new JLabel("Give this post a name:");
JLabel searchL = new JLabel("What do you want the searchterm to be?");
wind2 = new JFrame("EazyMoney");
wind2.setLayout(new BorderLayout());
JPanel all = new JPanel();
all.setLayout(new GridLayout(2,2));
searchtermF = new JTextField(30);
nameF=new JTextField(30);
all.add(nameL);
all.add(nameF);
all.add(searchL);
all.add(searchtermF);
done = new JButton("Press when you have filled in the information!");
done.addActionListener(this);
String prn = "The post in question: " + s;
JLabel header = new JLabel(prn);
wind2.add(header, BorderLayout.NORTH);
all.setVisible(true);
wind2.add(all, BorderLayout.CENTER);
wind2.add(done, BorderLayout.SOUTH);
wind2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
wind2.pack();
wind2.setLocationRelativeTo(null);
wind2.validate();
wind2.setVisible(true);
}
public void actionPerformed(ActionEvent e){
System.out.println("Action recorded, new window can now be shown. All information stored.");
System.out.println(" ");
}
}
The solutions I have tried is:
A simple block, that does a while(true){} and sets the variable to true after the first instance of g.giveName() have been called. I used the ActionListener to call a method that then changed the variable to false again, when the necessary input was given. This resulted in a gray box, with nothing in it.
Making a cyclic barrier that did the same as the block above. Used a separate thread to call g.giveName() and then call the await() from the action listener. Same result as above.
Making readFile be run by a separate thread and call invokeAndWait() on the g.giveName() function. Gave cannot call invokeAndWait() from the EDT-thread, even though it was run from a new thread.
I can not give examples of the code used in instances above, as I have tried a lot of different solutions and do not have it any more. Please take into account that it might have been implemented wrong, and thus might be a valid answer to my question, even though I could not seem to get it to work!
Final note: all work can be found here, if you wish to test the code:
https://github.com/Robiq/EazyMoneyWork
The way to avoid blocking the EDT if you need to execute something else on the same thread it is to temporarily create a new event queue. Here is some example code. In this case it blocks the current thread waiting for some other event to be signalled but you could replace this with whichever long running process is required.
First check if you are running on the EDT: SwingUtilities.isEventDispatchThread. Then if you are:
EventQueue tempEventQueue = new EventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(tempEventQueue);
try {
wait();
} catch (InterruptedException ex) {
// stop waiting on interrupt
} finally {
tempEventQueue.pop();
}
Something similar to this is how modal dialogs work in Swing. However in general it's not good practice. Much better is to understand which events to listen for to perform specific actions. In your case the user event should not 'stop' your program - it should disable inappropriate components until the user has responded and then re-enable them.

Program displays filenames in a JTextArea as it walks the directory tree but I don't know how to stop it via a keypress

There are two windows: a GUI for user input and Output window for list of filenames found. Execution must be user-stoppable via a keypress and must leave both windows open because the program processes subdirectories, so it can run a long time, possibly stepping thru 100_000 files, either producing tons of output or none at all, depending on how user's filename pattern matches files encountered in the selected starting node.
Here's my question:
How do I look for a keypress (e.g., ESC or CTRL-C) to allow user to terminate? (Clicking red X isn't an option since that closes windows; user needs to see what's been found before termination. Doing so does not close either window anyway since all buttons are disabled once tree walk begins.)
I've tried putting keyListeners in several places, but once the "Start" button is clicked, all the swing components are disabled.
This seems like such a common situation that I'm surprised I can't find any textbook, thread, or Google info that directly answers the question. So I'm afraid it's not gonna be at all easy. That would be no surprise. I may have found a clue here but I can't get it to compile and the link contained there doesn't lead to that code snippet.
The search begins when the Search button is clicked:
private void jbSearchActionPerformed(ActionEvent evt) {
SearchyGUI.doIt();
}
The doIt() method walks the directory tree by an extension of SimplefileVisitor:
public class OverriddenFileVisitor extends SimpleFileVisitor<Path> {
...
}
public static void doIt(){
try {
visitor = new OverriddenFileVisitor();
info.setVisible(true);
Files.walkFileTree(SearchyGUI.p , visitor);
}
catch (Exception e) { }
}
}
Output is written to jTextArea1 via the report() method:
public static void report(String s){
Output.jTextArea1.append(s + "\n");
}
This is done primarily in the visitFile() method of SimpleFileVisitor:
public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException {
report(foundkt + "--" + f.getFileName().toString());
return FileVisitResult.CONTINUE;
}
Here's the main class:
public class SearchyGUI {
static Output info;
static Path p ;
static FileVisitor visitor ;
static GUI gui
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
gui = new GUI();
gui.setVisible(true);
}
});
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
info = new Output();
}
});
}
The problem is you are hogging the GUI thread, so the GUI thread can't process any events originating from the user.
You need to create a new Thread and do the work in there. Then, to display output from that thread, you can use SwingUtilities.invokeLater or something like that.
The Key Bindings API is probably the best choice for monitoring key strokes.
I would also add a [Cancel] button to the UI, which shared the same action...
public class CancelAction extends AbstractAction {
public CancelAction() {
putValue(NAME, "Cancel");
}
public void actionPerformed(ActionEvent evt) {
// Perform the cancel operation...
}
}
Then some where else in your code...
CancelAction cancelAction = new CancelAction();
JButton cancelButton = new JButton(cancelAction);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
am.put("Cancel", am);
Now the other problem you're going to have is the fact that you look like you are running a long running task within the context of the Event Dispatching Thread. This is going to prevent your program from being able to update the UI or allow the user to interact with the UI.
If you need to make changes to the UI (ie, show the output of the file processing), you should try a SwingWorker.
The main reason being is that it allows you to execute the long running task in another thread, but provides the mechanism for re-syncing updates back to the EDT, where it is safe to make changes to the UI.
Take a look at Concurrency in Swing for more details.
Regardless of which direction you take, you're going to need to supply a reference to the object that is carrying out the task and provide some kind of "cancel" flag, which the "task" object will need to monitor
The way I had left this program last night was unsatisfactory since Exit resulted in user not being able to see the output so far displayed (it could be useful). So I established window listeners and used the close event to set a boolean aborted to true to prevent further output to the window, but the thread kept running, which led to intermittent problems if another search was started before the thread ended.
Here's how I fixed it.
The FileVisitor interface has 4 methods to implement to walk the tree--two for each file visited, two for each directory. Each returns a FileVisitResult which is normally FileVisitResult.CONTINUE. By changing the return value to FileVisitResult.TERMINATE in the file visitor thread, it terminates appropriately! That is, I set a flag that the thread could check and take appropriate action, which is exactly what #MadProgrammer suggested.
public static FileVisitResult disposition = FileVisitResult.CONTINUE;
...
private static void report(String s){
if (! aborted)
try{
Output.jTextArea1.append(s + "\n");
}
catch (Exception e){
aborted = true ;
disposition = FileVisitResult.TERMINATE;
}
}
...
#Override
public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException {
f1 = new File(f.getParent().toString() + "\\" + f.getFileName().toString());
long filesize = f1.length();
report(f.getFileName().toString() + "\t found in " + f.getParent().toString());
return disposition;
}
I am one happy camper! Thank you BOTH for your ideas and input.
Well, I made it stop. I guess if you wander the woods long enough you'll find a gnome. I read Robin's hint last week and sort of gave up. Then I read some more and more. And then more. But Robin assured me that gnomes DO exist in these here woods!
The code I used was a modification of some I found for a MatLab/Java app. (Why'd I even look at it?? Best apparent Google hint.)
I made the "file visitor" (directory tree walker component) startable as a thread as Robin advised:
public class OverriddenFileVisitor extends SimpleFileVisitor<Path> implements Runnable{
// ................................................................^^^^^^^^^^^^^^^^^^^
In doIt() I made a couple of changes, moving the lines that process the directory to the now-runnable class and started the file visitor as its own thread in doIt():
public static void doIt(){
try {
new OverriddenFileVisitor().startTh();
//^^^^^^^^^^
//(moved) Files.walkFileTree(SearchyGUI.p , visitor);
...
I added the new method in the previous line to OverriddenFileVisitor class: (This is the main part of the MatLab/Java code that made sense to me so I used and modified it.)
public void startTh() {
Thread t = new Thread(this);
t.start();
}
And I inserted the overridden run() method for the class:
public void run() {
try {
Files.walkFileTree(SearchyGUI.p , this); // Used to be in doIt().
}
catch (IOException ex) { }
}
It ran and gave correct results and stopped when I hit Exit button, which "became" enabled after revising the file visitor to run in its own thread, which is what #Robin Green was saying. I almost feel like I know what I've done.
P.S. Note that I already was able to get my output via invokeLater()--last several lines of original question.
It's not finished but it's much more satisfactory.

JFrame freezes while running the code continuously

I have problem while working with JFrame, which get freezes while
running the code continuously. Below is my code:
On clicking on btnRun, I called the function MainLoop():
ActionListener btnRun_Click = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
MainLoop();
}
};
Implementation of MainLoop():
void MainLoop()
{
Hopper = new CHopper(this);
System.out.println(Hopper);
btnRun.setEnabled(false);
textBox1.setText("");
Hopper.getM_cmd().ComPort = helpers.Global.ComPort;
Hopper.getM_cmd().SSPAddress = helpers.Global.SSPAddress;
Hopper.getM_cmd().Timeout = 2000;
Hopper.getM_cmd().RetryLevel = 3;
System.out.println("In MainLoop: " + Hopper);
// First connect to the validator
if (ConnectToValidator(10, 3))
{
btnHalt.setEnabled(true);
Running = true;
textBox1.append("\r\nPoll Loop\r\n"
+ "*********************************\r\n");
}
// This loop won't run until the validator is connected
while (Running)
{
// poll the validator
if (!Hopper.DoPoll(textBox1))
{
// If the poll fails, try to reconnect
textBox1.append("Attempting to reconnect...\r\n");
if (!ConnectToValidator(10, 3))
{
// If it fails after 5 attempts, exit the loop
Running = false;
}
}
// tick the timer
// timer1.start();
// update form
UpdateUI();
// setup dynamic elements of win form once
if (!bFormSetup)
{
SetupFormLayout();
bFormSetup = true;
}
}
//close com port
Hopper.getM_eSSP().CloseComPort();
btnRun.setEnabled(true);
btnHalt.setEnabled(false);
}
In the MainLoop() function, the while loop is running continuesly until the Running is true problem is that if i want to stop that while loop i have to set Running to false which is done at another button btnHalt:
ActionListener btnHalt_Click = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
textBox1.append("Poll loop stopped\r\n");
System.out.println("Hoper Stopped");
Running = false;
}
};
but btnHalt is not responding, whole frame is get freeze, also not
showing any log in the textarea.
Swing is a single thread framework. That is, there is a single thread responsible for dispatching all the events to all the components, including repaint requests.
Any action which stops/blocks this thread will cause your UI to "hang".
The first rule of Swing, NEVER run any blocking or time consuming tasks on the Event Dispatching Thread, instead, you should use a background thread.
This runs you smack into the second rule of Swing. Never create, modify or interact with any UI component outside of the EDT.
There are a number of ways you can fix this. You could use SwingUtilities.invokeLater or a SwingWorker.
SwingWorker is generally easier, as it provides a number of simple to use methods that automatically re-sync there calls to the EDT.
Take a read through Concurrency in Swing
Updated
Just so you understand ;)
Your MainLoop method should not be executed within the context of the EDT, this is very bad.
Also, you should not be interacting with any UI component from any thread other the then the EDT.

How can I handle when I change the screen

I try build the 2 Class: LoginScreen Class and MainScreen Class
When I run program it will show the login screen first then I use username and password to login the Mainscreen are pop-up but the login screen doesn't disappear.I am not sure how to handle it correctly.
Because the method I use is
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (OK.equals(cmd)) { //Process the password.
char[] input = passwordField.getPassword();
if (isPasswordCorrect(input)) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrameExample.main(null);
}
});
} else {
JOptionPane.showMessageDialog(controllingFrame,
"Invalid password. Try again.",
"Error Message",
JOptionPane.ERROR_MESSAGE);
}
//Zero out the possible password, for security.
Arrays.fill(input, '0');
passwordField.selectAll();
resetFocus();
} else { //The user has asked for help.
JOptionPane.showMessageDialog(controllingFrame,
"You can get the password by searching this example's\n"
+ "source code for the string \"correctPassword\".\n"
+ "Or look at the section How to Use Password Fields in\n"
+ "the components section of The Java Tutorial.");
}
}
I know this is the stupid code and wrong way to implement it but can you guide me to make the appropriate one.
I guess this method is a method of your first screen, which must be a JDialog or a JFrame. Just call setVisible(false) to hide the frame (you may also call dispose() if the dialog won't be used anymore).
Also, you shouldn't call the main method on JFrameExample. A main method is normally used to start a new application. Just do what the main method does from your action listener (probably new JFrameExample().setVisible(true)).
Finally, an event listener is always invoked in the event dispatch thread (EDT), so there is no point in using SwingUtilities.invokeLater from an event listener.
To recap, here's how the code should look like:
if (isPasswordCorrect(input)) {
setVisible(false); // or dispose();
JFrame mainFrame = new JFrameExample();
mainFrame.setVisible(true);
}

Categories