I am working on music application which creates a square and repaint the screen at every single event. The events are the sounds, I created them with sound.midi API. However, when I run the app the squares do not appear on the window! Could someone tell me what I am doing wrong?
Here is my main class :
import javax.sound.midi.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
public class BeatBoxProject {
static JFrame f = new JFrame ("My first mucic video");
static DrawPanel m1;
public static void main(String[] args) {
BeatBoxProject mini = new BeatBoxProject();
mini.go();
}
public void setUpGui(){
m1 = new DrawPanel();
f.setContentPane(m1);
f.setBounds(30, 30, 300, 300);
f.setVisible(true);
}
public void go(){
setUpGui();
try{ Sequencer player = MidiSystem.getSequencer();
player.open(); // we need the open the sequencer
player.addControllerEventListener(m1, new int [] {127});
Sequence seq = new Sequence( Sequence.PPQ, 4);
Track track = seq.createTrack();
int r = 0;
for (int i = 0; i < 60; i+=4) {
r = (int) ((Math.random()*50)+ 1);
track.add(makeEvent(144,1,r,100,i));
track.add(makeEvent(176,1,127,0,i));
track.add(makeEvent(128,1,r,100,i+2));
}
player.setSequence(seq); // lets put the disk into the player
player.setTempoInBPM(120); // lets set the beat(beat per minutes)
player.start();
}catch (Exception ex){
ex.printStackTrace();
}
}
public static MidiEvent makeEvent (int comd, int chan, int one, int two, int tick){
MidiEvent event = null;
try{
ShortMessage a = new ShortMessage();
a.setMessage(comd, chan, two, two);
event = new MidiEvent(a,tick);
}catch(Exception e) {}
return event;
}
}
And here is the DrawPanel Class:
import javax.sound.midi.ControllerEventListener;
import java.awt.*;
import javax.sound.midi.ShortMessage;
import javax.swing.*;
public class DrawPanel extends JPanel implements ControllerEventListener {
boolean msg = false; // its false unles we gonna have an event
public void controlChange(ShortMessage event) {
msg = true;
repaint();
}
public void paintComponent (Graphics g){
if (msg){ // wee need the msg because other thing can repaint the panel but we only want when event occurs
Graphics2D g2 = (Graphics2D) g;
int r = (int) (Math.random()* 250);
int gr = (int) (Math.random()* 250);
int b = (int) (Math.random()* 250);
g.setColor(new Color(r,gr,b));
int ht = (int) ((Math.random()*120) +10);
int width = (int) ((Math.random()*120) +10);
int x = (int) ((Math.random()*40) +10);
int y = (int) ((Math.random()*40) +10);
g.fillRect(x, y, width, HEIGHT);
msg=false;
}
}
}
Change
player.addControllerEventListener(m1, new int [] {127});
to
player.addControllerEventListener(m1, new int [] {0});
Related
I've been working on creating shapes such as rectangles using an ArrayList in java. I'm trying to get the rectangles to appear in the panel one at a time and slowly build an actual image. I've had no problem getting them to appear and stay as random rects (location, size, color)...but when I attempt to make these rects static...it all falls apart. Suggestions will be welcome.
Full code so far below:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.List;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GraphicMusicPlayer
implements ControllerEventListener {
static JFrame f = new JFrame("Graphic Music");
static MyDrawPanel myPanel;
#Override
public void controlChange(ShortMessage event) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
GraphicMusicPlayer mini = new GraphicMusicPlayer();
mini.go();
// TODO Auto-generated method stub
}
public void setUpGui() {
// Create the panel within the frame
myPanel = new MyDrawPanel();
f.setContentPane(myPanel);
f.setBounds(700, 700, 800, 800);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void go() {
setUpGui();
try {
// Make and open a sequencer.
Sequencer sequencer = MidiSystem.getSequencer();
sequencer.open();
// Register for events. 127 is the event.
sequencer.addControllerEventListener(myPanel, new int[] {93});
Sequence seq = new Sequence(Sequence.PPQ, 4);
Track track = seq.createTrack();
int r = 0;
int[] tonesList = new int[] {
65, 64, 65, 64, 65, 60, 63, 61,
58,65, 64, 65, 64, 65, 60, 63, 61,
58};
for (int i = 0; i < 18; i +=1) {
r = tonesList[i];
if (r>0){
track.add(makeEvent(144,5,r,93,i));//144 = NOTE_ON
track.add(makeEvent(176,5,93,125,i));//176 = get event
track.add(makeEvent(128,5,r,46,i+1));//128 = NOTE_OFF
}//end loop
sequencer.setSequence(seq);
sequencer.setTempoInBPM(30);
sequencer.start();
}
}
catch (Exception ex) {ex.printStackTrace();}
}
public static MidiEvent makeEvent(int comd, int chan, int one, int two, int tick) {
MidiEvent event = null;
try {
ShortMessage a = new ShortMessage();
a.setMessage(comd, chan, one, two);
event = new MidiEvent(a, tick);
} catch (Exception e) { }
return event;
}
#SuppressWarnings("serial")
class MyDrawPanel extends JPanel implements ControllerEventListener {
boolean msg = false;
ArrayList<Rectangle> rectangleList = new ArrayList<Rectangle>();
Map<Rectangle, Color> rectangleColors = new HashMap<Rectangle, Color>();
public void addRectangle(Rectangle rect){
rectangleList.add(rect);
}
public void controlChange(ShortMessage event) {
msg = true;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.BLACK);
while (msg) {
Graphics2D g2 = (Graphics2D) g;
int red = (int) (Math.random() * 250);
int green = (int) (Math.random() * 250);
int blue = (int) (Math.random() * 250);
Color rcolor = new Color(red, green, blue);
int x = (int) (Math.random() * 750);
int y = (int) (Math.random() * 750);
int width = (int) (Math.random() * 100);
int height = (int) (Math.random() * 100);
Rectangle nextRect = new Rectangle(x,y,width,height);
rectangleList.add(nextRect);
g.setColor(rcolor);
rectangleColors.put(nextRect, rcolor);
//paint all of the rectangles
for (Rectangle rect : rectangleList){
Color rectColor = rectangleColors.get(rect);
g.setColor(rectColor);
// Fill the rectangle with the rectangle color
g2.fill(rect);
}
msg = false;
}
}
}
}
In a Java applet, I'm trying to slow down the painting of an image made up of parts, so I wrote a test program to get the basic concept working. I'm using a thread to draw a number of boxes one at a time instead of a timer because I want to be able to click the go button to reset the drawing process at any time.
The problem is, after drawing a box, it moves down a bit and an extra of the label shows up at the top of the screen. When the mouse moves off the button at the bottom, a dummy button also shows up at the top of the screen. The dummy button doesn't respond to clicks (only the real one at the bottom does), it's just there.
I'm still pretty new at this, so any help would be greatly appreciated.
Here's the JApplet class:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TestDraw extends JApplet implements ActionListener
{
private DrawPanel panel;
private JLabel lbl1;
JButton go;
Thread t;
public void init()
{
lbl1 = new JLabel("hi");
go = new JButton("GO");
go.addActionListener(this);
panel = new DrawPanel();
getContentPane().setBackground(Color.yellow);
add(lbl1, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
add(go, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent ae){
// tried adding these. didnt help
//panel.validate();
//panel.repaint();
//validate();
panel.resetBoxes();
repaint();
}
public void start(){
t = new Thread(panel);
t.start();
}
}
Here's the DrawPanel Class:
import java.awt.*;
import java.security.SecureRandom;
import javax.swing.*;
public class DrawPanel extends JPanel implements Runnable
{
private SecureRandom randGen = new SecureRandom();
private Box[] boxes;
private int box2draw = 0;
public DrawPanel()
{
setBackground(Color.WHITE);
boxes = new Box[5];
for (int count = 0; count < boxes.length; count++){
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256), randGen.nextInt(256));
boxes[count] = new Box(x,y,w,h,color);
}
}
public void paintComponent(Graphics g)
{
boxes[box2draw].draw(g);
box2draw++;
}
public void resetBoxes(){
boxes = new Box[5];
for (int count = 0; count < boxes.length; count++){
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256), randGen.nextInt(256));
boxes[count] = new Box(x,y,w,h,color);
box2draw = 0;
}
}
public void run(){
while(true){
try{
Thread.sleep(750);
}
catch(InterruptedException e){
JOptionPane.showMessageDialog(null, "interrupted");
}
repaint();
}
}
}
And finally, the Box class:
import java.awt.Color;
import java.awt.Graphics;
public class Box
{
private int x;
private int y;
private int w;
private int h;
private Color color;
public Box(int x,int y,int w,int h,Color color)
{
// initialise instance variables
this.x = x;
this.y=y;
this.w=w;
this.h = h;
this.color=color;
}
public void draw(Graphics g)
{
g.setColor(color);
g.drawRect( x, y, w, h);
}
}
Thank you for your time!
Problems:
You've got code logic within a painting method -- something that you should never do -- including your incrementing an array index. You don't have full control of when or even if this method is called and so program logic does not belong there, just painting. If you need to increment your array index, do it elsewhere, perhaps within your thread's while (true) loop. Also take care not to have the index go beyond the size of the array.
You never call the super's paintComponent method within your override, and this will prevent the component from doing housekeeping painting, probably your main problem.
If you need to display multiple items, then consider either drawing to a BufferedImage and displaying that within paintComponent, or creating a collection of Shape objects and drawing all of them within paintComponent via a for-loop.
I prefer to use the Swing-safer Swing Timer. While it doesn't matter if only calling repaint() if you want to make any other Swing calls intermittently, it makes life much easier and coding safer.
For example
package foo1;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestDraw2 {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final DrawPanel2 drawPanel = new DrawPanel2();
JButton drawButton = new JButton(new AbstractAction("Draw!") {
#Override
public void actionPerformed(ActionEvent e) {
drawPanel.resetBoxes();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(drawButton);
JFrame frame = new JFrame("TestDraw2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawPanel);
frame.getContentPane().add(btnPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class DrawPanel2 extends JPanel {
private static final int BOX_COUNT = 5;
private static final int TIMER_DELAY = 750;
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private Random randGen = new Random();
private Box[] boxes;
private int box2draw = 0;
public DrawPanel2() {
setBackground(Color.YELLOW);
boxes = new Box[BOX_COUNT];
for (int count = 0; count < boxes.length; count++) {
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256),
randGen.nextInt(256));
boxes[count] = new Box(x, y, w, h, color);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < box2draw; i++) {
boxes[i].draw(g);
}
}
public void resetBoxes() {
boxes = new Box[BOX_COUNT];
for (int count = 0; count < boxes.length; count++) {
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256),
randGen.nextInt(256));
boxes[count] = new Box(x, y, w, h, color);
box2draw = 0;
}
repaint();
new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
box2draw++;
if (box2draw > BOX_COUNT) {
box2draw = BOX_COUNT;
((Timer) e.getSource()).stop();
}
repaint();
}
}).start();
}
}
My current code for grid to show video stream is as follow
//Function for displaying grid window//
public void createMap(int maxX, int maxY) {
gameGrid = new JPanel(new GridLayout(maxX, maxY, 1, 1));
gameGrid.setBackground(Color.GREEN);
for (int i = 0; i < maxX; i++) {
for (int j = 0; j < maxY; j++) {
JPanel panel = new JPanel();
panel.setPreferredSize(PREF_SIZE);
String name = String.format("[%d, %d]", i, j);
panel.setName(name);
panel.setBackground(Color.black);
gameGrid.add(panel);
}
}
}
I call this function as createMap(2,2) so it create grid for 2x2(4 window).
I want to show video stream in each of these grid.
I receiving the video stream through 4 cctv cameras attached with DVR.
I used following code for Showing video streams using opencv
package cctvmonitorsystem;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
// DB connectivity classes
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.SQLException;
import java.sql.ResultSet;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;
import org.opencv.imgproc.Imgproc;
import java.util.Arrays;
public class Video {
public static void main(String args[]) throws SQLException {
int maxCamNo = 4;
//create connection
DbConnect dbcon = new DbConnect();
Connection con = dbcon.openCon();
Statement stmt = con.createStatement();
String getDevice="select * from device order by d_id limit 1";
ResultSet res=stmt.executeQuery(getDevice);
// DECLARE VARIABLES FOR DEVICE ADDRESS & PORT
String devAddress = null;
int httpPort=0;
while (res.next())
{
devAddress=res.getString("d_address");
httpPort=res.getInt("d_http_port");
}
dbcon.closeCon(con); // Connection closed
VideoCapture vcam[] = new VideoCapture[maxCamNo];
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
for (int i = 0; i < vcam.length; i++) {
try {
vcam[i] = new VideoCapture();
vcam[i].open("http://"+devAddress+":"+httpPort+"/cgi-bin/view.cgi?chn=" + i + "&u=admin&p=");
//vcam[i]=new VideoCapture(i);
} catch (Exception e) {
e.printStackTrace();
}
if (!vcam[i].isOpened()) {
System.out.println("Camera " + i + " error");
}
}
AccessVideo av = new AccessVideo(vcam);
//Initialize swing components
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(av);
//frame.setSize(1024,768);
frame.setMaximumSize(null);
frame.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
frame.setVisible(true);
while (vcam[0].isOpened()) {
av.repaint();
}
}
}
class AccessVideo extends JPanel {
VideoCapture[] camera = new VideoCapture[6];
AccessVideo(VideoCapture[] camView) {
camera = camView;
}
public BufferedImage Mat2BufferedImage(Mat m) {
System.out.println("Channels - "+m.channels());
int type = BufferedImage.TYPE_BYTE_GRAY;
if (m.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = m.channels() * m.cols() * m.rows();
byte[] b = new byte[bufferSize];
m.get(0, 0, b); // get all the pixels
BufferedImage img = new BufferedImage(m.cols(), m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return img;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Mat[] mat = new Mat[camera.length];
int width=400;
int height=300;
int xpos = 0;
int ypos = 0;
int y = 1;
for (int i = 0; i < camera.length; i++) {
mat[i] = new Mat();
camera[i].read(mat[i]);
BufferedImage image = Mat2BufferedImage(mat[i]);
g.drawImage(image, xpos, ypos, width, height, null);
xpos = xpos + width;
if (y == 3) {
xpos = 0;
ypos = ypos + height;
y = 0;
}
y++;
}
}
}
Here the video streams will show according to x & y co-ordinates.
But I want to show these video streams in grids which i mention above.
Can anyone help me to resolve this issue.
I did something similar here is my code, but this is hardcoded to only 6 cameras.
This is the main function where I called the addComponentsToPane.
public static void main(String[] args) {
CamtestMonitor f = new CamtestMonitor("Monitor");
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//Set up the content pane.
f.addComponentsToPane(f.getContentPane());
//Display the window.
f.pack();
f.setVisible(true);
}
This is the addComponentsToPane function where we add 6 different ServerUI's. Each one extends the JLabel class and implements DataListener.
public void addComponentsToPane(final Container pane) {
final JPanel jp = new JPanel();
jp.setLayout(gl);
//Set up components preferred size
jp.add(new ServerUI(8880));
jp.add(new ServerUI(8891));
jp.add(new ServerUI(8892));
jp.add(new ServerUI(8893));
jp.add(new ServerUI(8894));
jp.add(new ServerUI(8895));
jp.setPreferredSize(new Dimension(1600, 900));
//Set up the horizontal gap value
gl.setHgap(gapSize);
//Set up the vertical gap value
gl.setVgap(gapSize);
//Set up the layout of the buttons
gl.layoutContainer(jp);
pane.add(jp, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.SOUTH);
}
There you have the Constructor of the ServerUI:
public ServerUI(int port) {
SocketServer server = new SocketServer(port);
server.setOnDataListener(this);
server.start();
}
Finally, here we have the overrrided function that refresh the image on each JLabel.
#Override
public void paint(Graphics g) {
synchronized (queue) {
if (queue.size() > 0) {
lastFrame = queue.poll();
}
}
if (lastFrame != null) {
g.drawImage(lastFrame, 0, 0, null);
}
}
You can have one thread for each camera, and also you extend JPanel class and override the paintComponent() method, like example below
public class MyJPanel extends JPanel {
private BufferedImage image;
public MyJPanel() {
super();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (image != null) {
g2.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), this);
}
}
public void refresh(BufferedImage image) {
this.image = image;
this.repaint();
}
}
public class CameraHandler implements Runnable {
VideoCapture vCapture;
MyJPanel mPanel;
public CameraHandler(VideoCapture vCapture, MyJPanel mPanel) {
this.vCapture = vCapture;
this.mPanel = mPanel;
}
public void run() {
while(vCapture.canReadFrame...) {
then convert the Mat to BufferedImage;
then call this:
mPanel.refresh(convertedImage);
}
}
}
public class MainClass {
public static void main(String[] args) {
VideoCapture vcam[] = new VideoCapture[maxCamNo];
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
for (int i = 0; i < vcam.length; i++) {
try {
vcam[i] = new VideoCapture();
vcam[i].open("http://"+devAddress+":"+httpPort+"/cgi-bin/view.cgi?chn=" + i + "&u=admin&p=");
//vcam[i]=new VideoCapture(i);
if(vcam[i].isOpen) {
new Thread(new CameraHandler(vcam[i], and you MyJPanel reference)).start();
}
} catch (Exception e) {
e.printStackTrace();
}
if (!vcam[i].isOpened()) {
System.out.println("Camera " + i + " error");
}
}
}
}
The code above is just an example to show the way, if you use this then need to arrange the code as your need. Hope this code help you!
I am trying to paint JFrame when music is played , these shapes are created on random no calculation and when a there is some music played there pops up a JFrame and these shapes are painted there, problem is that when i run this code there is no musical sound and shapes drawn but just a frame pops up and nothing else , Plase check this code and help me to correct it ..
public class MusicBeatsDrawing {
static JFrame frame;
DrawPanel dp =new DrawPanel();
public static void main(String[] args)
{
MusicBeatsDrawing mbd= new MusicBeatsDrawing();
mbd.go();
}
public void SetGui(){
frame= new JFrame("Simple frame ; ");
frame.setBounds(100, 100, 200, 200);
frame.setContentPane(dp);
frame.setVisible(true);
}
public void go()
{
SetGui();
try
{
Sequencer sequencer = MidiSystem.getSequencer();
sequencer.open();
Sequence seq= new Sequence(Sequence.PPQ, 4);
sequencer.addControllerEventListener(dp,new int [] {127});
Track track = seq.createTrack();
int r = 0;
for (int i = 0; i < 60 ; i+= 4) {
r = (int) Math.random()*50;
track.add(MakeEvent(144,1,r,100,i));
track.add(MakeEvent(176,1,127,0,i));
track.add(MakeEvent(128,1,r,100,i ));
}
sequencer.setSequence(seq);
sequencer.start();
}
catch(Exception e)
{e.printStackTrace(); }
}
public MidiEvent MakeEvent(int one , int two , int three , int four , int tick)
{
MidiEvent event= null;
try
{
ShortMessage sm= new ShortMessage();
sm.setMessage(one,two, three, four);
event= new MidiEvent(sm , tick );
} catch (InvalidMidiDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return event;
}
class DrawPanel extends JPanel implements ControllerEventListener
{
boolean msg= false ;
public void paintComponent(Graphics g)
{
if(msg){ Graphics2D g2d= (Graphics2D) g;
int red= (int) (Math.random()*255);
int green= (int) (Math.random()*255) ;
int blue= (int) (Math.random()*255);
g.setColor(new Color(red, green , blue));
int height= (int)Math.random()*255;
int width= (int)Math.random()*255;
int x= (int)Math.random()*255;
int y= (int)Math.random()*255;
g.fillRect(height, width, x, y);
msg = false;
}
}
#Override
public void controlChange(ShortMessage event) {
msg= true;
repaint();
}}
}
Swing GUI objects should be constructed and manipulated only on the event dispatch thread. With this change, your program works. A telltale symptom is that the program runs when you resize the frame, which causes the system to invoke repaint() repeatedly.
Addendum: Some additional issues merit attention,
Make setVisible() last.
The sequencer startup latency may be worth moving to the background.
Use named constants, e.g. ShortMessage.NOTE_ON, rather than magic numbers.
Instantiate Random for later use.
Follow Java naming conventions.
Revised SSCCE:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see http://stackoverflow.com/a/17767350/230513
*/
public class MidiDrawing {
private static final Random R = new Random();
public static void main(String[] args) {
EventQueue.invokeLater(new MidiDrawing()::display);
}
public void display() {
JFrame frame = new JFrame("Midi Drawing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawPanel dp = new DrawPanel();
frame.add(dp);
Sequencer sequencer = initSequencer(dp);
JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p.add(new JButton(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
sequencer.setTickPosition(0);
sequencer.start();
}
}));
frame.add(p, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private Sequencer initSequencer(DrawPanel dp) {
try {
Sequencer sequencer = MidiSystem.getSequencer();
Sequence seq = new Sequence(Sequence.PPQ, 3);
Track track = seq.createTrack();
int n = 60; // middle C
for (int i = 0; i < 3 * 12; i += 3) {
track.add(new MidiEvent(new ShortMessage(ShortMessage.CONTROL_CHANGE, 0, 0, n), i));
track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, n, 127), i));
track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, n + 3, 127), i));
track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_OFF, 0, n, 127), i + 3));
track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_OFF, 0, n + 3, 127), i + 3));
n++;
}
sequencer.open();
sequencer.setSequence(seq);
sequencer.addControllerEventListener(dp, new int[]{0});
return sequencer;
} catch (InvalidMidiDataException | MidiUnavailableException e) {
e.printStackTrace(System.err);
}
return null;
}
private static class DrawPanel extends JPanel implements ControllerEventListener {
private final Font font = this.getFont().deriveFont(24f);
private int data;
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.getHSBColor(R.nextFloat(), 1, 1));
g.fillRect(0, 0, getWidth(), getHeight());
g.setFont(font);
g.setColor(Color.black);
g.drawString(String.valueOf(data), 8, g.getFontMetrics().getHeight());
}
#Override
public void controlChange(ShortMessage event) {
data = event.getData2();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(256, 128);
}
}
}
I am having a problem with a game that I am trying to make in Java. I am trying to attach a MouseListener to my canvas, however, when I click on the canvas, nothing happens. I think I may be attaching the MouseListener to the wrong thing, but I don't know what to attach it to. I have tried attaching it to the JFrame and the canvas. Here is my code:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.Random;
public class Gravity extends Canvas {
public static final int screenw = 1024;
public static final int screenh = 768;
public static Random gen = new Random();
public static Boolean started = false;
public static int[] starx = new int[100];
public static int[] stary = new int[100];
public static Color[] starc = new Color[100];
public static JFrame frame;
public static Gravity canvas;
public static Image buffer;
public static Graphics bg;
public static int[] xh = new int[1000];
public static int[] yh = new int[1000];
public static int i = 0;
public static Image title;
public static ArrayList<Integer> ptx = new ArrayList<Integer>();
public static ArrayList<Integer> pty = new ArrayList<Integer>();
double x = 100;
double y = 100;
public Gravity(){
}
public void paint (Graphics g) {
frame.addMouseListener(new MouseListener(){
public void mouseClicked(MouseEvent e){
started = true;
System.out.println("Mouse was clicked");
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0) {}
});
buffer = createImage(screenw, screenh);
bg = buffer.getGraphics();
int w = getWidth();
int h = getHeight();
double px = getWidth()/2;
double py = getHeight()/2;
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //make stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
try {
title = ImageIO.read(new ByteArrayInputStream(Base64.decode(""))); //I have omitted the Base64 code for the image for my title screen
} catch (IOException e) {
e.printStackTrace();
}
bg.drawImage(title, 100, 100, null);
g.drawImage(buffer, 0, 0, null);
while (!started){
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
double xvel = -15;
double yvel = 10;
for (int j=0; j < 100; j++){ //store stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
}
Image test = createImage(200,200);
Graphics testg = test.getGraphics();
testg.drawLine(50,50,150,150);
while(true){
g.drawImage(buffer, 0,0, null);
try {
Thread.sleep(33);
} catch (Exception e) {
e.printStackTrace();
}
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //draw stars
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
bg.setColor(Color.BLUE);
if (i > 0){
for (int z=0; z < i-1; z++){
bg.drawLine(ptx.get(z), pty.get(z), ptx.get(z+1), pty.get(z+1));
}
}
bg.setColor(Color.CYAN);
bg.fillOval((int)px, (int)py, 25, 25); //planet
bg.setColor(Color.RED);
bg.fillRect((int)(x-5),(int)(y-5),10,10); //ship
double fg = (5*50000)/(Math.pow(dist(x,y,px,py),2));
double m = (y-py)/(x-px);
double ms = Math.sqrt(Math.abs(m));
if (m < 0) ms = -ms;
double xchg = fg;
double ychg = fg*ms;
if (x > px){
xchg = -xchg;
ychg = -ychg;
}
xvel += xchg;
yvel += ychg;
x += xvel;
y += yvel;
ptx.add((int)x);
pty.add((int)y);
i++;
}
}
public static void main(String[] args){
canvas = new Gravity();
frame = new JFrame();
frame.setSize(screenw, screenh);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(canvas);
frame.setVisible(true);
}
public static double dist(double x1, double y1, double x2, double y2){
double x = x2-x1;
double y = y2-y1;
return Math.sqrt((x*x)+(y*y));
}
}
General tips, in point form:
Don't mix AWT (e.g. Canvas) with Swing (e.g. JFrame) components. Instead of the Canvas, use a JPanel and override paintComponent(Graphics) rather than paint(Graphics).
Don't call Thread.sleep(n) on the EDT. Instead use a Swing based Timer to call repaint()
Don't perform long running operations on the paint method, especially starting an infinite loop! Even the creation of the buffer image should only be done in the case the screen size changes. (left as 'TODO' - BNI)
Add the MouseListener once in the constructor or an init() method rather than every time paint is called.
Highly recommend all of Nate's numbered list of notes.
Try this code, and compare it carefully to the original to see the changes.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.event.*;
import java.util.ArrayList;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.Random;
public class Gravity extends JPanel {
public static final int screenw = 800;
public static final int screenh = 600;
public static Random gen = new Random();
public static int[] starx = new int[100];
public static int[] stary = new int[100];
public static Color[] starc = new Color[100];
public static Image buffer;
public static Graphics bg;
public static int[] xh = new int[1000];
public static int[] yh = new int[1000];
public static int i = 0;
public static ArrayList<Integer> ptx = new ArrayList<Integer>();
public static ArrayList<Integer> pty = new ArrayList<Integer>();
double x = 100;
double y = 100;
Timer timer;
public Gravity(){
// set thre PREFERRED size!
setPreferredSize(new Dimension(screenw, screenh));
addMouseListener(new MouseListener(){
public void mouseClicked(MouseEvent e){
System.out.println("Mouse was clicked");
timer.start();
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0) {}
});
ActionListener animation = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
};
timer = new Timer(50, animation);
}
#Override
public void paintComponent(Graphics g) {
buffer = createImage(screenw, screenh);
bg = buffer.getGraphics();
int w = getWidth();
int h = getHeight();
double px = getWidth()/2;
double py = getHeight()/2;
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //make stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
g.drawImage(buffer, 0, 0, null);
double xvel = -15;
double yvel = 10;
for (int j=0; j < 100; j++){ //store stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
}
Image test = createImage(200,200);
Graphics testg = test.getGraphics();
testg.drawLine(50,50,150,150);
g.drawImage(buffer, 0,0, null);
try {
Thread.sleep(33);
} catch (Exception e) {
e.printStackTrace();
}
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //draw stars
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
bg.setColor(Color.BLUE);
if (i > 0){
for (int z=0; z < i-1; z++){
bg.drawLine(ptx.get(z), pty.get(z), ptx.get(z+1), pty.get(z+1));
}
}
bg.setColor(Color.CYAN);
bg.fillOval((int)px, (int)py, 25, 25); //planet
bg.setColor(Color.RED);
bg.fillRect((int)(x-5),(int)(y-5),10,10); //ship
double fg = (5*50000)/(Math.pow(dist(x,y,px,py),2));
double m = (y-py)/(x-px);
double ms = Math.sqrt(Math.abs(m));
if (m < 0) ms = -ms;
double xchg = fg;
double ychg = fg*ms;
if (x > px){
xchg = -xchg;
ychg = -ychg;
}
xvel += xchg;
yvel += ychg;
x += xvel;
y += yvel;
ptx.add((int)x);
pty.add((int)y);
i++;
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Gravity());
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
public static double dist(double x1, double y1, double x2, double y2){
double x = x2-x1;
double y = y2-y1;
return Math.sqrt((x*x)+(y*y));
}
}
Use a JComponent instead of a Canvas. You'll want to add the mouse listener to that object. You'll also need to set up the mouse listener in the constructor, not the paint() method.
Edit: You're doing to much in the paint() method as #AndrewThompson pointed out.
public Gravity() {
addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse was clicked");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
});
}
#Override
public void paint(Graphics g) {
buffer = createImage(screenw, screenh);
bg = buffer.getGraphics();
...
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); // black background
for (int j = 0; j < 100; j++) { // make stars
...
}
bg.drawImage(title, 100, 100, null);
g.drawImage(buffer, 0, 0, null);
double xvel = -15;
double yvel = 10;
for (int j = 0; j < 100; j++) { // store stars
...
}
Image test = createImage(200, 200);
Graphics testg = test.getGraphics();
testg.drawLine(50, 50, 150, 150);
g.drawImage(buffer, 0, 0, null);
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); // black background
for (int j = 0; j < 100; j++) { // draw stars
...
}
bg.setColor(Color.BLUE);
if (i > 0) {
for (int z = 0; z < i - 1; z++) {
bg.drawLine(ptx.get(z), pty.get(z), ptx.get(z + 1), pty.get(z + 1));
}
}
bg.setColor(Color.CYAN);
bg.fillOval((int) px, (int) py, 25, 25); // planet
bg.setColor(Color.RED);
bg.fillRect((int) (x - 5), (int) (y - 5), 10, 10); // ship
....
ptx.add((int) x);
pty.add((int) y);
}
public static void main(String[] args) {
...
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
...
}
Notes about your code:
Avoid making fields public static. 99.9% of the time this is not necessary, and usually leads to trouble later.
The painting code seems to be unnecessarily using buffers--the test Image is not used at all currently!
I removed the while(true) loop, but noticed you're drawing an image, modifying the image, and then drawing the image again. You can probably do this in one go.
You should be able to avoid using an Image buffer altogether since you're creating a new one each time paint() is called, and clearing it at the beginning. Just draw your graphics directly to g.
Avoid doing I/O from within the paint() method. Load your images during construction or in a background thread.
Your paint() method is tying up the Thread that handles the mouse click event, due to the while (!started) loop that never exits. started is never true because the MouseListener's mouseClicked() is never called because the paint() method is waiting for started to be true! If you remove that loop, the while (true) loop will have a similar effect. Any mouse events that happen while paint() is running will be queued until paint() returns.