threading issues with java swing and web start - java

EDIT: After fixing a few issues, the bigger issue that I am having is being caused by Apache POI which I am using. I am working on figuring that out now. Apparently it is being restricted by the Sandbox.
I'm very new to Swing, and created a small Swing app that I now need to have run via web start. I'm trying to use the FileOpenService and update a Text display. I think I am running into threading issues, because the FileOpenService dialog never appears, and my text display is not getting updated.
I can't really find any examples where they are doing anything different than I am right now.
Ideas?
Thanks!
Edit: I now have the FileOpenService dialog appearing. I changed my main to this:
public static void main(String[] args) throws Exception {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MainFrame();
}
});
}
However, I still can't get my display to update. This is where I am doing the update:
Runnable r = new Runnable() {
public void run() {
for (final String s : Logger.getMessages())
append(s + "\n");
}
};
try {
if (SwingUtilities.isEventDispatchThread())
r.run();
else
SwingUtilities.invokeAndWait(r);
}
and my append method:
private void append(Color c, String s) {// throws Exception {
StyleContext sc = StyleContext.getDefaultStyleContext();
AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY,
StyleConstants.Foreground, c);
int len = _textPaneLog.getDocument().getLength();
try {
_textPaneLog.getDocument().insertString(len, s, aset);
} catch (BadLocationException e) {
e.printStackTrace();
}
}

You are calling *.openFileDialog(foo,bar) right?
FileOpenService fos = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents fc = fos.openFileDialog(null, null);
Sorry if this is a dumb question, but until you update your question with source I can't think of much else.

The problem I was having had nothing to do with any of this. In another part of my code right near where I was trying to do this, I was causing an application exit. It was a hold over from something else I was trying out that got missed when I was working on implementing this.

Related

Losing AWT DrawingSurface when Canvas is hidden

I am writing 3d rendering module for an AWT/Swing application.
To provide good FPS, I can't draw using Swing/AWT methods and graphics. Instead, I obtain the Drawing Surface from the Canvas element, and then render directly to it. Something like this:
public class Window {
private Component canvas;
private JAWTDrawingSurface ds;
public static final JAWT awt;
static {
awt = JAWT.calloc();
awt.version(JAWT_VERSION_1_4);
if (!JAWT_GetAWT(awt))
throw new AssertionError("GetAWT failed");
}
public void lock() throws AWTException {
int lock = JAWT_DrawingSurface_Lock(ds, ds.Lock());
if ((lock & JAWT_LOCK_ERROR) != 0)
throw new AWTException("JAWT_DrawingSurface_Lock() failed");
}
public void unlock() throws AWTException {
JAWT_DrawingSurface_Unlock(ds, ds.Unlock());
}
public void Init2()
{
this.ds = JAWT_GetDrawingSurface(canvas, awt.GetDrawingSurface());
try
{
lock();
// Create GL Capabilities
unlock();
}
}
It works fine when I call it the first time.
But when I hide the canvas for any reason (for example minimizing window or displaying another panel instead of Canvas), the ds variable remains the same, but it doesn't work after that.
Basically, even if I make sure I call the variable only when it is visible and on top - any call using ds will throw an exception. For example lock() function stops working.
I'm wondering why's that?
Also I tried to basically obtain a new DS if I minimize and then maximize the window again, but this also doesn't work - the new DS address is returned as it should, but I can't use that new object just as I couldn't use the original one.
There's probably something stupid I'm missing here, but I can't figure out what.
Please help me sort this out. Thank you!
The solution:
When the Canvas is hidden, call eglMakeCurrent(eglDisplay,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT) to unbind the current context.
When you need to start drawing again, you need to do something like this:
public void Reinit()
{
System.err.println("Context Reinit()");
this.ds = JAWT_GetDrawingSurface(canvas, awt.GetDrawingSurface());
try
{
lock();
try
{
JAWTDrawingSurfaceInfo dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(ds, ds.GetDrawingSurfaceInfo());
JAWTX11DrawingSurfaceInfo dsiWin = JAWTX11DrawingSurfaceInfo.create(dsi.platformInfo());
this.display = dsiWin.display();
this.drawable = dsiWin.drawable();
eglDisplay = eglGetDisplay(display);
surface = eglCreateWindowSurface(eglDisplay,fbConfigs.get(0),drawable,(int[])null);
eglMakeCurrent(eglDisplay,surface,surface,context);
GLES.setCapabilities(glesCaps);
JAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi, ds.FreeDrawingSurfaceInfo());
}
finally
{
unlock();
System.err.printf("Unlock \n");
}
}
catch (Exception e)
{
System.err.println("JAWT Failed" + e.getMessage());
}
}
As You can see, I re-create the display and surface, but I use the previously created context for rendering, without needing to re-create it.

JavaFX Concurrency - Using a task, which runs in a thread, but hangs the UI

There are a lot of questions and answer around concurrency, and mine could be similar to others, but for me it's not a duplicate as for some reason I must be missing something and hope to get some advice...
My question is more one where I need a second pair of eyes to point out what I'm doing incorrectly to enable my code to run in a background thread, but also updated the GUI, without freezing it.
Initially, a PDF file is uploaded to the application, using a task in a thread.
This works fine.
A progress bar is displayed, which animates without issue:
uploadFile()
public void uploadFile(File fileToProcess) {
fileBeingProcessed = fileToProcess;
Task<Parent> uploadingFileTask = new Task<Parent>() {
#Override
public Parent call() {
try {
progressBarStackPane.setVisible(true);
pdfPath = loadPDF(fileBeingProcessed.getAbsolutePath());
createPDFViewer();
openDocument();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(MainSceneController.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
};
uploadingFileTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
fileHasBeenUploaded = true;
progressBarStackPane.setVisible(false);
uploadFilePane.setVisible(false);
tabPane.setVisible(true);
/* This is where I am getting issue, more so in createThumbnailPanels() */
setupThumbnailFlowPane();
createThumbnailPanels();
/****** ^^^^^^^^^^^^ ******/
}
});
uploadingFileTask.setOnFailed(evt -> {
uploadingFileTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(uploadingFileTask.getException().getSuppressed()));
});
Thread uploadingFileThread = new Thread(uploadingFileTask);
uploadingFileThread.start();
}
Once the document has been uploaded, it is displayed in a tab which allows the user to view the document.
There is a secondary tab, which, after upload, is disabled, until the completion of another task called createThumbnailPanelsTask;
However, before this task is ran, the FlowPane for the Thumbnail Panels is created. This seems to work without issue, and doesn't appear to be the cause of the GUI hanging (this is clearly a loop in createThumbnailPanelsTask, but for clarity I will show setupThumbnailFlowPane()):
setupThumbnailFlowPane()
public void setupThumbnailFlowPane() {
stage = model.getStage();
root = model.getRoot();
secondaryTabScrollPane.setFitToWidth(true);
secondaryTabScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
/**
This will be removed from here when refactored but for now it is here,
I don't think this is anything to do with my issue
**/
Set<Node> nodes = secondaryTabScrollPane.lookupAll(".scroll-bar");
for (final Node node : nodes) {
if (node instanceof ScrollBar) {
ScrollBar sb = (ScrollBar) node;
if (sb.getOrientation() == Orientation.VERTICAL) {
sb.setUnitIncrement(30.0);
}
if (sb.getOrientation() == Orientation.HORIZONTAL) {
sb.setVisible(false);
}
}
}
secondaryTab = new FlowPane();
secondaryTab.setId("secondaryTab");
secondaryTab.setBackground(new Background(new BackgroundFill(Color.LIGHTSLATEGRAY, new CornerRadii(0), new Insets(0))));
secondaryTab.prefWidthProperty().bind(stage.widthProperty());
secondaryTab.prefHeightProperty().bind(stage.heightProperty());
secondaryTab.setPrefWrapLength(stage.widthProperty().intValue() - 150);
secondaryTab.setHgap(5);
secondaryTab.setVgap(30);
secondaryTab.setBorder(new Border(new BorderStroke(Color.TRANSPARENT, BorderStrokeStyle.NONE, CornerRadii.EMPTY, new BorderWidths(8, 10, 20, 10))));
secondaryTab.setAlignment(Pos.CENTER);
}
Finally, createThumbnailPanels() is called, which is where I believe I am getting the problem.
What is suppose to happen is, after the document has uploaded, the upload file pane is hidden, revealing the Viewer Tab, and also the Secondary Tab.
The secondary tab is disabled at this point, and also has a loading image (a gif) on the left side of it.
The intended behaviour, is that the createThumbnailPanels() task will run in the background, and until it is complete, the tab will remain disabled, however, during this time, the gif image will be rotating, giving the impression there is some loading occurring.
Once the loading has completed, the gif is removed, and the tab is enabled, allowing the user to navigate to it, and see the generated thumbnail panels.
This all works, however, as mentioned, the task is hanging the GUI:
createThumbnailPanels()
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
#Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex = 0; thumbIndex < numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
model.setThumbnailAt(tn, thumbIndex);
eventHandlers.setMouseEventsForThumbnails(tb);
/*
I have added this in as I am under the impression that a task runs in a background thread,
and then to update the GUI, I need to call this:
*/
Platform.runLater(() -> {
secondaryTab.getChildren().add(tb);
});
}
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
/*
Further GUI modification run in setOnSucceeded so it runs on main GUI thread(?)
*/
secondaryTabScrollPane.setContent(secondaryTab);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
Everything, bar the GUI hanging while it creates the panels, works fine.
Once they've been created, the GUI can be controlled again, the loading gif has been removed, the tab is enabled and the user can navigate to it and view the panels.
Clearly, there is something I am missing about concurrency here.
As mentioned, I was under the impression that a Task runs in a background thread, so I'm a little confused by why it doesn't appear to be doing this. Again, clearly something I am missing.
I have read, and read, and read about concurrency, but just can't seem to work out where in my approach I have gone wrong. I am tempted to try using a Service, however, I feel that I am just over complicating things by considering that, and that there is clearly a simply way to do what I want to achieve.
Any help will be greatly appreciated... a push in the right direction, or some clarification on where I have gone wrong in my understanding.
Thanks in advance, no doubt it's something obvious that once sorted will help me avoid this issue in future!
UPDATED CODE
createThumbnailPanels()
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
#Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex= 0; thumbIndex< numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
eventHandlers.setMouseEventsForThumbnails(tb);
model.setThumbnailAt(tn, thumbIndex);
model.setThumbnailPanels(tb);
}
setThumbnailPanelsToScrollPane();
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
// setThumbnailPanelsToScrollPane();
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
setThumbnailPanelsToScrollPane()
public void setThumbnailPanelsToScrollPane() {
Task<Void> setThumbnailPanelsToScrollPaneTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
#Override
public Void call() {
Platform.runLater(() -> {
secondaryTab.getChildren().addAll(model.getThumbnailPanels());
secondaryTabScrollPane.setContent(main.informationExtractionPanel);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
});
return null;
}
};
setThumbnailPanelsToScrollPaneTask.setOnFailed(evt -> {
setThumbnailPanelsToScrollPaneTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(setThumbnailPanelsToScrollPaneTask.getException().getSuppressed()));
});
Thread setThumbnailPanelsToScrollPaneThread = new Thread(setThumbnailPanelsToScrollPaneTask);
setThumbnailPanelsToScrollPaneThread.start();
}
FYI: If I call setThumbnailPanelsToScrollPane(); in the setOnSucceeded, it doesn't appear to work.
getChildren().add is running on the JavaFX GUI thread(thats what Platform.runLater does), but its only required to run it in a Platform.runLater if the parent that you add children to is connected to the root of the shown gui, that means you should be able to add children to a parent that is not connected to any root, and add the whole parent to the root at the end of the children addition process, if you're doing Platform.runLater in any asynchronous code it will run on the gui thread in your case it is in your asynchronous for loop adding ThumbnailPanels and if the number of them is large the gui will hang.

Images disappear when minimize windows into a JavaFX app

I'm fixing a JavaFX application on JDK 8. The app shows multiple images using something like "jLabel.setIcon(new Image(...))". I know their is a component to show images in JavaFX but it is not the issue (because I'm not allowed to modify neither that part from the source nor the FXML files).
My problem is when minimize or change the window ((using ALT+TAB)) and wait some time (even up 1 hour approx) to come back to the window, this doesn't show the images anymore. However, if I move the window, the images come back.
In an attempt to fix the problem (independently of use the minimize button or ALT+TAB), I added in the final of the init() method this piece of code:
public void init() {
...
stage.focusedProperty().addListener((observable, oldvalue, newvalue) -> {
if (newvalue) repaintImages();
});
}
private void repaintImages() {
Object obj = new Object();
synchronized (obj) {
jLabel.repaint();
jLabel.revalidate();
obj.notify();
}
try {
obj.wait();
} catch (Exception e) {
}
}
The code before runs repaintImages() after the windows is focused. I used synchronized with the idea to be ensured the code is executed in thread-safe (independently of lag), Anyway that doesn't fix the problem (images are missing yet). After that, I change this the code to use runLater():
public void init() {
...
stage.focusedProperty().addListener((observable, oldvalue, newvalue) -> {
if (newvalue) Platform.runLater(()->{repaintImages();});
});
}
private void repaintImages() {
Platform.runLater(() -> {
jLabel.repaint();
jLabel.revalidate();
});
}
And it doesn't work either. Please, any suggestion is welcome.
I know, I wrote a long question, but maybe a suggestion could be to move the window when it is focused. In that case, please give the correct way to do this because I tried and not worked too (using synchronized and runLater()). This is the piece of code I used to move the window (placing this into a synchronized block or in the lambda function in runLater()):
Translate elements = null;
if (positive_flag) {
elements = new Translate(1, 0, 0);
positive_flag = false;
} else {
elements = new Translate(-1, 0, 0);
positive_flag = true;
}
anchorPane.getTransforms().addAll(elements);
Finally, consider that to prove if any solution works, I need to wait even up 1h.

Dynamical update a textfield on live events

i am at the moment developing a Softphone with javafx. and i kind of a have problem capturing incoming call to a textfield. an example of my code is here.
an incoming call is with Joptionpane successful bt i had like to have the value appear in call textfield just like telephone.
Thank you.
public void telephoneNumbs(String numbers) {
String replace = numbers.replace("sip:", "").trim().replace(".", ""); // Incoming Call Numbers from Sip UA
if (!replace.isEmpty()) {
List<TelephoneObj> telephons;
telTextField.setText(null); //init it with null
costumDao = new CostumersDao(); // costumers DB
telephons = costumDao.getOrCompareTelfone(numbers);
for (TelephoneObj tmp : telephons) {
System.out.println("Test: " + tmp.getTelephoneNums); // am getting exactle what i need here from my Database
//or
JOptionPane.showMessageDialog(null,"incoming:"+ tmp.getTelephoneNums); // it show it during incoming calls
//here is the problem. it wouldnt show the Value on the Textfield
telTextField.setText(tmp.getTelephoneNums); //try to push that Value(Telephone number) to show in JFXTextfield/it cold be any other Textfields
}
}
Sooo much happy today it went well with after 2days of thinking how to solve this miserable life of not taking time to think.
I finally got the answer by using Task to solve the problem.
Task<Void> task = new Task<Void>() {
{
updateMessage("");
}
#Override
public Void call() throws Exception {
while (true) {
updateMessage(callee);
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
break;
}
}
return null;
}
};
//neuLabel.textProperty().bind(task.messageProperty());
kdAddrTel.textProperty().bind(task.messageProperty());
Thread th = new Thread(task);
th.setDaemon(true);
th.start();

Passing input from JButton to console

statsButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
//Return the string "stats" to gameLoop() as cmd
}
});
public void gameLoop(){
Scanner lineScanner = new Scanner(System.in);
cmd = "";
System.out.print(getBoard().printBoard(false));
while (!cmd.equals("quit")) {
System.out.print(">");
Scanner wordScanner = new Scanner(lineScanner.nextLine());
if (wordScanner.hasNext()) {
cmd = wordScanner.next();
if (cmd.equals("board")) {
System.out.print(getBoard().printBoard(false));
} else if (cmd.equals("ships")) {
System.out.print(getBoard().printBoard(true));
} else if (cmd.equals("help")) {
printHelp();
} else if (cmd.equals("stats")) {
printStats();
} else if (cmd.equals("fire")) {
if(fire(wordScanner)) {
printStats();
cmd = "quit";
}
} else if (cmd.equals("quit")) {
} else if (!cmd.equals("")) {
System.out.println(ILLEGAL_COMMAND);
}
}
}
}
What I'm trying to do is that when the user clicks the statsButton, the String cmd in the gameLoop would be changed to "stats". The statsButton and the gameLoop() are located in two different classes. Anyone can give me an idea how to do it? (I've attempted pipedreader/pipedwriter) and I just can't seem to get it right.
*I'm basically trying to make my console application into a GUI application without changing the original console application.
Edit: What I've tried
Class textBased
PipedInputStream in = new PipedInputStream()
public void gameLoop(){
try{
in.connect(GUIclass.out);
Scanner lineScanner = new Scanner(in);`
Class GUIclass
PipedOutputStream out = new PipedOutputStream();
PrintWriter writer;
public GUIclass(){
final PrintWriter writer = new PrintWriter(out);
statsButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
writer.println("stats");
}
});
that's what I tried writing but it doesn't seem to work.
Regarding
I'm basically trying to make my console application into a GUI application without changing the original console application..."
My advice is simple: "don't do it".
The two applications have completely different structure, one being linear, the other being event-driven, and are not directly translatable to each other. Better to make a new GUI program from the ground up. Now if your non-GUI application contains some well-structured and behaved object-oriented classes, then by all means use those classes in your GUI's "model" or logic section, but don't try to directly translate the program flow of one type of application to the other.
Edit
Based on your posted requirements:
"You should be able to play your Battleship game through your GUI interface. In addition, the text-based front-end you wrote for project 1 should still "work" and be playable."
Correct me if I'm wrong, but I'm betting that you have several classes involved here, and only one of them is the "text-based front-end". If so, then use the non front-end classes as the model of your GUI as I suggested above, but do not use the text-based front-end for anything GUI related, and do not try to emulate it in your GUI.
Have the console application instantiate the button ActionListener and pass it to the UI. When the action event is fired, the listener will tell the console app that it happened. The method in the ActionListener will tell it what to do.
I agree with Hovercrafts comment (changed to a reply).
But in general for problems like this I would change the method signature of your gameLoop(). I would use:
public void gameLoop(Reader reader)
Then you can pass different types of readers to the loop depending on the requirement.
For a console you might do something like:
gameloop( new InputStreamReader( System.in ) );
For a GUI you could do something like:
gameLoop ( new StringReader("some text") );
Edit:
Without changing the method signature you can redirect System.in to come from the String retrieved by the ActionListener:
public class Test
{
public static void main(String args[]) throws Exception
{
String text = "some text";
System.setIn( new ByteArrayInputStream( text.getBytes() ) );
// gameloop();
Scanner lineScanner = new Scanner(System.in);
System.out.println( lineScanner.nextLine() );
}
}
If you have something like this :
class B {
public void gameLoop(){
..
}
}
and
class A{
statsButton.addActionListener(new ActionListener() {
...
});
}
You can declare reference to B in A with final . In that case it's will be visible in inner class ActionListener.
class A{
final B b = ...; //final variable is visible in inner class
statsButton.addActionListener(new ActionListener() {
b.gameLoop();
...
});
}

Categories

Resources