Background
So I've been trying to improve my code style and write a bit more functionally. I write software daily not as a requirement for my job (web design), but to expedite my responsibilities and automate them and save time (this approach has proven invaluable and made me somewhat of a superstar).
Issue
In one of my recent projects, I have tried to use a JFileChooser in the following way:
List<String> lines;
do {
lines = new ArrayList<>();
try {
lines = jFC.showOpenDialog(new JFrame() {
{
// originally setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}) == JFileChooser.APPROVE_OPTION
? Files.readAllLines(jFC.getSelectedFile().toPath())
: null;
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error selecting File.");
}
System.out.println("Here we go");
} while (lines == null || lines.isEmpty());
However neither my original implementation nor my revised one actually work, which I tested using print statements (The reimplemented version was never called.) I then tried to do the following:
JFrame test = new JFrame();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lines = jFC.showOpenDialog(test) == JFileChooser.APPROVE_OPTION
? Files.readAllLines(jFC.getSelectedFile().toPath())
: null;
Which produced the same result. I've searched for duplicates but could not find something that reproduced my exact situation. Essentially, I want my program to completely terminate anytime a JFileChooser is canceled but I am reluctant to change my ternary statements as it does not fix my intended behavior but instead proposes a work around. My intuition tells me its a scope problem but for windowClosing not to be called raises a few questions for me.
tl;dr
System.exit(0) on JFrame for JFileChooser not working, not going to change flow control so don't bother asking.
Related
I'm trying to write a logic in memory game that when I click on cards and they are not a pair (different ID), program should swap them back after 1s. If they are same, then leave them as they are.
The problem is that when I first click and the card appears, after second clicking on another (different) card it doesn't appear and swap the first card after 1s. someone knows why the second card does not appear after clicking?
Btw when the pair is correct, everything works fine, here is my fragment of the code responsible for that logic in listener:
final int copy = i;
card2.addActionListener((e) -> {
card2.setIcon(new ImageIcon(icons[copy].getAbsolutePath()));
if(firstClick == null)
{
firstClick = (Card)e.getSource();
}
else
{
Card secondClick = (Card)e.getSource();
if(firstClick.getID() != secondClick.getID())
{
try
{
Thread.sleep(1000);
} catch (InterruptedException e1)
{
//e1.printStackTrace();
}
firstClick.setIcon(new ImageIcon(background.getAbsolutePath()));
secondClick.setIcon(new ImageIcon(background.getAbsolutePath()));
firstClick = null;
}
else
firstClick = null;
}
});
While method actionPerformed is executing, the GUI cannot react to mouse and keyboard events, so basically your code is "freezing" your GUI for one second. I believe that the class javax.swing.Timer is what you need and at first glance it looks like the duplicate question that MadProgrammer referred to may help you.
I am having issues setting up a computer vs computer loop in my java game applet. I have been trying for 3 days now to effectively add a one second delay between the two computer-player turns, while also repainting the board. I have tried try/catch/thread.sleep and wait and a few other tricks, however none have been successful. In the program's current state, when a computer vs computer game is initiated, the program freezes for the duration of the game (with one second delays between moves) and then displays the final board when the game has finished. How can I make it so the program repaints/delays after each move? From all of the reading I have done, I am aware that the following implementation will not work but my issue is I cannot figure out how to do it any other way.
Thanks in advance!
The following code is inside my actionPerformed listener method
if (event.getSource() == startAIvAI)
{
drawing.clear();
while (drawing.hasWon() == -1 && !drawing.isFull())
{
go1();
repaint();
try {
Thread.sleep(1000);
} catch (Exception e) {}
go2();
repaint();
try {
Thread.sleep(1000);
} catch (Exception e) {}
}
}
When you invoke Thread.sleep(1000); which Thread do you think you're pausing? Probable the one handling event given your code start with if (event.getSource() == startAIvAI).
You should read this to understand what to do: Sleep method locks my GUI
I am currently using Vaadin 7.3+ and want to validate in a text field as the user types in real time.
This is what I tried so far:
textField.setTextChangeEventMode(TextChangeEventMode.LAZY);
textField.setNullRepresentation("");
textField.addTextChangeListener(new FieldEvents.TextChangeListener() {
#Override
public void textChange(FieldEvents.TextChangeEvent event) {
for (Validator v : textField.getValidators()) {
try {
v.validate(event.getText());
} catch (InvalidValueException e) {
log.warn("validation error: " + e.getMessage() + " and value was: {}", event.getText());
}
}
}
});
The problem is that although all the validators are being executed and validation is being done the red error indicator is not rendered until the focus leaves the field, i.e. the user hits enter or clicks somewhere else. I tried adding textField.markAsDirty but this did not work. Does anyone know of a solution to this problem? Or of a better solution in general for creating a real time validator on a text field?
Thanks in advance for your time and input :-)
Validators in core the Vaadin are not designed to work while typing, which is a shame for a RIA framework. This will hopefully be fixed in upcoming version. Making it work well today with core components is bit tricky, but doable. Your own solution probably has some UX issues if there is some latency between your server and the client - the cursor might jump into unintended place if user starts to retype again while validator is executed. I have worked on this a lot in Viritin add-on. By using its AbstractForm (or raw MBeanFieldGroup) together with MTextField this should work pretty well and without any configuration. You can try that solution with e.g. this example.
the problem here is, that the event sends the text, but does not actually modify the value of the input. the easiest way to go around this would be setting the value. e.g.
addTextChangeListener(new FieldEvents.TextChangeListener() {
#Override
void textChange(FieldEvents.TextChangeEvent event) {
final textField = event.source as TextField
textField.value = event.text
}
})
this would just trigger the change of the field and also the validators and all will go down to the client as expected.
edit
as you stated in the comments, the cursor pos should be kept. You can just validate the text from the event with whatever means you need. Key point here is, to just set the componentError of the field to get the error down the line for the field.
#Override
void textChange(FieldEvents.TextChangeEvent event) {
final tf = event.source as TextField
try {
tf.validate(event.text) // this works in groovy! not java.
tf.setComponentError(null)
}
catch (InvalidValueException e) {
tf.setComponentError(new SystemError(e))
}
}
This straight forward workaround solution seems to work fine although it is quite inelegant.
textField.setTextChangeEventMode(TextChangeEventMode.LAZY);
textField.setNullRepresentation("");
textField.addTextChangeListener(new FieldEvents.TextChangeListener() {
#Override
public void textChange(FieldEvents.TextChangeEvent event) {
try {
textField.setValue(event.getText());
// workaround cursor position problem
textField.setCursorPosition(event.getCursorPosition());
textField.validate();
} catch (InvalidValueException e) {
log.warn("validation error: " + e.getMessage() + " and value was: {}", delegate.getValue());
}
}
});
Take a look in the Vaadin Documentation. If I get you right it should be fine to set your field in immediate mode. Sometimes it is recommend to allow null values to avoid unnecessary warnings.
TextField field = new TextField("Name");
field.addValidator(new StringLengthValidator(
"The name must be 1-10 letters (was {0})",
1, 10, true));
field.setImmediate(true);
field.setNullRepresentation("");
field.setNullSettingAllowed(true);
Ive implemented the jFileChooser in my command line program and it works, just as it should with only one annoying issue. It seems that it opens underneath every window with no alert of any kind. In fact I even missed it a couple of times at first leading me to believe that i had implemented it wrong.
I have implemented this as follows:
System.out.println("Please select the file");
JFileChooser fc = new JFileChooser();
int retValue = fc.showOpenDialog(new JPanel());
if(retValue == JFileChooser.APPROVE_OPTION){
g.inputFile = fc.getSelectedFile();
}else {
System.out.println("Next time select a file.");
System.exit(1);
}
Essentially I only want the jFileChooser in order to have the user select a file as an input file. This is the only component that has a need for a GUI implementation, so if i can avoid writing up an GUI, that would be helpful.
So after trying a variety of things from different stack overflow topics I ended up with a result that consistently and reliably opens above every window on Windows 7.
public class ChooseFile {
private JFrame frame;
public ChooseFile() {
frame = new JFrame();
frame.setVisible(true);
BringToFront();
}
public File getFile() {
JFileChooser fc = new JFileChooser();
if(JFileChooser.APPROVE_OPTION == fc.showOpenDialog(null)){
frame.setVisible(false);
return fc.getSelectedFile();
}else {
System.out.println("Next time select a file.");
System.exit(1);
}
return null;
}
private void BringToFront() {
frame.setExtendedState(JFrame.ICONIFIED);
frame.setExtendedState(JFrame.NORMAL);
}
}
As it stands in my program it is an inner class and is invoked by calling:
System.out.println("Please select the file");
g.inputFile = g.new ChooseFile().getFile();
I think of two possible causes for something like this:
You're trying to mix AWT and Swing GUI's in the same program, or
You're trying to mix a console program (i.e., using System.out.println(...) and getting input via a Scanner object) with a Swing GUI.
If you are doing either of these two things, then you should simplify and make it only a Swing GUI type program. If this information doesn't help, then you may wish to give us more information about your problem.
Edit 1
I just noticed the details of your code. The new JPanel() part below is a problem:
int retValue = fc.showOpenDialog(new JPanel());
To make the JFileChooser act as a dialog to your top level window (which it is currently not doing and which is your chief problem), you should instead pass a component that is in the parent top level window, such as a JPanel or JButton that is held inside of your JFrame or JApplet.
Edit 2
OK, you're trying to mix a Java console program with a Swing GUI program which is like eating ice cream with pickles -- they just don't go well together. There is no top-level window to offer to the JFileChooser's showOpenDialog method so that it will act as a true dialog.
The best solution is to not do this, to instead re-write your application to be a complete Swing GUI.
In my code, I can just use null and it works. I'm using Java 7 on Windows 7.
JFileChooser chooser = new JFileChooser(System.getProperty("java.class.path"));
FileNameExtensionFilter filter = new FileNameExtensionFilter("CSV files", "csv");
chooser.setFileFilter(filter);
int returnVal = chooser.showOpenDialog(null);
if(returnVal == JFileChooser.APPROVE_OPTION) {
try {
Scanner inputFile= new Scanner(new File(chooser.getSelectedFile().getAbsolutePath()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
What is the general approach with Java swing to update a textarea with lines of text (say from a Thread) and then have the text caret flow to the bottom of the textarea as text is being added. Also update the scrollbar so that it is at the bottom.
I was thinking that I would have a stringbuffer and append text to that and then set the string in the textarea and position the scrollbar at the bottom.
Use append() to add the text, then setCaretPosition() to make sure you scroll with it.
myTextPane.append(textFromSomewhere);
myTextPane.setCaretPosition(myTextPane.getDocument().getLength());
The append() method doesn't do what you want?
And although you didn't ask: when you're generating something in a background thread, be sure to use SwingUtilities.invokeLater() to update your components.
From another thread, you should use java.awt.EventQueue.invokeLater to get on the EDT and then everything works.
So:
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
Document doc = text.getDocument();
int origLen = doc.getLength()
try {
doc.insertString(origLen, msg, null);
} catch (BadLocationException exc) {
// Odd APIs forces us to deal with this nonsense.
IndexOutOfBoundsException wrapExc = new IndexOutOfBoundsException();
wrapExc.initCause(exc);
throw wrapExc;
}
// IIRC, Position is a bit odd and
if (origLen == 0) {
text.setCaretPosition(doc.getLength());
}
}});
Should anyone read the API docs for JTextArea.append it claims to be thread-safe. JDK7 removes that unlikely claim (reminder: threading is hard). As a rule, in Swing I tend to always go straight for the model/Document.
I believe if the caret is at the end it should get moved on after an append. The only exception is if there is no text, because of the strange API. If it has been moved, then we probably don't want to update it after the append.
Note: If multiple threads are doing this, you don't necessarily know which will get there first.
If you are updating from a Thread, dont forget to use SwingWorker or some other AWT Thread-safe approach.
You can update the scrollbar without reading doc.length with:
scrollbar.setValue(scrollbar.getMaximum());
Update (wrapped into Invoke later, code from Tom Hawtin)
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
try {
textArea.append(msg);
} catch (BadLocationException exc) {
// Odd APIs forces us to deal with this nonsense.
IndexOutOfBoundsException wrapExc = new IndexOutOfBoundsException();
wrapExc.initCause(exc);
throw wrapExc;
}
JScrollBar bar = scrollPane.getVerticalScrollBar();
bar.setValue(bar.getMaximum());
}});