Can't set default value for SpinButton from ".ui" file

I’m trying to set the default value for a SpinButton from a .ui file, but the value always is set to lower, not to value as I would expect. Demonstration:

#!/usr/bin/env python3
import sys

import gi

gi.require_version("Gtk", "4.0")
from gi.repository import Gtk  # type: ignore


class Application(Gtk.Application):
    def do_activate(self) -> None:
        self.window = MainWindow(application=self)
        self.window.present()


# using Gtk 4.0;
#
# template $MainWindow: ApplicationWindow {
#     [content]
#     SpinButton {
#         value: 50.0;
#
#         adjustment: Adjustment {
#             lower: 0.0;
#             value: 50.0;
#             upper: 100.0;
#             page-increment: 1.0;
#             step-increment: 1.0;
#         };
#     }
# }
@Gtk.Template(
    string=r"""
<?xml version="1.0" encoding="UTF-8"?>
<!--
DO NOT EDIT!
This file was @generated by blueprint-compiler. Instead, edit the
corresponding .blp file and regenerate this file with blueprint-compiler.
-->
<interface>
  <requires lib="gtk" version="4.0"/>
  <template class="MainWindow" parent="GtkApplicationWindow">
    <child type="content">
      <object class="GtkSpinButton">
        <property name="value">50</property>
        <property name="adjustment">
          <object class="GtkAdjustment">
            <property name="lower">0</property>
            <property name="value">50</property>
            <property name="upper">100</property>
            <property name="page-increment">1</property>
            <property name="step-increment">1</property>
          </object>
        </property>
      </object>
    </child>
  </template>
</interface>
"""
)
class MainWindow(Gtk.ApplicationWindow):
    __gtype_name__ = "MainWindow"


appplication = Application()
appplication.run(sys.argv)

Am I doing something wrong, or is this a bug? I’m using Gnome on Wayland on Fedora 42, with GTK v4.18.6, Python v3.13.7, and PyGObject v3.50.0.

This is an unfortunate side effect of how values are set. Gtk.Builder sets spin_button.value, then adjustment.lower, then adjustment.value, then adjustment.upper – that is, in the same order you wrote them. But this means that when value gets set (both on the spin button and on the adjustment), the upper bound is still 0.0, so the value is clamped to 0.0 as well.

It starts working if you reorder the properties so that either value is assigned textually after the upper.

In my humble opinion, this should be considered an issue in Gtk.Builder. It should (somehow) know to use Gtk.Adjustment.configure() instead of setting the properties via g_object_setv(). Gtk.Adjustment.configure() already verifies the invariants, sets properties in the right order, and emits the signals optimally.

2 Likes

How could that possibly work, without changing the way to set up an adjustment entirely with a custom syntax?

Yes, that fixes things, thanks!

Ok, I’ve opened a new issue.

If I had a specific plan, I’d be submitting an MR :smiley:

But overall, GtkBuilder already has special support for many well-known types (mostly for parsing and setting properties of those types). So it could go “aha, I see this is a GTK_TYPE_ADJUSTMENT, let me call gtk_adjustment_configure () instead of g_object_setv ()”. Alternatively, GtkAdjustment could do it on its side, making these all into construct properties and overriding constructor vfunc (but this would cause GLib to take a slower path when constructing it :neutral_face:).

The exceptions usually concern values, or custom syntax; they do not to change how values are set. Adding a new exception means having a different behaviour that needs to be maintained and debugged.

There should be no functional difference between:

<object>
  <property name="foo"/>
  <property name="bar"/>
  <property name="baz"/>
</object>

and:

g_object_set (object, "foo", ..., "bar", ..., "baz", ..., NULL);

And the same considerations for setting properties in GtkAdjustment applies to both GtkBuilder and the generic GObject API.

If we want to add an exception for GtkAdjustment, I’d rather add some custom syntax for setting up an adjustment, and possibly turn every property read-only to force people to use gtk_adjustment_configure().