jf is a JFrame I'm trying to move a pixel in every 200 milliseconds. I created this method so I ciould pause the software for 200 milliseconds before cntinuing. millis and millisn are static longs.
public static void waitsec() {
millis =System.currentTimeMillis();
millisn =System.currentTimeMillis();
while (millisn<(millis+200)){
millisn=System.currentTimeMillis();
}
}
the following part is the part where I'm trying to make my JPanel (jp) to move slowly 50 pixels when a button is being pressed:
public void actionPerformed(ActionEvent h){
if (h.getSource() == button){
for (int i = 0; i <50; ++i){
waitsec();
out.println ("" + i);//I'm checkinf if the waitsec() is working OK
x +=1;
jp.setLocation(x, 0);
totalGUI.repaint();
jp.setVisible(true);//setting visible so I could focus on it
jp.requestFocusInWindow (); //suspicious part
jp.requestFocus ();
}}
}
The results of running this program are:
The numbers appear in the console one after another with 200 mmillis between them as expected.
The JPanel is not moving all the way once the numbers stop appearing (the program is done running). and if I try to minimize and maximize the window it doesn't show up till the program is done running as if the program has no focus at all...
why doesn't it focus although I had set it visible and focused it?
A number of things...
Firstly, you are blocking the Event Dispatching Thread, preventing it from processing any new events, including repaint events. This will make your application appear as it has hung, because essentially, it has.
Secondly, instead of rolling your waitSec method, you should be taking advantage of the available API functionality, for example, instead of trying to loop until a time period has passed, which while consume CPU cycles, you should use Thread.sleep instead.
Having said that though, you should NEVER sleep within the context of the EDT.
Instead, you should use something like a javax.swing.Timer which can be configured to raise an event on a regular bases. The benefit of this is it raises the event within the context of the EDT, making it safe to update the UI from within (unlike making your own Thread for example)
Take a look at Concurrency in Swing and How to use Swing Timers for more details
Thirdly, JPanel is not focusable by default, so calling requestFocusInWindow is not likely to have any effect
Fourthly, by default, Swing makes use of layout managers, so you may actually be struggling against this as well
Related
I have a for loop that changes the location of a JLabel in a JLayeredPane layout.
c.setVisible(true);
jLayeredPane.moveToFront(c);
for (int i = 0; i < 1000; ++i) {
c.setBounds(i, 500, 94, 136);
System.out.println(c.getLocation());
try {
Thread.sleep(2);
} catch (Exception e) {
}
}
This method is referencing to jLayeredPane, which is accessed from another class. It does indeed update the x value of the JLabel c, yet it does not show it on the screen.
What do I have to do to make it update in real time? I need this to run in order, not simultaneous to the next part of the program, thus I suppose multi-threading is disallowed.
What do I have to do to make it update in real time?
Don't use Thread.sleep(). This will cause the Event Dispatch Thread (EDT) to sleep, which means the GUI can't repaint itself until the entire loop is finished executing.
For animation you need to use a Swing Timer
I need this to run in order, not simultaneous to the next part of the program
When the Timer fires the required number of times you then stop the Timer and invoke a method containing the rest of your code.
Also, you should not be hardcoding "1000" as the loop control. You have no idea what the resolution of the screen will be when code is executed on different screens. You can use the Toolkit class and the getScreenSize() methods to get the appropriate dimension to use based on your actual requirement.
In my program, I need to wait for the user to input from a JFrame. When the user is done with the first input they press a JButton. This calls a constructor for a Class I made: HumanTrainer. In the constructor I need the user to have more inputs. I made two functions to wait and notify. But when the code gets to wait, everything freezes, and the JFrame doesn't update to what it should.
This is the action the first button preforms
startingButton.addActionListener((e)->{
Trainer[]t=new Trainer[2];//HumanTrainer extends Trainer
String[]names=new String[2];
for(int a=0;a<2;a++)
names[a]=((JTextField)(startingInputs[2][1+a])).getText();
grid.removeAll();//The JPanel that the Frame has
Frame.repaint();//The JFrame
Frame.validate();
if(isHuman[0])
t[0]=new HumanTrainer(names[0],grid,Frame);//The constructor
if(isHuman[1])
t[1]=new HumanTrainer(names[1],grid,Frame);
});
And the constructor for HumanTrainer
HumanTrainer(String name,JPanel grid,JFrame Frame){
super(name);
GridBagConstraints manager=new GridBagConstraints();
manager.gridx=0;
manager.gridy=0;
manager.gridheight=1;
manager.gridwidth=1;
manager.fill=GridBagConstraints.HORIZONTAL;
Font Format=new Font("Courier New",Font.PLAIN,14);
JButton cont=new JButton("Continue");//This is the button that when clicked should run the function that notifies
grid.add(cont,manager);
grid.repaint();//One of these four things SHOULD change the view of the frame
grid.validate();
Frame.repaint();
Frame.validate();
System.out.print("TEST");//This prints
cont.addActionListener((e)->{
made();//This is a function contained in HumanTrainer that only calls notify();
});
make();//This is a function contained in HumanTrainer that only calls wait(); With the proper try and catch
}
But when starterButton is pressed the screen freezes and doesn't update so that cont can be pressed.
Start by taking a look at Concurrency in Swing. Swing uses a single thread to dispatch it's events from, if you perform any long running or blocking operation from within the EDT (like calling wait), then it freeze your program and the user will have to terminate it.
You have two basic choices. You can either use a modal dialog to gather information from the user, see How to Make Dialogs, which will block the execution of the code at the point they are displayed, without blocking the entire EDT OR use a observer pattern, which can generate notifications that the user has provided what ever information you are expecting.
To be honest, a modal dialog is generally easier and can help prevent unexpected side effects
This...
make();//This is a function contained in HumanTrainer that only calls wait(); With the proper try and catch
would seem to be the core of your problem, but without more information and context of what you are trying to do, it's impossible to really suggest what you should do, however I'd recommend having a look at model-view-controller and seperare your code into more appropriate layers
First of all, you may want to close that for loop with {} so that it doesn't loop the whole code twice. Also you should check if wait() and notify() work correctly by individually testing them without java awt.
Okay, so this problem has been plaguing me for the past few days. And before anyone comments about it, yes I have made the jframe visible and I have added all the components that I need to it. Also, all components are added well before the GUI is set to visible and activate() is called.
So I am trying to run a couple simeple lines of code.:
g.setVisible(true);
g.activate();
Simple enough. g is an object made from a class I made GUI which extends JFrame. and activate() is a method that runs an infinite loop that just does a bunch of things until the user tells it to exit. However, when the program gets to the g.setVisible() line it opens a JFrame that is the size I specified however is completely devoid of anything. Then it moves onto the g.activate(); which at the moment runs for a specific amount of iterations and stops. At which point it finally decides that it can go back and display my GUI. The issue with that is that the GUI is meant to be updated by the loop from active() and keep the user in the know of what is going on.
Any help is appreciated let me know if you need more detials and thank you in advance.
In many different UI frameworks, it's common for the methods you call to queue some work rather than perform it immediately. From your description, it seems like setVisible() may be creating a native window but then queuing the rendering of the components. Since this code is (presumably) running in the UI thread, it won't perform the work it queued until after running activate().
Long-running tasks should never be run on the UI thread. In Swing, you can use SwingWorker or explicitly create a background thread.
So I'm making a graphical card game. Each card is a JPanel with a button and two images associated with it. I have a flip method, which is the first thing I call in my action listener when a card is clicked on.
public void flip()
{
if(b1.getIcon() == card2) b1.setIcon(card1);
else b1.setIcon(card2);
revalidate();
repaint();
}
However, for some reason the card doesnt flip (meaning the icons don't change) until some point after I call this method. For example, when I put a Thread.sleep after I call flip, I would assume that my program would pause after flip is completed, but it doesnt! It sleeps with the images not yet switched, and only switches them some at point after the sleep time has ended.
This is causing some major problems when I have a human play an AI in this card game, because AI events are happening before cards flip on the screen, even though I am calling flip() before I do any AI code. Can anyone clue me in as to what is going on here?
Repaint is a request to Swing to redraw not a call to redraw. Under the covers repaint() is posting a repaint job onto a queue of events. Along with that repaint job things like mouse moves, mouse clicks, keyboard activites, etc are posted there too. The Swing thread comes by and periodically executes jobs from that queue to redraw the UI, deliver mouse and keyboard events to components, etc. In fact the swing thread delivers these events quite frequently, but it just depends on how much work your UI is doing on that Swing thread. When it's out delivering mouse clicks, keyboard events, and redraws it can't read from that queue. So if your code takes a long time to repsond to any event the ability for swing to respond timely to new events is reduced or all together blocked.
That's why if you execute a blocking service call over the network with the Swing event dispatch thread your UI stops drawing until that call returns. This is why its important to move long running jobs like network calls off the Swing Thread, and use SwingUtilities.invokeLater() when the call comes back so you can update the UI on the Event thread. (invokeLater() does this by posting a job onto the Swing event queue we talked about above).
This is also why it's a terrible idea to call sleep on the Swing event thread. If you put that thread to sleep it can't service events from the queue. And the repaint() you requested isn't going to be done while the Swing thread is sleeping.
First remove your sleep call. Second. Don't write code that depends on painting to finish before executing more logic. Repaint and revalidate are special calls, and Swing will combine requests to repaint/revalidate so it doesn't redraw 10 times in a row (wasting vital cpu time).
Swing can't redraw your panel until you let the thread that called you leave the method it first called you back on. The sooner the thread leaves that method the sooner your repaint() will happen.
You haven't explained why you want the repaint to happen immediately so I can't give you any advice on how to structure your code so you don't care about it. But I'm telling you, you shouldn't care that the repaint hasn't happened yet.
Totally agree with #chubbsondubs. I just wanted to add that if your goal is to have some event happen some time after flip is called, consider using a swing timers. As you are painfully aware, calling Sleep does not have the desired effect. But if instead, you
create a timer for say 1 second
create an action to be performed when the timer fires
start the timer
then you can create the delay that you want without blocking the event dispatch thread.
public void flip()
{
if(b1.getIcon() == card2) b1.setIcon(card1);
else b1.setIcon(card2);
revalidate();
repaint();
javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do something interesting
}
});
t.repeats(false);
t.start();
}
I'm probably doing this wrong, so please be nice.
I'm developing a Java game, and I'm at the stage of testing character movement / animation.
The "person" can move up down left and right on a grid.
The class the grid is drawn in is the gamePanel class.
The buttons are in the gameControlPanel class.
I have a button which spawns a person on the grid.
I then have a button to move the person up down left and right.
When the move up button is pressed, it calls the move up method from the person class.
(At the moment, I'm only testing one "person" at a time.)
In that method is the following code...
int move = 10;
while(move!=0)
{
setTopLeftPoint(new Point((int)getTopLeftPoint().getX(),
(int)getTopLeftPoint().getY() - 3));
try
{
Thread.sleep(300);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
move-=1;
}
The problem is that I can't seem to call the repaint method for the gamePanel class from within the Person class.
To get around this, I created a timer in the gamePanel class which repaints every 20ms.
When I press the up button after the person is spawned, the button remains pressed down until the cycles of the while loop have been completed, and then the circle representation of the person is displayed in the grid square above.
I will try to answer any questions regarding this.
repaint() does not immediately repaint the GUI. Rather, it posts a message to the AWT thread telling it to paint at the next convenient opportunity. When it gets the chance, it will repaint your app. However, if you do this in an event handler, then the AWT thread is already busy and you need to exit the handler to give control back to the AWT handler.
As a general rule of thumb, you don't want to do any long-running calculations on the AWT thread (including in event handlers) since these will stop the app from responding to other events until your calculations are done. This will often appear to the user as stuck buttons like you described. To get around this, use a SwingWorker which can do the calculations on a separate thread.
Finally, something to be aware (but not necessarily to change) is that timers and sleeps do not guarantee when they will awaken. Rather, they guarantee that they will not waken before the amount of time elapses, but can theoretically sleep indefinitely. Also, not all machines have 1 ms timer resolution. In particular, on many windows machines the timers only have 55 ms resolution, so a 20 ms timer may give weird results.
If you want to repaint at a certain interval, javax.swing.Timer is probably the class for you. In the specific case of repaint you can call it from a non-EDT thread, but you may get yourself into difficulty as you are now dealing with multiple threads.
I don't have much experience making games but having a loop to control all animation is a fundamental aspect of game programming. Most simple 2d games only have 1 loop to render most of its animation.
In my experience a good way to render a whole bunch of stuff is to have a collection of all the entities in your game in one place and just loop over this collection passing the Graphics object to each entity.
This will allow each entity to draw itself onto the graphics object. Although this is just one way to do it.
synchronized ( entities ) {
for ( Entity e : entities ) {
e.draw( g );
e.doAction();
}
}
Is the button press handled by the event-dispatch thread of your GUI?
If this is a case, then the repaint method on the GUI will not fire until the event dispatch thread ends (i.e. when the button is released and it breaks out of the loop). I had this problem recently, and the best solution I can suggest is to make the class with the movement algorithm threadable and fire off a thread when the keypress is detected. This allows the event-dispatch thread to finish and therefore allows the gui to repaint.
For more information on threading see Starting a thread.