Reputation: 437
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
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
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