Search code examples
pythoncomboboxgtk3

Gtk3 - Python - Glade - 'Linked' comboboxes on different windows


I have two Gtk.Comboboxes on a main window :

  • the first, has its own Gtk.Liststore
  • the second one, linked to a Gtk.TreeModelFilter which is linked to a second Gtk.Liststore

When I select a value in the first one, the second one displays values according to my selection. This works well in the main window.

But in my application, I need to use this comboboxes combination many times, and when I use it on another window, this generates a Gtk Error that I don't understand:

"Gtk-CRITICAL **: 00:27:38.289: gtk_tree_model_filter_set_visible_func: assertion 'filter->priv->visible_method_set == FALSE' failed"

Could someone explain me what's wrong with my code and what is the meaning of that error message?

Here is the glade file (test.ui)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkListStore" id="liststore1">
    <columns>
      <!-- column-name id -->
      <column type="gchararray"/>
      <!-- column-name name -->
      <column type="gchararray"/>
    </columns>
    <data>
      <row>
        <col id="0" translatable="yes">1</col>
        <col id="1" translatable="yes">fruits</col>
      </row>
      <row>
        <col id="0" translatable="yes">2</col>
        <col id="1" translatable="yes">colors</col>
      </row>
    </data>
  </object>
  <object class="GtkListStore" id="liststore2">
    <columns>
      <!-- column-name id -->
      <column type="gchararray"/>
      <!-- column-name name -->
      <column type="gchararray"/>
    </columns>
    <data>
      <row>
        <col id="0" translatable="yes">10</col>
        <col id="1" translatable="yes">lemon</col>
      </row>
      <row>
        <col id="0" translatable="yes">11</col>
        <col id="1" translatable="yes">apple</col>
      </row>
      <row>
        <col id="0" translatable="yes">12</col>
        <col id="1" translatable="yes">strawberry</col>
      </row>
      <row>
        <col id="0" translatable="yes">20</col>
        <col id="1" translatable="yes">blue</col>
      </row>
      <row>
        <col id="0" translatable="yes">21</col>
        <col id="1" translatable="yes">yellow</col>
      </row>
      <row>
        <col id="0" translatable="yes">22</col>
        <col id="1" translatable="yes">purple</col>
      </row>
    </data>
  </object>
  <object class="GtkTreeModelFilter" id="filter">
    <property name="child_model">liststore2</property>
  </object>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <signal name="destroy" handler="on_quit" swapped="no"/>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">20</property>
        <child>
          <object class="GtkToolbar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Open other</property>
                <property name="use_underline">True</property>
                <signal name="clicked" handler="on_other" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkGrid">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_left">20</property>
            <property name="margin_right">20</property>
            <property name="row_spacing">5</property>
            <property name="column_spacing">10</property>
            <property name="column_homogeneous">True</property>
            <child>
              <object class="GtkComboBox" id="w1_combo1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="model">liststore1</property>
                <property name="active">0</property>
                <property name="id_column">0</property>
                <property name="active_id">0</property>
                <child>
                  <object class="GtkCellRendererText"/>
                  <attributes>
                    <attribute name="text">1</attribute>
                  </attributes>
                </child>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkComboBox" id="w1_combo2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="model">filter</property>
                <property name="active">0</property>
                <property name="id_column">0</property>
                <property name="active_id">0</property>
                <child>
                  <object class="GtkCellRendererText"/>
                  <attributes>
                    <attribute name="text">1</attribute>
                  </attributes>
                </child>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Combo1</property>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Combo2</property>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkDialog" id="window2">
    <property name="can_focus">False</property>
    <property name="type_hint">dialog</property>
    <property name="transient_for">window1</property>
    <child>
      <placeholder/>
    </child>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
            <child>
              <object class="GtkButton" id="btn_close">
                <property name="label" translatable="yes">Close</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="valign">center</property>
            <property name="margin_left">20</property>
            <property name="margin_right">20</property>
            <property name="margin_top">20</property>
            <property name="margin_bottom">20</property>
            <property name="vexpand">True</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkGrid">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="valign">center</property>
                <property name="margin_top">20</property>
                <property name="margin_bottom">20</property>
                <property name="vexpand">True</property>
                <property name="row_spacing">5</property>
                <property name="column_spacing">10</property>
                <property name="column_homogeneous">True</property>
                <child>
                  <object class="GtkComboBox" id="w2_combo1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="model">liststore1</property>
                    <property name="active">0</property>
                    <property name="id_column">0</property>
                    <property name="active_id">0</property>
                    <child>
                      <object class="GtkCellRendererText"/>
                      <attributes>
                        <attribute name="text">1</attribute>
                      </attributes>
                    </child>
                  </object>
                  <packing>
                    <property name="left_attach">0</property>
                    <property name="top_attach">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkComboBox" id="w2_combo2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="model">filter</property>
                    <property name="active">0</property>
                    <property name="id_column">0</property>
                    <property name="active_id">0</property>
                    <child>
                      <object class="GtkCellRendererText"/>
                      <attributes>
                        <attribute name="text">1</attribute>
                      </attributes>
                    </child>
                  </object>
                  <packing>
                    <property name="left_attach">1</property>
                    <property name="top_attach">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Combo1</property>
                  </object>
                  <packing>
                    <property name="left_attach">0</property>
                    <property name="top_attach">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Combo2</property>
                  </object>
                  <packing>
                    <property name="left_attach">1</property>
                    <property name="top_attach">0</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
    <action-widgets>
      <action-widget response="-7">btn_close</action-widget>
    </action-widgets>
  </object>
</interface>

and the python file (test.py):

#!/usr/bin/env python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import sys

class Main:
    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file('test.ui')
        self.builder.connect_signals(self)
        self.obj = self.builder.get_object

        self.window = self.obj('window1')
        self.combo1 = self.obj('w1_combo1')
        self.combo2 = self.obj('w1_combo2')
        self.combo1.connect('changed', self.on_select, self.combo2)
        self.filter = self.obj('filter')
        self.filter.set_visible_func(self.filter_func)

        self.combo1_val = None

        self.window.show()

    def on_other(self, btn):
        Other(self.obj)

    def on_select(self, combo, next_combo):
        self.combo1_val = combo.get_active_id()
        self.filter.refilter()
        next_combo.set_active(0)

    def filter_func(self, model, iter, data):
        if self.combo1_val is None: return True
        else: return self.combo1_val == model[iter][0][:1]

    def on_quit(self, widget):
        Gtk.main_quit()

class Other:
    def __init__(self, obj):

        self.obj = obj
        self.window = self.obj('window2')

        self.combo1 = self.obj('w2_combo1')
        self.combo2 = self.obj('w2_combo2')
        self.combo1.connect('changed', self.on_select, self.combo2)
        self.filter = self.obj('filter')
        self.filter.set_visible_func(self.filter_func)
        self.btn_close = self.obj('btn_close')
        self.btn_close.connect('clicked', self.on_hide)

        self.combo1_val = None

        self.window.show()

    def on_select(self, combo, next_combo):
        self.combo1_val = combo.get_active_id()
        self.filter.refilter()
        next_combo.set_active(0)

    def filter_func(self, model, iter, data):
        if self.combo1_val is None: return True
        else: return self.combo1_val == model[iter][0][:1]

        self.window.show()

    def on_hide(self, btn):
        self.window.hide()



def main():
    app = Main()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

Thank you in advance.


Solution

  • The problem was due to the fact that the second combobox on the main window and the second combobox on 'other' window were using the same filter. I've created another filter and the second comboboxes have now their own filter linked to a common Gtk.Liststore.

    Everything works fine, now.

    Here are the corrected files :

    The glade one :

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.22.1 -->
    <interface>
      <requires lib="gtk+" version="3.20"/>
      <object class="GtkListStore" id="liststore1">
        <columns>
          <!-- column-name id -->
          <column type="gchararray"/>
          <!-- column-name name -->
          <column type="gchararray"/>
        </columns>
        <data>
          <row>
            <col id="0" translatable="yes">1</col>
            <col id="1" translatable="yes">fruits</col>
          </row>
          <row>
            <col id="0" translatable="yes">2</col>
            <col id="1" translatable="yes">colors</col>
          </row>
        </data>
      </object>
      <object class="GtkListStore" id="liststore2">
        <columns>
          <!-- column-name id -->
          <column type="gchararray"/>
          <!-- column-name name -->
          <column type="gchararray"/>
        </columns>
        <data>
          <row>
            <col id="0" translatable="yes">10</col>
            <col id="1" translatable="yes">lemon</col>
          </row>
          <row>
            <col id="0" translatable="yes">11</col>
            <col id="1" translatable="yes">apple</col>
          </row>
          <row>
            <col id="0" translatable="yes">12</col>
            <col id="1" translatable="yes">strawberry</col>
          </row>
          <row>
            <col id="0" translatable="yes">20</col>
            <col id="1" translatable="yes">blue</col>
          </row>
          <row>
            <col id="0" translatable="yes">21</col>
            <col id="1" translatable="yes">yellow</col>
          </row>
          <row>
            <col id="0" translatable="yes">22</col>
            <col id="1" translatable="yes">purple</col>
          </row>
        </data>
      </object>
      <object class="GtkTreeModelFilter" id="w1_filter">
        <property name="child_model">liststore2</property>
      </object>
      <object class="GtkWindow" id="window1">
        <property name="can_focus">False</property>
        <signal name="destroy" handler="on_quit" swapped="no"/>
        <child>
          <placeholder/>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <property name="spacing">20</property>
            <child>
              <object class="GtkToolbar">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkToolButton">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Open other</property>
                    <property name="use_underline">True</property>
                    <signal name="clicked" handler="on_other" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="homogeneous">True</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkGrid">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="margin_left">20</property>
                <property name="margin_right">20</property>
                <property name="row_spacing">5</property>
                <property name="column_spacing">10</property>
                <property name="column_homogeneous">True</property>
                <child>
                  <object class="GtkComboBox" id="w1_combo1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="model">liststore1</property>
                    <property name="active">0</property>
                    <property name="id_column">0</property>
                    <property name="active_id">0</property>
                    <child>
                      <object class="GtkCellRendererText"/>
                      <attributes>
                        <attribute name="text">1</attribute>
                      </attributes>
                    </child>
                  </object>
                  <packing>
                    <property name="left_attach">0</property>
                    <property name="top_attach">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkComboBox" id="w1_combo2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="model">w1_filter</property>
                    <property name="active">0</property>
                    <property name="id_column">0</property>
                    <property name="active_id">0</property>
                    <child>
                      <object class="GtkCellRendererText"/>
                      <attributes>
                        <attribute name="text">1</attribute>
                      </attributes>
                    </child>
                  </object>
                  <packing>
                    <property name="left_attach">1</property>
                    <property name="top_attach">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Combo1</property>
                  </object>
                  <packing>
                    <property name="left_attach">0</property>
                    <property name="top_attach">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Combo2</property>
                  </object>
                  <packing>
                    <property name="left_attach">1</property>
                    <property name="top_attach">0</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
        </child>
      </object>
      <object class="GtkTreeModelFilter" id="w2_filter">
        <property name="child_model">liststore2</property>
      </object>
      <object class="GtkDialog" id="window2">
        <property name="can_focus">False</property>
        <property name="type_hint">dialog</property>
        <property name="transient_for">window1</property>
        <child>
          <placeholder/>
        </child>
        <child internal-child="vbox">
          <object class="GtkBox">
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <property name="spacing">2</property>
            <child internal-child="action_area">
              <object class="GtkButtonBox">
                <property name="can_focus">False</property>
                <property name="layout_style">end</property>
                <child>
                  <object class="GtkButton" id="btn_close">
                    <property name="label" translatable="yes">Close</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkBox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="valign">center</property>
                <property name="margin_left">20</property>
                <property name="margin_right">20</property>
                <property name="margin_top">20</property>
                <property name="margin_bottom">20</property>
                <property name="vexpand">True</property>
                <property name="orientation">vertical</property>
                <child>
                  <object class="GtkGrid">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="valign">center</property>
                    <property name="margin_top">20</property>
                    <property name="margin_bottom">20</property>
                    <property name="vexpand">True</property>
                    <property name="row_spacing">5</property>
                    <property name="column_spacing">10</property>
                    <property name="column_homogeneous">True</property>
                    <child>
                      <object class="GtkComboBox" id="w2_combo1">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="model">liststore1</property>
                        <property name="active">0</property>
                        <property name="id_column">0</property>
                        <property name="active_id">0</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">1</attribute>
                          </attributes>
                        </child>
                      </object>
                      <packing>
                        <property name="left_attach">0</property>
                        <property name="top_attach">1</property>
                      </packing>
                    </child>
                    <child>
                      <object class="GtkComboBox" id="w2_combo2">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="model">w2_filter</property>
                        <property name="active">0</property>
                        <property name="id_column">0</property>
                        <property name="active_id">0</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">1</attribute>
                          </attributes>
                        </child>
                      </object>
                      <packing>
                        <property name="left_attach">1</property>
                        <property name="top_attach">1</property>
                      </packing>
                    </child>
                    <child>
                      <object class="GtkLabel">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="label" translatable="yes">Combo1</property>
                      </object>
                      <packing>
                        <property name="left_attach">0</property>
                        <property name="top_attach">0</property>
                      </packing>
                    </child>
                    <child>
                      <object class="GtkLabel">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="label" translatable="yes">Combo2</property>
                      </object>
                      <packing>
                        <property name="left_attach">1</property>
                        <property name="top_attach">0</property>
                      </packing>
                    </child>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
        </child>
        <action-widgets>
          <action-widget response="-7">btn_close</action-widget>
        </action-widgets>
      </object>
    </interface>
    

    And the python one :

    #!/usr/bin/env python3
    # -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 
    
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    import sys
    
    class Main:
        def __init__(self):
    
            self.builder = Gtk.Builder()
            self.builder.add_from_file('test.ui')
            self.builder.connect_signals(self)
            self.obj = self.builder.get_object
    
            self.window = self.obj('window1')
            self.combo1 = self.obj('w1_combo1')
            self.combo2 = self.obj('w1_combo2')
            self.combo1.connect('changed', self.on_select, self.combo2)
            self.filter = self.obj('w1_filter')
            self.filter.set_visible_func(self.filter_func)
    
            self.combo1_val = None
    
            self.window.show()
    
        def on_other(self, btn):
            Other(self.obj)
    
        def on_select(self, combo, next_combo):
            self.combo1_val = combo.get_active_id()
            self.filter.refilter()
            next_combo.set_active(0)
    
        def filter_func(self, model, iter, data):
            #print(self.combo1_val, model[iter][0][:1])
            if self.combo1_val is None: return True
            else: return self.combo1_val == model[iter][0][:1]
    
        def on_quit(self, widget):
            Gtk.main_quit()
    
    class Other:
        def __init__(self, obj):
    
            self.obj = obj
            self.window = self.obj('window2')
    
            self.combo1 = self.obj('w2_combo1')
            self.combo2 = self.obj('w2_combo2')
            self.combo1.connect('changed', self.on_select, self.combo2)
            self.filter = self.obj('w2_filter')
            self.filter.set_visible_func(self.filter_func)
            self.btn_close = self.obj('btn_close')
            self.btn_close.connect('clicked', self.on_hide)
    
            self.combo1_val = None
    
            self.window.show()
    
        def on_select(self, combo, next_combo):
            self.combo1_val = combo.get_active_id()
            self.filter.refilter()
            next_combo.set_active(0)
    
        def filter_func(self, model, iter, data):
            #print(self.combo1_val, model[iter][0][:1])
            if self.combo1_val is None: return True
            else: return self.combo1_val == model[iter][0][:1]
    
            self.window.show()
    
        def on_hide(self, btn):
            self.window.hide()
    
    
    
    def main():
        app = Main()
        Gtk.main()
    
    if __name__ == "__main__":
        sys.exit(main())