Each time I minimise and reopen my Java window, the graphics change - java

I got a bit of a problem which I am completely baffled about. Not even sure where to start to debug the problem.
I have a JFrame which promotes the user to enter a number, lets say the number was 4. I sent this number to my paint component to create a vertical line of 4 x 50. SO the line should be 200 in length. Which it does.
But if I minimise and reopen the window, that 4 seems to be multiplied again by 50 giving 200. Then that again is multiplied by 50 making the line 10,000.
I placed my paint component code in hope that it would help as the issue must be there, but if more code is needed I'll be happy to post.
class mainPanel extends JPanel
{
int processes;
public mainPanel(int x) //the value x is passed from another class, this was the number the user chooses...i.e 4
{
processes = x;
setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 5));
}
public Dimension getPreferredSize() {
return new Dimension (1000, 1000);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int storedProcesses = processes;
// Draw Title
g.setFont(new Font("TimesRoman", Font.PLAIN, 28));
g.drawString("We place the title here",380,50);
processes = processes * 50;
g.drawLine(100,100,100, processes+100); //Vertical (down) line
//labels for vertical line
g.setFont(new Font("TimesRoman", Font.PLAIN, 20));
int y = 125;
for (int i=1; i<=storedProcesses; i++) //this loop keeps repeating and getting larger for some unknown reason...then its repainting my JFrame
{
g.drawString(String.valueOf(i), 70,y);
y=y+50;
System.out.println("Loop" + storedProcesses); //used for debugging
}
g.drawLine(100,processes+100,1000,processes+100); //Horizontal (across) line
}
}
I've attached some screen shots to illustrate what's going on with 1 process as an example.
Before I minimise the window (this is fine):
This is after I minimise and re maximise the window

You can't control when Swing invokes the paintComponent() method. Therefore you should NEVER change a property of your class in the paintComponent() method. All variables that you manipulate in the paintComponent() method should be local variables.
processes = processes * 50;
Your current code is modifying the "processes" variable. Don't do this.

Related

How to make buttons on Java Swing Application when constantly redrawing frames

The simple version is that I'm drawing Graphics2D 60 times a second on a JPanel and it uses the drawstring method to create a bunch of labels. How would I go about making so I can click on those labels and have something happen?
Explanation: As it currently stands I have a system setup that says for every object in the world draw a string to the side (So I can see a list of all objects in the world). This is done with a for loop and the Graphics2D drawstring method.
The JPanel is being updated 60 times a second so this is being redrawn 60 times a second. I want to be able to click on these object labels so I can select the items, how would I go about turning them into buttons?
I messed around with JButton for awhile but it didn't seem to do me any good because whenever I added it the JPanel would go blank and only the button would render (Plus it didn't render to the right size).
More Details:
I use a
for(int I=0; I < sceneObjects.size(); I++) {
}
loop to grab every object in an object ArrayList. Each object has a String variable "Name". Before the loop class I sent an int called YPosition, and for every object the YPosition goes up by 20 so that the labels don't all stack on top of each other. I'm using the g2d.DrawString method to achieve this. But I need to be able to select the object labels.
I apologize if I forgot something in my question, let me know.
For those who are curious, the code looks exactly like this (Can't be compiled as is):
g2d.setFont(new Font("Arial", Font.PLAIN, hierarchyWidth / 26));
g2d.setColor(Color.black);
int oYPos = 20;
// For every object in existence
for(int i=0; i < engine.sceneObjects.activeObjects.size(); i++) {
GameObject theObject = engine.sceneObjects.activeObjects.get(i);
// If the scrollbar is within range of the hierarchy
// (Based on HierarchyHeight so that it's resolution friendly)
if(oYPos >= hierarchyScroll && oYPos < hierarchyScroll + hierarchyHeight) {
// If the object has no parent
if(theObject.transform.parent == null) {
g2d.drawString(theObject.name, hierarchyPosition.x + 5, hierarchyPosition.y + oYPos);
} else { // If the object has a parent
}
}
oYPos += 20;
}
// Track the last oYPos so that the scrollbar can adjust itself accordingly
lastOYPos = oYPos;
My guess would be some sort of class create for each of these labels and a Boolean stored called isSelected, and then rendering the label according to the class, but this seems a bit more complicated than I'd like to do

JAVA, display phrase in increasing font sizes in a frame

I am having an issue getting this code to run properly. It compiles and initially the frame displays properly. The problem is that when I manually re-size the frame by either maximizing or by dragging the side of the frame over, the text disappears. I am using jGRASP, not sure if that is the issue or not. The code seems to make sense to me, and like I said, it compiles(I know that does not necessarily make it right). I'm still a newbie at this so if anyone can point me in the right direction I would be very appreciative.
import javax.swing.*;
import java.awt.*;
public class JFontSizes extends JFrame {
int x = 5;
int y = 50;
String homework = "This is the first homework assignment";
public JFontSizes() {
super("Increasing Font Sizes");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics brush) {
super.paint(brush);
// This works sometimes. I am not sure if it is a jGRASP issue or something else.
// If I resize the frame, the text disappears, and I cannot get the text to start at the top of the frame
for(int n = 6; n<= 20; ++n) {
brush.setFont(new Font("Serif", Font.PLAIN, n));
brush.drawString(homework, x, y);
y += 15;
}
}
public static void main(String[] args) {
JFontSizes frame = new JFontSizes();
frame.setSize(400, 500);
frame.setVisible(true);
}
}
When first time paint() is called the value of y was 5. And it is incremented in a loop. So that before leaving paint() its value will be 275.
But when you resize your frame paint() is called again and this time the value of y is 275 and when brush.drawString(homework, x, y); is called the homework is printed at 275px bottom from top left corner.
So what you need to do is re-initialize y every time :
public void paint(Graphics brush) {
y = 50;
....
Edit :
As commented by camickr you should override paintComponent(...) instead of paint(...) until you have some specific reason to override paint().
And you mean you are not able to print text at top (even in beginning) then it is because you had initialized y with 50. Which means the text will be drawn at 50px from top.

How do I make the lines draw without an actionlistener?

I am writing a GUI that is supposed to write lines and circles to a panel and I am supposed to use sliders to change how fast they add to the panel. I am supposed to add a clear button that will clear the entire panel and then when I move the sliders they should make the circles and lines begin to write on the panel again. There should be a specific stop point at the beginning of the sliders. We have been told to do this without actionlisteners on the sliders. I am having some trouble understanding how to make that work.
Below are the requirements for the assignment:
Write a Swing program that provides the following functionality:
Draw random length lines of random color at random coordinates with pauses between the drawing of each line.
Allow the user to set the length of the pause between lines with a slider. Have the slowest value actually stop drawing lines (i.e., it slows to a stop once it is at that value on the slider).
Have a clear button that clears all the lines & circles. Be sure that the clear button is operational at all times.
Draw random size circles of random color at random coordinates with pauses between the drawing of each circle. (Use draw, not fill.)
Allow the user to set the length of the pause between circles with a slider. Have the slowest value actually stop drawing circles (i.e., it slows to a stop once it is at that value on the slider). This is independent of the lines' speed.
The circles and lines are both drawn independently, each in their own Thread.
Do not use Timer for this, extend Thread and/or Runnable.
public class OhMy extends JFrame
{
private static final int MAX_COLOR = 225;
private static final long STOP_SLEEP = 0;
public OhMy()
{
this.setTitle("Oh My Window");
Container canvas = this.getContentPane();
canvas.setLayout(new GridLayout(2,1));
JPanel panControl = new JPanel(new GridLayout(1,1));
JPanel panDraw = new JPanel(new GridLayout(1,1));
canvas.add(panControl);
canvas.add(panDraw);
panControl.add(createPanControl());
panDraw.add(createPanDraw());
this.setSize(800, 600);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private JPanel createPanControl()
{
JPanel panControl = new JPanel();
JLabel lines = new JLabel("Lines");
panControl.add(lines);
lines.setForeground(Color.RED);
JSlider sldSpeedLines = new JSlider(1, 30, 5);
panControl.add(sldSpeedLines);
JButton btnClear = new JButton("Clear");
panControl.add(btnClear);
btnClear.setForeground(Color.RED);
JSlider sldSpeedCircles = new JSlider(0, 30, 5);
panControl.add(sldSpeedCircles);
JLabel circles = new JLabel("Circles");
panControl.add(circles);
circles.setForeground(Color.RED);
btnClear.addActionListener((e)->
{
repaint();
});
return panControl;
}
private JPanel createPanDraw()
{
JPanel panDraw = new JPanel();
class LinesThread extends Thread
{
#Override
public void run()
{
try
{
Graphics g = panDraw.getGraphics();
while(g == null)
{
Thread.sleep(STOP_SLEEP);
g = panDraw.getGraphics();
}
Random rand = new Random();
int red = rand.nextInt(MAX_COLOR);
int green = rand.nextInt(MAX_COLOR);
int blue = rand.nextInt(MAX_COLOR);
Color color = new Color(red, green, blue);
int x1 = rand.nextInt(panDraw.getWidth());
int y1 = rand.nextInt(panDraw.getHeight());
int x2 = rand.nextInt(panDraw.getWidth());
int y2 = rand.nextInt(panDraw.getHeight());
g.setColor(color);
g.drawLine(x1, y1, x2, y2);
}
catch(InterruptedException e1)
{
//awake now
}
}
}
return panDraw;
}
/**
* #param args
*/
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new OhMy();
}
});
}
}
You state:
"We have been told to do this without actionlisteners on the sliders..."
Good, because JSliders won't accept an ActionListener.
JSliders will accept a ChangeListener though, but you likely don't even need to use this.
Instead, give the clear button an ActionListener (you've no way to get around using ActionListeners at all).
In that ActionListener, reset the drawing and get the values from the JSliders by simply calling getValue() on it.
Don't get your Graphics object by calling getGraphics() on the JPanel since the Graphics object thus obtained will not be stable risking a broken image, or worse, a NullPointerException (to see what I mean, minimize and restore your current application while its drawing).
Instead either draw on a BufferedImage that is displayed in the JPanel's paintComponent method or draw directly in paintComponent itself.
Avoid using a Thread and Thread.sleep, but instead use a Swing Timer -- it's much easier this way to be sure that your code is threading appropriately.
Use this value to adjust the speed of your Swing Timer.
Edit
Thanks to Abishek Manoharan for pointing out problems in my answer...
If the JSliders need to change the speed of drawing while the drawing is proceeding, then you will in fact need to use ChangeListener on the slider.
In that listener change a field that will tell the Thread how long to sleep.
I see that you're also required to use background threads. If so, then be sure to make all Swing calls on the Swing event thread. So if you're in the background thread and need to make a Swing call, then queue it on the Swing event thread by calling SwingUtilities.invokeLater(...) and pass in a Runnable that has your Swing call.

graphics not showing on resized subpanel?

I am having issues with a small project im working on. I am trying to create a Moveable message panel when holding down the mouse button but i am stuck on one part.
I want to place the A small panel with a size of 50x30 pixels that contains the message "java" in it and have this small panel in a larger panel and place that panel into my JFrame.
However, when i do so the message "java" disappears and only the the small panel in the larger panel appears. I added borders to my panels to make sure that my panels were actually visible. Please help and here is my code:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
public class MovingPanel extends JFrame {
private String message;
private int x = 100;
private int y = 100;
public MovingPanel() {
JPanel panel = new JPanel();
MessagePanel p1 = new MessagePanel("Java");
panel.setBorder(new LineBorder(Color.RED, 2));
panel.setLayout(null);
p1.setLocation(x, y);
p1.setSize(50, 30);
p1.setBorder(new LineBorder(Color.BLACK, 2));
p1.setLayout(new BorderLayout());
panel.add(p1);
add(panel);
}
public static void main(String[] args) {
MovingPanel frame = new MovingPanel();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Test Message Panel");
frame.setVisible(true);
}
class MessagePanel extends JPanel {
public MessagePanel(String s) {
message = s;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(message, x + 20, y + 10);
}
}
}
Maybe you can try to use a simple JLabel component instead of your "MessagePanel".
First thing you need to understand is this.
The second and third arguments of this g.drawString(message, x + 20, y + 10); method are the x and y location of the panel.
With the above being said, you have to remember that it is the x and y location of the containing panel, which is MessagePanel.
You have the size of your MessagePanel object set at 50, 30, yet you are trying to access a point 120 (x + 20) and 110 (100 + 10), which does not exist since you size the size of the panel.
So now that's understood, let's say you want to paint the message at the very left corner of the MessagePanel, so you try and do this g.drawString(message, 0, 0);. This still would show anything as the point starts from the bottom left corner of the message, so the message would actually be riding just above the visible area.
When drawing strings, you need to consider the FontMetrics, which allows you to get the size of the string you are trying to draw, so you can position the message exactly on the screen where you want it.
A simple fix would be just set an x and y a little above 0, 0, like 15, 15. Though this might get your message to draw, it wouldn't be centered. You can keep on changing and getting different numbers to check if it is aligned in the middle, but the proper way is to use FontMetrics
As a said a simple (but maybe not desired) fix is to just change this
g.drawString(message, x + 20, y + 10);
To
g.drawString(message, 15, 15);
And you will see the message.
Instead of what you are doing though, this is how I would do it.
Instead of using two panels, I would just use one - the one that's doing the painting.
Don't set the size of it, instead override getPrefferedSize inside that class, to whatever size you want the main panel to me.
When you draw, just draw a rectangle the size you want at the specified coordinates.
Also draw the message in the same paintComponent method.
call pack() on the JFrame.
If you do the above, there's no need to try and move the location of the MessagePanel. Instead move the x and y coordinates when you call repaint, You can have offsets for the message. Like
int boxX = 100;
int boxY = 100;
int messageOffset = 15;
Then you can paint like this
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(boxX, boxY, 50, 30);
g.drawString(message, boxX + messageOffset, boxY + messageOffset);
}
Now in your action methods, just alter the boxX and/or boxY and call repaint.
Also, if you want a thicker line, look into Graphics2D API, you can setStroke.

User-selected marker in time series data in Java

My code plots 5000 points of time series data in a panel that is 581 pixels wide by default, but this width changes when the user resizes the window. My code also plots several rectangular markers that each identify a local maximum/peak in this same space.
I need to enable the user to right click on any of the rectangular-peak-markers so that the user can manually delete any false peak. The problem is that my code is reporting different x-coordinates than expected when the user right-clicks on a peak-marker. I suspect that the reason may have to do with rounding error in converting from 581 x-pixels back to 5000 data indices. But I am not certain of the reason.
Can anyone suggest a solution that enables my users to manually select one of the above-described peak markers by right-clicking on it?
I am enclosing relevant sections of the code below. My actual code is very, very long, and too complicated to post. But the relevant portions below should be enough for someone to see the logic of my approach, and to then suggest a more effective approach.
The code that declares the class in question is:
class SineDraw extends JPanel implements MouseMotionListener, MouseListener {
// lots of code, including the two segments excerpted below
}
This segment of code overloads the paintComponent of the JPanel so that my data is plotted:
// declare some variables
ArrayList<Double> PeakList = new ArrayList<Double>() // this ArrayList is populated by an extraneous process
visiblePoints = 5000
hstep = getWidth()/visiblePoints //=581/5000 by default, but will change when user resizes window
int numPeaks = PeakList.size();
// scale (y-coordinate) data relative to height of panel
pts = new double[visiblePoints]
for (int i = 0; i < pts.length-1; i++){pts[i]=//data vertical scaled to fill panel;}
// plot the 5000 time-series-data-points within the 581 pixels in x-axis
for (int i = 1; i < visiblePoints; i++) {
int x1 = (int) ((i - 1) * hstep);
int x2 = (int) (i * hstep);
int y1 = (int)pts[i - 1];
int y2 = (int)pts[i];
g2.drawLine(x1, y1, x2, y2);
}
// plot a rectangle for each of the local peaks
for(int m=0;m<=(numPeaks-1);m++){
if(i==(int)(PeakList.get(m)){
int currentVal = (int)pts[(int)(PeakList.get(m)];
g2.drawRect((int)(PeakList.get(m), currentVal, 6, 6);
}
}
This section of code is for handling the right-clicking of the mouse:
public void mousePressed(MouseEvent e){
// check to see if right mouse button was clicked
boolean jones = (e.getModifiers()&InputEvent.BUTTON3_MASK)==InputEvent.BUTTON3_MASK;
if(jones==true){
// test the value returned as x-coordinate when user right-clicks (code always underestimates x-coordinate of local peaks by this test)
double ReverseHstep = visiblePoints/getWidth();
int getX_ConvertedTo_i = (int) (e.getX()*ReverseHstep);
System.out.println("getX_ConvertedTo_i is: "+getX_ConvertedTo_i );
// check to see if peaklist contains a value within the x-coordinates of the user-selected-rectangle
if(PeakList.contains((double)(e.getX()-3))
||PeakList.contains((double)(e.getX()-2))
||PeakList.contains((double)(e.getX()-1))
||PeakList.contains((double)(e.getX()))
||PeakList.contains((double)(e.getX()+1))
||PeakList.contains((double)(e.getX()+2))
||PeakList.contains((double)(e.getX()+3))
){
// handling code will go here, but for now it is a print test that never succeeds because x-coordinate is always underestimated
System.out.println("You just selected a peak!");
}
}
repaint();
}
I suggest you create objects (in this case Rectangles) for each thing you want to be clickable. Here is an over-simplified example of how you can make something you draw clickable. The key thing to take away from this is the mouseClicked method which will display a dialog only if the mouse clicked within the rectangle.
One tricky point is that I wasn't able to figure out how to make the rectangle filled in with color without drawing another rectangle over it. I'll leave that one for you ;-)
public class Canvas extends JPanel implements MouseListener{
private Rectangle rect = new Rectangle(100,100);
public Canvas(){
this.addMouseListener(this);
rect.setSize(100, 100);
}
#Override
public void paintComponent(Graphics g){
g.setClip(rect);
g.setColor(Color.RED);
g.fillRect(0, 0, 100, 100);
}
#Override
public void mouseClicked(MouseEvent e){
if(rect.contains(e.getPoint())){
JOptionPane.showConfirmDialog(this, "Click!");
}
}
// The rest of the MouseListener methods have been cut out
public static void main(String[] a){
JFrame frame = new JFrame("Canvas Thingy");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(0, 0, 300, 300);
frame.add(new Canvas());
frame.setVisible(true);
}
}

Categories