Why isn't anything being drawn to my JPanel? - java

Since I'm new I can't post more than two links, but this is an x-post from reddit.com/r/learnprogramming, just for full disclosure.
I'll basically just be pasting what I said there to here. Thanks for your help, if you can help.
I'm writing somewhat of a graphing application. I currently only have it able to graph sin(x), but that's not the point of this question. I am not able to draw to my main panel. Here is what it currently looks like.
I had an overridden paint function in my Window.java class, which drew the sin(x) function and the axes, but when I made an inner class which extended JPanel(), it would no longer draw.
I then tried to make a separate file, but that didn't draw anything either.
What could be preventing it from drawing?
Here are all my files in question.
edit: code in question:
GraphDraw.java:
//import stuff
Public class GraphDraw extends JPanel {
SinX sinx = new SinX();
GraphPanel p = new GraphPanel();
#Override
public void paintComponent(Graphics gc) {
super.paintComponent(gc);
Graphics2D g = gc;
p.paintComponent(g);
sinx.paint(g);
}
}
And in Window.java, I initialize GraphDraw and add it to my main panel, which is underneath the buttons in the picture and above the x/y min/max labels.
GraphDraw drawer = new GraphDraw();
/*
GUI code
*/
mainPanel.add(drawer);
SinX.java
//import stuff
public class SinX extends Component {
public void paint(Graphics g) {
g.setColor(Color.red);
for(double x=-400;x<=400;x=x+0.5) {
double y = 50 * sin(x*((Math.PI)/180));
int Y = (int)y;
int X = (int)x;
g.drawLine(400+X,300-Y,400+X,300-Y);
}
}
}

First, before anything else, do the following:
Change you object from Component to JComponent
Do not ever, ever call paintComponent() or paint() on a graphics object from swing or awt, use object.repaint(); (For reasons I won't go into here, because it's long and complicated)
From there I would try calling setVisible(true); on all your objects. If you are getting this code from a tutorial, then stop and use a different tutorial. You need to learn how swing and the AWT library work before you can start making user interfaces. Nobody uses AWT anymore because Swing is much better. For reasons why, look at the following page. If you are too lazy to do that, its because it's more optimized and more powerful.
What is the difference between Swing and AWT?

Related

Java Custom Button (not JButton) Problems

Ok, so, what I've been trying to figure out is how I can make a custom MouseListener for all my buttons that would not require listing every single one of them in the Handler, because I'm going to have a lot of them. Here's the code I have in my Listener as of now:
package com.dinobuilding.handler;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import com.BLANK.BLANKScreen;
import com.BLANK.BLANKWindow;
import com.BLANK.menufeature.Button;
public class ButtonHandler implements MouseListener {
public BLANKWindow dbw;
public BLANK Screen dbs;
static Button button = new Button();
public int buttonX = button.x;
public int buttonY = button.y;
public int buttonSizeX = button.xSize;
public int buttonSizeY = button.ySize;
public ButtonHandler(BLANKWindow dbw, BLANKScreen dbs) {
this.dbw = dbw;
this.dbs = dbs;
}
public static void setButton(Button b) {
button = b;
}
public int mouseEventX;
public int mouseEventY;
Graphics g;
public void mouseClicked(MouseEvent e) {
mouseEventX = e.getLocationOnScreen().x;
mouseEventY = e.getLocationOnScreen().y;
if(mouseEventX <= buttonX && mouseEventX >= buttonX + buttonSizeX) {
if(mouseEventY <= buttonY && mouseEventY >= buttonY + buttonSizeY) {
button.onClicked(dbs, dbw, g);
}
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
And here's the code in the first button that I'm trying to implement:
package com.BLANK.menus;
import java.awt.Color;
import java.awt.Graphics;
import com.BLANK.BLANKScreen;
import com.BLANK.BLANKWindow;
import com.BLANK.handler.ButtonHandler;
import com.BLANK.menufeature.Button;
public class MainMenuPlayButton extends Button {
public static int x;
public static int y;
public static int xSize;
public static int ySize;
public static String s;
public static Graphics g;
public MainMenuPlayButton(int x, int y, int xSize, int ySize, String s, Graphics g) {
super(x, y, xSize, ySize, s, g);
this.x = x;
this.y = y;
this.xSize = xSize;
this.ySize = ySize;
this.s = s;
this.g = g;
setColor(new Color(0, 226, 26));
draw();
}
public MainMenuPlayButton() {
}
public static void draw() {
drawButton(x, y, xSize, ySize, g, s);
ButtonHandler.setButton(new MainMenuPlayButton());
}
public void onClicked(BLANKScreen dbs, BLANKWindow dbw, Graphics g) {
setColor(new Color(216, 0, 0));
}
I think my main problem is that the code in the ButtonHandler gets called before the code in the Button class and therefore the ButtonHandler is utilizing the Button class itself, not the MainMenuPlayButton class. If you need the Button class as well, simply tell me, however I can't imagine why. Thank you in advance!
Edit
Ok, after debugging some, I have found that I in fact have the opposite problem. The button is never being clicked. The getSource() method could work, however I don't really know how to use that and I don't think that I could use that without hardcoding every single button, which is really something I do not want to do.
EDIT 1:
Do you think maybe I could do use the MouseEvent's getX or getXOnScreen? By the way, I registered the ButtonHandler using frame.addMouseListener on my JFrame, so...
EDIT 2:
It would seem that the getX method does not work either. If you could help me on that, I would very much appreciate that.
If you want to get the object that was pressed and tripped the MouseListener, use the MouseEvent's getSource() method. For example, this might work:
public void mouseClicked(MouseEvent e) {
(YourButton) button = (YourButton) e.getSource();
button.onClicked(...);
}
Other bits:
Rename your class from Button to something else, since the Button name clashes with the java.awt.Button class, and this can cause difficult to debug errors.
I cringe any time I see a Graphics field declared in a class, as it suggests possible inappropriate painting. Make sure that you really know what you're doing if you ever use one of these as a field since it's easy to get image loss or a NullPointerException if not used correctly, since the Graphics object is frequently changed by Java, and this change is completely out of your (the programmer's) control. Don't say that you haven't been warned.
Edit
Regarding your comments:
Yes, I do know what I'm doing with the Graphics field, however, if it makes you feel better, know that it's only temporary and I will be changing it to something else later.
OK, I've just been burned on this before. As long as you get it from a BufferedImage and don't try to get it by calling getGraphics() on a component or by pulling it out of a paint or paintComponent method, then you might be OK.
Also, I'm pretty sure that I'm getting the object it clicked correctly, but I can't get it to access the correct subclass of Button. It's only getting the Button class itself, not the MainMenuPlayButton.
Sorry, but this doesn't make sense since you don't get "classes" when you obtain a reference, an object pure and simple, and in fact you would get the very same object that the ButtonListener was added to and that tripped the listener, and the class of this reference will be whatever class your button is. I am assuming that you're adding your MouseListener directly to your "Button" object, correct? Again, time to do some debugging.
Edit 2
Regarding the most recent edit to your question:
Ok, after debugging some, I have found that I in fact have the opposite problem. The button is never being clicked. The getSource() method could work, however I don't really know how to use that and I don't think that I could use that without hardcoding every single button, which is really something I do not want to do.
No, there is no need to hard-code each button, trust me. That's the whole reason for using listeners that are added to the buttons.
EDIT 1: Do you think maybe I could do use the MouseEvent's getX or getXOnScreen? By the way, I registered the ButtonHandler using frame.addMouseListener on my JFrame, so... the
There's one of your problems. If you want to listen to your buttons, you're going to want to be able to register listeners on the button itself. If you have an array or collection of them registering listeners is easy. And no, I don't recommend using x and y on screen since it makes your program extremely fragile. If you did this, any changes to the structure of your GUI would require subsequent hard-code changes to your x and y handling. Ugh.
This begs the question of why create your own Button class, and why not instead use JButtons or a subclass of JButtons. You appear to be re-inventing the wheel, but (sorry to be blunt) creating one that is square.
Edit 3
But you cast the variable to a button, meaning that if I have multiple buttons I have to cast each and every one of them to a different thing.
No absolutely not as the magic of polymorphism should work here. But they're objects of the same type, no? Or do you have many different subclasses of your Button class? And regardless, inside of the mouseClicked(...) method, you appear to want to call only one method on your button, onClicked(...), which I imagine has to be an object of the super class, right? So by calling this method on the current button, It should call its own correct code.
The problem I have with JButton is that they already exist. I can't edit them and I can't customize them, ...
This is patently not true. You can change their appearance and behaviors by many means, including by subclassing or by a factory creation method. Plus they already come with the machinery for being able to register listeners and respond to mouse actions.
...Also, would I have to register/make a new handler for each and every one of the buttons?
Again, you appear to be forgetting that polymorphism should take care of all of this. One handler should do, depending on how well-behaved your code is.
I am going to have a LOT of buttons, and I don't think that that would be a viable solution. If not the getX how would I get it to do something when the thing is clicked?
I've given you my recommendation, other than sometimes it is better to re-write sections of code if the design can be improved, meaning again you may want to consider retrofitting your code to use JButtons.

javax.swing.JFrame being overwritten

I am developing using Java SE on NetBeans 7.3.1 on Windows 7.
My java main method has the following calls
static Vector<Point2D> acceptedByFilter, coords;
// Some code to read the coords from a file
// Some code to filter coords and produce a subset called acceptedByFilter
DisplayInputPoints();
DisplayPointsAcceptedByFilter();
These methods are defined as follows
static protected void DisplayPointsAcceptedByFilter(){
Panel panel=new Panel();
panel.DisplayInputPoints(acceptedByFilter, xMin, xMax, yMin, yMax, true, "Points accepted by filter");
}
static void DisplayInputPoints(){
Panel panel=new Panel();
panel.DisplayInputPoints(coords, xMin, xMax, yMin, yMax, true, "Original Points");
}
Panel.DisplayInputPoints is defined as follows
import javax.swing.JFrame;
import javax.swing.JPanel;
public
class Panel extends JPanel
{
public static void DisplayInputPoints(Vector<Point2D> coords, double xMin, double xMax, double yMin,
double yMax, boolean invert, String label){
JFrame frame = new JFrame(label);
Panel panel = new Panel();
panel.setPreferredSize(new Dimension((int)Math.round(xMax-xMin) + 10,
(int)Math.round(yMax-yMin) + 10));
panel.loadPoints(coords);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
frame.repaint();
}
}
When I call
DisplayInputPoints();
the first frame appears and it displays the points in the coords vector. When I call
DisplayPointsAcceptedByFilter();
I get another frame and the points in the acceptedByFilter appear in both frames. That is, the first frame gets overwritten by the display that is in the second frame.
What is the best way to stop the first frame being overwritten by what is supposed to be only in the second frame?
Your code structure appears to be way off, and that is likely your main problem. For instance,
You shouldn't be using static methods or fields for one thing. The only static methods should be the main and associated methods and utility methods. The only static fields should be class fields such as constants.
Your DisplayPointsAcceptedByFilter creates a Panel object which causes the 2nd JFrame to display. Get the display code out of the Panel class, it doesn't belong there and is causing your problem.
Don't name a class with the same name as a Java core class, such as Panel.
Your Panel class isn't even a real OOP class, but nothing but a holder of a static method. This flies against all OOP rules.
If this were my code, I'd scrap it and re-write it using OOP principles from the get-go.
For more detailed and better help, consider supplying more information in your question and perhaps creating and posting an sscce.
Edit
Consider creating:
A class that reads in the coordinates from a file. It can then put the coordinates into an ArrayList. It should have no static methods or fields.
A class for filtering the coordinates. It also should have no static methods or fields and no GUI code.
A GUI class that extends JPanel that is for displaying the data.
The GUI class accepts an array list of coordinaates and then somehow draws the coordinates.
The GUI class accepts new coordinates with a setter method, and then will re-draw the new coordinates.
A class with a main method that creates a JFrame for your GUI class to be displayed in, and that orchestrates all your other classes, gets them started.
Have you thought about making Panel an abstract class, and perhaps altering it specifically for two purposes? I mean i feel like there's more code you should show to properly understand the problem

Is it possible to make different mouseClicked() for the same class?

My java program already have a Canvas, I plan to add 4 Components with MouseListener to that Canvas to act as 4 Buttons. Of course 4 "buttons" will respond differently
Can i create only ONE class for that 4 components (I dont know how to do this), or I have to define 4 classes for 4 components (this is easier)?
MouseEvent extends EventObject which has the method getSource().
As an aside, it is better to add an ActionListener to buttons, since it will react not only to the mouse, but also to the keyboard.
public class ciCanvas extends Container
{
public void paint()
{
// I draw an image here, it works perfectly
Image MainMenuPlayBtImage =
Toolkit.getDefaultToolkit().getImage(ciConfig.CI_BT1_PATH) ;
Label MainMenuPlayText = new Label(ciConfig.CI_TEXT_PLAYBT) ;
Point MainMenuPlayLocation = new Point(100, 100) ;
ciTitle MainMenuBt = new ciTitle(MainMenuPlayText, MainMenuPlayBtImage, MainMenuPlayLocation) ;
this.add(MainMenuBt) ;
}
}
ciTitle extends Component and the following 4 lines of code aims to add a Component to this Container
I hope this will display that ciTitle Component but nothing appear, note that i had also implemented codes for ciTitle.paint() method (see below)
public void paint(Graphics _GraphicsDev)
{
setSize(
this.ciTitleImage.getWidth(this),
this.ciTitleImage.getHeight(this));
_GraphicsDev.drawImage(
this.ciTitleImage,
this.ciTitleLocation.x,
this.ciTitleLocation.y,
this) ;
}
There are still some codes that have no definition here but i hope those are enough to describe the problem

How do I add a MouseListener to a graphics object inside another graphics object?

I'm working on a GUI for a card game and am using the ACM's student graphics library for the sake of familiarity. I have written a program that draws my solitaire game to the screen, and am having trouble making it interactive.
Background:
There are a lot of classes here, and I'll do my best to describe them each.
Top level JFrame containing the application.
GCanvas (that holds all the graphics objects)
SolitaireGameControl (GCompound holding all the other GCompounds making up the solitaire game)
Array of PileViews, a pile of cards (GCompound consisting of an array of Cards)
Cards (GCompound consisting of rectangles and labels)
(GCompound: a collection of graphics objects treated as one object. (If car was a GCompound, it would have GOval[] wheels, GRect body and so when I add it to the canvas, it displays as one object))
A card as seen from the top-level class would look like a bit like this: jFrame.gCanvas.solitaireGameControl.pileViews[pile number].cardView
What I've been trying to do is add a MouseListener to every single card, so that when a card is clicked and a MouseEvent is fired, MouseEvent e.getSource() = the card that was clicked.
Here's how it looks now:
public SolitaireGameControl(SolitaireGame game) {
this.game = game; // Model of the game.
this.pileViews = PileView.getPileViews(game.drawPiles); // ArrayList of PileViews (the pile of cards)
for(PileView pv : pileViews) {
for(CardView cv : pv.cardViews) {
cv.addMouseListener(this); // add a mouseListener to the card
}
}
this.addMouseListener(this); // if I don't include this, nothing happens when I click anything. If I do include this, this whole object is the source.
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(e.getSource()); // should return the card I clicked.
}
When I run this program, the source of every event is SolitaireGameControl, granted I leave in the this.addMouseListener(this);. If I take out this statement, nothing is printed at all, leading me to believe that the mouseListeners I have added are only working one level deep. (The first GCompound on the canvas, not the GCompounds inside it.)
Therefore, my question is as follows: Is there a way to get a MouseListener for a GCompound inside of a GCompound inside of a GCompound, and have MouseEvent's getSource to correctly identify the card? If not, is there a way to restructure my program to make it work as intended? (I know I should really be using a better graphics library for starters.)
That would make sense. From my experience, if I put some components inside a top-level container, the container is the one that receives input events.
Have you tried an approach where you do something like:
/* This is the mouse listener for the top-level container. */
#Override
public void mouseClicked(MouseEvent e) {
for(PileView pv : pileViews) {
for(CardView cv : pv.cardViews) {
if(cv.getBounds().contains(e.getPoint())) {
cv.dispatchEvent(e);
}
}
}
}
... and then handle mouse clicks on a 'CardView' level normally.
When the top-level container receives a mouse event, it checks if the mouse interacted with a card based on the location of the event (if the card's area contains the point). If it did, it passes down the mouse event to the card's mouse listener.
I'm assuming that the elements near the beginning of 'pv.cardViews' are the cards that are more to the front.

Update a BufferedImage in a JFrame

I have a BufferedImage displayed in a JFrame through my own class. I opted to display the BufferedImage using my own class so I can scale it. My paintComponent and update
public class MyBuffIm{
public void paintComponent(Graphics canvas) {
if (bi == null) {
} else {
//bi, maxWidth, and maxHeight were passed to constructor
canvas.drawImage(bi, 0, 0, maxWidth, maxHeight, null);
}
}
public void update(Graphics canvas) {
super.update(canvas);
if(bi != null){
//Got this from some tutorial in the net.
//Done out of desperation :|
paintComponent(bi.getGraphics());
}
}
}
I overrode update since the docs are saying something like "If this component is not a lightweight component, the AWT calls the update method in response to a call to repaint". I'm not exactly sure if my component is lightweight or not.
In any case, I have the following code in my Runnable (does not work as I expect it to):
BufferedImage p = SomeMyBuffIm.getBuffIm();
Vector<Point> randomPixels = getRandomPixels(500);
int limit = randomPixels.size()
for (i = 0; i < limit; i++) {
Point rp = randomPixels.get(i)
p.setRGB(rp.x, rp.y, Color.red.getRGB());
}
SomeMyBuffIm.repaint();
mainFrame.repaint(); //JFrame call to repaint
I'd like to think that, since I'm scaling my image, I just can't discern the difference between the new and old images. But I've tried the largest values for getRandomPixels still to no effect. My test image, by the way, is just a white sheet so red pixels should stand out in it.
Anything wrong I'm doing?
I overrode update since the docs are saying something like "If this component is not a lightweight component, the AWT calls the update method in response to a call to repaint". I'm not exactly sure if my component is lightweight or not.
No you should NOT override update(). You would do that with AWT but not with Swing.
If you update the BufferedImage then all you need to do is invoke repaint() on your instance of the MyBuffin class.
If you need more help than post your SSCCE that demonstrates the problem.

Categories