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);
}
Related
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.
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);
}
I am programming a small space-themed dungeon crawler game with a GUI made with Swing in Java. I am using the MVC programming paradigm. The game is represented as a grid on which buttons can be clicked to move the player over the board or attack enemies.
In the model/logic of the game I generate an abstract representation of the game environment using a 2D array by randomly assigning objects to x (column) and y (row) coordinates using the following method:
//methods
public void setGalaxy() {
galaxyArray = new GalacticObject[this.row][this.col];
//construct the array
Random rand = new Random();
int random;
for (int i = 0; i < this.row; i++) {
for (int j = 0; j < this.col; j++) {
GalacticObject obj = new GalacticObject(i,j);
random = rand.nextInt(100);
if (random < 5) {
//asteroid (5 percent chance)
obj.makeAsteroid();
} else if (random < 9) {
//blackhole (4 percent chance)
obj.makeBlackHole();
} else {
//rest is open (= empty)
obj.makeOpen();
}
galaxyArray[i][j] = obj;
}
}
}
Now I want to draw the actual GUI game board backed by this 2D array of objects in a JPanel container using a grid of JButtons. I want to basically call all the objects in the galaxyArray and draw a JButton with an corresponding image overlay at the corresponding coordinates in a ButtonGrid. What would be the best way to achieve this using Swing?
Currently, I also have written the following draft for a method that draws a JButtonGrid but I still fail to see what is the best strategy of having it backed by a 2D array of objects.
Furthermore, adding the icon for the buttons is not working for reasons unclear to me. (I commented that part out.)
public JPanel makeGameBoard() {
/** This method makes a first version of the game board when the object is first called */
//create a new JPanel
JPanel boardPanel = new JPanel();
// create a gridButton of JButtons defined by the width and length
JButton[][] gridButton = new JButton[this.boardWidth][this.boardLength];
// set layout
boardPanel.setLayout(new GridLayout(this.boardWidth,this.boardLength));
//for-loops to place buttons in gridButton
for(int y = 0; y < this.boardLength; y++) {
for(int x = 0; x < this.boardWidth; x++){
// creates new button (with coordinates as string); gridButton[x][y] needs to be reused as coordinate system
gridButton[x][y]=new JButton(x+":"+y);
gridButton[x][y].setActionCommand(x+":"+y);
gridButton[x][y].setText("");
gridButton[x][y].addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
String com = e.getActionCommand();
System.out.println(com);
}
});
// add icon to every button
// try {
// Image img = ImageIO.read(getClass().getResource("resources/invisible.png"));
// gridButton[x][y].setIcon(new ImageIcon(img));
// }
// catch (IOException ex) {
// System.out.println("Image file for gridButton not found!");
// }
// add the gridButton to the panel
boardPanel.add(gridButton[x][y]);
}
}
//return boardPanel
return boardPanel;
}
Essentially I am wondering what is the best strategy to access the objects stored in the galaxyArray from my makeGameBoard method so that the variables of the objects in the array can be used to draw an image on the button with corresponding coordinates in the grid.
/*EDIT Thanks to the suggestion and linked tutorials from the friendly people below, I have made it work! Here is the code for the board View for anyone having similar questions:
public void updateGameBoard(Galaxy board) {
/** This method initializes the board when the galaxy is first created in the game logic and updates it throughout the game */
Galaxy galaxyArray = board;
for (int r = 0; r < this.boardWidth; r++) {
for (int c = 0; c < this.boardLength; c++) {
//make galactic object and extract info from Galaxy
GalacticObject temporary = new GalacticObject(r,c);
temporary = galaxyArray.getGalacticObject(r,c);
//check the object and set the corresponding tile image
if (temporary.isAsteroid()) {
gridButton[r][c].setText("A");
} else if (temporary.isAsteroidField()) {
gridButton[r][c].setText("AF");
} else if (temporary.isBlackHole()) {
gridButton[r][c].setText("BH");
} else if (temporary.isOpen()) {
gridButton[r][c].setText("");
}
}
}
}
The exact details depend on how you implement the observer pattern. Following this outline, the example game cited here simply lets the main view, RCView, keep a private reference to the array named board, which is maintained by the game model, RCModel. Because RCView implements the Observer interface, the view's update() implementation simply iterates through the board and updates its array of tiles in response. Note that the update() method signature includes a reference to the Observable requesting the update. In your example,
public void update(Observable model, Object arg) {
GalacticObject galaxyArray = ((GameModel) model).getGalaxyArray();
//loop though model, updating view components
this.repaint();
}
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.
I wish to place a small Jframe right above the Button, on ActionPerformed
I directly tried to get the X (getX()) and Y(getY()) co-ordinates of the JScrollPane in which the button is added, but it always seems to return wrong co-coordinates
values returned by jScrollPane1.getLocation()
java.awt.Point[x=10,y=170]
The above values are same independent on where I place the JScrollPane on the screen.
This works if I remove the JScrollPane and directly try to get the Jpanels co-ordinates!!
for example
private void showDialog() {
if (canShow) {
location = myButton.getLocationOnScreen();
int x = location.x;
int y = location.y;
dialog.setLocation(x - 466, y - 514);
if (!(dialog.isVisible())) {
Runnable doRun = new Runnable() {
#Override
public void run() {
dialog.setVisible(true);
//setFocusButton();
//another method that moving Focus to the desired JComponent
}
};
SwingUtilities.invokeLater(doRun);
}
}
}
This nice method will help you:
// Convert a coordinate relative to a component's bounds to screen coordinates
Point pt = new Point(component.getLocation());
SwingUtilities.convertPointToScreen(pt, component);
// pt is now the absolute screen coordinate of the component
Add: I didn't realise, but like mKorbel wrote, you can simply call
Point pt = component.getLocationOnScreen();
Since you want to spawn a new frame right above a given component, you want to get the screen coordinates of your component.
For this, you need to use the getLocationOnScreen() method of your component.
Here is a useful code snippet :
public void showFrameAboveCmp(Frame frame, Component cmp) {
Dimension size = cmp.getSize();
Point loc = cmp.getLocationOnScreen();
Dimension frameSize = frame.getSize();
loc.x += (size.width - frameSize.width)/2;
loc.y += (size.height - frameSize.height)/2;
frame.setBounds(loc.x, loc.y, frameSize.width, frameSize.height);
frame.setVisible(true);
}