Display an image using EventDispatchThread vs without - java

So I'm trying to display an image(ball) which I'll eventually control with user input. For know, the image just gets displayed over intervals using thread's sleep method.
I've made 2 classes, one that extends JPanel and the other extends JFrame.
The JPanel subclass looks like this:
public class BallPanel extends JPanel {
private Image ball;
private int x,y;
public BallPanel(){
try {
ball=ImageIO.read(new File("C:\\Users\\Owner\\Desktop\\ball.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x=10;
y=10;
Thread thread = new Thread() {
#Override
public void run(){
loop();
}
};
thread.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(ball,x,y,null);
}
public void loop(){
while(true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
In the loop method I call the sleep method to allow repaint to be called over intervals. Then, loop() is called in the constructor.
The JFrame subclass looks like this:
public class BallFrame extends JFrame {
public BallFrame(){
setVisible(true);
setSize(800,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setContentPane(new BallPanel());
}
public static void main(String args[]){
//SwingUtilities.invokeLater(new Runnable() {
// #Override
// public void run() {
new BallFrame();
// }
//});
}
}
Now the interesting, or perhaps confusing thing, is that when I run the code as it is shown here, with the anonymous inner class commented out, the ball doesn't always appear. Sometimes I need to re-size the frame (i.e call repaint) before the ball is shown. However, when I call it through the even dispatch thread using the anonymous inner class the ball appears every time I run the code. What is the reason for this?

It has little to do with starting the UI from within the EDT or not (although you should cause that can cause lots of other weird and interesting issues) and more to do with the fact that you've called setVisible before you've established the contents of the UI.
This is possibly an example of a race condition between the system trying to get the EDT up and running and the OS calls responding before it's established.
In either case you SHOULD start the UI from within the EDT and call setVisible last.
Swing can be lazy about updating the UI, this is actually a deliberate design choice as well as a good idea. You don't always want the UI updated after each and every change you make (like adding/removing components), so it hands over some of the control to the developer to decided when it's best to revalidate container hierarchy and request repaints
I would also avoid using a Thread to update the state of the UI as this could cause dirty paints as Swing uses a passive rendering approach (painting when it feels it's required) and consider using a Swing Timer which updated from within the EDT OR use a BufferStrategy and employ a active rendering approach, which you can then control

Related

contentpane.removeAll does not remove JPanel

I have a JPanel with a start button when that button is pressed it calls through the mainFrame the start() function in the controller
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (e.getSource().equals(start)) {
System.out.println("hi");
try {
f.c.start();
} catch (KludgeException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
the start() function calls the askQuesions() function which loops over questions creates a question panel for them and stores the answers.
public void start() throws KludgeException{
System.out.println("start");
askQuestions();
ConductInference();
}
public void askQuestions() throws KludgeException {
QuestionsPanel qp = new QuestionsPanel(main);
for(data.containers.Question q : kludge.getQuestions()){
qp.addQuestion(q.getQuestion(), q.getType());
main.setPanel(qp);
synchronized(this){
while(!next){
try {
wait();
kludge.setSystemValue(q.getValueName(), v);
//System.out.println("waitOver");
} catch (InterruptedException e) {}
}
}
next = false;
//System.out.println("next question");
}
System.out.println("questions over;");
}
this is a function in the mainFrame which is a JFrame it set the necessary panel.
public void setPanel(JPanel p){
main.getContentPane().removeAll();
main.getContentPane().add(p);
main.validate();
System.out.println("all removed, added and validated");
}
My problem is this... the program gets stuck on the startPanel when the stat button is pressed it freezes. If i skip the whole startPanel and tell it to go straight to the questions it works fine. but still i dont want it to go straight to the questions. For some reason it switches between the question panels fine but not between the startPanel and questionPanels..
You've got a concurrency issue and are calling long-running code on the Swing event thread, an issue that will prevent this thread from doing its important jobs such as painting the GUI and interacting with the user. The solution is to do the long-running code in a background thread such as provided by a SwingWorker. That and read up on Swing concurrency: Lesson: Concurrency in Swing
OK, I'm now sure that my original recommendation -- to use a background thread -- is wrong, that instead you've over-complicated your code with the while loop, the synchronized block and the wait. Yes these are blocking the event thread, and yes, this is hamstringing your application, making it freeze and become totally unresponsive, but the solution is not to use a background thread but instead you will want to get rid of the while (true) loop, the synchronized block and the wait() call and in their place use event listeners and call back methods. The exact wiring of this will depend on code that we're not yet privy to, but that is the solution to this problem. For instance, the question panel could notify a control class that a question has been answered, to change the state of the model so that it moves on to the next question. The model then changes, and this can notify the view that it must update itself and now display this next question.
Side notes:
you're better off using a CardLayout to swap views then to directly swap them. The tutorial can be found here: CardLayout tutorial.
And regarding: main.setPanel(qp);
You appear to be re-adding the QuestionPanel to the main within the for loop. If so, you only need to and only should add it once.

Is there a way to know if a JPanel is finished loading it's content?

I'm writing an application that adds cards (JPanels) to a CardLayout during runtime. The problem is that some components on the card loads faster than others, making it appear glitchy and not properly rendered before it's displayed.
I want it to be ready when shown for the first time.
I have solved the issue temporary by a loading screen, which makes the thread sleep for 1500 ms. Is there a more exakt way to know if everything on the panel is loaded?
private void showLoadingScreen() {
final Component glassPane = getGlassPane();
setGlassPane(loadingPanel);
loadingPanel.setVisible(true);
Thread thread = new Thread() {
public void run() {
try {;
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
setGlassPane(glassPane);
}
};
thread.start();
}
Do all your file loading and data manipulation in a background thread that is created using a SwingWorker. Then before executing the SwingWorker, add a PropertyChangeListener. When the PropertyChangeEvents newValue is SwingWorker.StateValue.DONE, then you know all background work is done and you can display your GUI.
Please have a look at the Concurrency in Swing tutorial, and for an example, please have a look at this answer to a similar question.

Impossible Java Memory Consistency Error

first of all I'm not an English native speaker so I apologize for any eventual “weird” writing.
I'm developing a Swing Java application on Eclipse that updates a Jpanel. This panel contains several sub-panels, and I'm constantly switching the panels “modes”, what happens to be a MouseListener changing so they respond in a slightly different manner to the user mouse inputs.
Regardless of what the application do, it's happening an error that seems to have no logical explanation to me. At some point in my code I try to update the panels to what I called neutralMode. This happens on the following method:
//Guarded block (see http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html)
private synchronized boolean waitsForUserSatisfactionAnswer()
{
while(!userIndicatedSatisfaction)
{
try {
wait();
} catch (InterruptedException e) {}
}
userIndicatedSatisfaction = false; //reset for future new query
getObjectSetVisualizationPanel().neutralMode();
//getObjectSetVisualizationPanel().queryPatternMode();
return userSatisfied;
}
This updating doesn't work (the call to neutralMode() dont do what is expected). However the call to queryPatternMode() (commented on the line right below) works perfectly. So I decided to COPY queryPatternMode()'s body and PASTE it on neutralMode()'s body ECXATLY THE SAME! AND IT STILL DOESNT WORK!
The methods code is like this:
public void queryPatternMode()
{
System.out.println("Inside queryPatternMode!!!");
System.out.println("panels.size(): " + panels.size());
for (DigitalObjectPanel panel : panels)
{
System.out.println("Inside the loop!!!");
panel.resetBehavior();
panel.setQuerySelectionBehavior(gui);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.validate();
}
});
}
}
public void neutralMode()
{
System.out.println("Inside neutralMode!!!");
System.out.println("panels.size(): " + panels.size());
for (DigitalObjectPanel panel : panels)
{
System.out.println("Inside the loop!!!");
panel.resetBehavior();
panel.setQuerySelectionBehavior(gui);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.validate();
}
});
}
}
What happens is that, when I call neutralMode(), the “panels” collection happens to be empty (panels.size() equals zero). However when I call queryPatternMode() instead, the collection happens to have it's expected size (20 panels). But both methods are equals, and both are called from the same place!!!
What it could be??? Is there any possible explanation for that??
It definitely looks like a synchronisation issue. You should check how many threads are accessing the collection 'panels'.
It is just a stroke of luck that it works for you with queryPatternMode() all the time, and not with neutralMode(). On another fine day, it might be other way around.

Concurrency in Java - wait for execution to be finished

So I am currently doing some work with Multi-Threading in Java and I'm pretty stuck on a, most likely, simple thing.
I currently have a JButton that, when pressed invokes the method as follows:
private void clickTest() throws InterruptedException{
statOrganizer.incrementHappiness();
Thread t = new Thread(new Happiness(workspaceHappy));
t.start();
}
and then takes around 10-30 seconds to complete. During this time however, it is still possible to re-click the JButton so that it messes with how the information is displayed.
What I want to do is during the time this particular thread is "alive", disable the button so that it is no longer possible to click it(and thus activate this thread once it's already going). Once the thread is finished, I want to re-enable the button again.
The button code just looks like this
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 1) {
try {
clickTest();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Disable the button right before starting the thread. In the thread, at the end, post an event that would re-enable the button (using invokeLater).
You may also need a cancel option, but that's a separate question.
Try the following:
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 1) {
try {
clickTest();
button.setEnabled(false);//this assume 'button' is final
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Then, modify the run method of your Happiness class:
public void run()
{
//your processing code here
...
button.setEnabled(true);
//reference to button can be passed in constructor of Happiness
// or access using getter, ... This really depend on your implementation.
}
The nice solution for this is to use a glass pane to capture all events and stopping them from propagating to any of your UI elements on the panel under the glass pane. Of course while you only have one or two, you can call setEnabled(false) on them manually but glass panes give you more flexibility, you'll never have to worry about adding a new element to your UI and forgetting to disable it during background processing.
Probably an overkill for one button though.
Another, unrelated thing you should consider is the use of Executors instead of launching threads for background tasks. It results in cleaner and more scalable code.

Problems with ActionListener and SystemUtil.invokeLater

I have look all over the web, and found no solution to my problem. For a AP Comp Sci project, I am making a Set of games, that will be run from a JFrame with JButtons. I have the games all ready, along with action listeners, but the games dont launch properly. The JFrame and JButtons are all setup correctly too.
private static class TetListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
GameCenter.quit();
GameCenter.startTetris();
}
}
GameCenter.quit() does nothing but run JFrame.dispose(), and GameCenter.startTetris(); constructs a new Tetris object, then run the play() method to start the game. All of Tetris is coded properly and works correctly when it is run in the main method (outside the actionlistener). But as soon as I put it in the ActionListener, it fails to be constructed properly. I tracked the problem down to:
public BlockDisplay(BoundedGrid<Block> board)
{
this.board = board;
grid = new JPanel[board.getNumRows()][board.getNumCols()];
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() // <<<<<<<<<<------------------- Problem Here
{
public void run()
{
createAndShowGUI(); // <<<<<<<<<<<<-------- Never Run
}
});
//Wait until display has been drawn
try
{
while (frame == null || !frame.isVisible()) // <<<<<<<-------- Never Resolved
{
Thread.sleep(1);
}
}
catch(InterruptedException e)
{
e.printStackTrace();
System.exit(1);
}
}
So the program always hangs. I also made a Pacman game that uses this SwingUtilities.invokeLater, so it doesnt work either. I cant figure out why this is happening or how to fix it.
Any help is appreciated. Let me know if you need any more info.
If the thread that runs SwingUtilities.invokeLater is already the swing event thread and you run in this while loop, yup, your application will hang.
Get rid of the while loop.

Categories