Repainting a JPanel in a loop with a delay - java

I'm trying to create a program which will visualize different sorting algorithms by drawing a set of bars representing an array along for each time the sort loops. However, when I set the array from within the sorter class which in turn repaints the panel, it seems that it only calls paintComponent() for the first and last iteration, not showing the steps in between.
Here is the sort code which calls the setNumberArray() method:
public void bubbleSort() {
int[] x = getNumberArray();
boolean doMore = true;
while (doMore) {
doMore = false;
for (int count = 0; count < x.length - 1; count++) {
if (x[count] > x[count+1]) {
int temp = x[count]; x[count] = x[count+1]; x[count+1] = temp;
doMore = true;
}
}
// Update the array
SorterGUI.getSorterPanel().setNumberArray(x);
// Pause
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Sorter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Which calls:
public void setNumberArray(int[] numberArray) {
this.numberArray = numberArray;
repaint();
}
Finally drawing the bars:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int length = numberArray.length;
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.white);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.gray);
for(int count = 0; count < length; count++) {
g2d.fill3DRect((getWidth() / length) * (count + 1), 0,
getWidth() / length, getHeight() - (numberArray[count] * 3),
true);
playSound(numberArray[count]);
}
System.out.print(".");
}
I know it's not repainting in between (with or without the delay) because it only prints one "." when I start sorting.

Forget the paintImmediately as that won't solve your problem. The issue is that you're calling Thread.sleep on the EDT, the main Swing thread known as the event dispatch thread, and this will put your Swing app to sleep (as you're finding out). Instead use a Swing Timer for your delay and all will work well. Either that or do your Thread.sleep in a background thread.

You can use JComponent.paintImmediately to force immediate painting

Related

Java: Drawing single dots at specified (x,y) coordinates?

I am working on science research and am getting strange results from my code, and as a visual learner I thought it efficient to print my data to screen as it is analysed to try and see where the code is going wrong. For reference, I am analyzing a nonlinear waveform.
Here is the code for analysis:
public void getMachineCode(int trial, int wave){
double[] tempwave = new double[5000];
int index = 0;
for(int x = 0; x < 5000; x++){
tempwave[x] = waves[trial][wave][x];
}
for(int repeat = 5; repeat > 0; repeat--){
int tempstart = index;
if(tempwave[index] > 0){
while(tempwave[index] > 0){
index++;
}
}else{
while(tempwave[index] < 0){
index++;
}
}
int midwave = index - tempstart;
if(tempwave[midwave] > 0){
System.out.println(0);
}else{
System.out.println(1);
}
}
}
Here, all I want is to print the (x,y) coordinates of my trial to the screen as the index increases so it is a constantly changing graph, something like:
if(tempwave[index] > 0){
while(tempwave[index] > 0){
index++;
printpixel(index,y); //something to show where the code is scanning
}
}else{
while(tempwave[index] < 0){
index++;
printpixel(index,y);
}
I am not very familiar with java graphics and was looking into using a JFrame but it is hard for me to implement. There will be about 4000 data points to plot, so I might also have to only print every few points or is there a way to make a better visual? Any ideas? Thanks!
This should give you basic idea.
Normally with Swing graphics, you override the paintComponent() method and draw from some shared state
Instead you can just draw to a large image, and then draw that image using paintComponent()
I've compressed this into a single self contained example, this can be separated out in a larger application
Example
public static void main(String[] args) throws Exception {
final BufferedImage image = new BufferedImage(1280, 768,
BufferedImage.TYPE_INT_RGB);
JPanel canvas = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
};
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout()); // <== make panel fill frame
frame.add(canvas, BorderLayout.CENTER);
frame.setSize(500, 500);
frame.setVisible(true);
// do you drawing somewhere else, maybe a different thread
Graphics g = image.getGraphics();
g.setColor(Color.red);
for (int x = 0; x < 100; x += 5) {
for (int y = 0; y < 100; y += 5) {
g.drawRect(x, y, 1, 1);
}
}
g.dispose();
canvas.repaint();
}

Moving Black and White Balls

I'm kind of new to Java and OO programming, here is the code of moving black and white balls problem. First let me explain the program that I want in the output: there are n balls(for example 6 balls) on the window, one black and one white, in each move we only are allowed to move just one ball and this movement should be shown on the screen, and at the end all the white balls should be on one side and all the black balls should be on the other side. Here is an example of six balls:
I have written the program and it seems working good and no flaws in the algorithm, but my problem is that I can't show animation of the movement of the balls, in each movement one ball should swap its place with its neighbor ball, but all I get is the final arrangements of the balls. Please someone help me with the animation part. I would be really thankful for that.
code:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.*;
public class DrawPanel extends JPanel implements ActionListener
{
Timer myTimer = new Timer(2000, this);
public static final int NUMBER_OF_CIRCLES = 10; //number of circles which are to moved
static int[] circles = new int[NUMBER_OF_CIRCLES];
public void paintComponent(Graphics g)
{
int x = 0; //start point of circles;
int length = 40; //diagonal of the circles
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle;
//painting n circles based on the array
for(int index = 0; index<10; index++)
{
if(circles[index] == 0){ //if the element of the arrayy is 0 then draw a void circle
circle = new Ellipse2D.Double(x, 120, length, length);
g2.draw(circle);
}
else if(circles[index] == 1){ //if the element of the array is 1 them draw a filled circle
circle = new Ellipse2D.Double(x, 120, length, length);
g2.fill(circle);
}
x += 45; //increas start pont of the next circle 45 pixles
}
myTimer.start();
}
public void actionPerformed(ActionEvent e)
{
int tmp; //template for swaping elements
int condition; //condition of the forS
arrayFill(circles); //fills the array based on the writen method, one 1 and one 0 like: 0 1 0 1 0 1 0 1
//here is the part which works good, it changes palces of an elemen at time.
//at the end of this part the array would be like: 1 1 1 1 0 0 0 0
if(NUMBER_OF_CIRCLES % 2 == 0)
condition = circles.length/2 -1;
else
condition = circles.length/2;
for(int i = circles.length-1, k = 1; i>condition; i--, k++)
{
for(int j = i - k; j<i ;j++)
{
tmp = circles[j];
circles[j] = circles[j+1];
circles[j+1] = tmp;
//if we call arrayPrint method it will print the array but I don't know why repaint is not working here
//arrayPrint(circles);
repaint();
}
}
}
//fills the array, one 1 and one 0. Example: 0 1 0 1 0 1 0 1 0 1
public static void arrayFill(int[] array)
{
for(int i = 0; i<array.length; i++)
{
if( i%2 == 0)
array[i] = 0;
else
array[i] = 1;
}
}
}//end of class
And the main Class:
import javax.swing.JFrame;
public class BlackAndWhiteBallsMoving {
public static void main(String[] args)
{
DrawPanel myPanel = new DrawPanel();
JFrame myFrame = new JFrame();
myFrame.add(myPanel);
myFrame.setSize(600, 500);
myFrame.setTitle("Black And White Balls Moving");
myFrame.setVisible(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}//end of class
The events triggered by the Timer are performed on the same event thread as the repaints. Calling repaint does not actively perform a paint event, rather it queues one for later. When you call your repaints from within the timer event, they will only get executed once the timer event is completed.
What you need to do is refactor your loop so that only a single swap is performed each time the timer triggers. I've done this for you as an example:
public class DrawPanel extends JPanel implements ActionListener {
public static final int NUMBER_OF_CIRCLES = 10;
Timer myTimer = new Timer(500, this);
int[] circles = new int[NUMBER_OF_CIRCLES];
public DrawPanel() {
arrayFill(circles);
if(NUMBER_OF_CIRCLES % 2 == 0) {
condition = circles.length/2 -1;
} else {
condition = circles.length/2;
}
i = circles.length - 1;
k = 1;
myTimer.start();
}
int i, j, k;
int condition;
boolean outer = true;
#Override
public void actionPerformed(ActionEvent e) {
if(outer) {
if(i > condition) {
j = i - k; // set j
outer = false; // and move to the inner loop swap
} else {
myTimer.stop(); // the outer loop is done so stop the timer
}
}
if(!outer) {
int tmp = circles[j];
circles[j] = circles[j+1];
circles[j+1] = tmp;
repaint();
j++;
if(j >= i) {
i--;
k++;
outer = true; // move to the outer condition
} // next time the timer triggers
}
}
#Override
protected void paintComponent(Graphics g) {
int x = 0;
int length = 40;
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle;
for(int index = 0; index<10; index++) {
if(circles[index] == 0){
circle = new Ellipse2D.Double(x, 120, length, length);
g2.draw(circle);
} else if(circles[index] == 1){
circle = new Ellipse2D.Double(x, 120, length, length);
g2.fill(circle);
}
x += 45;
}
//myTimer.start();
}
public static void arrayFill(int[] array) {
for(int i = 0; i<array.length; i++) {
if( i%2 == 0) {
array[i] = 0;
} else {
array[i] = 1;
}
}
}
}
(I'm sure it could be factored another way.)
Also:
I added #Override annotations which you should use. Doing so will warn you when you make certain mistakes. (Like misspelling a method name or incorrectly declaring its signature.)
I moved circles to an instance variable because I don't see a reason it should be static. It is part of the state of the DrawPanel instance.
I created a constructor which initializes variables such as circles.
paintComponent is a protected method and it should remain so unless there is a reason to promote it to public.
(I removed your comments and changed the bracing style just to condense the code for my answer.)
As a side note, you should read the tutorial Initial Threads. You are not creating your GUI on the Swing event thread. Basically you need to wrap your code in main inside a call to invokeLater:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// create and show your GUI
}
});
}
The basic problem is in your actionPerformed method. Your two for loops are rearranging the array to its final arrangement very quickly. Each loop iteration will take a matter of nanoseconds to milliseconds to complete (it depends on how the repaint() method works). The entire process is finished in less than 50 milliseconds or so. That's too fast for your eyes to keep up.
Basically, the repaint() method is working, but it's working too fast for human eyes to keep up.
If you break up the for loops into something that does one step of the algorithm each time it's called, you can trigger that from a timer and see the animation at a human-detectable speed.
add a paint thread. it should always call repaint() like,
new Thread(){ // this can be started on main or constructor of object
public void run(){
while(true){
repaint();
try {
Thread.sleep(50);
} catch(Exception e){ } 
}
  }
}.start();
and then, on action performed, mark moving objects like movingObjects, keep a animate_x = 0 and keep a boolean variable like existAnimation
then on paintComponent, increase animate_x
animate_x = animate_x + 1;
if (animate_x >= MAX_WIDTH_OF_ANIMATION){
existAnimation = false;
}
and use this existAnimation, animate_x and movingObjects
like,
public void paintComponent(Graphics g)
{
int x = 0; //start point of circles;
int length = 40; //diagonal of the circles
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle;
//painting n circles based on the array
for(int index = 0; index<10; index++)
{
int paint_x = x;
if (movingObjects.has(circles[index])){
paint_x += animate_x;
}
if(circles[index] == 0){ //if the element of the arrayy is 0 then draw a void circle
circle = new Ellipse2D.Double(paint_x, 120, length, length);
g2.draw(circle);
}
else if(circles[index] == 1){ //if the element of the array is 1 them draw a filled circle
circle = new Ellipse2D.Double(paint_x, 120, length, length);
g2.fill(circle);
}
x += 45; //increas start pont of the next circle 45 pixles
}
myTimer.start();
}

Image not rendering after loops

I've recently started learning Java and am attempting to make a basic game. To render the map I have two boolean array's configured (arr_mapx, arr_mapy), these are then checked using two for loops and two if statements to determine if both are true. If both are true the image should render, but it does not. I have successfully rendered the image outside the loop so it is not a problem with the image variable or file.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(img_player1, int_player1_x, int_player1_y, this);
if (map_drawn == false)
{
map_drawn = true;
for(int int_x = 0; int_x < 20; int_x ++)
{
if(arr_mapx[int_x] == true)
{
for(int int_y = 0; int_y < 20; int_y ++)
{
if(arr_mapy[int_y] == true)
{
g2.drawImage(img_obstacle, (int_x + 1)*32, (int_y + 1)*32, this);
//Appears to do nothing
showStatus("It Works!" + int_x + int_y +map_drawn);
//Draws fine, with correct variables
}
}
}
}
}
}
I refactored the code to reduce nesting. (See code block below. Caveat: Just using a text editor so don't expect bug free code.) The first thing that popped out at me is that map_drawn is never set to false. It draws once and is set to true and on subsequent entries into the function you exit before drawing anything in the loops because the value of map_drawn never changed to false anywhere.
Tough to say where it should change since we're just seeing a small part of your game. Is there logic in another function to set the value of map_drawn?
By the way, be careful about what you do in the paint handler (that is what you're working in here). Custom animations like this can tough to debug because of the behavior of the function. For example, if you stop in it with a debugger and then run the paint function will fire off again right away because the display needs updating. I don't know what showStatus does but it should not be a dialog. It should be logging to console or somewhere else. You definitely do not want the user to interact with UI launched from paint!
Good luck!
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(img_player1, int_player1_x, int_player1_y, this);
if (map_drawn) return;
map_drawn = true;
for(int int_x = 0; int_x < 20; int_x ++)
{
if(!arr_mapx[int_x]) continue;
for(int int_y = 0; int_y < 20; int_y ++)
{
if(!arr_mapy[int_y]) break;
g2.drawImage(img_obstacle, (int_x + 1)*32, (int_y + 1)*32, this);
//Appears to do nothing
showStatus("It Works!" + int_x + int_y +map_drawn);
//Draws fine, with correct variables
}
}
}

Timer with paint Component

I am trying to paint circles onto my panel but the colour of the circle is determined by some parameters. Firstly the circles should be painted as white then go into a for loop checking which paramater matches and should paint the circle in that colour. the location of the circles are stored in an array. my code i have done so far does not work. i am clearly doing something wrong but i am new to java and coding so i am very stuck. if someone could show me how to edit/change my code i would really appreciate it. ArrayList circlesT is an arraylist of the circles locations and temp is the array with values i have the parameters too.
public void paintComponent(Graphics g) {
drawShapes(g, circlesT);
}
public void drawShapes(Graphics g, final ArrayList<Shape> circlesT) {
final Graphics2D ga = (Graphics2D) g;
ga.drawImage(newImage, 0, 0, null);
for (int i = 0; i < circlesT.size(); i++) {
ga.draw(circlesT.get(i));
ga.setPaint(Color.white);
ga.fill(circlesT.get(i));
}
Timer timer = new Timer();
TimerTask t;
t = new TimerTask() {
#Override
public void run() {
for (int i = 0; i < 10; i++) {
if (read.temp.get(i) < 31 && read.temp.get(i) > 30) {
ga.draw(circlesT.get(i));
ga.setPaint(Color.green);
ga.fill(circlesT.get(i));
} else if (read.temp.get(i) < 32 && read.temp.get(i) > 31) {
ga.draw(circlesT.get(i));
ga.setPaint(Color.red);
ga.fill(circlesT.get(i));
} else if (read.temp.get(i) < 33 && read.temp.get(i) > 32) {
ga.draw(circlesT.get(i));
ga.setPaint(Color.yellow);
ga.fill(circlesT.get(i));
}
}
}
};
//repaint();
timer.schedule(t, 0, 1000);
}
A few points.
You shouldn't keep hold of the Graphics object from paintComponent beyond the end of the method invocation.
In general you should use javax.swing.Timer instead of java.util.Timer.
What you should actually be doing in the timer task/action is to update the state of your data and call repaint.
The code doing all the painting should be called in paintComponent before it returns.

Lock graphice object in J2ME

it seems to be very stupid question, but I really need a help, I'm making a task with drawing a background transparent gradient image and then draw some objects over it, the problem is I want to draw this image once but the other objects will be drawn multi time to perform some animation
the code is as following, this is the code that i want to run once, and I have created a boolen variable = false and then set it to true
public void drawLockLayer(Graphics g) {
try {
lock = Image.createImage(Paths.lock);
g.drawImage(lock, 0, 0, LGBMainMidlet.width, LGBMainMidlet.height);
System.out.println("After Draw Image");
drawOnce = true;
} catch (Exception ex) {
ex.printStackTrace();
}
}
other code is as following
public void paint(Graphics g, Rectangle rect) {
synchronized (g) {
if (drawOnce == false) {
drawLockLayer(g);
}
}
pos = (int) ((System.currentTimeMillis() % 2700) / 300);
int i = 0;
g.setColor(bgColor);
g.fillRoundRect(startX, startY, width, height, 20, 20);
g.setColor(fgColor);
g.setFont(font);
g.drawString(loadMsg, startX + (spacing / 4), startY + (spacing / 4));
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
int thickness = 6;
if (i == pos) {
thickness = 14;
} else if (i == pos - 1) {
thickness = 10;
}
g.fillRect((startX + x * spacing - (thickness / 2)) + (width / 3), (startY + y * spacing - (thickness / 2)) + (height / 3), thickness, thickness);
i++;
}
}
}
it is enter the method but it is not draw the background image , What I want to do is to lock the graphics object until he finish drawing the image then continue with other code
can anyone help please
Several issues with the code:
synchronizing on a Graphics is generally a bad idea
paint is only supposed to be called from one thread. are you sure it isn't and you actually need to synchronize? where does rect come from?
to make it easier to maintain, you should set drawOnce to true in paint()
You need to set drawOnce to false before calling paint.
It looks like your code is based on GameCanvas. What do you think of the below approach?
class MainCanvas extends GameCanvas {
// rect is an attribute
private void updateScreen() {
drawOnce = false;
paint(getGraphics(), rect);
}
public void start() {
new DataThread().start();
new AnimationThread().start();
}
class DataThread extends Thread {
public void run() {
while (/*IO stuff not done*/) {
// save data
updateScreen();
}
}
}
class AnimationThread extends Thread {
public void run() {
while (/*IO stuff not done*/) {
// sleep a little
updateScreen();
}
}
}
// drawLockLayer and paint(Graphics, Rectangle) methods
} // end of MainCanvas class

Categories