user5846939
user5846939

Reputation: 437

Syncing two JSliders

I have written a little program as a beginner exercise to just convert Celcius to Fahrenheit and vice versa with two JSliders. I have the two JSliders listening on each other, and the goal is that whenever I move one slider, either the Celcius one (the upper one in the window) or the Fahrenheit one (lower), the other should move automatically to the right location however, the program has a bug I can't understand. When I run it, it works, but my Celcius degree JLabel underneath the Celcius bar "Clabel" is only shown in measures of 0,5,10,15 .. and so on when I move its slider. And my Fahrenheitbar only jumps in distances of 9, like 0,9,18,27 so on. Why is this? Why won't both bars move smoothly without stutters?

Here is my code:

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;


public class ThermoWindow extends JFrame {

    private JSlider C, F;
    private JPanel panel, labelpanel;
    private JLabel Flabel, Clabel;

    public ThermoWindow() {
        super("Thermometer Converter");
        panel = new JPanel(new BorderLayout());
        labelpanel = new JPanel(new BorderLayout());
        C = new JSlider(JSlider.HORIZONTAL,-100,100,0);
        C.setMajorTickSpacing(10);
        C.setMinorTickSpacing(1);
        C.setPaintTicks(true);
        C.setPaintLabels(true);
        F = new JSlider(JSlider.HORIZONTAL,-100,100,toFahrenheit(0));
        F.setMajorTickSpacing(10);
        F.setMinorTickSpacing(1);
        F.setPaintTicks(true);
        F.setPaintLabels(true);

        Clabel = new JLabel("   C°: " + 0);
        Flabel = new JLabel("   F°: " + toFahrenheit(0));

        C.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                F.setValue(toFahrenheit(C.getValue()));
                Clabel.setText("   C°: " + C.getValue());
            }
        });

        F.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                C.setValue(toCelcius(F.getValue()));
                Flabel.setText("   F°: " + F.getValue());
            }
        });

        panel.add(C, BorderLayout.NORTH);
        panel.add(F, BorderLayout.SOUTH);
        labelpanel.add(Clabel, BorderLayout.NORTH);
        labelpanel.add(Flabel, BorderLayout.SOUTH);
        panel.add(labelpanel);

        add(panel);
        setBounds(900,200,900,200);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setResizable(false);
        setVisible(true);
    }

    public int toFahrenheit(int temp){

        temp = (int)(temp/(5/(double)9))+32;

        return temp;
    }

    public int toCelcius(int temp){

        temp = (int)((temp-32)*(5/(double)9));

        return temp;
    }

}

    public class myThermometer {
        public static void main (String[] args){
            new ThermoWindow();
        }
    }

If I comment out the following line in the second Changelistener

C.setValue(toCelcius(F.getValue()));

then moving the upper Celcius bar shows every single Celcius degree moved under the celcius bar, like 0,1,2,3,4 and so on (like it should). But if I do this, I lose any functionality on the Celcius bar when moving the Fahrenheit bar of course, since I am no longer controlling the Celcius bar with whatever value is set by moving the Fahrenheit slider. So I am stuck with either only the upper JSlider being able to control the lower one and showing each single change in value (like 20,21,22 C) OR both JSliders listening on each other but only in intervals of 0,5,10,15 C and so on.

Can anyone tell me why I can't get smooth feedback of each single digit change when both sliders listen in on each other? Why is it only changing in steps of 5s or 9s?

Upvotes: 2

Views: 332

Answers (2)

AJNeufeld
AJNeufeld

Reputation: 8705

Your issue, as you've discovered, is each ChangeListener changing the value of the other slider. Slider C changes slider F's value, which changes slider C's value, which changes slider F's value. This mutual recursion only ends when each change does not cause a change in the other slider's position, which happens on the 5's (in C), and 9's (in F).

You need code like the following:

   C.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            if (!updateInProgress) {
                updateInProgress = true;
                F.setValue(toFahrenheit(C.getValue()));
                updateInProgress = false;
            }
            Clabel.setText("   C°: " + C.getValue());
        }
    });

    F.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            if (!updateInProgress) {
                updateInProgress = true;
                C.setValue(toCelcius(F.getValue()));
                updateInProgress = false;
            }
            Flabel.setText("   F°: " + F.getValue());
        }
    });

Plus declare private boolean updateInProgress = false; in your ThermoWindow class.

Upvotes: 3

Jean-Baptiste Yunès
Jean-Baptiste Yunès

Reputation: 36441

The problem is that each time you set a value in one the value of the other changes and this also triggers its callback. Since you are rounding the values, starting from 0 and until you move the Celsius cursor to 5, the Fahrenheit value is stuck to 32, and similar on the other side.

Upvotes: 0

Related Questions