Cross-coupling comboboxes

classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

Cross-coupling comboboxes

Rob Pearce
Hello experts,

I have a GTKmm (2.24) application in which two different windows each
have a ComboBoxText controlling the same item, so that whenever the user
makes a selection on one of them, the other one needs to be updated
accordingly. This is implemented by calling the set_active with the
result of get_active_row_number.

Because the content of those combo boxes has got rather voluminous, I
now want to convert them to hierarchical. So the ...Text version is no
good and I'm using a ComboBox with a TreeStore behind it. The row number
obviously no longer works, so...

What's the best way to do this?

Thanks,

Rob


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Gtkmm mailing list
>>>
This is implemented by calling the set_active with the
result of get_active_row_number.

Because the content of those combo boxes has got rather voluminous, I
now want to convert them to hierarchical. So the ...Text version is no
good and I'm using a ComboBox with a TreeStore behind it. The row number
obviously no longer works, so...
>>>


I don't know if GTK+ 2 had these, but as of GTK 3, I would look into property :active or :active-id (ensuring rows have values for those of course). You could continue syncing them manually, if you need or simply want to, or alternatively you could automate it via GLib's bind_property().


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Carlos Gomez
In reply to this post by Rob Pearce
Hi,

If both ComboBoxes has the same items, perhaps you should use
Gtk::TreeModel::Path.

On 05/06/2019 18:58, Rob Pearce wrote:

> Hello experts,
>
> I have a GTKmm (2.24) application in which two different windows each
> have a ComboBoxText controlling the same item, so that whenever the
> user makes a selection on one of them, the other one needs to be
> updated accordingly. This is implemented by calling the set_active
> with the result of get_active_row_number.
>
> Because the content of those combo boxes has got rather voluminous, I
> now want to convert them to hierarchical. So the ...Text version is no
> good and I'm using a ComboBox with a TreeStore behind it. The row
> number obviously no longer works, so...
>
> What's the best way to do this?
>
> Thanks,
>
> Rob
>
>
> _______________________________________________
> gtkmm-list mailing list
> [hidden email]
> https://mail.gnome.org/mailman/listinfo/gtkmm-list
>
_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Rob Pearce-2
On 06/06/2019 09:18, Carlos Gomez wrote:
> If both ComboBoxes has the same items, perhaps you should use
> Gtk::TreeModel::Path.

They not only have the same items, they share a TreeModel instance and a
TreeModel::Columns instance (I believe this is an officially approved
scheme). So yes, Path should work. In fact, can I not use the ::iterator
directly?


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Carlos Gomez
In that case I think so, but never did. Give it a try. My idea was to
use Path to get an iterator, but if we are talking about the same
instance, the iterator should work.

On 06/06/2019 18:13, Rob Pearce wrote:

> On 06/06/2019 09:18, Carlos Gomez wrote:
>> If both ComboBoxes has the same items, perhaps you should use
>> Gtk::TreeModel::Path.
>
> They not only have the same items, they share a TreeModel instance and
> a TreeModel::Columns instance (I believe this is an officially
> approved scheme). So yes, Path should work. In fact, can I not use the
> ::iterator directly?
>
>
> _______________________________________________
> gtkmm-list mailing list
> [hidden email]
> https://mail.gnome.org/mailman/listinfo/gtkmm-list
>
_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Rob Pearce-2
Thanks, it seems to work. At least, mostly...

One of the other changes I'm making needs me to test an existing bit of
functionality which replaces the underlying "stuff". This means that the
TreeModel content is clear'd then re-built. With the simple list and
ComboBoxText, this worked fine. Now, using the TreeModel and passing
iterators, I get a shed load of
Gtk-CRITICAL **: 12:24:08.859: gtk_tree_store_get_path: assertion
'iter->user_data != NULL' failed
interspersed with my own debug report of invalid iters passed to
on_combo_changed()

I don't think this is connected with the coupling. If I comment out the
forward coupling call it makes little difference. I've also tried
calling unset_active() before the clear() but it didn't help.

Any ideas?

On 07/06/2019 09:15, Carlos Gomez wrote:

> In that case I think so, but never did. Give it a try. My idea was to
> use Path to get an iterator, but if we are talking about the same
> instance, the iterator should work.
>
> On 06/06/2019 18:13, Rob Pearce wrote:
>> On 06/06/2019 09:18, Carlos Gomez wrote:
>>> If both ComboBoxes has the same items, perhaps you should use
>>> Gtk::TreeModel::Path.
>>
>> They not only have the same items, they share a TreeModel instance
>> and a TreeModel::Columns instance (I believe this is an officially
>> approved scheme). So yes, Path should work. In fact, can I not use
>> the ::iterator directly?
>>
>>
>> _______________________________________________
>> gtkmm-list mailing list
>> [hidden email]
>> https://mail.gnome.org/mailman/listinfo/gtkmm-list
>>
>
>

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Gtkmm mailing list
>>>
Now, using the TreeModel and passing
iterators, I get a shed load of
Gtk-CRITICAL **: 12:24:08.859: gtk_tree_store_get_path: assertion
'iter->user_data != NULL' failed
interspersed with my own debug report of invalid iters passed to
on_combo_changed()

I don't think this is connected with the coupling. If I comment out the
forward coupling call it makes little difference. I've also tried
calling unset_active() before the clear() but it didn't help.
>>>

Yep:

```
G_DEBUG=fatal-criticals gdb ./your_exe
run
bt
```


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Rob Pearce-2
On 07/06/2019 13:26, Daniel Boles via gtkmm-list wrote:
>>>
Now, using the TreeModel and passing
iterators, I get a shed load of
Gtk-CRITICAL **: 12:24:08.859: gtk_tree_store_get_path: assertion
'iter->user_data != NULL' failed
interspersed with my own debug report of invalid iters passed to
on_combo_changed()

I don't think this is connected with the coupling. If I comment out the
forward coupling call it makes little difference. I've also tried
calling unset_active() before the clear() but it didn't help.
>>>

Yep:

```
G_DEBUG=fatal-criticals gdb ./your_exe
run
bt
```

That gives me:

Thread 1 "vpctrl" received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff5c87745 in _g_log_abort () from /usr/lib64/libglib-2.0.so.0
(gdb) bt
#0  0x00007ffff5c87745 in _g_log_abort () from /usr/lib64/libglib-2.0.so.0
#1  0x00007ffff5c88a51 in g_logv () from /usr/lib64/libglib-2.0.so.0
#2  0x00007ffff5c88c2f in g_log () from /usr/lib64/libglib-2.0.so.0
#3  0x00007ffff732ac04 in gtk_tree_store_get_path () from /usr/lib64/libgtk-x11-2.0.so.0
#4  0x00007ffff7a8cfb6 in Gtk::TreeModel_Class::get_path_vfunc_callback(_GtkTreeModel*, _GtkTreeIter*) () from /usr/lib64/libgtkmm-2.4.so.1
#5  0x00007ffff71bf7fa in gtk_combo_box_set_active_iter () from /usr/lib64/libgtk-x11-2.0.so.0
#6  0x000055555562c6e2 in TRateControlDlg::SetMaterialComboIter (this=0x5555559fcf30, iter=...)
    at AutoControl.h:60
#7  0x000055555562c231 in TAppWindow::SetMaterialIter (this=0x7fffffffd880, iter=...)
    at MainView.cc:993
#8  0x0000555555678041 in MaterialSelector::on_combo_changed (this=0x55555597b628)
    at DepositionView.cc:570
#9  0x000055555567f8f2 in sigc::bound_mem_functor0<void, MaterialSelector>::operator() (
    this=0x5555559f4478) at /usr/include/sigc++-2.0/sigc++/functors/mem_fun.h:1991
#10 0x000055555567f4ee in sigc::adaptor_functor<sigc::bound_mem_functor0<void, MaterialSelector> >::operator() (this=0x5555559f4470) at /usr/include/sigc++-2.0/sigc++/adaptors/adaptor_trait.h:256
#11 0x000055555567ef40 in sigc::internal::slot_call0<sigc::bound_mem_functor0<void, MaterialSelector>, void>::call_it (rep=0x5555559f4440) at /usr/include/sigc++-2.0/sigc++/functors/slot.h:136
#12 0x00007ffff66b61a8 in Glib::SignalProxyNormal::slot0_void_callback(_GObject*, void*) ()
   from /usr/lib64/libglibmm-2.4.so.1
#13 0x00007ffff5d63b6d in g_closure_invoke () from /usr/lib64/libgobject-2.0.so.0
#14 0x00007ffff5d763e3 in signal_emit_unlocked_R () from /usr/lib64/libgobject-2.0.so.0
#15 0x00007ffff5d7f802 in g_signal_emit_valist () from /usr/lib64/libgobject-2.0.so.0
#16 0x00007ffff5d7fe6f in g_signal_emit () from /usr/lib64/libgobject-2.0.so.0
#17 0x00007ffff71b9c81 in gtk_combo_box_set_active_internal () from /usr/lib64/libgtk-x11-2.0.so.0
#18 0x0000555555678a4c in MaterialSelector::Populate (this=0x55555597b628)
    at DepositionView.cc:676
#19 0x000055555567b938 in TDepositionView::Populate (this=0x55555597b470) at DepositionView.cc:883

and thence into bits of my code.

With the coupling commented out, the backtrace is:

Thread 1 "vpctrl" received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff5c87745 in _g_log_abort () from /usr/lib64/libglib-2.0.so.0
(gdb) bt
#0  0x00007ffff5c87745 in _g_log_abort () from /usr/lib64/libglib-2.0.so.0
#1  0x00007ffff5c88a51 in g_logv () from /usr/lib64/libglib-2.0.so.0
#2  0x00007ffff5c88c2f in g_log () from /usr/lib64/libglib-2.0.so.0
#3  0x00007ffff732ac04 in gtk_tree_store_get_path () from /usr/lib64/libgtk-x11-2.0.so.0
#4  0x00007ffff7a8cfb6 in Gtk::TreeModel_Class::get_path_vfunc_callback(_GtkTreeModel*, _GtkTreeIter*) () from /usr/lib64/libgtkmm-2.4.so.1
#5  0x00007ffff71bf7fa in gtk_combo_box_set_active_iter () from /usr/lib64/libgtk-x11-2.0.so.0
#6  0x00005555556419e5 in MaterialSelector::set_active (this=0x555555950a28, iter=...)
    at DepositionView.h:48
#7  0x0000555555641a38 in TDepositionView::SetMaterialComboIter (this=0x555555950870, idx=...)
    at DepositionView.h:128
#8  0x000055555563b221 in TRateControlDlg::MaterialClick (this=0x55555581ae00)
    at AutoControl.cc:231
#9  0x00005555556432f0 in sigc::bound_mem_functor0<void, TRateControlDlg>::operator() (
    this=0x555555b059a8) at /usr/include/sigc++-2.0/sigc++/functors/mem_fun.h:1991
#10 0x0000555555642df6 in sigc::adaptor_functor<sigc::bound_mem_functor0<void, TRateControlDlg> >::operator() (this=0x555555b059a0) at /usr/include/sigc++-2.0/sigc++/adaptors/adaptor_trait.h:256
#11 0x00005555556427d8 in sigc::internal::slot_call0<sigc::bound_mem_functor0<void, TRateControlDlg>, void>::call_it (rep=0x555555b05970) at /usr/include/sigc++-2.0/sigc++/functors/slot.h:136
#12 0x00007ffff66b61a8 in Glib::SignalProxyNormal::slot0_void_callback(_GObject*, void*) ()
   from /usr/lib64/libglibmm-2.4.so.1
#13 0x00007ffff5d63b6d in g_closure_invoke () from /usr/lib64/libgobject-2.0.so.0
#14 0x00007ffff5d763e3 in signal_emit_unlocked_R () from /usr/lib64/libgobject-2.0.so.0
#15 0x00007ffff5d7f802 in g_signal_emit_valist () from /usr/lib64/libgobject-2.0.so.0
#16 0x00007ffff5d7fe6f in g_signal_emit () from /usr/lib64/libgobject-2.0.so.0
#17 0x00007ffff71bcf76 in gtk_combo_box_model_row_deleted () from /usr/lib64/libgtk-x11-2.0.so.0
#18 0x00007ffff5d63b6d in g_closure_invoke () from /usr/lib64/libgobject-2.0.so.0
#19 0x00007ffff5d768f3 in signal_emit_unlocked_R () from /usr/lib64/libgobject-2.0.so.0
#20 0x00007ffff5d7f802 in g_signal_emit_valist () from /usr/lib64/libgobject-2.0.so.0
#21 0x00007ffff5d7fe6f in g_signal_emit () from /usr/lib64/libgobject-2.0.so.0
#22 0x00007ffff732d100 in gtk_tree_store_remove () from /usr/lib64/libgtk-x11-2.0.so.0
#23 0x00007ffff732d295 in gtk_tree_store_clear_traverse () from /usr/lib64/libgtk-x11-2.0.so.0
#24 0x00007ffff732d245 in gtk_tree_store_clear_traverse () from /usr/lib64/libgtk-x11-2.0.so.0
#25 0x00007ffff732d245 in gtk_tree_store_clear_traverse () from /usr/lib64/libgtk-x11-2.0.so.0
#26 0x00007ffff732e606 in gtk_tree_store_clear () from /usr/lib64/libgtk-x11-2.0.so.0
#27 0x0000555555678a6d in MaterialSelector::Populate (this=0x555555950a28)
    at DepositionView.cc:677
#28 0x000055555567b93c in TDepositionView::Populate (this=0x555555950870) at DepositionView.cc:883

Which means it actually gets a bit further before the first critical. However, entry #8 of that second backtrace is invoking the signal_changed handler of the second combobox, which then passes that to the primary at #5, moving that away from its "unset" state.

Is it going to be enough to "unset_active" the second combobox before clear()-ing the TreeStore? Or do I need to do something more complex? (I think the code needs re-structuring to reduce the levels of nesting needed for even that simple option, but that's a separate matter)

Thanks,

Rob


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Gtkmm mailing list
It's not particularly clear what's going on. Posting the minimal code required to reproduce this on a pastebin and linking it would be great.

Are you taking an iterator into the model, then clearing the model, then trying to use the former iterator again? That definitely won't end well, I don't think.

In that case you probably should indeed use either TreePath or :active-id, since those are only instructions how to get to the right place in an equivalent model - rather than being intrinsically tied to the validity of the original state of the same model, like iterators are. That said, even path/ID probably only work if, after you clear and rebuild for whatever unclear reason, the item you want to reselect has the same path/ID (or you're happy to select a different item that now has that path/ID).



_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Rob Pearce-2
On 07/06/2019 19:56, Daniel Boles via gtkmm-list wrote:
> It's not particularly clear what's going on. Posting the minimal code
> required to reproduce this on a pastebin and linking it would be great.
>
Yes, I know, but this is all well embedded into a large code base, so it
won't be easy. I'll try building up from my initial hack of the ComboBox
example to see if, and with which additions, I can reproduce the problem.


> Are you taking an iterator into the model, then clearing the model,
> then trying to use the former iterator again? That definitely won't
> end well, I don't think.

No. At least, not knowingly. The only place I'm using an iterator is for
the "signal_changed" handler of one combobox to pass it directly to the
other combobox. (well, there are other places where I use one to iterate
or to populate a newly added entry, but those aren't involved in this
bit). What appears to happen, which I wasn't expecting, is the
m_refTreeModel->clear(); call invokes the combobox signal_changed
handler for every row? And when it does so, the iterator returned by
get_active() is invalid, which my code doesn't test for in the secondary
case, so that would be worth doing... hold on...


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Gtkmm mailing list
> What appears to happen, which I wasn't expecting, is the m_refTreeModel->clear(); call invokes the combobox signal_changed handler for every row?

yeah, gtk_combo_box_model_row_deleted() does that if the reference to the previous active_row is now invalid, and i presume sets the active model to the nearest still-valid one, and so on, etc.

another way around this might be to block the signal while you clear the model.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Rob Pearce-2
In reply to this post by Rob Pearce-2
On 07/06/2019 21:11, Rob Pearce wrote:
> And when it does so, the iterator returned by get_active() is invalid,
> which my code doesn't test for in the secondary case, so that would be
> worth doing... hold on...

Yep, that seems to have fixed it.

There may be a better fix. It would be nice if calling clear() on the
model didn't invoke the signal_changed of BOTH comboboxes 328 times each
(I think I mentioned the selection is getting rather large) but as long
as I return immediately if get_active() returns an invalid iterator then
it doesn't cause problems.

Thanks for your help.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Gtkmm mailing list
> There may be a better fix. It would be nice if calling clear() on the
> model didn't invoke the signal_changed of BOTH comboboxes 328 times each

Blocking the signal is another way to do it. That may or may not have any quantitative difference in speed (from sigc not having to call your handler), but it might well lead to nicer handler code if you don't have to check for all the abnormal conditions, by simply blocking so it's never called in such cases instead.

Another idea I have is to unset the model from the ComboBoxes before clearing the model, then reassociate it with them afterwards. That might result in only notify::model being emitted, rather than all the ::changed.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Rob Pearce-2
On 08/06/2019 12:44, Daniel Boles via gtkmm-list wrote:
> > There may be a better fix. It would be nice if calling clear() on the
> > model didn't invoke the signal_changed of BOTH comboboxes 328 times each
>
> Blocking the signal is another way to do it. That may or may not have
> any quantitative difference in speed (from sigc not having to call
> your handler), but it might well lead to nicer handler code if you
> don't have to check for all the abnormal conditions, by simply
> blocking so it's never called in such cases instead.
Yes, that sounds like a possibility. AFAICT the "blocking" has to be
applied per-signal, so I need to keep track of all the connection
instances for all the comboboxes, yes?
>
> Another idea I have is to unset the model from the ComboBoxes before
> clearing the model, then reassociate it with them afterwards. That
> might result in only notify::model being emitted, rather than all the
> ::changed.

That also sounds promising. I had wondered about something similar
(making the new model separately, then swapping to it, before deleting
the old one) but if there's no issue with the comboboxes being
"disconnected" for the duration of the update then your version would be
easier.

Thanks.

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Cross-coupling comboboxes

Gtkmm mailing list
> AFAICT the "blocking" has to be
> applied per-signal, so I need to keep track of all the connection
> instances for all the comboboxes, yes?

Yeah, I often forget that glibmm/gtkmm use Glib::SignalProxy rather than sigc::signal. The former will need you to retain and un/block the individual handler connections, whereas the latter would've let you do all at once. I guess it makes sense; we don't normally want to have to block everything including GTK/GLib's own handlers.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list