I have an application that is using a BufferStrategy created by a JFrame object to paint shapes. The code is pretty standard, what you would find elsewhere:
// JFrame class
frame.setVisible( true );
frame.createBufferStrategy( 2 );
new Logic( frame.getBufferStrategy() ).loop();
// Logic class
public Logic( BufferStrategy bs ){
this.bs = bs;
}
public void loop(){
// looping should be in another thread
Thread loop = new Thread(new Runnable() {
public void run(){
looooooooop();
}
});
loop.start();
}
Now the looooooooop function is where the drawing is actually happening:
g = (Graphics2D)bs.getDrawGraphics();
// draw stuff here, leaving out for simplicity
g.dispose();
bs.show();
Toolkit.getDefaultToolkit().sync();
Now the issue is about 75% of the time the application boots up and runs great, the other 25% of the time the frame appears but only the background colour of the frame can be seen, it is like the graphics objects isn't drawing anything on the screen. I have tried debugging and figuring out were this issue is stemming but it is really hard to do so. I am thinking it has something to do with the logic loop running in another thread (maybe this explains the inconsistent nature of the application) but haven't had any luck. Does this issue sound familiar to anyone? Thanks!
Edit - After removing the separate thread this is still happening so it can't be because of that.
Edit 2 - It seems like this is only happening when full screen mode is enabled (with or without the call to setVisible()):
this.setVisible( true );
if(isFullScreen){
GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.setFullScreenWindow(this);
}
Is this a known issue or am I missing something? Thanks!
Edit 3 - This is only happening on Ubuntu - verified through brute force. Can't find anything online regarding a bug so I am not sure what to think.
If you think creating a thread is necessary (I don't think so), I would do this instead:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
looooooooop();
}
});
My recommendation is to try it without creating the new thread.
UPDATE:
Try these events in this order:
// Create a canvas object (where you are actually doing the drawing)
frame.add(canvas);
frame.setVisible(true);
canvas.createBufferStrategy(2);
canvas.requestFocus();
// Do your drawing now.
You can't create the buffer before it's displayed. Also, if you are calling pack() on the frame anywhere on your code, just remove it.
Related
I am trying to make a simple event driven TicTacToe game using JavaFX. Currently I am struggling with termination once certain conditions are met. To put it into more details, the player can click on the GridPane elements, which are Canvas Objects, and then they are filled with "X" or "O" shapes respectively (I am using strokeLine and strokeOval methods of GraphicsContext). Code below:
private static void placeX(Canvas square){
GraphicsContext gc = square.getGraphicsContext2D();
gc.setLineWidth(10.0f);
gc.setStroke(Color.CORNFLOWERBLUE);
gc.strokeLine(square.getWidth()*0.2, square.getHeight()*0.2, square.getWidth()*0.8, square.getHeight()*0.8);
gc.strokeLine(square.getWidth()*0.8, square.getHeight()*0.2, square.getWidth()*0.2, square.getHeight()*0.8);
}
Once 3 of the same shapes appear in line or diagonally the program should terminate. I am doing this using Platform.exit(). Code below:
class HandleGame implements EventHandler<MouseEvent>{
#Override
public void handle(MouseEvent e){
Canvas can = (Canvas)e.getTarget();
//function to check if the canvas is clear
placeX(can);
if(game.isEnded()){ //checks if the same shape appears three times
Platform.runLater(new Runnable() {
#Override
public void run(){
try {
Thread.sleep(5000);
}
catch(InterruptedException exc){
System.out.println("Got something: " + exc.getMessage());
}
Platform.exit();
}
});
}
}
}
This event handler is attached to every Canvas object in GridPane and triggers on mouse release. The problem I am having is that once the last Canvas is clicked, before the shape appears on the Canvas the specified Runnable is executed and the rendering is unnaturally delayed (the "X" shape appears only for a second before closing). Strangely enough 1 out of 10 runs it executes as expected. How can I make the rendering trigger before the Thread.sleep() and following Platform.exit()? Why on rare occasions the rendering is actually performed before Thread.sleep()? I did a little research but could not find anything decisive, I am newbie when it comes to JavaFx. Appreciate your help.
Based on #Slaw and #VGR comments I managed to solve the problem using two different methods, one utilizing PauseTransition and second Thread. Code below:
Using PauseTransition:
if(game.isEnded()){
PauseTransition termination = new PauseTransition(Duration.seconds(1d));
termination.setOnFinished(event -> Platform.exit());
termination.play();
}
Using Thread:
if(game.isEnded()){
new Thread(() -> {
try{
Thread.sleep(2000);
}
catch(InterruptedException exc){
; //exception handling code here
}
Platform.exit();
}).start();*/
I'm developing a cli-based custom web crawler in Java. Since it needs to have a user-friendly graphical interface for showing the result, I should add a swing frame to that involving some trees, labels, tables and so on.
That graphical interface is one of its switches, and must be started just in case user calls it. Thus, I have to start this interface in a new thread so that the application can proceed with other tasks and update components inside of GUI frame if needs.
My GUI class is some thing like:
public class Frame extends JFrame {
......
public static JLabel urlLabel;
......
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
And, I fork it from my main class like this:
.....
if(cmd.gui){
Frame.run();
Frame.urlLabel.setText("New Url");
}
......
Unfortunately, the text of label doesn't change. I already tested repaint(), revalidate() and such other functions like these, but, nothing turned up.
I tested getText() in order to make sure it is possible to access urlLabel from main class, and it worked (I could retrieved text of label).
I was wondering how I can sort out this issue? (Basically, I need to start a swing frame in a different thread and control its components from the main thread)
Thanks in advance.
If you use invokeLater(), your Runnable will be started in the EventThread after the current operation in this thread is finished. If your label is not updated, it might be that your EventThread is busy doing something else - e.g. crawling the web.
You definitely need to make sure that your crawling work is done in another thread (start a new one, don't use the one that runs anyway, since this is most probably the EventThread). Then you might use invokeLater() in this other Thread to change the label in the EventThread.
Hint: You can check if you are in the EventThread by using SwingUtilities.isEventDispatchThread().
Remember that your data/models will be used by different threads and that this might cause problems when the data is changed in your worker thread while your GUI is trying to display it.
Thank you guys for helping.
Finally, I could overcome this obstacle by using SwingUtilities.invokeLater for updating the label's text.
I mention the approach here, since someone else might need it:
The main class:
public class Frame extends JFrame {
......
private static JLabel urlLabel;
......
public JLabel getU(){
return urlLabel;
}
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
The GUI class:
if(cmd.gui){
Frame().run();
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
gui.getU().setText("New Url");
}
});
}
.....
Just a question about this manner:
Since I need to update some labels and tree nodes a couple of times during crawling, wanted to know if starting a new Runnable for each of those would be overload? If yes, how can I manage that?
---UPDATE---
According to the #xander's comment, it is possible to use lambda instead of Runnable. I think lambda doesn't have overload as much as a new object does.
There are two java files Animate and Anim1.The Anim1 file has the JFrame and I want to attach the Animate file which has the logic of text scrolling by the screen(supposed to be the JFrame screen).But I cannot find a way.And also the code is throwing the following compile time error-
Exception in thread "Thread-0" java.lang.Error: Unresolved compilation problem:
The method repaint() is undefined for the type Animation
import java.awt.Graphics;
public class Animation implements Runnable {
int x=500;
String s="hello world";
public void run(){
while(true){
repaint();
try{
Thread.sleep(50);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public void paint(Graphics g){
g.drawString("hello world", x,-10 );
x--;
if(x< -100){
x=500;
}
}
}
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Anim1 {
public static void main(String[] args){
Animation a= new Animation();
Thread t= new Thread(a);
t.start();
JFrame frame= new JFrame("animate");
frame.setVisible(true);
frame.setSize(400,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
The repaint method is found in Swing components -- same for the paint and paintComponent. Calling it inside of a class that does not extend a Swing comopnent does not make sense unless you're making the repaint call on another object that is a component. Likewise giving a non-component class a paint method makes no sense since you're not overriding any painting method.
Likely your Animation class should extend JPanel so that it can override paintComponent (not paint) and so that the repaint() call makes sense. Also always pre-pend your painting method (or any other method that you think overrides a parent method) with the #Override annotation. This way the compiler will complain to you when you're not overriding the method correctly (or at all). You'll also want to call the super's painting method inside of your override.
Most important: read the Swing painting tutorials as it's all explained there. Check out: Performing custom painting
Your painting method would look something like so:
#Override
protected void paintComponent(Graphics g) {
// always call the super's method to clean "dirty" pixels
super.paintComponent(g);
// then draw the String. Make y a field too, so it can be controlled
// more easily
g.drawString(s, x, y);
}
Note that I don't change the x value or call any state-changing code within my painting method, and that's by design. The paintComponent method is for painting only. You don't have full control over whether or even if it will be called, and it can be called multiple times.
Instead the x value should be changed within the "game loop", here your Runnable's run() method. Also, as per my comments, while this code can be created using Thread/Runnable, it's safer to instead use a Swing Timer to drive the animation instead, since this saves you from having to worry so much about Swing threading issues.
Perhaps .repaint() needs to be passed something to work. Often animations will take place on a canvas then you would update that. The update method would have to be made separate with all of the things you wish to update.
canvas.update()
public void update(){
what it is you wish to update
canvas.draw()
}
I hope this helps. EDIT so your canvas is your JFrame
The goal is a GUI where in a window, having the play button pressed will release the GUI resources and start to run my game (the game is done outside of Swing, in LWJGL).
This is the first way I can see myself doing it. To me the safer option looks like this:
public static void main(String[] args) {
// setup gui objects
JFrame frame = ...;
frame.setVisible(true);
// wait for the play button to be pressed
synchronized(frame) {
frame.wait();
}
// free the gui objects, start the game
frame.dispose();
startGame();
}
// by the way, this method is called from the button's action listener
public void onPlayButtonPress(JFrame parent) {
// end the UI
synchronized(parent) {
parent.notify();
}
}
The reason this seems 'safe' to me is that the cleanup for the GUI and the game execution is done inside the main thread. Here is the second way I can see myself doing it, but I'm not sure how I feel about it:
public static void main(String[] args) {
// setup gui objects
JFrame frame = ...;
frame.setVisible(true);
}
public void onPlayButtonPress(JFrame parent) {
// free the gui objects
parent.dispose();
startGameInNewThread();
}
This second way of doing it seems more preferrable in that it is simpler, but I am worried of some problems coming up. For example:
Disposing the frame in the Event Dispatch Thread could mess things up?
Starting a new thread for the game while the main thread has already terminated?
So the reason I'm asking this question is because I'm looking for the best way to implement this, but I'm also kind of curious if and why any wierd things will happen if I implement my program the second way.
I currently have a JFrame where on it's content pane I draw images on from a game loop at 60 frames per second. This works fine, but at the right side, I now have more Swing elements on which I want to display some info on when selecting certain parts of the content pane. That part is a static GUI and does not make use of a game loop.
I'm updating it this way:
public class InfoPanel extends JPanel implements Runnable {
private String titelType = "type: ";
private String type;
private JLabel typeLabel;
private ImageIcon icon;
public void update() {
if (this.icon != null)
this.typeLabel.setIcon(this.icon);
if(this.type != null || this.type != "")
this.typeLabel.setText(this.titelType + this.type);
else
this.typeLabel.setText("");
}
public void run() {
try {
Thread.sleep(150);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.update();
}
(this method is only called when the player has actually moved, so it's just called once - not 60 times per second)
I noticed that, when calling this update()-method from the game loop, I get flickering effects. I assume this is because updating the UI takes some time, so I decided to put it in a new thread. This reduced the flickering, but didn't solve it.
Next, I decided to give the new thread low priority as the part of the screen which is redrawed 60 times a second is far more important. This reduced the flickering again, but it still happened. Then, I decided to use Thread.sleep(150); in the new thread before calling the update()-method, which solved the flickering effect on my system completely.
However, when running it on other systems, it still happens. Not as often as before (maybe one time per 20 seconds), but it's still pretty annoying. Apparantly, just updating the UI in another thread doesn't solve the problem.
Any ideas how to completely eleminate the flickering?
Call the update() in SwingUtilities.invokeAndWait() which stops the thread and updates UI in EDT.
Problem is that you are use Thread.sleep(int), that stop and freeze GUI during EventDispatchTread more in the Concurency in Swing, example demonstrating freeze GUI by using Thread.sleep(int), example for Runnable#Thread
If you want to delay whatever in Swing then the best way is implements javax.swing.Timer