Search code examples
gtk3pygobject

Remove widget from Gtk viewport / scrolled window in dynamic GUI


I am building a GUI (Python binding of GTK3) where one Gtk Scrolled Window (from Glade) can contain different treeviews. The program launches with an empty window and the first time around everything works with:

 self.scrolled_window.add_with_viewport(treeview)
 self.main_window.show_all()

Edit: Picture of the test program (see source below):

enter image description here

The second time around I get the following error:

(main.py:15905): Gtk-CRITICAL **: gtk_scrolled_window_add_with_viewport: assertion 'gtk_bin_get_child (GTK_BIN (child_widget)) == NULL' failed

I think that I might need to empty the viewport or scrolled window first, but don't know how that is done and can't find any documentation for it.

Edit: When using self.scrolled_window.remove(self.scrolled_window.get_child()) I don't see the dataset at the start up and error messages appear when switching layers.

Edit: I updated the code once more so the example data is there right from the start.

The glade file (test_project.glade):

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
  <requires lib="gtk+" version="3.10"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <property name="default_width">440</property>
    <property name="default_height">250</property>
    <signal name="destroy" handler="on_window1_destroy" swapped="no"/>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow2">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox" id="box2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkButton" id="add_layer">
                <property name="label" translatable="yes">add layer</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_add_layer_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="add_data">
                <property name="label" translatable="yes">add data</property>
                <property name="name">add_data</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_add_data_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

The python code:

#!/usr/bin/python3

from gi.repository import Gtk
import random

class BottomTreeView(Gtk.TreeView):
    def __init__(self, store):
        Gtk.TreeView.__init__(store)
        self.store = store

        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Dataset", renderer, text=0)
        self.append_column(column)

        for x in range(10):
            self.store.append(None, [str(random.random())])

class MainWindow(object):
    def __init__(self, builder):
        self.main_window = builder.get_object("window1")
        self.sw1 = builder.get_object("scrolledwindow1")
        self.sw2 = builder.get_object("scrolledwindow2")
        self.layernumber = 0

        self.topstore = Gtk.TreeStore(str, object)
        self.topview = Gtk.TreeView(self.topstore)
        renderer1 = Gtk.CellRendererText()
        column1 = Gtk.TreeViewColumn("Name", renderer1, text=0)
        self.topview.append_column(column1)
        self.sw1.add_with_viewport(self.topview)

        #Adding test data in a dumb way
        self.store1 = Gtk.TreeStore(str)
        for x in range(10):
            self.store1.append(None, [str(random.random())])
        self.view1 = Gtk.TreeView(self.store1)
        self.rend1 = Gtk.CellRendererText()
        self.col1 = Gtk.TreeViewColumn("Dataset", self.rend1, text=0)
        self.view1.append_column(self.col1)
        self.sw2.add_with_viewport(self.view1)
        self.topstore.append(None, ["Layer 1", self.view1])

        self.store2 = Gtk.TreeStore(str)
        for x in range(10):
            self.store2.append(None, [str(random.random())])
        self.view2 = Gtk.TreeView(self.store2)
        self.rend2 = Gtk.CellRendererText()
        self.col2 = Gtk.TreeViewColumn("Dataset", self.rend2, text=0)
        self.view2.append_column(self.col2)
        self.topstore.append(None, ["Layer 2", self.view2])

        self.select = self.topview.get_selection()
        self.select.connect("changed", self.on_tree_selection_changed)
        self.main_window.show_all()

    def on_add_layer_clicked(self, widget):
        self.layernumber += 1
        store = Gtk.TreeStore(str)
        bottom = BottomTreeView(store)
        self.topstore.append(None, [str(self.layernumber), bottom])

    def on_add_data_clicked(self, widget):
        self.selection = self.topview.get_selection()
        model, treeiter = self.selection.get_selected()
        print("Adding data to {0}".format(model[treeiter][0]))
        datasheet = model[treeiter][1]
        datasheet.store.append(None, [str(random.random())])

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

    def on_tree_selection_changed(self, selection):
        print("\n>>> Selection changed")
        model, treeiter = selection.get_selected()
        print(">>> Model: {0}, Treeiter: {1}".format(model, treeiter))
        if treeiter != None:
             print(">>> You selected Layer {0}.".format(model[treeiter][1]))
             tree_obj = model[treeiter][1]
             #The following lines mess up the program from the startup
             #self.sw2.remove(self.sw2.get_child())
             #self.sw2.add_with_viewport(tree_obj)
             #self.main_window.show_all()

def main():
    builder = Gtk.Builder()
    objects = builder.add_objects_from_file("test_project.glade",
         ("window1", ""))

    window_instance = MainWindow(builder)
    builder.connect_signals(window_instance)
    Gtk.main()

if __name__ == "__main__":
     main()

Solution

  • To clear out the scrolled window, do this:

    self.scrolled_window.remove(self.scrolled_window.get_child())