Post on 12-Jan-2016
transcript
CS12420 – Swing and threads
Lynda Thomas
ltt@aber.ac.uk
Thread
• Thread of control– The idea was covered by Chris
• Can have multiple ones in one program
• See example Bouncer in this directory
In fact,
• We have already been using multiple threads, because
• When you use event handling code, this is handled by a different thread
Concurrency and Swing tutorial
http://java.sun.com/docs/books/tutorial/ uiswing/concurrency/index.html
A well-written Swing program uses concurrency to create a user interface that never "freezes" — the program is always responsive to user interaction, no matter what it's doing
A Swing programmer deals with the following kinds of threads:
• Initial threads, the threads that execute initial application code.
• The event dispatch thread, where all event-handling code is executed. Most code that interacts with the Swing framework must also execute on this thread.
• Worker threads, also known as background threads, where time-consuming background tasks are executed.
The programmer does not need to provide code that explicitly creates these threads: they are provided by the runtime or the Swing framework.
But the programmer works with them!
BUT what have we been doing?
• Initial threads, the threads that execute initial application code.
• The event dispatch thread, where all event-handling code is executed. Most code that interacts with the Swing framework must also execute on this thread.
• Worker threads, also known as background threads, where time-consuming background tasks are executed.
We have created our GUIs here!
We would have done complex
stuff here – since all we had
Should have done it here
Should have done it here
…what you ?need? to know (cookbook style)
1. invokeLater()
2. SwingWorker
And a couple of other things
1. Threads for animation
2. Double Buffering
1. invokeLater()
Swing components should be created, queried, and manipulated on the event-dispatching thread
We have not been doing this.
public static void invokeLater(Runnable runnable) – Causes runnable to have its run method called
in the dispatch thread of the EventQueue. This will happen after all pending events are processed.
So…
• Look at the new Bank in this directory
• It calls invokeLater() to be sure that the GUI is built in the right thread - we should ask the event dispatch thread to build our user interface rather than doing it ourself
• Code next slide
NEW:
public static void main(String args[]) { new BankSwingMVC();}
public BankSwingMVC() {try {
SwingUtilities.invokeLater(new Runnable() { public void run() {
buildBankSwing(); }
} );
} catch (Exception e) {
S.o.p("invokeLater exception"+e); } }
public void buildBankSwing() {setTitle("Bank"); setDefaultCloseOperation(…..);buttonPanel = new ButtonPanel(this);add ("North",buttonPanel); textPanel = new TextPanel();add ("Center",textPanel); pack(); setVisible(true);
}
OLD:
public static void main(String args[]) { new BankSwingMVC();}
//constructor builds a window
public BankSwingMVC() {setTitle("Bank"); setDefaultCloseOperation(…..);buttonPanel = new ButtonPanel(this);add("North",buttonPanel); textPanel = new TextPanel();add("Center",textPanel); pack();setVisible(true);
}
……
2. SwingWorker
• Time consuming code needs to be placed in a separate thread so that your GUI can still operate while it is running
• Since Java 1.6 this has been formalised in a SwingWorker thread
• First let’s run the code to see WHY THIS IS A PROBLEM
• Life-no-threads – doesn’t work properly• No update• Can’t stop• Can’t even quit
How apply to Life
• There are 3 subdirectories here:– Life-no-threads – doesn’t work properly– Life-home-made-threads – an old solution– Life-properlythreads – PROPER WAY OF DOING IT
– I’m actually going to go over the older solution, because it is easier to see the forest for the trees
– see next slide
Basic algorithm for when runningpublic void runalot() { int i=0; s=“first iteration" ;
while (true) { calc(); if (!change()) {
s="stable in "+i+" moves"; repaint(); break;} if (i==100){
s="100 times still changing";repaint();break;}
copy(); i++; s=i+" iterations";
try{ Thread.sleep(20); } catch (Exception e) {
System.out.println("exception "+e);}
repaint(); } }
forever{ calculate next generation copy into current generation slowdown paint screen}
The problem is that runalot() is resource intensive
particularly calc() which looks at every square many times
for every square (all 100 of them) look at ALL its neighbours and decide
if it lives in next generation
While it is running nothing else can happenSo, instead, it needs to be in its own thread
DOESN’T WORK:public void actionPerformed(ActionEvent e){ if (e.getSource()==b1) c.step(); if (e.getSource()==b2) c.clear(); if (e.getSource()==b3) c.runalot(); if (e.getSource()==b4)
c.stopit(); }….class NewCanvas extends JPanelimplements MouseListener {//lots deletedpublic void runalot() { int i=0; s="1 iteration" ; while (true) { calc(); if (!change()) {
s="stable in "+i+" moves"; repaint(); break;}
if (i==100){s="100 times still
changing";repaint();break;}
copy(); i++; s=i+" iterations"; try{ Thread.sleep(20); } catch (Exception e) {
System.out.println("exception "+e);}
//tried it with the above and without still won't repaint or stop!
repaint(); } }
OLD (homemade solution WORKS)://lots deleted//in the actionPerformed() : if (e.getSource()==b3){
runningThread=new Thread(c); runningThread.start();}
class NewCanvas extends JPanelimplements MouseListener, Runnable{//lots deletedpublic void run(){ //calling start() calls run() int i=0; s=“first iteration" ;
keepGoing=true; try{while (keepGoing) { calc(); if (!change()) {
s="stable in "+i+" moves"; repaint(); break;} if (i==100) {
s="100 still changing"; repaint(); break;} copy(); i++; s=i+" iterations";
Thread.sleep(200); repaint(); } } catch (InterruptedException e) {
}}
DOESN’T WORK:public void actionPerformed(ActionEvent e){ if (e.getSource()==b1) c.step(); if (e.getSource()==b2) c.clear(); if (e.getSource()==b3) c.runalot(); if (e.getSource()==b4)
c.stopit(); }….class NewCanvas extends JPanelimplements MouseListener {//lots deletedpublic void runalot() { int i=0; s=first iteration" ; while (true) { calc(); if (!change()) {
s="stable in "+i+" moves"; repaint(); break;}
if (i==100){s="100 times still
changing";repaint();break;}
copy(); i++; s=i+" iterations"; try{ Thread.sleep(20); } catch (Exception e) {
System.out.println("exception "+e);}
//tried it with the above and without still won't repaint or stop!
repaint(); } }
CORRECT SOLUTION Java6 and later:class NewCanvas extends JPanel implements MouseListener { //no need for Runnable lots deletedpublic void startCalc(){ // This method has work to do and so properly creates a SwingWorker, that is, // gets another thread to do that work see ***** below for how kicked off class Calculator extends SwingWorker<List<String>, String> { public List<String> doInBackground() { int i=0; s=“starting" ; keepGoing=true; try{ while (keepGoing) { board.calc(); i++; if (!board.isChange()) {s="stable in "+i+" moves"; repaint(); break;} if (i>=100) //had to make this >= {s="100 times still changing";repaint();break;}
board.copy(); s=“iteration “+ I; // Call the inherited publish method and publish an intermediate result. //This method prepares intermediate results ready for the event dispatch thread (EDT).
// When ready the EDT will call the process method with the list of messages. publish(s);
i++; Thread.sleep(200); } } catch (InterruptedException e) { } return null; } // Overrides the inherited process method called by the EDT when it is ready with a list of intermediate data protected void process(List<String> messagesSoFar) { for (String message : messagesSoFar) { System.out.println(message); repaint(); } } } // ***** //Create the worker object and then schedule to run it on a worker thread Calculator worker = new Calculator(); worker.execute(); }
• Read more at : http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
• The example code is at: http://java.sun.com/products/jfc/tsc/articles/threads/src/Example1.java
• If you need this you will know (GUI will seize up)• You should know about this idea, so you can look
it up when needed• And you should use the ‘invokeLater’ stuff in 1.
In this lecture (so far) 2 things
• How to properly set up GUI using invokeLater()
• How to use SwingWorker so that your GUI doesn’t freeze
Here endeth the things about Swing that could be on a
CS124 exam
A couple of other odds and ends
1. Animation
• You may also need to use a new thread for animation
• Animation is basically just drawing different images (think on paper) in a loop (draw, cover, redraw, etc)
• Needs thread because otherwise the calls to repaint()
get stacked up and then you will not see the
multiple images
• See code in sample-animation-code directory
//This is an example of how to run an animation
//It uses a class called NewCanvas to display the animation
public class Main extends JFrame implements ActionListener{
private NewCanvas c; //panel for animation
private JPanel p; //panel for button
private JButton b; //push button
///////////////////////////////////////////////////////////
public static void main(String args[]) {
new Main();
}
//////////////////The next bit is actually separate from the animation
//////////////////use the invokeLater()
public Main() {
try{
SwingUtilities.invokeLater(new Runnable()
{public void run() {
build();
}});
}
catch (Exception e) {
System.out.println("invokeLater exception"+e);
}
}
public void build(){
//deleted
}
///////////////////////////////////////////////////////when button pressed
public void actionPerformed(ActionEvent e){
if (e.getSource()==b){
c.move(); //run the animation
}
}
}
//This is the class that actually displays the animation
//puts a picture of aber in background and runs 3 faces
public class NewCanvas extends JPanel implements Runnable {
private MediaTracker mTracker;
private Image backgroundImage; //needed for background
private Image animateImage[]=new Image[3]; //needed for the images
private Thread animator; //need to run this in separate thread
private int current=0; //which image for animation
//set up
public NewCanvas() {
//all mediatracker stuff deleted
repaint();
current=0;
}
//called when button pressed
public void move(){
animator=new Thread(this);
animator.start(); //CALLS run()
}
//do the animation
public void run(){
for (int j=0;j<6;j++) { //LOOP!!!!
try {
Thread.sleep(200);
} catch (InterruptedException e) {
break;
}
current=(current+1)%3;
repaint(); //calls paintComponent()
}
}
//draw the image
public void paintComponent(Graphics g) {
2. Double Buffering
• Double buffering• Change a picture all at once instead of redrawing a
bit at a time• Several years ago this was really important
because of speed• Still matters for games – these examples don’t
really show benefit• run BadUpdateFrame and GoodUpdateFrame
BadUpdateFrame.java
import javax.swing.*;public class BadUpdateFrame extends SimpleFrame {
private UpdatePanel uppane;private Drawing drawing;public BadUpdateFrame() { drawing = new Drawing(); uppane = new UpdatePanel(drawing); add(uppane); pack();}public void badDemo() {
uppane.repaint();drawing.flipLines();uppane.repaint();drawing.flipLines();uppane.repaint();drawing.flipLines();
}…… //deleted
}
GoodUpdateFrame.java
/* create 2 drawings:
drawing1 and drawing2
/*
public void goodDemo() { uppane.repaint(); drawing2.flipLines(); uppane.setDrawing(drawing2); uppane.repaint(); drawing1.copy(drawing2); drawing1.flipLines(); uppane.setDrawing(drawing1); uppane.repaint();}*/
There is more about this in later modules!
What have I taught that could be on an exam?
• What is a design pattern?• What is the MVC design pattern?• What is the Observer/Observable design pattern?• Why do we need SwingWorker threads?• What does invokeLater() do?• What does a Listener class do and how do you use it?• Suppose I want to put a button in a Swing application,
what are the three things that I need to do?