It gives this error only on first run when application initializing:
index = self.stack.get_pages().get_selection().get_nth(0)
^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Child' object has no attribute 'get_pages
After first try, and everything initalized, same binding function works properly and can access stack.get_pages(). It is only an issue when first creation.
Questions
Why widgets are not initalized when called from binding functions? self.stack obviously have .get_pages() function, but not accesible when first window activated and first binding method called.
What is the better way of getting the selected page index and total page count?
Expressions are only reevaluated when their inputs change. Because blueprint doesn’t manage a closure’s application code, it can’t tell what changes might affect the result. Therefore, closures must be pure, or deterministic. They may only calculate the result based on their immediate inputs, not properties of their inputs or outside variables.
With other words: Trying to call self.stack from the closure will not work. As I understand it, GTK uses the provided lookup parameters of the closure to determine if something updated and it needs to call the closure. If you can use random outside parts in the closure, they are from an context GTK can’t promise to exist, etc.
So, for a quick and dirty solution, you could implement a new property, like last-page-name, in your class and set the name of the last page in it, updated when needed. Then, you add this property to the closure expression as an parameter. The closure itself could then work out if the button should be visible by comparing if the last-page-name and visible-child properties have the same value.
But the same code works when you trigger it later after the initialization.
For a brief moment at the startup of the program, self.stack returns gtktemplate.Child, after some time passed, it returns the real type of widget Gtk.Stack.
I think binding works before matching properties with real widgets in the template file.
It is regardless if I use Blueprint or directly . XML files.
Well, this is just a guess.
But it could be that after GTK has initialized the visible-child property, it expects everything the closure needs to be available, and runs the closure before other things are initialized.
The main point I see here is that using elements outside of the scope of the closure is unsupported behavior. If it does work sometimes, then by accident.
So, the solution to avoiding this error is by not using unsupported behaviors.
Passing stack as parameter and then using methods from there doesn’t seem like a good solution either, if you ask me.
First of all, the expression expects properties with notify signal, as it uses this signal to get notified about changes. stack is a template child however, not a property. Also, the pages of a stack are available as a property (Gtk.Stack:pages).
Also, calling methods and more on the stack pages selection model still seems to be close to unsupported behavior and also a bit expensive for a method called on every page change.
Finally, from looking at your UI file, it seems you want behavior as implemented by Adw.Carousel. If so, this might be a better fit then, as it also provides properties for page position and number for a proper expression closure.