CS1: Lecture 38 – Slideshow
Dear students,
A primary benefit of object-oriented design is that it helps us organize our programs coherently. We can put code and the data that it regularly processes together into the same chunk. Objects allow for the separation of concerns. Class A can focus on its task, and be very good at it. It can be tested and perfected in isolation. Class B can focus on its task, and be very good at it. If the code for classes A and B were mixed together in class C, the developer’s brain would be pulled in different directions, and its quality would suffer.
So, when we model our problems with objects, we should make cohesive entities. Let’s see a particular occasion where the separation of concerns comes up: when writing an application with a graphical user interface. There are two tasks: to present a view to the user, and to model the data that’s being interacted with.
As it turns out, view code tends to change a lot—we switch operating systems, windowing libraries, and so on. Model code tends to change less frequently. In addition to separating by concerns, we also like to “separate that which changes from that which doesn’t.”
Further yet, by separating the data from its presentation, we can test it more easily. Testing graphical applications is annoying. You have to issue fake mouse clicks, which is cumbersome.
Today, then, let’s write a slideshow application in which concerns have been separated. We’ll start with the model, which knows nothing about presenting data. Once that’s in place, we’ll implement the view, which reads from the model and presents its data.
Let’s follow this checklist:
- Create an
ImageCycler
class that gathers up all the images in a directory. - Have the cycler focus on a single image in the sequence.
- Allow the cycler to advance and retreat through the sequence.
- Imbue the cycler with the ability to yield the currently selected
File
. - Create a window that accepts an
ImageCycler
. - Give it left and right buttons.
- Add listeners for button clicks.
- Give it a label that displays the name of the currently selected image.
- Instead of the file name, display the image.
TODO
Here’s your TODO to complete before we meet again:
- Homework 6 is due by the end of Sunday. If you completed it early for resubmission, your resubmitted work and an email to me is due by the end of Sunday.
- The CS 145 final exam is Monday, December 16, from 3-4:50 PM in Phillips 007. The CS 148 final exam is Monday, December 16, from 10-11:50 PM in Phillips 276. One double-sided sheet of notes is allowed. Questions will span all topics we’ve discussed this semester, with an emphasis on objects.
See you next class!
P.S. It’s time for a haiku!
When they run your code
They won’t see the hours you spent
Till they get the bill
P.P.S. Here’s the code we wrote together…
ImageCycler.java
package lecture1213.cs145;
import java.io.File;
import java.util.Arrays;
public class ImageCycler {
private File[] images;
private int i;
public ImageCycler(File directory) {
images = directory.listFiles();
Arrays.sort(images);
i = 0;
}
public File getCurrent() {
return images[i];
}
public void forward() {
i = (i + 1) % images.length;
}
public void backward() {
i = (i - 1 + images.length) % images.length;
}
}
Main.java
package lecture1213.cs145;
import java.io.File;
public class Main {
public static void main(String[] args) {
File directory = new File("/Users/johnch/checkouts/unversioned/images/pets");
ImageCycler cycler = new ImageCycler(directory);
new Slideshow(cycler);
// for (int i = 0; i < 30; ++i) {
// System.out.println(cycler.getCurrent());
// cycler.backward();
// }
}
}
Slideshow.java
package lecture1213.cs145;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Slideshow extends JFrame {
private ImageCycler cycler;
private JLabel label;
public Slideshow(ImageCycler cycler) {
this.cycler = cycler;
JButton leftButton = new JButton("<");
JButton rightButton = new JButton(">");
add(leftButton, BorderLayout.WEST);
add(rightButton, BorderLayout.EAST);
label = new JLabel();
add(label, BorderLayout.CENTER);
leftButton.addActionListener(e -> {
cycler.backward();
loadCurrent();
});
rightButton.addActionListener(e -> {
cycler.forward();
loadCurrent();
});
loadCurrent();
setSize(1024, 768);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private void loadCurrent() {
try {
BufferedImage image = ImageIO.read(cycler.getCurrent());
label.setIcon(new ImageIcon(image));
} catch (IOException e) {
System.err.println(e);
}
}
}
ImageCycler.java
package lecture1213.cs148;
import java.io.File;
import java.util.Arrays;
public class ImageCycler {
private File[] images;
private int i;
public ImageCycler(File directory) {
images = directory.listFiles();
Arrays.sort(images);
i = 0;
}
public void advance() {
i = (i + 1) % images.length;
}
public void retreat() {
i = (i - 1 + images.length) % images.length;
}
public File getCurrent() {
return images[i];
}
}
Main.java
package lecture1213.cs148;
import java.io.File;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
File directory = new File("/Users/johnch/Desktop/pets");
ImageCycler cycler = new ImageCycler(directory);
new Slideshow(cycler);
// for (int i = 0; i < 30; ++i) {
// System.out.println(cycler.getCurrent());
// cycler.retreat();
// }
}
}
Slideshow.java
package lecture1213.cs148;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Slideshow extends JFrame {
private ImageCycler cycler;
private JLabel label;
public Slideshow(ImageCycler cycler) {
this.cycler = cycler;
JButton leftButton = new JButton("<");
JButton rightButton = new JButton(">");
add(leftButton, BorderLayout.WEST);
add(rightButton, BorderLayout.EAST);
label = new JLabel();
add(label, BorderLayout.CENTER);
leftButton.addActionListener(e -> {
cycler.retreat();
loadCurrent();
});
rightButton.addActionListener(e -> {
cycler.advance();
loadCurrent();
});
loadCurrent();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(600, 400);
setVisible(true);
}
private void loadCurrent() {
try {
BufferedImage image = ImageIO.read(cycler.getCurrent());
label.setIcon(new ImageIcon(image));
} catch (IOException e) {
System.err.println(e);
}
}
}