0

I'm relatively new to making UIs with Swing in Java, and I'm having trouble getting buttons and sliders to work in my project. I was trying to test that my sliders and buttons worked with prints, but found that they wouldn't work.

I will get results if I don't check what slider/button was interacted with. I.E. I will only get results if ANY slider or button in the project is pressed. But obviously I want them all to to do different things.

Here is my code so far

Main.java

//Imports here, just saving space

public class Main
{
    public static void main(String[] args) throws IOException
    {
        ImageProcessor e = new ImageProcessor();
        e.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        e.initGUI();
    }
}

ImageProcessor.java

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

public class ImageProcessor extends JFrame
{

    JPanel          imagePane;
    JCheckBox       xView, yView, zView, mip;
    JLabel          xSliceLabel, ySliceLabel, zSliceLabel;
    JSlider         xSliceSlider, ySliceSlider, zSliceSlider;
    JLabel          xImageIcon, yImageIcon, zImageIconl;
    BufferedImage   xImage, yImage, zImage;
    short           cthead[][][];
    short           min, max;
    double          ai, aj, ak;
    JButton         test;


    public void initGUI()
    {
        // Build the window to contain everything
        setTitle("Simple Example");
        setSize(1200, 900);

        /////// MAKE THE GUI ///////


        // Container Window
        Container container = getContentPane();
        container.setLayout(null);


        // Check boxes to select view
        JCheckBox xView = new JCheckBox("X View");
        xView.setBounds(150, 45, 65, 20);
        container.add(xView);

        JCheckBox yView = new JCheckBox("Y View");
        yView.setBounds(225, 45, 65, 20);
        container.add(yView);

        JCheckBox zView = new JCheckBox("Z View");
        zView.setBounds(300, 45, 65, 20);
        container.add(zView);


        // Panel to show CT Head image
        JPanel imagePane = new JPanel();
        imagePane.setBounds(25, 75, 512, 512);
        imagePane.setBorder(BorderFactory.createTitledBorder("Image View"));
        container.add(imagePane);

        // Image labels to display BufferedImages
        JLabel xImageIcon = new JLabel();
        imagePane.add(xImageIcon);



        // Checkbox for MIP on/off
        JCheckBox mip = new JCheckBox("MIP");
        mip.setBounds(125, 630, 65, 20);
        container.add(mip);


        // Slider labels
        JLabel xSliceLabel = new JLabel("X Slice");
        xSliceLabel.setBounds(50, 675, 60, 25);
        container.add(xSliceLabel);

        JLabel ySliceLabel = new JLabel("Y Slice");
        ySliceLabel.setBounds(50, 725, 60, 25);
        container.add(ySliceLabel);

        JLabel zSliceLabel = new JLabel("Z Slice");
        zSliceLabel.setBounds(50, 775, 60, 25);
        container.add(zSliceLabel);


        // Slice sliders
        JSlider xSliceSlider = new JSlider(0, 255, 76);
        xSliceSlider.setBounds(125, 675, 256, 25);
        container.add(xSliceSlider);

        JSlider ySliceSlider = new JSlider(0, 255, 76);
        ySliceSlider.setBounds(125, 725, 256, 25);
        container.add(ySliceSlider);

        JSlider zSliceSlider = new JSlider(0, 112, 76);
        zSliceSlider.setBounds(125, 775, 256, 25);
        container.add(zSliceSlider);

        JButton test = new JButton("Test");
        test.setBounds(50, 700, 60, 25);
        container.add(test);

        // Handler class
        GUIEventHandler handler = new GUIEventHandler();

        xSliceSlider.addChangeListener(handler);
        ySliceSlider.addChangeListener(handler);
        zSliceSlider.addChangeListener(handler);

        test.addActionListener(handler);


        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

    }

    private class GUIEventHandler implements ActionListener, ChangeListener
    {
        public void stateChanged(ChangeEvent e)
        {
            if (e.getSource() == xSliceSlider)
            {
                System.out.println("x Slider changed!");

            } else if (e.getSource() == ySliceSlider)
            {
                System.out.println("y Slice changed!");

            } else if (e.getSource() == zSliceSlider)
            {
                System.out.println("Z slice changed!");
            }

            //This will work if any of the sliders change state
            //System.out.println("Something changed");
        }

        public void actionPerformed(ActionEvent e)
        {
            if (e.getSource() == test)
            {
                System.out.println("Testing...");
            }

            //This will also work if any button is pressed
            //System.out.println("Testing again...");
        }
    }
}

I'd greatly appreciate anyone knowledgable looking at this and pointing me in the right direction.

Thanks a lot, everyone!

RawLamb
  • 117
  • 1
  • 8
  • 1
    1) Java GUIs have to work on different OS', screen size, screen resolution etc. using different PLAFs in different locales. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556) along with layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). 2) It's quite common that each button or slider will have a unique listener added. – Andrew Thompson Feb 28 '18 at 17:24

3 Answers3

4

You defined:

  • fields for buttons and sliders
  • local variables of the same name

In the handler, you access fields, but they are never initialized!

In initGUI, rewrite some lines:

JSlider xSliceSlider = new JSlider(0, 255, 76); // define a local variable

as:

xSliceSlider = new JSlider(0, 255, 76); // initialize the field
Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
2

I bet this is not how your GUI looks in your computer... does it?

enter image description here

The text is cropped, but... why? You might be wondering.

Well, that is because of you using a null-layout and setting manually the bounds of each component and their sizes.

But... how do I solve this?

First of all some background reading: Null layout is evil, Why is it frowned upon to use a null layout in Swing?, this answer

Now, for your actual question, you can follow @MadProgrammer's answer to this question where he says:

Realistically, if you can, you should be giving each slider it's own listener and dealing with it directly from the source...

JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener() {
    public void stateChanged(ChangeEvent e) {
        JSlider slider= (Slider)e.getSource();
        // Do funky stuff
    }
});

Following the above tips, you can fix your GUI as follows and let the Layout managers to handle the window size for you, so the GUI looks right in every computer, for example:

enter image description here

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class ImageProcessingCorrected {
    private JFrame frame;

    private JPanel pane;
    private JPanel imagePane;
    private JPanel boxesPane;
    private JPanel controlsPane;
    private JPanel slidersPane;

    private JCheckBox[] boxes;

    private JButton button;

    private JCheckBox mipBox;

    private JSlider[] sliders;

    private static final String[] AXIS = new String[] {"X", "Y", "Z"};

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ImageProcessingCorrected()::createAndShowGui);
    }

    @SuppressWarnings("serial")
    private void createAndShowGui() {
        frame = new JFrame(getClass().getSimpleName());

        pane = new JPanel();
        imagePane = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 400);
            }
        };
        boxesPane = new JPanel();
        controlsPane = new JPanel();
        slidersPane = new JPanel();

        mipBox = new JCheckBox("MIP");

        button = new JButton("Test");
        button.addActionListener(e -> {
            System.out.println("Test button pressed!");
        });

        pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
        boxesPane.setLayout(new GridLayout(1, 3));
        slidersPane.setLayout(new GridLayout(3, 2, 5, 15));

        boxes = new JCheckBox[3];
        sliders = new JSlider[3];

        for (int i = 0; i < boxes.length; i++) {
            boxes[i] = new JCheckBox(AXIS[i] + " View");
            boxes[i].setHorizontalAlignment(SwingConstants.CENTER);
            boxesPane.add(boxes[i]);
        }

        for (int i = 0; i < sliders.length; i++) {
            sliders[i] = new JSlider(0, 100, 50);
            sliders[i].setName(AXIS[i] + " Slice");
            JLabel label = new JLabel(AXIS[i] + " Slice");
            label.setHorizontalAlignment(SwingConstants.CENTER);

            sliders[i].addChangeListener(e -> {
                System.out.println(((JSlider) e.getSource()).getName() + " changed!");
            });

            slidersPane.add(label);
            slidersPane.add(sliders[i]);
        }

        controlsPane.add(mipBox);
        controlsPane.add(button);

        imagePane.setBorder(BorderFactory.createTitledBorder("Image View"));

        pane.add(boxesPane);
        pane.add(imagePane);
        pane.add(controlsPane);
        pane.add(slidersPane);

        frame.add(pane);

        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Here's a sample of what the sysout calls print:

X Slice changed!
X Slice changed!
X Slice changed!
Y Slice changed!
Y Slice changed!
Y Slice changed!
Y Slice changed!
Z Slice changed!
Z Slice changed!
Z Slice changed!
Z Slice changed!
Test button pressed!
Frakcool
  • 10,915
  • 9
  • 50
  • 89
  • This is quite useful to know. I didn't know that null-layout was frowned upon. I suppose it just seemed easier in theory to just set everything explicitly myself, rather than letting a manager deal with it. Thank you! – RawLamb Mar 01 '18 at 05:02
  • Exactly that's the impression it gives but in the long run it's better to use a layout manager :) – Frakcool Mar 01 '18 at 05:05
2

Instead of using one and the same event handler for several GUI objects it is quite common to use separate event handlers for each GUI object. This typically is done with anonymous classes implementing the event listener interfaces.

In your application you would replace your code

    // Handler class
    GUIEventHandler handler = new GUIEventHandler();

    xSliceSlider.addChangeListener(handler);
    ySliceSlider.addChangeListener(handler);
    zSliceSlider.addChangeListener(handler);

    test.addActionListener(handler);

by this code

    xSliceSlider.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            System.out.println("x Slider changed!");
        }
    });
    ySliceSlider.addChangeListener(new ChangeListener() {     
        @Override
        public void stateChanged(ChangeEvent e) {
            System.out.println("y Slider changed!");
        }
    });
    zSliceSlider.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            System.out.println("z Slider changed!");
        }
    });

    test.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Testing...");
        }
    });

Doing this way you don't need the GUIEventHandler class anymore, and there is no need to examine e.getSource() in the event handling methods.

By the way: If you are running Java 8+ you can rewrite the code above even more concisely by using lambda expressions for the event handlers:

    xSliceSlider.addChangeListener(e -> {
        System.out.println("x Slider changed!");
    });
    ySliceSlider.addChangeListener(e -> {     
        System.out.println("y Slider changed!");
    });
    zSliceSlider.addChangeListener(e -> {
        System.out.println("z Slider changed!");
    });
    test.addActionListener(e -> {
        System.out.println("Testing...");
    });
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49