Drawing a weighted tree using MouseListener - java

I'm new at Java. I'm trying to use GUI to input and display a weighted binary tree. To create root node, user clicks on screen. Subsequently, he drags from one point to another to create an edge and a new node. Once he releases the mouse, he should be prompted to enter the weight, and then edge (with weight displayed) and new node should be displayed.
In the input, while I'm able to create the root, when I try to drag to create the next node, the program goes into a loop.
Code is given below.
class myPanel extends JPanel implements MouseListener, MouseMotionListener
{
int node_is_present[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //1 if node exists, 0 otherwise
int no_of_nodes=0;
int node_coords[][]=new int[15][2]; //stores co-ordinates of each node
int weight[]=new int[15]; //stores weight of line in the order entered
int x1,x2,y1,y2;
int tracker=-1;
int SIZE=20;
public void clear()
{
x1=0;
y1=0;
x2=0;
y2=0;
}
#Override
public void mouseClicked(MouseEvent me) {}
#Override
public void mousePressed(MouseEvent me) //will store intial x,y
{
tracker=1;
x1=me.getX();
y1=me.getY();
repaint();
}
#Override
public void mouseReleased(MouseEvent me) //will store final x,y and update arrays
{
x2=me.getX();
y2=me.getY();
if(x1!=x2)
tracker=2;
node_is_present[no_of_nodes]=1;
node_coords[no_of_nodes][0]=me.getX();
node_coords[no_of_nodes][1]=me.getY();
no_of_nodes++;
repaint();
}
#Override
public void mouseEntered(MouseEvent me){}
#Override
public void mouseExited(MouseEvent me) {}
#Override
public void mouseDragged(MouseEvent me){}
#Override
public void mouseMoved(MouseEvent me) {}
public void paintComponent(Graphics g)
{
if(tracker==1 && no_of_nodes==0) //for root node
{
g.drawOval(x1-(SIZE/2),y1-(SIZE/2),SIZE,SIZE);
g.drawString(String.valueOf(no_of_nodes+1),x1-(SIZE/2),y1-(SIZE/2));
}
else if(tracker==2 && no_of_nodes>0)
{
g.drawLine(x1 -(SIZE/2) , y1 -(SIZE/2) , x2 -(SIZE/2) , y2 -(SIZE/2));
g.drawOval(x2-(SIZE/2),y2-(SIZE/2),SIZE,SIZE);
g.drawString(String.valueOf(no_of_nodes+1),x2-(SIZE/2),y2-(SIZE/2));
String str=JOptionPane.showInputDialog("Enter the weight of the edge: ");
int w=Integer.parseInt(str);
weight[no_of_nodes-1]=w;
g.drawString(String.valueOf(w),Math.round((x1+x2)/2),Math.round((y1+y2)/2));
clear();
}
}
myPanel()
{
this.setSize(1500, 500);
addMouseListener(this);
addMouseMotionListener(this);
clear();
}
}
public class tree1
{
public static void main(String[] args) {
JFrame frame=new JFrame();
myPanel panel=new myPanel();
frame.setSize(1500, 800);
frame.setLocation(0, 0);
frame.setTitle("Tree");
frame.setBounds(100,100,1500,800);
frame.add(panel);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Painting in Swing may occur at any time for any reason. As #HoverCraftFullOfEels has already pointed out, you should have NO application within any paint method, these should contain ONLY the functionality required to paint your component.
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing for more details about the paint sub system
Basically, within your paintComponent, call super.paintComponent first and remove, in particular, the else block.
Make yourself some kind of model, which holds the required data and then use the paintComponent method to paint that model.

Related

Drawing circles: separation of code into classes

I'm trying to draw circles with random colors and random diameters on mousePressed event but I'm having some issues when I tried to organize my code a little bit "separate my code into classes".
Controller class
public class Controller implements MouseListener {
private HashSet<Circle> circleSet = new HashSet<>();
private int r,g,b,d;
#Override
public void mousePressed(MouseEvent e) {
r = new Random().nextInt(256);
g = new Random().nextInt(256);
b = new Random().nextInt(256);
d = 10+new Random().nextInt(100);
circleSet.add(new Circle(e.getX()-d/2,e.getY()-d/2,d,d,new
Color(r,g,b),0,0));
}
public HashSet<Circle> getCircleSet() {
return circleSet;
}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
}
View class
public class View extends JPanel{
Controller controller;
HashSet<Circle> circleHashSet;
public View() {
repaint();
controller = new Controller();
circleHashSet = controller.getCircleSet();
this.addMouseListener(controller);
listen();
System.out.println(circleHashSet.size());
}
public void listen() {
new javax.swing.Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
circleHashSet = controller.getCircleSet();
System.out.println(circleHashSet.size());
}
});
}
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
for (Circle circle:this.circleHashSet) {
paintCircle(g,circle);
}
System.out.println(circleHashSet);
}
public void paintCircle(Graphics graphics, Circle circle) {
graphics.setColor(circle.color);
graphics.fillOval(circle.x,circle.getY(),circle.a,circle.b);
}
circleHashSet still empty even though I did:
//circleHashSet = controller.getCircleSet();
Circles are added into circleSet after pressing the mouse but I can't get them into the View class.
First of all, the problem is that you are calling the paintComponents(Graphics g) method instead of the paintComponent(Graphics g) method.
If you are done changing this, you should create a separate Circle POJO class for the circles, because the only avaliable Circle avaliable in the basic Java API is from the JavaFX package.
After setting the constructor for the circle class, you can add to the HashSet more easily. Like so: circleSet.add(new Circle(e.getX() - d / 2, e.getY() - d / 2, d, new Color(r, g, b)));
Another problem is that the Timer object you created is never used for anything. First, you should make a Timer object and after initializing it, call the start() method on it to start the checking of the circles.
put this line of code
circleHashSet = controller.getCircleSet();
within paintComponents() method. The reason is that you only get the value of HashSet when you start the program while the hashset is empty at this time. You should renew the value of circleHashSet after painting one with mouse.

why are all my keybindings only apply to one object when I clearly assigned some keybindings to the other object?

So I created two objects from tank1 class, and their names are tank1 and tank2. I want tank1 to shift right with RIGHT key and to shift left with LEFT key, and tank2 to shift right with D and to shift left with A key. But when I finished compiling my code, the tank2 shift left with A, and LEFT keys, and shift right with D, and RIGHT keys, and tank1 doesn't shift with any key at all. Is there any way to fix this so tank 1 shift with LEFT, RIGHT key?
here is my gridbasegame class:
public class GridBasedGameDriver {
private JFrame frame = new JFrame("my world");
private JPanel panel;
private List<Drawable> drawables= new ArrayList();
private Terrain terrain;
private Tank1 Tank1;
private Tank1 Tank2;
public static void main(String[] args) {
new GridBasedGameDriver().start();
}
private void start() { // REPAINT
setUpGame();
frame.setBackground(new Color(127, 127, 127));
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel() { public void paintComponent(Graphics g) { super.paintComponent(g); drawGame(g); } }; // what does paint componet and draw game do,
//and where does the
//super come from?
panel.setPreferredSize(new Dimension(800,600)); // doesn't matter, can be set again manually
frame.add(panel); // so frame needs to add panel
frame.pack(); // no idea, probably not important
// int b= (int)(Math.random()*100+100); //100-199, only here for fun doesn't change a damn thing
panel.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"),"slideRight");
panel.getActionMap().put("slideRight",new AbstractAction(){
public void actionPerformed(ActionEvent arg0) {
Tank1.moveRight();
panel.repaint();
}
});
panel.requestFocusInWindow();
panel.getInputMap().put(KeyStroke.getKeyStroke("LEFT"),"slideLeft");
panel.getActionMap().put("slideLeft",new AbstractAction(){
public void actionPerformed(ActionEvent arg0) {
Tank1.moveleft();
panel.repaint();
}
});
panel.requestFocusInWindow();
panel.getInputMap().put(KeyStroke.getKeyStroke("D"),"slideRight");
panel.getActionMap().put("slideRight",new AbstractAction(){
public void actionPerformed(ActionEvent arg0) {
Tank2.moveRight();
panel.repaint();
}
});
panel.requestFocusInWindow();
panel.getInputMap().put(KeyStroke.getKeyStroke("A"),"slideLeft");
panel.getActionMap().put("slideLeft",new AbstractAction(){
public void actionPerformed(ActionEvent arg0) {
Tank2.moveleft();
panel.repaint();
}
});
panel.requestFocusInWindow();
setUpObjects();
frame.repaint();
}
private void setUpObjects() {
terrain = new Terrain(panel.getWidth(), panel.getHeight()); // where does the panel come from?
terrain.initialize();
List<Integer> b=terrain.getlist();
Tank1 = new Tank1(20,0,b);
Tank2 = new Tank1(740,1,b);
drawables.add(terrain); // I get it, so the terrain has drawable, and once it gets added to drawable array it implements its own drawable
drawables.add(Tank1);
drawables.add(Tank2);
}
public void drawGame(Graphics g) {
for(Drawable dr:drawables) {
dr.draw(g);
}
}
and here is my tank1 class:
public class Tank1 implements Drawable {
public int gi;
public int it;
public List<Integer> b;
List<Integer> Krell = new ArrayList<>();
public Tank1(int gi, int it, List<Integer> b) {
this.b=b;
this.gi=gi;
this.it=it;
}
public void moveleft() {
gi=gi-1;
}
public void moveRight() {
gi=gi+1;
}
public void draw(Graphics g) {
// TODO Auto-generated method student
if (it==0) {
g.setColor(new Color(230,50,58)); // draws that recoatlve
}
if(it==1) {
g.setColor(new Color(120,160,60)); // draws that recoatlve
}
g.fillRect(gi, b.get(gi)-25, 25, 25); //draws that rectangle
}
}
Might first thought is, JPanel is not focusable by default, so requesting focus in the window won't do anything. Besides, the point of using key bindings like this is to avoid having to deal with "grab focus" style hacks.
There are a couple of variations to getInputMap, one which allows you to define the focus context in which the bindings will be triggered. You might consider using WHEN_IN_FOCUSED_WINDOW, for example...
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"),"slideRight");

Jittering when trying to move undercorated JFrame

I have made a custome JFrame called mainWindow that is undecorated. I have added a JLabel named dragBar at the top of it and gave it desired dimensions (as shown below). When I click on the label I make the window move according to my mouse by using two listeners; one MouseListener and one MouseMotionListener.
The problem is that whenever I click on the label the window does move according to my mouse's location but it spazzes all over my screen until I stop moving the mouse or let go of the click button.
Is my method wrong? What is causing this issue?
Here is my code:
//what i use to make the dragBar
private JLabel dragBar = new JLabel();
private Point initialClick; //the initial point where I click on the label
//my mainWindow JFrame
private JFrame mainWindow = new JFrame();
private Dimension mainWindowSize = new Dimension(680,410);
//the code I use to set up my mainWindow JFrame
mainWindow.setUndecorated(true);
mainWindow.setShape(new RoundRectangle2D.Double(0, 0, 670, 400, 5, 5));
mainWindow.setSize(mainWindowSize);
mainWindow.setMinimumSize(mainWindowSize);
mainWindow.setResizable(false);
mainWindow.setLocation((screen_size.width/2)- mainWindow.getWidth()/2, (screen_size.height/2)- mainWindow.getHeight()/2);
mainWindow.getContentPane().setBackground(new Color(46, 48, 50, 255));
//the code I use to set up my dragBar label
topContainer.add(dragBar,3); //a Jlayeredpane that contains the dragBar label and is added to the mainWindow
dragBar.setSize(topContainer.getSize());
dragBar.setLocation(0,0);
dragBar.addMouseListener(new MouseListener() {
#Override public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
initialClick = e.getPoint();
}
#Override public void mouseReleased(MouseEvent e) {}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
});
dragBar.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
int changeX = e.getX()-initialClick.x;
int changeY = e.getY()-initialClick.y;
mainWindow.setLocation(mainWindow.getX()+changeX, mainWindow.getY()+changeY);
}
#Override public void mouseMoved(MouseEvent e) {}
});
a Jlayeredpane that contains the dragBar label
Don't think I would use a JLayeredPane for this. Just add a component to the BorderLayout.PAGE_START of the frame.
The basic logic for dragging a component is something like:
public class DragListener extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me)
{
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}
However in your case you don't want to drag the label, but instead drag the window, you your logic needs to forward the events to the window.
Check out Moving Windows for a more complex implementation of the above code that also adds additional features that easily allow you to move a window.

Displaying characters typed at mouse location in Java GUI

I have a Java exercise using KeyListeners that I have been stuck on for a while. Any help would be greatly appreciated. The exercise is:
"Write a program to get a character input from the keyboard and display the character where the mouse points."
I did some debugging and it seems like the KeyListener is never registering when a key is pressed.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class EventProgrammingExercise10 extends JFrame {
CharPanel chars;
private int x;
private int y;
String s;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
EventProgrammingExercise10 frame = new EventProgrammingExercise10();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public EventProgrammingExercise10() {
setTitle("EventProgrammingExercise10");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
chars = new CharPanel();
chars.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
chars.repaint();
}
});
add(chars);
}
public void setX(int n) {
x = n;
}
public void setY(int n) {
y = n;
}
class MouseLocListener extends MouseMotionAdapter {
public void mouseMoved(MouseEvent e) {
setX(e.getX());
setY(e.getY());
}
}
class CharPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(String.valueOf('a'), x, y);
}
}
}
Thanks.
A KeyListener will only work if the component that owns it has the focus. You must first make your JPanel focusable, i.e., setFocusable(true), and then request that it have focus, i.e., requestFocusInWindow().
I wouldn't use a MouseListener at all. What I'd do if I had to use a KeyListener, and what I know works, would be:
Make my JPanel Focusable and have focus
Give it a BufferedImage that is exactly its size and draw this in its paintComponent method.
Add a KeyListener/KeyAdapter to it
In the KeyAdapter, keyPressed method, Use the MouseInfo class to get a PointerInfo object: PointerInfo pInfo = MouseInfo.getPointInfo()
Use PointerInfo to get the current mouse's location on screen via pInfo.getLocation();
Get the drawing JPanel's locationOnScreen.
Translate the mouse pointer location to one that is relative to that of the component's using simple vector graphics.
If the point is in bounds of the location, get a Graphics object from the BufferedImage
Draw the char in the BufferedImage
Repaint the JPanel
Look #Hovercraft and you forget to add the MouseLocListener. Than it works :)
chars.addMouseMotionListener(new MouseLocListener());
chars.setFocusable(true);
chars.requestFocusInWindow();
It looks like you should attach lKeyListener not to chars panel but to the frame itself.
This way KeyListener will allways work even if panel losses focus for any reason.

mouse listener on frame

Hi
i am trying to add a mouse listener to my frame to get the position of the mouse clicked and check if it is inside the circle, the problem is that it is not triggering
public class CircleDraw extends Frame implements MouseListener {
static int circles = 0;
private double color;
double mousex = 0;
double mousey = 0;
int score;
public void mouseClicked(MouseEvent evt)
{
mousex = evt.getX();
mousey = evt.getY();
}
public void mouseEntered (MouseEvent me) {}
public void mousePressed (MouseEvent me) {}
public void mouseReleased (MouseEvent me) {}
public void mouseExited (MouseEvent me) {}
public void paint(Graphics g) {
try {
this.addMouseListener(this);
while (circles < 20) {
color = 10*Math.random();
Shape circle = new Ellipse2D.Double(900*Math.random(),900*Math.random(), 50.0f, 50.0f);
Graphics2D ga = (Graphics2D)g;
ga.draw(circle);
if(color >2)
ga.setPaint(Color.green);
else
ga.setPaint(Color.BLACK);
ga.fill(circle);
if(circle.contains(mousex, mousey) && color > 2)
score ++;
else
if(circle.contains(mousex, mousey) && color < 2)
score--;
Thread.sleep(1000);
System.out.println(circles);
System.out.println(mousex);
System.out.println(mousey);
circles ++;
ga.setPaint(Color.white);
ga.fill(circle);
}
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
Frame frame = new CircleDraw();
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we){
System.exit(0);
}
});
frame.setSize(1000, 1000);
frame.setVisible(true);
}}
It is deadly to add your mouselistener in the paint() method, since this method is called very very often (with each repaint), and so many listeners are added (with each repaint).
You should add the listener to your content-panel and not to the JFrame itself. This will do it. You can do this in the constructor of your class:
public CircleDraw() {
this.getContentPane().addMouseListener(this);
}
This won't solve your problem completely I think, since you won't get your mouseclick while your paint-method is active. Your code-design (especially your while-loop) does not give any time to other events to fire. So the mouseclick-event will be handled after your 20 loops. You can check this by adding
public void mouseClicked(MouseEvent evt) {
mousex = evt.getX();
mousey = evt.getY();
System.out.println("X: "+mousex+"/ Y: "+mousey);
}
to your code. You have to run your GUI in a different thread (e.g. use SwingUtilities and a Runnable() therefor). I recommend you to get a good book on JAVA development. Or you can start with online-tutorials like this one.
IMHO you should not try to deal with awt, instead use SWING or SWT for GUI-design, since this is much more compfortable.
add the listener in the constructor, the paint is called repeatedly
Here are some of the problems I see with that source:
Adds the listener in paint()
Calls wait() within the paint() method.
Calls System.exit() within the paint() method (not strictly a problem, but very unusual).
Is poorly formatted and hard to understand
Calls deprecated methods.
Codes in AWT in the wrong millennium.

Categories

Resources