Sign up

SpinButton Signal Oddness...

I hooked up some signals to a few SpinButtons (one with double increments, the other two with float) and I'm getting some double-firings on the two float buttons, but not all the time.

The buttons are as follows:

  • double,
  • float with no extra precision, and
  • float with precision down to 1000ths.

The results when they're hooked up to the onValueChanged signal:

  • the double never double-fires (how's that for irony?),
  • the float with no extra precision always double-fires, and
  • the 1000ths float only double-fires as it's crossing a boundary of 3/1000ths in either direction.

With the onOutput signal:

  • double always double-fires,
  • float with no extra precision always double-fires except for the first time it's clicked, and
  • 1000ths float always double-fires.

When I say "double-fires" I mean that it reports the value before and after the value changes, so something like:

click:

  • 0.1
  • 0.2
    click:
  • 0.2
  • 0.3
    click:
  • 0.3
  • 0.4

Here's the code:

// SpinButton madness

import std.stdio;

import gtk.MainWindow;
import gtk.Main;
import gtk.Box;
import gtk.Widget;
import gtk.SpinButton;
import gtk.Adjustment;
import gtk.c.types;

void main(string[] args)
{
	Main.init(args);

	TestRigWindow myTestRig = new TestRigWindow("Test Rig with SpinButton");
	
	Main.run();
	
} // main()


class TestRigWindow : MainWindow
{
	AppBox appBox;
	
	this(string title)
	{
		super(title);
		addOnDestroy(&quitApp);
		
		appBox = new AppBox();
		add(appBox);
		
		showAll();

	} // this() CONSTRUCTOR
	
		
	void quitApp(Widget widget)
	{
		writeln("Bye.");
		Main.quit();
		
	} // quitApp()

} // class myAppWindow


class AppBox : Box
{
	DoubleSpinButton doubleSpinButton;
	FloatSpinButton floatSpinButton;
	PrecisionSpinButton precisionSpinButton;
		
	this()
	{
		super(Orientation.VERTICAL, 10);
		
		doubleSpinButton = new DoubleSpinButton();
		packStart(doubleSpinButton, false, false, 0);

		floatSpinButton = new FloatSpinButton();
		packStart(floatSpinButton, false, false, 0);

		precisionSpinButton = new PrecisionSpinButton();
		packStart(precisionSpinButton, false, false, 0);

	} // this()

} // class AppBox


class DoubleSpinButton : SpinButton
{
	double minimum = -48;
	double maximum = 48;
	double step = 2;

	Adjustment adjustment;
	double initialValue = 0;
	double pageIncrement = 8;
	double pageSize = 0;
	
	this()
	{
		super(minimum, maximum, step);

		adjustment = new Adjustment(initialValue, minimum, maximum, step, pageIncrement, pageSize);
		setAdjustment(adjustment);
//		addOnValueChanged(&valueChanged);
		addOnOutput(&outputValue);
		
	} // this()

	void valueChanged(SpinButton sb)
	{
		writeln("Double", getValue());
		
	} // valueChanged()

	
	bool outputValue(SpinButton sb)
	{
		writeln("Double: ", getValue());
		
		return(false);
		
	} // outputValue()
	
} // class DoubleSpinButton


class FloatSpinButton : SpinButton
{
	float minimum = -1.0;
	float maximum = 1.0;
	float step = .1;

	Adjustment adjustment;
	float initialValue = 0.0;
	float pageIncrement = 0.5;
	float pageSize = 0.0;
	
	this()
	{
		super(minimum, maximum, step);
		adjustment = new Adjustment(initialValue, minimum, maximum, step, pageIncrement, pageSize);
		setAdjustment(adjustment);
		setWrap(true);
		
		// WHY do these trigger twice?
		addOnValueChanged(&valueChanged);
//		addOnOutput(&outputValue);
		
	} // this()

	void valueChanged(SpinButton sb)
	{
		writeln("Float Standard", getValue());
		
	} // valueChanged()

	
	bool outputValue(SpinButton sb)
	{
		writeln("Float Standard: ", getValue());
		
		return(false);
		
	} // outputValue()
	
} // class FloatSpinButton


class PrecisionSpinButton : SpinButton
{
	float minimum = -1.0;
	float maximum = 1.0;
	float step = .001;
	uint precision = 3;

	Adjustment adjustment;
	float initialValue = 0.0;
	float pageIncrement = 0.01;
	float pageSize = 0.0;
	
	this()
	{
		super(minimum, maximum, step);
		adjustment = new Adjustment(initialValue, minimum, maximum, step, pageIncrement, pageSize);
		setAdjustment(adjustment);
		setDigits(precision);

//		addOnValueChanged(&valueChanged);
		addOnOutput(&outputValue);
		
	} // this()

	void valueChanged(SpinButton sb)
	{
		writeln("Float Precision", getValue());
		
	} // valueChanged()

	
	bool outputValue(SpinButton sb)
	{
		writeln("Float Precision: ", getValue());
		
		return(false);
		
	} // outputValue()
	
} // class PrecisionSpinButton

Re: SpinButton Signal Oddness...

On Tue, 23 Apr 2019 18:15:01 GMT, Ron Tarrant wrote:

I hooked up some signals to a few SpinButtons (one with double increments, the other two with float) and I'm getting some double-firings on the two float buttons, but not all the time.

I saw the note from here

When setting multiple adjustment properties via their individual setters, multiple “changed” signals will be emitted. However, since the emission of the “changed” signal is tied to the emission of the “notify” signals of the changed properties, it’s possible to compress the “changed” signals into one by calling gobjectfreezenotify() and gobjectthawnotify() around the calls to the individual setters.

Alternatively, using a single gobjectset() for all the properties to change, or using gtkadjustmentconfigure() has the same effect of compressing “changed” emissions.

But I think it doesn't really apply here(?). Rather it has to do with rounding maybe? if you use double instead of float for the config variables it triggers only once. in my test even changing only variable step to type double changed that. But you have to use variables to get it triggered twice, new Adjustment(0.0f, ... doesn't reproduce it.

// SpinButton madness

import std.stdio;

import gtk.Main;
import gtk.MainWindow;
import gtk.Widget;
import gtk.SpinButton;
import gtk.Adjustment;

class MySpinButton : SpinButton
{

	this()
	{

		// triggers once
		//super(0.0f, 1.0f, 0.1f);
		//Adjustment a = new Adjustment(0.0f, -1.0f, 1.0f, 0.1f, 0.5f, 0.0f);

		// triggers once
		//super(0.0, 1.0, 0.1);
		//Adjustment a = new Adjustment(0.0, -1.0, 1.0, 0.1, 0.5, 0.0);

		// triggers twice
		float step = 0.1;
		super(0.0, 1.0, step);
		Adjustment a = new Adjustment(0.0, -1.0, 1.0, step, 0.5, 0.0);


		setAdjustment(a);
		addOnValueChanged(&onValueChanged);
	}

	void onValueChanged(SpinButton sb)
	{
		writeln("onValueChanged : ", getValue());
	}
}

void main(string[] args)
{
	Main.init(args);

	MainWindow window = new MainWindow("title");
	window.setSizeRequest(400, 200);

	MySpinButton msb = new MySpinButton();

	window.add(msb);

	window.showAll();
	Main.run();
}

Re: SpinButton Signal Oddness...

On 23-04-2019 20:15, Ron Tarrant wrote:

I hooked up some signals to a few SpinButtons (one with double increments, the other two with float) and I'm getting some double-firings on the two float buttons, but not all the time.

The buttons are as follows:

  • double,
  • float with no extra precision, and
  • float with precision down to 1000ths.

The results when they're hooked up to the onValueChanged signal:

  • the double never double-fires (how's that for irony?),
  • the float with no extra precision always double-fires, and
  • the 1000ths float only double-fires as it's crossing a boundary of 3/1000ths in either direction.

With the onOutput signal:

  • double always double-fires,
  • float with no extra precision always double-fires except for the first time it's clicked, and
  • 1000ths float always double-fires.

When I say "double-fires" I mean that it reports the value before and after the value changes, so something like:

click:

  • 0.1
  • 0.2

click:

  • 0.2
  • 0.3

click:

  • 0.3
  • 0.4

Here's the code:

...

There seem to be a precision/rounding error when using float, changing
the step variable in the two float spin button classes to double fixes
the issue.

A float can't exactly represent 0.1 with the precision of float that
is actually 0.100000001490116119384765625 witch seems to be the source
of the issue.

Re: SpinButton Signal Oddness...

On Wed, 24 Apr 2019 17:08:50 +0200, Mike Wey wrote:

A float can't exactly represent 0.1 with the precision of float that
is actually 0.100000001490116119384765625 witch seems to be the source
of the issue.

Ah! Okay, that makes sense. It would also explain why, as the spinner passes zero, it dumps out that exponential notation.

There seem to be a precision/rounding error when using float, changing
the step variable in the two float spin button classes to double fixes
the issue.

One other change I had to make was switching back to addOnValueChanged(). I guess because I used setDigits() to extend precision, it was setting up that second signal talked about in that note you linked to. For whatever reason, the addOnOutput() callback still double-fires, but addOnValueChanged() doesn't.

So, problem solved. Thanks very much for the help, Mike.