How to avoid Thread.sleep() in a for loop from interrupting the UI Thread? - javafx-8

I have the following pseudo code to clarify my problem and a solution. My original posting and detailed results are on Stack Overflow at: Wait() & Sleep() Not Working As Thought.
public class PixelArtSlideShow { // called with click of Menu item.
create List<File> of each selected pixelArtFile
for (File pixelArtFile : List<File>) {
call displayFiles(pixelArtFile);
TimeUnits.SECONDS.sleep(5); }
}
public static void displayFiles(File pixelArtFile) {
for (loop array rows)
for (loop array columns)
read-in sRGB for each pixel - Circle Object
window.setTitle(....)
}
// when above code is used to Open a pixelArtFile, it will appear instantly in a 32 x 64 array
PROBLEM: As detailed extensively on the other post. Each pixelArtFile will display the setTitle() correctly and pause for about 5 secs but the Circle’s will not change to the assigned color except for the last file, after the 5 secs have passed. It's like all the code in the TimeUnits.SECONDS.sleep(5); are skipped EXCEPT the window.setTitle(...)?
My understanding is the TimeUnits.SECONDS.sleep(5); interrupts the UI Thread uncontrollable and I guess must somehow be isolated to allow the displayFiles(File pixelArtFile) to fully execute.
Could you please show me the most straight forward way to solve this problem using the pseudo code for a more completed solution?
I have tried Runnables, Platform.runLater(), FutureTask<Void>, etc. and I'm pretty confused as to how they are meant to work and exactly coded.
I also have the two UI windows posted on the web at: Virtual Art. I think the pixelArtFile shown in the Pixel Array window may clarify the problem.
THANKS

Don't sleep the UI thread. A Timeline will probably do what you want.
List<File> files;
int curFileIdx = 0;
// prereq, files have been appropriately populated.
public void runAnimation() {
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(5), event -> {
if (!files.isEmpty()) {
displayFile(curFileIdx);
curFileIdx = (curFileIdx + 1) % files.size();
}
})
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
// prereq, files have been appropriately populated.
public void displayFile(int idx) {
File fileToDisplay = files.get(idx);
// do your display logic.
}
Note, in addition to the above, you probably want to run a separate task to read the file data into memory, and just have a List<ModelData> where ModelData is some class for data you have read from a file. That way you wouldn't be continuously running IO in your animation loop. For a five second per frame animation, it probably doesn't matter much. But, for a more frequent animation, such optimizations are very important.

Related

How to save the amount of lives when the player dies and then uses one life?

Hi I'm struggling with this issue.I searched a lot before asking here.
So i have programmed an arcade game which when it dies it pops up a menu with a continue option using one life out of five.Like Subway Surfers with the keys.
The issue is that when i use one heart i want to save it.But everytime i restart the game it keeps saying the initial number which is five.Or without using a life,it by itself,subtracts by one without even clicking the "Save me" button.
Here is my code:
public Text heart;
public Text heart2;
public int counter;
void Start () {
heart.text = "" + counter;
heart2.text = "" +counter;
}
// Update is called once per frame
void Update () {
}
public void SaveMe()
{
heart.text = counter.ToString();
heart2.text = counter.ToString();
int heartScore = PlayerPrefs.GetInt("Life2", 0);
heartScore--;
if(heartScore<0)
{
heartScore = counter;
}
PlayerPrefs.SetInt("Life2", heartScore);
heart.text = PlayerPrefs.GetInt("Life2", 0).ToString();
heart2.text = PlayerPrefs.GetInt("Life2", 0).ToString();
}
}
Then i call this method in the button script.
You should need to save life counter in a file or PlayerPrefs in unity so that loading a scene doesn't effect the counters.
Stores and accesses player preferences between game sessions.
When Unity loads a new scene (if not in an additive way) it will destroy everything that belongs to it before loading the new one. If you don't want a GameObject to be destroyed automatically, read the instructions for DontDestroyOnLoad
Also consider generally to separate your game model (in this occasion the variable counter) to a different Component and use scriptable objects for managing it/them. It will make your life easier in the long run.

JavaFX Canvas Update

I've been working on switching my applications from Swing to JavaFX. I've been working on a room escape game which displays a description of the item on which the user clicks. In Swing, I'd subclass JComponent and override the paintComponent(Graphics) method. I could draw the text there, knowing that the method is constantly called to update the screen. However, using the JavaFX Canvas, there is no method that is called constantly, which makes this task harder. I attempted save()ing the GraphicsContext after I drew the images and called restore() when I wanted to remove the text, but to no avail. Here's the important code:
package me.nrubin29.jescape;
import javafx.application.Platform;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.shape.Rectangle;
import java.util.Timer;
import java.util.TimerTask;
public class RoomPane extends Canvas {
private Room room;
private Toast toast;
public RoomPane() {
super(640, 480);
setOnMouseClicked(e -> {
for (JObject o : room.getObjects()) {
if (o.getBounds().contains(e.getX(), e.getY())) {
toast = new Toast(o.getDescription());
}
}
});
new Timer().schedule(new TimerTask() {
#Override
public void run() {
if (toast == null) {
return;
}
if (toast.decrement()) { // Decrements the internal counter. If the count is 0, this method returns true.
toast = null;
Platform.runLater(() -> getGraphicsContext2D().restore());
}
else {
Platform.runLater(() -> getGraphicsContext2D().strokeText(toast.getText(), 300, 100));
}
}
}, 0, 1000);
}
public void changeRoom(Room room) {
this.room = room;
GraphicsContext g = getGraphicsContext2D();
g.drawImage(room.getBackground(), 0, 0);
for (JObject o : room.getObjects()) {
g.drawImage(o.getImage(), getCenterX(o.getBounds()), getCenterY(o.getBounds()));
}
g.save();
}
}
I attempted save()ing the GraphicsContext after I drew the images and called restore() when I wanted to remove the text, but to no avail.
save and restore have nothing to with removing things like text, what they do is save in a stack the state of various settings like a stroke or fill to use to draw shapes and allow them to be popped off the stack for application later. Those routines don't effect the pixels drawn on the canvas at all.
To remove something from a GraphicsContext, you can either draw over the of it, or clear it. For your code, what you could do is snapshot the canvas node where you are trying to save it, then draw your snapshot image onto the canvas where you are trying to restore it. It is probably not the most efficient way of handling drawing (a smarter routine which just draws only damaged area where the text is would be better, but probably not required for your simple game).
However, using the JavaFX Canvas, there is no method that is called constantly
Rather than using a timer to trigger canvas calls, use a AnimationTimer or a Timeline. The AnimationTimer has a callback method which is invoked every pulse (60 times a second, or as fast as JavaFX can render frames, whichever is the lesser), so it gives you an efficient hook into the JavaFX pulse based rendering system. The Timeline can have keyframes which are invoked at user specified durations and each keyframe can have an event handler callback which is invoked at that duration.
Using the built-in JavaFX animation framework, you don't have to worry about multi-threading issues and doing things like Platform.runLater which overly complicate your code and can easily lead to subtle and serious errors.
On a kind of unrelated note, for a simple game like this, IMO you are probably better off recoding it completely to use the JavaFX scene graph rather than a canvas. That way you will be working at a higher level of abstraction rather than clip areas and repainting damaged paint components.

A way to check a BufferedImage repaint()?

I'm working in Eclipse and I want to know if I can make an if statement that checks to see if the BufferedImage has been painted/drawn onto the frame. For some reason, it's not painting the correct image because clickable regions are appearing on that picture when they are not supposed to.
For example, when I click the region to go from 4>5 everything is good. When I click from 5 to go to 4 I end up at 6 because the 'regions' from 4 are appearing in 5 (The image should always be painted before the clickable regions are shown) before it's even being painted. I want to restrict this to check if the image has been painted onto the frame first.
I really don't want to use anything else besides what I have right now (so no new classes being implemented to do this task), I really just want a simple yet effective way to resolve this. Here is what I'm talking about:
...
MouseAdapter mouseHandler = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
repaint();
if(n==0)
{
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133
{
n = 4;
}
return;
}
if(n==5)
{
if(...)
{
n = 4;
}
return();
}
if(n==6)
{
if(...)
{
n = 5;
}
if(...)
{
n = 0;
}
if(...)
{
n = 6;
}
return();
}
}
...
I think you might need to give a little more information. The problem might lie in how you repaint, not whether it was painted.
If you are running another thread as your main program, you might instead want to send the mouse events synchronously to that so that the main thread can process the mouse click and then repaint.
Another solution might be to override the repaint method and paint the buffered images there, but you may have done that.
Also, a little off topic, I noticed that you used for loops to determine if the mouse was clicked in a specific area.
You could shorten the code:
for(int i=459; i<491; i++){
if(e.getX()==i){
for(int j=111; j<133; j++){
if(e.getY()==j){
//action taken
}
}
}
}
To:
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133{
//action taken
}
This would take up less space in your code and less time checking every pixel.
Back to your question.
I dont know of a function to tell if a buffered image has been painted. The ploblem that you are having though might of might not be in the code provided. Posting the rest of your code would be beneficial.
Okay I found the solution, I forgot to come back to this question and let you know. The problem was that the mouse was double clicking for some reason. You could almost say it was 'recursive'. I decided to move the mouseListener from inside the paintComponent to outside of it, and surely enough that fixed it.

Best practices Implementing ProgressMonitorDialog in Eclipse RCP

I wish to Show progress of a long running operation(process) in UI, so that the user can understand the status of the background job. This is the way I have implemented and I feel like the code is absurd. Below is my code
dialog.run(true,false, new IRunnableWithProgress() {
#Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask("Main process", 10);
for (int i = 0; i < 10; i++) {
if (monitor.isCanceled()) return;
monitor.subTask("Status message");
sleep(1000);
// worked increases the monitor, the values are added to the existing ones
monitor.worked(1);
if(i == 3) {
sleep(3000);
callMe();//calling a long running function
}
if(i == 9) {
monitor.subTask("finishing setup..... please wait ");
sleep(2000);
}
}
monitor.done();
}
});
Note: There is a sleep method somewhere in the code
here at i == 3 an operation/function is called that takes a minimum of 5 minutes, post execution of the function the progress continues.
I don't want the progress to be stopped while executing the function(long running operation) rather progress must be shown even while executing it.
can someone show the correct programming practices in showing progress
The reason your code feels absurd is that wrapping the long-running method in a IRunnableWithProgress.run() really does not add much in itself, there is no magic.
To make the ProgressMonitorDialog (or e.g. the related Job API) work for you, you need to change "callMe()" so it takes "IProgressMonitor monitor" as a parameter, and use that to listen for cancel-requests, and use it also for reporting progress.
To say the same again, using different wording: You need to recursively add "IProgressMonitor monitor" as a parameter to all long-running method calls. All long-running operations must be build with this (IProgressMonitor) in mind if any progress is to be reported and/or you want it to be cancelable.

I need to know when a VerticalPanel changes size

I'm using gwt-dnd to implement drag-and-drop functionality in my GWT program. To get scrolling to work right, I need
<ScrollPanel>
<AbsolutePanel>
<VerticalPanel>
<!-- lots of draggable widgets -->
</VerticalPanel>
</AbsolutePanel>
</ScrollPanel>
I have to manually set the size of the AbsolutePanel to be large enough to contain the VerticalPanel. When I add widgets to the VerticalPanel, though, the size reported by VerticalPanel.getOffsetHeight() isn't immediately updated - I guess it has to be rendered by the browser first. So I can't immediately update the AbsolutePanel's size, and it ends up being too small. Argh!
My stop-gap solution is to set up a timer to resize the panel 500ms later. By then, getOffsetHeight will usually be returning the updated values. Is there any way to immediately preview the size change, or anything? Or, alternatively, can I force a render loop immediately so that I can get the new size without setting up a timer that's bound to be error-prone?
This is a common problem with DOM manipulations. The offsetHeight doesn't update until a short time after components are added. I like to handle this using a recursive timer until a pre-condition is violated. E.g. In your case let there be a function which adds components and will be defined as below:
public void addComponent(Widget w)
{
final int verticalPanelHeight = verticalPanel.getOffsetHeight();
verticalPanel.add(w);
final Timer t = new Timer(){
public void run()
{
if(verticalPanelHeight != verticalPanel.getOffsetHeight())
absolutePanel.setHeight(verticalPanel.getOffsetHeight() + 10 + "px");
else
this.schedule(100);
}
};
t.schedule(100);
}