How to put Object into a panel? - java

So I have a list of Object, with a random height and weight. I also have a random number of those objects into a variable.
What I'm trying to do is to print all those object into the correct panel (I have 2 panel).
First of, my GUI and Object class (Blocks) are 2 separated class. Into the GUI, I'm doing this :
private JPanel initPanelBloc() {
panelBloc = new JPanel();
bloc = new Bloc(false);
panelBloc.add(bloc);
return panelBloc;
}
My Bloc class :
public class Bloc extends JPanel{
private int hauteur, largeur, nombreBloc;
private boolean premierPassage = true;
private ArrayList<Bloc> listeBlocRestant;
private Random rand = new Random();
public Bloc(boolean premierPassage) {
this.hauteur = 10 + rand.nextInt(50 - 10);
this.largeur = 10 + rand.nextInt(50 - 10);
listeBlocRestant = new ArrayList<Bloc>();
if(premierPassage == true) {
this.nombreBloc = 5 + rand.nextInt(30 - 5);
insererBlocList();
}
}
public ArrayList<Bloc> insererBlocList(){
premierPassage = false;
for(int i=0; i<nombreBloc; i++) {
Bloc bloc = new Bloc(false);
listeBlocRestant.add(bloc);
}
return listeBlocRestant;
}
public void paintComponent(Graphics2D g) {
Graphics2D g2 = (Graphics2D) g;
g2.fillRect(10, 20, this.largeur, this.hauteur);
}
I've got also a 3rd class where I call the GUI class :
public Optimisation() {
this.aff = new InterfaceGraphique();
}
And its in the above class where I need to do what I want.
I did not write in this what I want to do because I still don't know how to do it. Should I create a for each loop and take the list of blocks and for every blocks I want them to be print on the panel, with an x and y (of the fillRect) change between blocs ? I'm really lost, I tried to think about this yesterday but still no clue..
Cordially

I'm lost lol I do not understand everything in there since its with the click and so on
Well, the clicks are really not relevant to the painting concept.
The painting concept is you store the object you want to paint in an ArrayList. Then in the paintComponent() method you iterate through the ArrayList to paint each object.
In my example you have a method addRectangle(...) which adds one Rectangle object at a time. You can manually add a Rectangle by invoking this method without using a mouse. This allows you to add Rectangles of a different size/location/color.
For example you just change the code as follows:
private static void createAndShowGUI()
{
DrawingArea drawingArea = new DrawingArea();
drawingArea.addRectangle(new Rectangle(10, 10, 200, 100), Color.RED);
drawingArea.addRectangle(new Rectangle(210, 110, 20, 100), Color.BLUE);
Now the red rectangle will appear when you run the code.
The key points are:
you need a way to add the object you want to paint to your class
you then need to paint these objects in your paintComponent() method. You can't hardcode the painting the way you are currently doing it.
In your code your Bloc object will need to contain the information needed to paint the bloc.

Related

Java newbie: how do i create a thread that controls my paintComponent method?

this year i started java at school, i've been asked to create a program that with 3 threads paints 30 random circles (10 for each thread).
I don't know how to work with the paintComponent very well but here's what i've done:
class MioPanel extends JPanel implements Runnable {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension dimCerchio1 = new Dimension(50, 50);
for(int i = 0; i < 10; i++) {
g.setColor(Color.black);
g.drawOval((int) ((Math.random()*this.getWidth()) - (dimCerchio1.width)), (int) (Math.random()*this.getHeight() - (dimCerchio1.height )), dimCerchio1.width , dimCerchio1.height);
}
/*for(int i = 0; i < 10; i++) {
g.setColor(Color.red);
g.drawOval((int) ((Math.random()*this.getWidth()) - (dimCerchio1.width)), (int) (Math.random()*this.getHeight() - (dimCerchio1.height )), dimCerchio1.width , dimCerchio1.height);
}
for(int i = 0; i < 10; i++) {
g.setColor(Color.blue);
g.drawOval((int) ((Math.random()*this.getWidth()) - (dimCerchio1.width)), (int) (Math.random()*this.getHeight() - (dimCerchio1.height )), dimCerchio1.width , dimCerchio1.height);
}*/
}
#Override
public void run() {
}
The run method is empty 'cause i don't actually know how i can work with both thread and paint
Here's the main:
public class Main {
public static void main(String[] args) {
MFrame mframe = new MFrame("Cerchi casuali");
Thread first = new Thread(new MioPanel());
/*Thread second = new Thread(new MioPanel());
Thread third = new Thread(new MioPanel());*/
}
}
I would appreciate any help, sorry for my english if something's wrong.
You don't try and put everything in one class.
Create a class that starts the Swing components on the SwingUtilities invokeLater method. Create the JFrame in this class.
Create a DrawingPanel class that extends JPanel. Add the DrawingPanel to the JFrame.
Create a Balls class that generates the x and y center and the radius of 10 balls. You can use the java.awt.Point class to hold the center. This class is a Java object. It does nothing but hold the center and radius of the balls.
Create a Drawing class that implements Runnable. Pass an instance of the Balls class to the Drawing class.
Create 3 instances of the Drawing class. Pass the 3 instances of the Drawing class to the DrawingPanel. Use the 3 instances in the paintComponent method. Do nothing but draw the balls in the paintComponent method.
Pass the instance of the DrawingPanel to the 3 instances of the Drawing class.
Start 3 threads with these 3 instances after the GUI is completed.

Storing different objects in an array java

What I need to do is storing different objects of an "Obstacle" super class in an array, and then perform the same actions as if I had one. So I need to be able to draw all the objects and also make them collide with the ball class.
So I tried to put all the instances of an object in an array in a for loop, and then I tried to paint them. But I can't figure out how to use the functions on the objects if they are stored in an array.
I tried to do this:
for(int i = 0;i<objects.length;i++){
objects[0].paint(g);
}
but the "paint(g)" part is just highlighted in red and it doesn't work.
If someone could help me I would be really happy! I haven't stored objects in arrays before, so I'm kind of clueless as what to do.
I also tried making the for loop like this:
if(i>=1 && i<15){
Obstacle star = new StarObstacle(rand.nextInt(400),rand.nextInt(400));
objects[i]= star;
star.paint(g);
}
Here they actually show up, but the stars are just flying all over the screen, so something must be changing x and y values all the time.
edit: sorry accidentally added the whole code instead of only the part I need help on.
It's the for loop in the Main(int x,int y){} scope
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
public class Main extends JPanel implements KeyListener {
Ball b;
TriangleObstacle o;
BorderObstacle border;
StarObstacle s;
Player player;
int bounceCount=0;
Object[] objects;
Random rand = new Random();
Main(int width, int height) {
//create a new black ball at the center of the screen
b = new Ball(width*0.5f, height*0.5f, 3, 0,0,0);
//make a border around the window
border = new BorderObstacle(width, height);
objects = new Object[30];
//setup a triangle obstacle
o = new TriangleObstacle(width*0.3f, height*0.7f, 200, 50);
s = new StarObstacle(400,300);
player = new Player();
this.addKeyListener(this);
this.setFocusable(true); //needed to make
for(int i = 0; i < objects.length;i++){
if(i==0){
objects[0]= new Player();
}
if(i>=1 && i<15){
objects[i]= new StarObstacle(rand.nextInt(400),rand.nextInt(400));
}
if(i>15){
objects[i]= new TriangleObstacle(30,30,rand.nextInt(400),rand.nextInt(400));
}
}
}
public void update() {
//move over all obstacles and check whether they should bounce the ball
border.bounceBall(b);
o.bounceBall(b);
s.bounceBall(b);
if(player.bounceBall(b)){
bounceCount++;
}
//move ball based on speed and location
b.move();
player.move(); //updates my player object.
this.repaint(); //runs the paint method on the object
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(new Font("TimesRoman", Font.PLAIN, 20));
g.drawString("Amount of bounces on Player: " + bounceCount, 300, 100);
b.paint(g);
o.paint(g);
s.paint(g);
player.paint(g);
border.paint(g);
}
public static void main(String[] args) {
int width = 800;
int height = 600;
JFrame frame = new JFrame("Pinball"); //create a new window and set title on window
frame.setSize(width, height); //set size of window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //set the window to close when the cross in the corner is pressed
Main m = new Main(width,height-22); //-22 to account for menubar height //create a new object and runs constructor for initial setup of the program
frame.add(m); //add the content of the object to the window
frame.setVisible(true); //make the window visible
while (true) { //keep running a loop
//each time the loop is run do
m.update(); //run the update method on the object
try {
Thread.sleep(10); //stops this part of the program for 10 milliseconds to avoid the loop locking everything. Now the screen has time to update content etc.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode(); //gets input as keycode.
if(code==KeyEvent.VK_RIGHT){
player.setRight(true); //sets the movement for right to true, making it move by 5 pixels in the positive direction, for each update.
player.setLeft(false);
}
if(code==KeyEvent.VK_LEFT){
player.setLeft(true);
player.setRight(false);
}
}
#Override
public void keyReleased(KeyEvent e) { //keyReleased setting them to false to prevent the object to keep moving.
int code = e.getKeyCode();
if(code==KeyEvent.VK_RIGHT){
player.setRight(false);
}
if(code==KeyEvent.VK_LEFT){
player.setLeft(false);
}
}
}
You need to write your own method paint(Graphic g) in your classes "Player", "StarObstacle", "TriangleObstacle".
something like this:
class TriangleObstacle extend Obstacle {
public paint(Graphics g) {
g.drawPolygon (...);
}
}
Also, avoid to use an array of Objects (Objects[]). Better use your superclass Obstacles for the array (Obstacle[] myObstacle = new Obstacle[30];
And don't put the Player object inside the array of Obstacle it wouldn't make sense.
At final you could have:
Obstacle[] myObstacles = new Obstacle[30];
Player player = new Player();
....
for (Obstacle obstacle : myObstacles) {
obstacle.paint(g);
}
player.paint(g);
This:
for(int i = 0;i<objects.length;i++){
objects[0].paint(g);
}
Doesn't achieve what you want because you are always accessing index 0.
Try this:
for(int i = 0;i<objects.length;i++){
objects[i].paint(g);
}
This allows you to access and paint each object present on the index represented by i. I can't guarantee that this will solve the problem, but it is a problem.
You need to cast the object and use the correct index so :
objects[0].paint(g);
should be
((Observer)objects[i]).paint(g);
However this is completely the wrong approach. Please read up on java generics. http://docs.oracle.com/javase/tutorial/java/generics/
You should be using a List or some other collection with an upper bound wildcard eg
List<? extends Observer>
Which will provide type safety.
You must create an array of Obstacles instead of objects, the in your Obstacles interface add a method definition called paint(). Then you have to override the paint() method in every implementation of Obstacles. And when you call paint method in objects of the array it will call the corresponding method.
Instead of an array, can you put the objects in a list? Something like...
List<Obstacle> someList = [however you populate your array, just populate the list instead];
for(int i = 0; i<somelist.length; i++) {
someList.get(i).paint(g);
}

How do I use text fields to set an ellipse's dimensions?

The project I am working on requires a text field for the user to enter the width of an ellipse. When the user clicks somewhere on a panel, it draws an ellipse with the specified width. When I ran it, the width never changed.
This is in initialize():
tTextWidth = new JTextField();
tTextWidth.setBounds(42, 457, 86, 20);
frame.getContentPane().add(tTextWidth);
tTextWidth.setColumns(10);JButton tSetWidth = new JButton("Set Width");
tSetWidth.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
SetTextToWidth(tTextWidth.getText());
}
});
This is right after initialize():
public void SetTextToWidth(String tWidth)
{
if(tWidth == null)
{
tWidth = "50";
}
int tIntWidth = Integer.parseInt(tWidth);
if(tIntWidth == 0)
{
tIntWidth = 50;
}
RoundSprite tSpriteWidth = new RoundSprite();
tSpriteWidth.SetSpriteWidth(tIntWidth);
}
This is in the class RoundSprite:
private float mX;
private float mY;
int mWidth;
int mHeight;
Color mColor;
void DrawSprite(Graphics2D g2)
{
AffineTransform tOldTransform = g2.getTransform();
g2.setColor(mColor);
g2.translate(mX, mY);
g2.draw(new Ellipse2D.Double(0, 0, mWidth, mHeight));
g2.setTransform(tOldTransform);
g2.translate(mX - (mWidth / 2), mY - (mHeight / 2));
}
public void SetSpriteWidth(int tWidth)
{
mWidth = tWidth;
}
So two main things...
One...
ActionListener will only be triggered when the user presses the action key for the platform, in most cases the Enter key, just so you know ;)
Two...
In your SetTextToWidth is creating a new instance of RoundSprite which has no context to what is been displayed on the screen...
I you thinking, this would mean that ALL instances RoundSprite should be changed, which is not what you want.
As discussed in this simular question, you first need to define which sprite you are actually trying to change and then apply the change you want to that specific instance (and repaint the output)...
Side Notes
You might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others

How to repaint out of focus dialog without gaining its focus?

I made some menu and it is to update conmmon variables (for text on grid) then the out-of-focus dialog must repaint the grid. Here is the screenshot:
The main control panel is always at top position and 'Data Display' panel is always sitting behind it. When press a button on front panel, Data Display must update its grid. Currently, the common variable 0.4 on the grid is updated by adding listener and works fine. But the grid itself is not repainting anymore. How can I repaint the out-of-focus dialog in real time?
Here is the code of the front panel:
public class MainDisplayForm extends javax.swing.JFrame {
Storage st = new Storage();
DisplayForm dF = new DisplayForm();
....
public MainDisplayForm() {
initComponents();
Btn_IncreaseGain.addActionListener(new ButtonListener_IncreaseGain());
}
....
} //MainDisplayForm ends here.
class ButtonListener_IncreaseGain implements ActionListener {
DisplayForm dF = new DisplayForm();
Storage st = new Storage();
ButtonListener_IncreaseGain()
{
}
public void actionPerformed(ActionEvent e) {
st.iGain = 20;
dF.revalidate();
dF.repaint();
System.out.println("Testing");
}
}//Listener ends here.
Here is code of Data Display:
public void paint(Graphics g)
{
g2 = (Graphics2D) g;
paintComponents(g2);
//added numbers are for adjustment.
int x = this.jPanel1.getX()+8;
int y = this.jPanel1.getY()+30;
int width = this.jPanel1.getWidth()+19;
int height = this.jPanel1.getHeight()+40;
//labelling voltages
label0.setText(st.zero);
label1.setText(st.v1);
label2.setText(st.v2);
label3.setText(st.v3);
label4.setText(st.v4);
label5.setText(st.v3);
label6.setText(st.v4);
g2.setColor(Color.darkGray);
for(int i=x; i<width; i=i+80)
{
g2.drawLine(i, y, i, height);
}
int j = 0;
for(int i=y; i<height; i=i+80)
{
j++;
//st.iGain
g2.setColor(Color.orange);
if(j==1)
{
double k1 = st.iGain * 0.4;
st.v1 = Double.toString(k1);
g2.drawString(st.v1, x+5, y+10);
}
if(j==2)
{
double k2 = st.iGain * 0.3;
st.v2 = Double.toString(k2);
g2.drawString(st.v2, x+5, y+90);
}
g2.setColor(Color.DARK_GRAY);
g2.drawLine(x, i, width, i);
....
} //grid info is not completed yet.
Thanks,
Focus isn't the issue and has nothing to do with your current problem. The solution is to change the properties of the data grid by updating fields it contains via setter methods and calling repaint on the JComponent (perhaps a JPanel, or some other component that derives ultimately from JComponent) held by the data grid. The paintComponent method of this component should use its class fields to update what it draws.
You almost never paint in the paint method of a JComponent and certainly you don't want to draw directly into a top-level window. You also probably don't want to set text of JLabels, JTextFields, or any other JTextComponent. from within paint/paintComponent.
I can't see why your code is not working and can only guess that the likely cause of your problem is in code not shown.
Edit 1:
Just guessing, but you may have a problem of references. I notice that your listener class creates new DisplayForm and Storage objects:
DisplayForm dF = new DisplayForm();
Storage st = new Storage();
There's a good possibility that these objects are not the ones being displayed, especially if you create these objects elsewhere and display them. Again I'm just guessing since I don't see the rest of your code, but perhaps you should to pass references for these objects into the DisplayForm via constructor or setter method parameters.
Edit 2:
e.g.,
public void setDisplayForm(DisplayForm dF) {
this.dF = dF;
}
// same for Storage
And in the main program:
public MainDisplayForm() {
initComponents();
ButtonListener_IncreaseGain btnListenerIncreaseGain = new ButtonListener_IncreaseGain();
btnListenerIncreaseGain.setDisplayForm(....);
btnListenerIncreaseGain.setStorage(....);
Btn_IncreaseGain.addActionListener(btnListenerIncreaseGain);
}

repainting an applet from a swingworker used to compute triangles and circum-circles

I am trying to replicate the applet found here as a part of an exercise. The applet is using Fortune's algorithm to generate both; a Voronoi diagram and Delaunay triangulation. I am just interested in generating the Delaunay Triangulation in a plane and thus, would be using the incremental algorithms i.e. adding 1 point at a time. I intend to show the triangles being generated at every stage when a sample point is added.
I am using a SwingWorker class to create an instance of the Triangulate class which contains the algorithm. I am calling the triangulate method inside a for loop which iterates through the set of sample points when the start button on the GUI is clicked.
Here's the code for that:
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
SwingWorker<List<Triangle>, Triangle> worker = new SwingWorker<List<Triangle>, Triangle>() {
#Override
protected List<Triangle> doInBackground() throws Exception {
Triangulate dt = new Triangulate(drawingPanel.pointsList());
dt.preTriangulate(); //Set-up a bounding triangle and obtain a random permutation of the points
List<PlanarPoint> pointsList = dt.pointsList();
for (int i = 0; i < pointsList.size(); i++) {
PlanarPoint sample = pointsList.get(i);
dt.triangulate(sample);
List<Triangle> list = dt.trianglesList(); //Obtaining the list of triangles at every stage. Good Idea??
for (int j = 0; j < list.size(); j++) {
publish(list.get(j));
}
Thread.sleep(500);
}
dt.removeTriangles(dt.trianglesList()); // Remove all the triangles containing bounding-triangle vertices
return dt.trianglesList();
}
protected void process(List<Triangle> triangles) {
for (Triangle triangle : triangles) {
g = drawingPanel.getGraphics();
PlanarPoint p1 = triangle.getVertex1();
PlanarPoint p2 = triangle.getVertex2();
PlanarPoint p3 = triangle.getVertex3();
g.drawLine((int) Math.ceil(p1.x), (int) Math.ceil(p1.y),
(int) Math.ceil(p2.x), (int) Math.ceil(p2.y));
g.drawLine((int) Math.ceil(p2.x),(int) Math.ceil(p2.y),
(int) Math.ceil(p3.x),(int) Math.ceil(p3.y));
g.drawLine((int) Math.ceil(p3.x),(int) Math.ceil(p3.y),
(int) Math.ceil(p1.x),(int) Math.ceil(p1.y));
}
}
};
worker.execute();
}
});
Here is the Triangulate class which computes a Delanuay Triangulation of a set of points:
public class Triangulate {
private List<PlanarPoint> pointsList;
private List<Triangle> triangleList;
private Triangle boundingTriangle;
private List<Edge> edgeList;
public Triangulate(List<PlanarPoint> pointsList) {
this.pointsList = pointsList;
this.triangleList = new ArrayList<Triangle>();
this.edgeList = new ArrayList<Edge>();
}
public List<Triangle> trianglesList() {
return triangleList;
}
public List<PlanarPoint> pointsList() {
return pointsList;
}
public void preTriangulate() {
boundingTriangle = getBoundingTriangle(pointsList);
triangleList.add(boundingTriangle);
randomPermutation(pointsList);
}
public void triangulate(PlanarPoint samplePoint) {
// A procedure implementing the Bowyer - Watson algorithm
// to calculate the DT of a set of points in a plane.
}
public void removeTriangles(List<Triangle> trianglesList) {
// A procedure to remove all triangles from the list sharing
// edges with the bounding-triangle
}
private Triangle getBoundingTriangle(List<PlanarPoint> pointsList) {
//Obtains a bounding-triangle for a set of points
}
public void randomPermutation(List<PlanarPoint> pointsList) {
//Obtains a random permutation of a set of points
}
}
I have 3 other classes
PlanarPoint - sub-class of Point2D.Double which implements Comparable to provide a y-co-ordinate based sorting
Triangle - A class which determines a circum-circle and circum-radius for the triangle and determines whether a point lies inside the circumcircle of the triangle
Edge - A class which represents Edge as the one having 2 PlanarPoints as its end-points.
DrawingPanel - A class which acts as the surface on which points are added at click events and drawn on the screen.
Now, here are a few concerns which I have
Is there a better way to show the triangles and possibly circum-circles by iterating over a set of points and then calling a function of the Triangulate class to get the existing circum-circles and triangles
Should all the drawing be restricted to the DrawingPanel class since in the code snippets above I am painting in the class which extends JApplet/JFrame and thus whenever the window is resized, the drawn triangles are lost? Is there a design pattern which I can follow?
Is the usage of SwingWorker over spawning another thread justified over here except for the fact that the time to compute the DT of a set of points is a time-consuming task?
If I have missed any details, please let me know
Thanks,
Chaitanya
Suggestions:
Don't use getGraphics() to get a Graphics object since the Graphics object obtained won't persist if any repaint is performed (something out of your control). Instead draw to a BufferedImage and have the JPanel or JComponent draw the BufferedImage in its paintComponent override, or add your image data to a Collection of some sort, and have the paintComponent override method iterate through the Collection using the information to draw your images.
Don't draw directly in a top level window such as a JFrame or JApplet, but instead in a component that derives from JComponent, often either JComponent itself or JPanel.
Read the Swing graphics tutorials as they will explain all of this and more.
SwingWorker is fully justified since you want to create a thread that is background to a Swing application yet interacts with the Swing application -- the very situation that SwingWorkers were created for.

Categories

Resources