How to draw graphs using Java Swing - java

I am learning Java Swing. I followed a YouTube lectures playlist that provided this github code for drawing graphs. I am providing the basic structure here:
package graphapp;
import com.sun.corba.se.impl.orbutil.graph.Graph;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JComboBox;
import javax.swing.JFrame;
public class GraphApp extends JFrame {
int x,y;
int ax,by;
JComboBox cb,cb1;
String s="";
String se ="";
public GraphApp(){
setTitle("Graph App");
setSize(900,700);
String[] graphs = {"select..","parabola","ax^2+bx+c","ax^3","y=mx","y=mx+c","sin(x)","cos(x)","tan(x)","sinc function","signum(x)","X-graph","cubic function","sin+cos unequal amp","sin^3","cos^3","sin^3+cos^3","Amplitude Modulation"};
cb = new JComboBox(graphs);
cb.setBounds(700, 100, 120, 25);
add(cb);
cb.setEditable(false);
String[] select = {"Draw graph","Erase"};
cb1 = new JComboBox(select);
cb1.setBounds(700, 150, 120, 25);
add(cb1);
cb1.setEditable(false);
setLayout(null); //add it its very important otherwise Combo Box will occupy the whole screen.
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 30; //x=200;
y = 300;
}
public void paint(Graphics g){
super.paint(g); //This method was not called in The Master Branch at github
g.setColor(Color.BLACK);
g.drawString("Design by ..", 700, 400);
g.drawString("Debasish Roy", 700, 420);
g.drawString("Y", 310, 40);
g.drawString("Y'", 310, 600);
g.drawString("X", 30, 314);
g.drawString("X'", 600, 314);
if(x==300&&y==300){
g.drawString("Origin(0,0)", 310, 314);
}
g.drawString("Select Graphs", 710, 100);
g.drawLine(300, 30, 300, 600);
g.drawLine(30,300,600,300);
if(x>599||y<40){
g.drawString((String) cb.getSelectedItem(), 200, 200);
s= String.valueOf(cb.getSelectedItem());
se = String.valueOf( cb1.getSelectedItem());
x=30;
y=300;
}
if(s.equals("parabola")&& se.equals("Draw graph")){
g.setColor(Color.GREEN);
run1(); // function to set x and y values
}
//Other checks to find the type of graph selected
else{
g.setColor(Color.white);
run();
}
g.fillOval(x, y, 3, 3);
repaint(); // When I run this code, the window keeps flickering. I think it is because this method called without any check.
//However I don't know much about this method
}
public void run(){
try{
Thread.sleep(1);
if(x<600&&y>30&&y!=600){
ax = x-300;
by = y-300;
ax++;
by = (int) (40*(1+1*Math.cos(.2*ax/3.14))*Math.cos(ax/3.14)+40*(40*Math.sin(.2*ax/3.14))/ax); // AM+sinc(x) function
x=300+ax;
y=300-by;
}
}catch(Exception e){
System.out.println("ERROR");
}
}
public static void main(String[] args){
new GraphApp();
Thread t1 = new Thread();
t1.start();
}
}
When I run this code, no graph gets drawn. Also, when I change selection in JComboBox no change is detected. Please help me what am I missing to grab here.

When I run this code, no graph gets drawn.
You should NOT be overriding paint(...) on a JFrame. Custom painting is done by overriding paintComponent(...) of a JPanel and then you add the panel to the frame. Read the section from the Swing tutorial on Custom Painting for more information and working examples to get you started.
A painting method is for painting only. You should NOT invoke repaint(). Swing will determine when a component needs to repaint itself or you invoke repaint() in an external method. The painting code should only paint the current state of the component. If you have properties that change then you need methods to change those properties and then repaint() the component.
You should NOT invoke Thread.sleep() in a painting method.
You are creating a Thread that does nothing. You need to pass a runnable object to the Thread if you want to execute code when the Thread starts. However, in this case if you want animation of the graph then you should be using a Swing Timer for the animation. When the Timer fires you update the properties of your class and then invoke repaint().
Also, when I change selection in JComboBox no change is detected.
You need to add an ActionListener to the combo box. Read the section from the Swing tutorial on How to Use Combo Boxes.
So you need to spend time reading the Swing tutorial to learn some Swing basics before continuing on with your project.

Related

Java Swing paint() Twice Show Once when Full Screen

I'm making a small game using Java Swing. A class Game extends JPanel and implements ActionListener, a Timer in it calls repaint() to update the screen, then create a JFrame and add Game to it.
Then I noticed when I make the window full screen, the screen is updated once when paint() is called twice. This is weird! Any help would be appreciated.
Here is an SSCCE, the example creates a variable count, and paint count then count+=1; in repaint(). When not full screen, it shows 0 1 2 3 4... When full screen, it shows 4 6 8 10...
Example code here:
package test.swing.FullScreenDropFrameRate;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
class Game extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private int count = 0;
public Game() {
new Timer(1000, this).start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paint(Graphics g) {
// bg
g.setColor(Color.black);
g.fillRect(0, 0, getSize().width, getSize().height);
// info string
g.setColor(Color.white);
g.setFont(new Font("serif", Font.BOLD, 30));
g.drawString(""+count, 0, 30);
count += 1;
g.dispose();
}
}
public class TestFullScreenFrameRate {
public static void main(String[] args) {
JFrame jf = new JFrame("Snake");
jf.setBounds(100, 35, 800, 600); // x, y, width, height
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(new Game());
jf.setVisible(true);
}
}
OK, so the problem is that I shouldn't put anything which updates the game state in paint(). Java calls it when needed. Thanks to #Andrew Thompson.
Some other mistakes, improvements and documentation:
1)Documentation - JComponent - paint(): Invoked by Swing to draw components. ... A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just override paintComponent.
2)Documentation - Font - SERIF: A String constant for the canonical family name of the logical font "Serif". It is useful in Font construction to provide compile-time verification of the name.
3)Oracle - about Painting
4)Documentation - Graphics - dispose(): For efficiency, programmers should call dispose when finished using a Graphics object only if it was created directly from a component or another Graphics object.
5)A Java Game Tutorial on Youtube: Bad example! DO NOT use paint() and g.dispose() here!!!

Paint function defined in separate thread not drawing (Java)

I have three files, here is the main one, titled Display.java:
import java.awt.*;
import javax.swing.*;
public class Display{
static JFrame main = new JFrame("hello");
static Container c = main.getContentPane();
static StartScreen start = new StartScreen();
static screenTracker track = new screenTracker();
public static void main(String[] args) {
main.setSize(new Dimension(1920,1080));
main.setVisible(true);
if(track.screen==1) {
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.add(start, BorderLayout.CENTER);
}
}
}
My second file is titled: StartScreen.java. It contains my paint function:
import java.applet.Applet;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class StartScreen extends Applet{
private static final long serialVersionUID = 1L;
int x = 0;
static Container c = Display.c;
static Color gray = new Color(128,128,128);
public void paint(Graphics g) {
Thread t = new Thread() {
#Override
public void run() {
while(true) {
c.setBackground(gray);
g.clearRect( 0 , 0 , getWidth() , getHeight() );
BufferedImage img1 = null;
BufferedImage img2 = null;
BufferedImage img3 = null;
try {
img1 = ImageIO.read(new File("images/img1.png"));
img2 = ImageIO.read(new File("images/img2.png"));
img3 = ImageIO.read(new File("images/img3.png"));
}
catch(IOException e) {
g.drawString("bad", 200, 200);
}
String title1 = "hello: ";
String title2 = "Gamee ";
String title3 = "people";
Color pink = new Color(244,66,182);
Color black = new Color(0,0,0);
g.setColor(black);
g.setFont(new Font("TimesRoman", Font.PLAIN, 50));
g.drawString(title1, x+600, 200);
g.setColor(pink);
g.setFont(new Font("TimesRoman", Font.ITALIC, 50));
g.drawString(title2, 860, 200);
g.setFont(new Font("TimesRoman", Font.PLAIN, 50));
g.setColor(black);
g.drawString(title3, 960, 200);
g.drawImage(img1, 200, 250, null);
g.drawImage(img2, 700, 150, 1000, 750, null);
g.drawImage(img3, 500, 250, null);
x++;
try {
sleep(10); // milliseconds
} catch (InterruptedException ex) {}
}
}
};
t.start();
}
}
My third file is short:
public class screenTracker {
int screen = 1;
}
Right now I just want the paint function in StartScreen.java to display on my JFrame. I want hello to move across the screen. I made the thread t so the screen can close. If I get rid of the thread t, or create it in Display.java (inside the if statement, around where I set the default close operation and add the startscreen to the container c) the program draws what I want, but the Jframe won't close. I have looked in a lot of other websites, and questions, but I haven't been able to figure this out. I am new to multithreading and graphics in java,
Many problems:
Don't extend Applet. Applets are dead. For custom painting you would simply extend JPanel and add the panel to the frame.
You would override paintComponent() in the JPanel, not paint().
A painting method is for painting only you should NOT:
read the image in the method. The painting method gets called many times. You want painting to be fast and should not be doing I/O. Read the images in the constructor of your class
be creating Threads. Again since the painting method is called multiple times that you can't control you don't want to keep creating Threads. If you want animation of some kind then you should be using a Swing Timer.
I suggest you read the section from the Swing tutorial on Custom Painting. It contains working examples that will show you how to better structure your code.
There is also a section on How to Use Swing Timers.
If we speak about graphics in java we have only one thread responsible for It the EDT ([1][Event Dispatch Thread]). In other words whatever you want to do with the view will be handled by and has to be handled by EDT, yes your view also with all setbackroungs, frames...
But be aware, it is only one thread, when this tread is busy doing some calculation cannot react to the user events, so your view will freezee. What you can do in another thread is to prepare the data (in your case read the file images)
To work in the EDT you use SwingInvoker.invokelater() or check if you are already in EDT by using swingutilities.isEventDispatchThread()
[1] https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

Java GUI does not appear

This is for my university project and I am busting my brain around why my Java GUI does not work. This is the situation: the code compiles and executes without an issue.
This code should create 300 X 300 frame center the desktop and create circle and it print my name underneath.
I got it working until the frame, but no circle
package gui;
import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
public class GUI extends JFrame{
public void Paint (Graphics g){
super.paintComponents(g);
g.setColor(Color.yellow);
g.fillOval(50, 50, 200, 200);
g.setColor(Color.BLACK);
g.drawArc(75, 60, 150, 150, -25, -125);
g.fillOval(100, 100, 25, 25);
g.fillOval(175, 100, 25, 25);
g.setColor(Color.BLUE);
g.setFont(new Font("Serif", Font.BOLD,18));
g.drawString("My Nanme is BOB", 33, 275);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
GUI GUI = new GUI() ;
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.setSize(300,300);
GUI.setTitle("BOB's GUI App");
GUI.setVisible(true);
GUI.setLocationRelativeTo(null);
I really appreciate your output. also please give me a hint why it does not work
Java is case-sensitive :
public void Paint (Graphics g)
Will never override
public void paint (Graphics g)
In your main, you have written
GUI GUI = new GUI() ;
You should make the second GUI something else. For example,
GUI gui = new GUI();
Your paint() method should have a lowercase 'P'. As you currently have it, you're not overriding the existing JFrame method.
To avoid such issues in the future, I would investigate the #Overrides annotation. If you use this on the above, it will tell you you're not overriding anything!

Getting a Circle to orbit around another

I have been doing a ton of research and none of the questions I found really answered my question and that is why I am making this post.
I want to create a program that will have a circle, a "planet" orbit around a another circle, a "Sun."
I have the static gui set up, but nothing I have found in my book or online really helps solves the orbit problem. Any ideas?
NOTE: eventually the program needs to be multithreaded (one for the planet and one for the Sun) but I want to break the problem down before I get back into trying to get that to work so for now please disregard it.
GUI:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class PlanetsGUI extends JPanel
{
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
setBackground(Color.BLACK);
paintSun(g);
paintPlanet(g);
}
public void paintSun(Graphics g)
{
super.paintComponent(g);
//create circle and fill it as yellow to represent the sun
g.setColor(Color.YELLOW);
g.drawOval(100, 75, 75, 75);
g.fillOval(100, 75, 75, 75);
} //end paintSun
public void paintPlanet(Graphics g)
{
//create circle and fill it as blue to represent the orbiting planet
g.setColor(Color.BLUE);
g.drawOval(35, 50, 50, 50);
g.fillOval(35, 50, 50, 50);
}//end paintPlanet
}//end class PlanetsGUI
MAIN:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JFrame;
public class OrbitingPlants_main
{
private static final ExecutorService execute + Executors.newFixedThreadPool(2);
public static void main(String[] args)
{
PlanetsGUI planet = new PlanetsGUI();
JFrame frame = new JFrame();
frame.setTitle("Orbiting Planets");
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(planet); //add panel onto frame
frame.setVisible(true);
//PlanetsLogic r = new PlanetsLogic();
//Thread sun = new Thread(sun);
//sun.start();
//execute.submit(new );
}//end main
}//end class
You're hard-coding where the images are being drawn, for example:
g.setColor(Color.BLUE);
g.drawOval(35, 50, 50, 50);
g.fillOval(35, 50, 50, 50);
making for images that can never change.
Instead get rid of the hard coded numbers and use variables, values that can change:
g.setColor(Color.BLUE);
// variables used below are fields declared in the class
g.drawOval(planetX, planetY, planetW, planetW); // probably don't need this line
g.fillOval(planetX, planetY, planetW, planetW);
and then change the values in your thread (or better, Swing Timer).
As far as "orbiting", that would be using the equations of a circle (or ellipse if you wanted to be extremely precise), and perhaps using a parametric equation to determine your planetX and planetY values.

Java - Do game loop in paint method or separate-threaded while loop?

I am trying to code an application that gets user mouse input and draws a dot on each click (not that that is especially important). Where should the game loop functions be, in the paint method or in a separate while loop? Here is my code:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class Main extends JPanel{
static Rectangle scrnSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
//static Dimension wholeScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
//public static int taskbarHeight = wholeScreenSize.height - scrnSize.height;
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillOval(0, 0, 30, 30);
g2d.drawOval(0, 50, 30, 30);
g2d.fillRect(50, 0, 30, 30);
g2d.drawRect(50, 50, 30, 30);
g2d.draw(new Ellipse2D.Double(0, 100, 30, 30));
//do loop stuff here?
}
public static void main(String[] args) throws InterruptedException {
//init
JFrame frame = new JFrame("DrillSweet");
frame.setLayout(new GridLayout());
Main main = new Main();
frame.add(main);
frame.setSize(new Dimension(scrnSize.width, scrnSize.height));
frame.setPreferredSize(new Dimension(scrnSize.width, scrnSize.height));
frame.setMaximumSize(new Dimension(scrnSize.width, scrnSize.height));
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//game loop
while(true){
//do loop stuff here?
main.repaint();
Thread.sleep(10);
}
}
}
First things you need know...
You don't control the paint process, so don't screw with it. Painting is done by the RepaintManager, you can make suggestions to the RepaintManager about what should be painted, but it's the responsibility of the RepaintManager to decide what and when something should be painted.
Swing is not a thread safe framework. That is, all updates to the UI must be done from within the context of the Event Dispatching Thread...Take a look at Concurrency in Swing for more details...
Painting in Swing is a complex series of method calls which chain together to produce the final result, failure to honour this chain will result in nasty painting artifacts...
So, taking all that into account...
DON'T EVER perform any operation within in ANY paint method that may take time to execute, this includes, looping infinitely, loading of resources or using things like Thread.sleep
If you must change the UI, do so from within the context of the Event Dispatching Thread
Call super.paint or better yet, override paintComponent instead (and call super.paintComponent before you do any custom painting)
Instead of a Thread, based on you needs, consider using a Swing Timer, which will allow you to schedule a regular callback which will be done within the context EDT. This will ensure a level of synchronisation, as any changes you make to properties of the UI can't be used within any paint method (as your call back and the paint method are been called from the same thread) and help prevent possible dirty paints...
You may also like to take a look at Painting in AWT and Swing and Initial Threads and Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? for more information...
I not an expert in java, but I find it really useful to have a separated Timer for repaint and another for calculations.
Something like:
ActionListener counter = new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
repaint();
}
};
new Timer(40, counter).start();
and another timer for calculations
ActionListener counter = new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
for(Enemies en: enemies)
{
en.moveEnemies();
}
turret.moveShoots();
checkColision();
}
};
new Timer(5, counter).start();
With this, I ensure to have some precision with the calculation and constant game speed, independent from having good fps or a fluid animation.

Categories

Resources