1

A few days ago I posted a question about a program that caused text on screen to change color when the mousewheel was scrolled. It was unfortunately a badly put together question with too much code posted to be particularly useful.

I had several responses, one of which was from the user trashdog who posted something that fixed the problem (which can be found at the bottom of this page here: Window going blank during MouseWheelMotion event) , however having read the class descriptions of all the things I didn't know about in the program he posted and gone through its execution I don't understand why his achieves a different effect from mine.

His seems to log every mouse wheel movement where as mine only does the initial movement. Also several people commented that they couldn't replicate the effect of my program probably because it was so big.

Below is an extremely simplified version which still elicits the same effect (I hope).

Question: What is the fundamental difference between the two programs that fixes the screen going blank when the mouse wheel events are being processed?

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.LinkedList;
import javax.swing.JFrame;

public class WheelPrinter implements MouseWheelListener, Runnable {

    JFrame frame;
    LinkedList colorList;
    int colorCount;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        WheelPrinter w = new WheelPrinter();
        w.run();
    }

    public WheelPrinter() {
        frame = new JFrame();
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addMouseWheelListener(this);
        frame.setVisible(true);
        frame.setBackground(Color.WHITE);
        colorList = new LinkedList();
        colorList.add(Color.BLACK);
        colorList.add(Color.BLUE);
        colorList.add(Color.YELLOW);
        colorList.add(Color.GREEN);
        colorList.add(Color.PINK);
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
        colorChange();
    }

    @Override
    public void run() {
        while(true) {
            draw(frame.getGraphics());
            try {
                Thread.sleep(20);
            } catch (Exception ex) {

            }
        }
    }

    public void draw(Graphics g) {
        g.setColor(frame.getBackground());
        g.fillRect(0,0,frame.getWidth(),frame.getHeight());
        g.setFont(new Font("sansserif", Font.BOLD, 32));
        g.setColor(frame.getForeground());
        g.drawString("yes", 50, 50);
    }

    public void colorChange() {
                colorCount++;
        if (colorCount > 4) {
            colorCount = 0;
        }


        frame.setForeground((Color) colorList.get(colorCount));
    }

    }

(Try spinning your mouse wheel really hard if you try running my code and it will become even more obvious)

Community
  • 1
  • 1
John Smith
  • 231
  • 3
  • 12

2 Answers2

1
  1. while(true) { is endless loop, without break; f.e.

  2. use Swing Timer instead of Runnable#Thread delayed by Thread.Sleep()

  3. paint to the JPanel or JComponent, not directly to the JFrame

  4. all painting to the Swing JComponent should be done in paintComponent()

  5. more in the 2D Graphics tutorial

edit

enter image description here

enter image description here

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 * based on example by @trashgod
 * 
 * @see http://stackoverflow.com/a/10970892/230513
 */
public class ColorWheel extends JPanel {

    private static final int N = 32;
    private static final long serialVersionUID = 1L;
    private final Queue<Color> clut = new LinkedList<Color>();
    private final JLabel label = new JLabel();

    public ColorWheel() {
        for (int i = 0; i < N; i++) {
            clut.add(Color.getHSBColor((float) i / N, 1, 1));
        }
        //clut.add(Color.BLACK);
        //clut.add(Color.BLUE);
        //clut.add(Color.YELLOW);
        //clut.add(Color.GREEN);
        //clut.add(Color.PINK);
        label.setFont(label.getFont().deriveFont(36f));
        label.setForeground(clut.peek());
        label.setText("@see http://stackoverflow.com/a/10970892/230513");
        setBackground(Color.white);
        add(label);
        label.addMouseWheelListener(new MouseAdapter() {

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                label.setForeground(clut.peek());
                clut.add(clut.remove());
            }
        });
    }

    @Override
    public Dimension getPreferredSize() {
        int w = SwingUtilities.computeStringWidth(label.getFontMetrics(
                label.getFont()), label.getText());
        return new Dimension(w + 20, 80);
    }

    private void display() {
        JFrame f = new JFrame("ColorWheel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ColorWheel().display();
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Thank you very much! What does f.e. stand for? And won't the program not stay open and listen if there isn't a while loop keeping it going and drawing? – John Smith Jun 12 '12 at 20:09
  • aaaach, really I can't see reason bothering with Graphics, especially in the case that you linked great example by @trashgod, a few more than minor changes, non_important for healt on this world, please see my edit here – mKorbel Jun 12 '12 at 21:01
1

The fundamental difference is that you are trying to interact with the Graphics object from the wrong thread and without knowing anything about the state the Graphics object is in at the time.

The generally correct way to interact with a Graphics object in Swing is by making a custom component which overrides the paintComponent(Graphics) method. You do your drawing while inside that method.

Your colorChange() method can tell your component to re-draw itself by calling repaint(), which will eventually lead to a call to paintComponent(Graphics) on the correct thread at the correct time.

See tutorial here

Enwired
  • 1,563
  • 1
  • 12
  • 26