Skip to main content
Engineering LibreTexts

15.4: The Slide Show Program

  • Page ID
    15157
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Problem Specification

    Let’s suppose our slide show will repeatedly display a set of images named “slide0.gif,” “slide1.gif,” and “slide2.gif.” Suppose these images are stored on a Web site on www.cs.trincoll.edu and are stored in a directory named /~ram/jjj/slideshow. This means our program will have to load the following three URLs:

    http://www.cs.trincoll.edu/~ram/jjj/slideshow/slide0.gif
    http://www.cs.trincoll.edu/~ram/jjj/slideshow/slide1.gif
    http://www.cs.trincoll.edu/~ramjjj/slideshow/slide2.gif

    We want our show to cycle endlessly through these images, leaving about 5 seconds between each slide.

    User Interface Design

    The user interface for this program is graphical, but it doesn’t contain any GUI components. It just needs to display an image every 5 seconds. It can use a simple paint() method to display an image each time it is repainted:

    public void paint(Graphics g) {
        if (currentImage != null)
            g.drawImage(currentImage, 10, 10, this);
    }

    The assumption here is that the currentImage instance variable will be set initially to null. Each time an image is downloaded, it will be set to refer to that image. Because paint() is called before the program starts downloading the images, it is necessary to guard against attempting to draw a null image, which would lead to an exception.

    Problem Decomposition

    One problem we face with this program is getting it to pause between each slide. One way to do this is to set up a loop that does nothing for about 5 seconds:

    for (int k = 0; k < 1000000; k++ ) ;// Busy waiting

    However, this isn’t a very good solution. As we saw in Chapter 14, this is a form of busy waiting that monopolizes the CPU, making it very difficult to break out of the loop. Another problem with this loop is that we don’t really know how many iterations to do to approximate 5 seconds of idleness.

    A much better design would be to use a separate timer thread, which can sleep() for 5 seconds between each slide. So our program will have two classes: one to download and display the slides and one to serve as a timer (Figs. 15.10 and 15.11). ( Java Swing has a Timer class that works a little bit differently, see the javadoc for a description.)

    SlideShowFrame—This JFrame subclass will take care of downloading and displaying the images and starting the timer thread.

    Timer—This class will implement the Runnable interface so that it can run as a separate thread. It will repeatedly sleep for 5 seconds and then tell the frame to display the next side.

    The SlideShowFrame class

    What should we do with the images we download? Should we repeatedly download and display them, or should we just download them once and store them in memory? The second of these alternatives seems more efficient. If an image has already been downloaded, it would be wasteful to download it again.

    So we’ll need an array to store the images. Our slide show will then consist of retrieving the next image from the array and displaying it. To help us with this task, let’s use a nextImg variable as an array index to keep track of the next image. Even though it isn’t absolutely necessary, we could use a third variable here, currentImage, to keep track of the current image being displayed. Thus, our frame needs the following instance variables:

    private static final int NIMGS = 3;
    private Image[] slide = new Image[NIMGS];
    private Image currentImage = null;
    private int nextImg = 0;

    Given these variables, let’s now write a method to take care of choosing the next slide. Recall that the paint() method is responsible for displaying currentImage, so all this method needs to do is to update both currentImage and nextImg. This method should be designed so that it can be called by the Timer thread whenever it is time to display the next slide, so it should be a public method. It can be a void method with no parameters, because the frame already contains all the necessary information to display the next slide. Thus, there’s no need for information to be passed back and forth between Timer and this method:

    public void nextSlide() {
        currentImage = slide[nextImg];
        nextImg = (nextImg + 1) % NIMGS;
        repaint();
    }// nextSlide()

    The method’s algorithm is very simple. It sets currentImage to whatever slide is designated by nextImg and it then updates nextImg’s value. Note here the use of modular arithmetic to compute the value of nextImg. Given that NIMGS is 3, this algorithm will cause nextImg to take on the repeating sequence of values 0, 1, 2, 0, 1, 2, and so forth. Finally, the method calls repaint() to display the image.

    The frame’s constructor, SlideShowFrame() method will have two tasks:

    Download and store the images in slide[].

    Start the Timer thread.

    As we discussed, downloading Web resources for an application requires the use of the javax.imageio.ImageIO.read() method. Here we just place these method calls in a loop:

    for (int k=0; k < NIMGS; k++) 
        slide[k] = javax.imageio.ImageIO.read(getCodeBase(), 
                     "gifs/demo" + k + ".gif");

    Note here how we convert the loop variable k into a String and concatenate it right into the URL specification. This allows us to have URLs containing “slide0.gif,” “slide1.gif,” and “slide2.gif.” This makes our program easily extensible should we later decide to add more slides to the show. Note also the use of the class constant NIMGS as the loop bound. This too adds to the program’s extensibility.

    The task of starting the Timer thread involves creating an instance of the Timer class and calling its start() method:

    Thread timer = new Thread(new Timer(this));
    timer.start();

    Note that Timer is passed a reference to this frame. This enables Timer to call the frame’s nextSlide() method every 5 seconds. This programming technique is known as callback and the nextSlide() method is an example of a callback method (Fig. 15.12).

    This completes our design and development of SlideShowFrame, which is shown in Figure [fig-slideframe].

    import java.awt.*;
    import javax.swing.*;
    import javax.imageio.ImageIO;
    import java.net.*;
    
    public class SlideShowFrame extends JFrame  {
        public static final int WIDTH=300, HEIGHT=200;
        private static final int NIMGS = 3;
        private Image[] slide = new Image[NIMGS];
        private Image currentImage = null;
        private int nextImg = 0;
    
        public void paint(Graphics g) {
            g.setColor(getBackground());
            g.fillRect(0, 0, WIDTH, HEIGHT);
            if (currentImage != null)
                g.drawImage(currentImage, 10, 10, this);
        }//paint()
    
        public void nextSlide() {
            currentImage = slide[nextImg];
            nextImg = (nextImg + 1) % NIMGS;
            repaint();
        }// nextSlide()
    
        public SlideShowFrame() {
            for (int k=0; k < NIMGS; k++) 
                slide[k] = ImageIO.read(getCodeBase(), 
                                 "gifs/demo" + k + ".gif");
            Thread timer = new Thread(new Timer(this));
            timer.start();
            setSize( WIDTH, HEIGHT );
        }// constructor
    }// SlideShowFrame

    The Timer Class

    The Timer class is a subclass of Thread, which means it must implement the run() method. Recall that we never directly call a thread’s run() method. Instead, we call its start() method, which automatically calls run(). This particular thread has a very simple and singular function. It should call the SlideShowFrame.nextSlide() method and then sleep for 5 seconds. So its main algorithm will be:

    while (true) {
        frame.nextSlide();
        sleep( 5000 );
    }

    However, recall that Thread.sleep() throws the InterruptedException. This means that we’ll have to embed this while loop in a try/catch block.

    To call the frame’s nextSlide() method, we also need a reference to the SlideShowFrame, so we need to give it a reference, such as an instance variable, as well as a constructor that allows the frame to pass Timer a reference to itself.

    Given these design decisions, the complete implementation of Timer is shown in Figure [fig-timerthread]. To see how it works, download it from the Java, Java, Java Web site and run it.

    public class Timer implements Runnable {
        private SlideShowFrame frame;
    
        public Timer( SlideShowFrame app ) {
            frame = app;
        }
    
        public void run() {
            try {
                while ( true ) {
                    frame.nextSlide();
                    Thread.sleep( 5000 );
                }
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
        }// run()
    }// Timer

    Describe the design changes you would make to SlideShowFrame if you wanted to play a soundtrack along with your slides. Assume that the sounds are stored in a sequence of files, “sound0.au,” sound1.au,” and so forth, on your Web site.


    This page titled 15.4: The Slide Show Program is shared under a CC BY 4.0 license and was authored, remixed, and/or curated by Ralph Morelli & Ralph Wade via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.