This is a bug that I tracked down yesterday and since then having a hard time to actually describe it.
The bug itself starts in the main method:
public static void main(String[] args) {
new LogManager();
new ConfigManager();
new Rendering();
new ScriptManager();
new FileManager();
new SleepManager(System.currentTimeMillis()); <-- Here it is as if the JVM just stops reading code
Since I work with eclipse I used breakpoints to locate the problematic line. And it actually reaches that last line(new SleepManager) but the method breakpoint at the SleepManager constructor is never reached:
public SleepManager(long t) {
lastCheck = t;
There is only that one constructor in the class. And in addition there is no exception whatsoever.
But eventually I managed to narrow it down to my JFramework in the Rendering Class:
public class Rendering extends JPanel implements Runnable {
private static final Log LOG = new Log(Rendering.class.getSimpleName());
private static GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
public Rendering() {
//define basics
systemFullScreen_width = device.getDisplayMode().getWidth();
systemFullScreen_height = device.getDisplayMode().getHeight();
width = (systemFullScreen_width > 0 ? systemFullScreen_width : width);
height = (systemFullScreen_height > 0 ? systemFullScreen_height : height);
//crate framework
frame = new JFrame();
frame.addWindowListener(new FrameClose());
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.setSize(width, height);
frame.setUndecorated(true);
frame.setBackground(new Color(0.0f,0.0f,0.0f,0.5f));
frame.setVisible(true);
frame.setTitle(" Monitor ");
frame.setResizable(false);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setLocationRelativeTo(null);
frame.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke> emptySet()); //TODO remove if not needed
panel = this;
panel.setSize(width, height);
panel.setBackground(new Color(0.4f,0.4f,0.4f,0.9f));
--> frame.add(panel, 0); //here i suspect the error
panel.revalidate();
panel.setVisible(true);
//start rendering
this.start();
//frame.setAlwaysOnTop(true); //TODO enable at the end
//set input listeners
frame.addKeyListener(input);
frame.addMouseListener(input);
frame.addMouseMotionListener(input);
frame.addMouseWheelListener(input);
LOG.info("Rendering started!");
}
Ok so what I actually did accomplish in that method (since it worked when I wrote it and the transparent grey background is still working/visible) is that I try to create a half-transparent semi-fullscreen grey background to later render my own GUI element on it aka Graphics2D. Had rough time working around the fullscreen/transparency/lightweight container problem but still open for better solutions if someone has them.
So the marked line(frame.add(panel, 0);) seems to be the problem because it works when I remove the line (simple as that) and it seems to be the only line that does so.
The panel.revalidate(); was my first idea it did not help, jet i decided to keep it anyway.
Can't wrap my head around why that one line that was already done by runtime should stop another method, 200 lines beneath from being called? Any ideas?
Edit1:
Thanks to Matt who pointed out that these code lines are indeed somehow important to the subject:
public void render(Graphics2D g2d){
while(isRunning){
renderGUI(g2d); //this method does nothing jet its empty
}
frame.dispose();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
render((Graphics2D) g);
}
#Override
public void run() {
while(isRunning){
try{
frame.repaint();
Thread.sleep(5);
}catch(Exception e){
LOG.error(e);
}
}
}
Edit2:
What actually led me to the solution: Matt mentioned "EDT" what I didn't knew so I googled it and learn what the Event Dispatch Thread is. So now I knew, before this.start to start the thread gets called, the EDT actually at some point calls the paintComponent method first and enters this ugly child-code doubleloop-ception that I totally overlooked. Honestly... Shame on me. Thx Matt.
Related
I have created a program that draws a thick line.
import javax.swing.*;
import java.awt.*;
public class Movement {
int xGrid = 50;
public static void main(String[] args) {
Movement m = new Movement();
m.animate();
}
public void animate() {
JFrame frame = new JFrame("Movement");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ScreenDisplay display = new ScreenDisplay();
frame.getContentPane().add(display);
frame.setSize(400, 400);
frame.setVisible(true);
for (int aL = 0; aL < 200; aL++) {
xGrid++;
display.repaint();
try {
Thread.sleep(50);
} catch (Exception ex) { }
}
}
class ScreenDisplay extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillOval(xGrid, 175, 50, 50);
}
}
}
Because of the method "Thread.sleep(50)", the speed of the program slows down a little.
So I got a little curious and removed the "sleep()" method.
What I expected to output was the exact same output, just extremely fast.
However, it just prints out one circle in the frame.
I don't really know why it outputs just one circle, none of the researches I've done back up the answer.
Can anyone please explain why?
From Component.repaint documentation:
Repaints this component.
If this component is a lightweight component, this method
causes a call to this component's paint
method as soon as possible. Otherwise, this method causes
a call to this component's update method as soon
as possible.
By the looks of it, your for loop finishes so quickly that by the time the component calls the repaint method, it has already finished and therefore only paints the final circle stored in the buffer.
Question: So I am attempting to understand how threads work with graphics so I created a program that should set the color of the screen to red using a user thread. However, when I run the program, it opens my JFrame window an infinite number of times on top of each other and I have to quit the program for it to stop. How do I prevent this from happening? Thanks in advance.
UPDATE: So a lot of you have explained to me the culprit (commented out now): frame.add(new MWT) that repeatedly calls the constructor and creates a new object. However, How do I simply add the Canvas to the JFrame without any static instances? Thanks
Class Code
public class MWT extends Canvas implements Runnable
{
private Thread fast;
public static void main(String [] args){
MWT draw = new MWT();
}
public MWT(){
JFrame frame = new JFrame("Thread Drawings");
frame.setVisible(true);
frame.setFocusable(true);
frame.setSize(600,500);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
// CULPRIT
//frame.add(new MWT());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
start();
}
private void stop() {
if (fast== null){
return;
}
else{
System.exit(0);
}
}
private void start() {
if (fast != null){
return;
}
else{
fast = new Thread(this);
fast.start();
}
}
#Override
public void run() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D) g;
render(g2d);
}
public void render(Graphics2D g2d){
g2d.setColor(Color.RED);
g2d.fillRect(0, 0,600,500);
stop();
}
}
The problem is with the constructor for MWT at the line add(new MWT()). So when you construct a new MWT, you create a new JFrame, then you called MWT() again, creating a new JFrame, calling MWT() again, and so on. Eventually you should run into a stack overflow though.
To solve that, you could either extend JFrame, and add components that go inside it in its constructor, or just add the current instance.
public class MWT extends Canvas implements Runnable {
// change the constructor so it doesn't make a new JFrame
// change the constructor so it doesn't add a new instance to the JFrame
// leave the rest unchanged
}
public class ThreadedGraphicsDemo extends JFrame {
private MWT mwt;
public ThreadedGraphicsDemo(MWT mwt) {
this.mwt = mwt;
add(mwt);
// set exit behavior, size, pack, visible etc
}
}
public class Demo {
public static void main(String[] args) {
MWT mwt = new MWT();
ThreadedGraphicsDemo tgd = new tgd(mwt);
}
}
This approach will allow you to easily change the GUI, and behavior in the future.
The quick fix:
instead of add(new MWT()), change it to add(this) to add the instance of MWT that you've instantiated
Well, I tracked down the source of your infinitely spawning windows. You're instantiating the object you're constructing in constructor.
frame.add(new MWT());
The thing with this is that, since the object isn't completely done constructing your instance, it's going to still go off and create an instance of that, which leads to a lot of calls to the MWT object.
If you want to add the current instance of the MWT object you're constructing, you can use this instead:
frame.add(this);
Before you read, this will be informative: Java JFrame won't show up after using .setVisible(true) after being invisible
Hello I am working on a library API that let's you capture an area of the screen, and it returns you a class that contains the ByteArrayInputStream and utility methods like createBufferedImage, createFile, etc.
Basically you create a Bootstrap instance, and pass the capturer type you want as a dependency (ScreenshotCapturer or GifCapturer):
Bootstrap b = new Bootstrap(new ScreenshotCapturer());
And the beginCapture method receives an object that implements ScreenCaptureCallback which is the callback event that the captured result will be passed to.
This is a short background.
Now when you use the beginCapture method, basically what it does is creates new instance of SelectionCamera, this is basically the component that paints the selection area you're selecting when dragging the mouse, and updates the listeners.
once created instance, it calls super.setVisible(true);
After that method gets called, the frame will show up, and also show the old painted screen for like 600-500miliseconds, I am not exactly sure, but it disappears so quickly.
Take a look at this live example:
Note use the video option, otherwise you will not see what I'm seeing as gif is too slow to show it!
http://gyazo.com/d2f0432ada37842966b42dfd87be4240
You can see after I click Screenshot again, it shows the old selected area and disappears quickly. (by the way the frame you see in the gif is not part of the app, just dummy hello world example usage).
The process of image capture.
Step 1
beginCapture gets called:
public void beginCapture(final ScreenCaptureCallback c) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
capturer.setCallback(c);
capturer.beginSelection();
}
});
}
Step 2
beginSelection gets called in the Capturer class (ScreenshotCapturer extends Capturer (abstract)
#Override
public void beginSelection() {
super.init();
this.setHotkeys();
super.getCamera().startSelection();
}
Step 3
CaptureCamera#startSelection() gets called
public void startSelection() {
super.getContentPane().removeAll();
super.getContentPane().repaint();
super.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
this.selector = new SelectionCamera();
this.selectionMosueAdapter.updateCamera(this.selector);
this.selectionMouseMotion.updateCamera(this.selector);
super.add(this.selector);
super.setVisible(true);
super.repaint();
super.getContentPane().repaint();
}
Step 4
The user selects an area, and both mouse listener and mouse motion listens to it(Take a look at mouse motion):
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
Point startPoint = this.selector.getStartPoint();
int x = Math.min(startPoint.x, dragPoint.x);
int y = Math.min(startPoint.y, dragPoint.y);
int width = Math.max(startPoint.x - dragPoint.x, dragPoint.x - startPoint.x);
int height = Math.max(startPoint.y - dragPoint.y, dragPoint.y - startPoint.y);
this.selector.setCameraDimension(width, height);
this.selector.setCoordinates(x, y);
this.camera.repaint(); // important
}
by the way this.selector is SelectorCamera which is the component that paints the selection area.
Step 5
CaptureCamera#endSelection() gets called, this method gets the x,y, width, height from the selection camera and passes it to the capturer class which uses Robot to get screenshot with that rectangle, and before that it removes ALL components from the content pane, and repaints everything and then sets visibility to false.
public void endSelection() {
super.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
int x = this.selector.getCameraX();
int y = this.selector.getCameraY();
int w = this.selector.getCameraWidth();
int h = this.selector.getCameraHeight();
super.getContentPane().removeAll();
super.getContentPane().repaint();
//super.repaint();
super.setVisible(false);
this.c.startCapturing(x, y, w, h);
}
Basically this is the last step, rest steps are unnecessary for the debugging as it only sends back the callback.
I really tried my best explaining the process of my application, I've tried figuring it out for 5 and half hours now, and no luck at all. Tried different ways, by creating new SelectionCamera object as you see, doesn't work.
Why is it doing this? Is it something to do with the swing core?
SelectionCamera code: https://github.com/BenBeri/WiseCapturer/blob/master/src/il/ben/wise/SelectionCamera.java
Thanks in advance.
Based on this example...
try {
final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
b.beginCapture(new ScreenCaptureCallback() {
#Override
public void captureEnded(CapturedImage img) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
b.beginCapture(new ScreenCaptureCallback() {
#Override
public void captureEnded(CapturedImage img) {
System.out.println("...");
JFrame frame = new JFrame();
frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
});
}
});
System.out.println("Hello");
} catch (AWTException exp) {
exp.printStackTrace();
}
I won't focus on the initialise stage of the first round, I will focus on the initialisation of the second round as this is where the problem is...
b.beginCapture call's this.capturer.beginSelection();, which calls super.getCamera().startSelection(); which calls setVisible(true) (CaptureCamera been a JFrame).
This will immediately show what ever was previously displayed on the CaptureCamera. It's important to note here, that no new instances of objects were created through the process...
Now, I made a lot of changes to the base testing this, but it appears that the problem is with the restoration of the frame when it's made visible for the second time. This seems to be an issue with the transparency support of the Window as it seems to restore the last "known" state instead of repainting the window immediately...
Now, I tried clearing the selector before making the CaptureCamera invisible to no eval, as the window seems to be made invisible before the selector is painted.
The final solution I came up with was to call dispose on the CaptureCamera, which releases it's native peer and therefore destroys the old graphics context, forcing the frame to rebuild itself when it is made visible again.
"A" problem with this could be the fact that when all the windows are disposed (and the only running threads are daemon threads), the JVM will exit...
This was an issue during my testing as I was using a javax.swing.Timer to put a delay between the first and second capture process so I could see where the problem was occurring and this caused my JVM to exit (as the timer uses a daemon thread and I had no other windows open).
I got around this by creating a tiny (1x1) transparent window in the Capturer class, keep this in mind if the JVM exists gracefully for no reason ;)
Side Notes...
Now, there is an issue with SelectionCamera (which extends JPanel), it is opaque, but is using a transparent background, this is incredibly dangerous as Swing only knows how to deal with opaque or fully transparent components.
public SelectionCamera() {
super.setBackground(new Color(0, 0, 0, 0));
super.setVisible(false);
}
Should be updated to something like...
public SelectionCamera() {
setOpaque(false);
//super.setBackground(new Color(0, 0, 0, 0));
super.setVisible(false);
}
I'm also confused over the use of super.xxx, the only reason you would do this is if you had overrriden those methods and didn't want to call them at this time...In my testing, I removed all the calls to super where a method wasn't overridden in the current class (and I wasn't already in the overriden method)
Also, the paintComponent method should be calling super.paintComponent
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0, 0, 0, 0.5f));
g.fillRect(this.x, this.y, this.width, this.height);
}
Make Frame left to be -10,000 then set visible true, add a timer 2 seconds (try lower to 25-100 milliseconds, just to give it sligth pause to invalidate content) , on timer :left to 0 . I think it works due to caching & double buffereing. Frame shows what it had in buffer, buffer points to old image due to caching/ lazy repaint.
Alternative :
Maybe a repaint or invalidate before your show would work too, and don't need to do the left -10,000. I dont work much with ui-swing, just a but years back and remember some strange things like this.
I'm a bit desperate over here. I Have this jFrame and I need to close after I press Escape. This is easily done by using keyTyped Event. In the keyTyped event I tried to use System.exit which closes the window, but the process is still running in the task manager (and in the netbeans, even If I run from the jar file, it is still running in the task manager).
I have tried dispose, setVisible(false) along with other things but nothing seems to work.
EDIT:
Code
public Sketch(int width, int height)
{
sketch = new JFrame();
area = new JLabel();
sketch.setUndecorated(true);
sketch.setMinimumSize(new Dimension(width, height));
sketch.setSize(width, height);
area.setBounds(0, 0, width, height);
sketch.getContentPane().setLayout(null);
sketch.getContentPane().add(area);
sketch.pack();
sketch.setLocationRelativeTo(null);
sketch.setVisible(true);
sketch.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
imageBGR = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
imageGRAY = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
keyEvents();
setup();
Thread t = new Thread()
{
#Override
public void run()
{
running=true;
while(running)
draw();
}
};
t.start();
}
private void keyEvents()
{
sketch.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e)
{
if(e.getKeyChar()==KeyEvent.VK_ESCAPE)
{
running=false;
System.exit(0);
}
}
});
}
NOTE: The setup function is a blank function that I override when extending this class.
EDIT2: SOLVED
I found out what I was doing wrong. It appears that in the class that I was extended this one, I was using a webcam. When I call the System.exit function the webcam led is turned off, so I thought I didn't need to properly release it, but it was in fact needed.
The default behaviour when closing your frame is not to exit the program, but just to close the window. In order to exit the program you have to do:
jframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
You can also do:
jFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Your running variable needs to be declared volatile, and you need to set it to false at some point before closing.
Having said that, this code looks dangerous and bad:
Thread t = new Thread()
{
#Override
public void run()
{
running=true;
while(running)
draw();
}
};
t.start();
You've no Thread.sleep(...) within there, so it risks hogging the CPU, and as it looks like you're trying to modify Swing state off of the Swing event thread, you're in danger of intermittent thread failure. This suggests to me that you want to use a Swing Timer instead.
Note that for more complete help, consider posting a minimal code example that demonstrates your problem, an SSCCE. This will allow us to run your code and modify it and perhaps even correct it. Please read the link before replying as it supplies many important details on the SSCCE requirements.
I am trying to create a program with 2 JPanel using BorderLayout. The center panel is for random drawing of rectangle while the south panel is for the buttons.
I am getting a weird image of the button on the top left corner of the JFrame whenever I hover the mouse cursor over the North or South button. I did some research and found out that this could be the reason for having a transparent background. I tried using super.paintComponent(g) for the panel but the rest of the rectangles drawn earlier disappear. I need the rectangles to stay in the JPanel but not the weird image on the top left.
I don't know what I am doing wrong, hopefully someone can help or give some clue on how to solve this problem.
public class TwoBRandomRec extends JFrame{
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
TwoBRandomRec rec = new TwoBRandomRec();
rec.setSize(500,500);
rec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rec.setVisible(true);
}
public TwoBRandomRec() {
//Create the buttons
JButton north = new JButton("North");
JButton south = new JButton("South");
DrawPanel drawPanel = new DrawPanel(500,500);
JPanel southP = new JPanel();
southP.add(south);
southP.add(north);
this.add(drawPanel, BorderLayout.CENTER);
this.add(southP, BorderLayout.SOUTH);
this.setTitle("TwoButtonRandomRec");
this.pack();
}
public class DrawPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Random rand = new Random();
private int x,y,xSize,ySize;
private int height,width;
public DrawPanel(int w,int h) {
width = w;
height = h;
}
public void RandomX(){
x=rand.nextInt(width-1);
xSize=rand.nextInt(width-x);
}
public void RandomY(){
y=rand.nextInt(height-1);
ySize=rand.nextInt(height-y);
}
public Color RandomColour(){
rand.nextInt(height);
return new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255));
}
#Override
protected void paintComponent(Graphics g) {
RandomX();
RandomY();
g.setColor(RandomColour());
g.fillRect(x, y, xSize, ySize);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
repaint();
}
}
}
You're not calling super.paintComponent
protected void paintComponent(Graphics g) {
super.paintComponent(g); // <-- Insert me here and watch problem go away
RandomX();
RandomY();
g.setColor(RandomColour());
g.fillRect(x, y, xSize, ySize);
try {
Thread.sleep(50); // <-- This is an INCREDIBLY bad idea, NEVER, EVER do this
} catch (InterruptedException e) {
}
repaint(); // <-- This is a bad idea, watch CPU max out...
}
You are obligated to call super.paintComponent to ensure that the paint chain is upheld correctly and things like opacity and cleaning up of the graphics context takes place.
The Graphics object is shared between components through a single repaint pass, failure to honor the correct paint chain will result in, well, problems like yours
Never update the UI in anyway from any paint method (this includes calling repaint), this is just causing your paint method to be recalled, over and over and over...until you CPU hits 100% and program hangs.
Never, EVER do any time consuming or block operations within the paint methods (or the UI generally), this will make it look like the program as hung and make the users upset (you think zombi hordes are bad :P). Blocking in this way prevents the EDT from responding to paint requests...
I'd recommend having a read through:
Performing Custom Painting
2D Graphics
Painting in AWT and Swing
Concurrency in Swing