Mika2019
Mika2019

Reputation: 69

calling repaint() from a thread

I'm just trying to figure out how Threads work (i.e. I'm a beginner). This program does not serve any purpose other than this.

However, it does not work as intended. My goal was make a the Circle brighter and brighter so that it disappears in the background. But the repaint() call in the Thread does not seem to work.

Here is my code:

public class Run {

    public static void main(String[] args) {
        new Circle();
    }
}


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JFrame;

@SuppressWarnings("serial")
public class Circle extends JFrame implements Runnable {

    private boolean running = false;
    private Thread t;
    Color currentColor;
    int currentRed, currentGreen, currentBlue;

    public Circle() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(150,150));
        start();
        repaint();
        pack();
        setVisible(true);
    }

    @Override
    public void paint(Graphics g) {
            //super.paint(g);
            //currentColor = g.getColor();
            //currentRed = currentColor.getRed();
            //currentGreen = currentColor.getGreen();
            //currentBlue = currentColor.getBlue(); 
            g.setColor(new Color(currentRed++,currentGreen++,currentBlue++));
            g.fillOval(7,30,100,100);
    }


public synchronized void stop() {
    running = false;
    t.interrupt();
}

public synchronized void start() {
    System.out.println("start");
    if (!running) {
        running = true;
        t = new Thread(this);
        t.start();
    }

}

    @Override
    public void run() {
        while(running) {
            try {
                Thread.sleep(5);
                System.out.println("run");
                repaint();
                System.out.println("repaint");   
            } catch(InterruptedException e) {
        }
        }
    }

}

I also tried to call paint() instead, but I'm aware that I shouldn't do this and it didn't work anyways. It kind of makes sense to me. But it is unclear to me, why repaint doen't work...

Upvotes: 0

Views: 1343

Answers (1)

c0der
c0der

Reputation: 18812

The following is a working version of your code. Note the comments:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Circle extends JFrame implements Runnable {

    private boolean running = false;
    private Thread t;
    int currentRed = 0, currentGreen = 0, currentBlue = 0;

    public Circle() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(150,150));
        //repaint(); //not really needed here
        pack();
        setVisible(true);
        start();
    }

    @Override
    public void paint(Graphics g) {
        g.setColor(new Color(currentRed,currentGreen,currentBlue));
        g.fillOval(7,30,100,100);
        //modify color and make sure it is valid
        currentRed = ++currentRed % 256;
        currentGreen = ++currentGreen % 256; //Equivalent to: currentGreen = currentGreen == 255 ? 0 : currentGreen+1;
        currentBlue = ++currentBlue % 256;
    }

    public synchronized void stop() {
        running = false;
        t.interrupt();
    }

    public synchronized void start() {
        if (!running) {
            running = true;
            t = new Thread(this);
            t.start();
        }
    }

    @Override
    public void run() {
        while(running) {
            try {
                Thread.sleep(5);
                repaint();
            } catch(InterruptedException e) { //do not mute exceptions  
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Circle());
    }
}

A better implementation incorporating many of the tips you got in comments would be:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Circle extends JPanel { //do custom painting on a JPanel

    private boolean running = false;
    private int currentRed = 0, currentGreen = 0, currentBlue = 0;
    private final Timer timer; //use swing tools to update gui
    private static final int DELAY = 5, W = 100, H = 100, OFFSET = 25;

    public Circle() {
        timer = new Timer(DELAY, e -> repaint());
        setPreferredSize(new Dimension(W + 2*OFFSET,H + 2*OFFSET));
    }

    public static void createAndShowGui(){
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Circle circle = new Circle();
        frame.add(circle);
        frame.pack();
        frame.setVisible(true);
        circle.start();
    }

    @Override
    public void paintComponent(Graphics g) { //for custom painting override paintComponent
        super.paintComponent(g); //always call super
        g.setColor(new Color(currentRed,currentGreen,currentBlue));
        g.fillOval(OFFSET,OFFSET,W,H);
        //modify color and make sure it is valid
        currentRed = ++currentRed % 256;
        currentGreen = ++currentGreen % 256;
        currentBlue = ++currentBlue % 256;
    }

    public void stop() {
        running = false;
        timer.stop();
    }

    public void start() {
        if (!running) {
            running = true;
            timer.start();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

Upvotes: 2

Related Questions