【问题标题】:Remove widget from Gtk viewport / scrolled window in dynamic GUI从动态 GUI 中的 Gtk 视口/滚动窗口中删除小部件
【发布时间】:2015-01-02 19:00:54
【问题描述】:

我正在构建一个 GUI(GTK3 的 Python 绑定),其中一个 Gtk 滚动窗口(来自 Glade)可以包含不同的树视图。该程序以一个空窗口启动,并且第一次一切正常:

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

编辑:测试程序的图片(见下面的源代码):

第二次出现以下错误:

(main.py:15905): Gtk-CRITICAL **: gtk_scrolled_window_add_with_viewport: 断言 'gtk_bin_get_child (GTK_BIN (child_widget)) == NULL' 失败

我认为我可能需要先清空视口或滚动窗口,但不知道这是如何完成的,也找不到任何文档。

编辑: 当使用 self.scrolled_window.remove(self.scrolled_window.get_child()) 我在启动时看不到数据集并且出现错误消息切换图层时。

编辑:我再次更新了代码,因此示例数据从一开始就在那里。

林间空地文件 (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>

python代码:

#!/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()

【问题讨论】:

标签: gtk3 pygobject


【解决方案1】:

要清除滚动窗口,请执行以下操作:

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

【讨论】:

  • 这适用于在小部件之间切换一次。第二次调用小部件时程序崩溃:“警告:g_object_freeze_notify:断言'G_IS_OBJECT(对象)'失败”。我将尝试在我的问题中包含一个示例程序。
  • 取得了一些进展。或许可以自己回答。需要一个小时:)
【解决方案2】:

在 ptomato 回答之后,我意识到我正在使用以下方法添加 TreeView:

  self.scrolled_window.add_with_viewport(some_treeview)

出于某种原因,scrolled_window.remove() 函数不喜欢视口。相反,应该使用:

  self.scrolled_window.add(some_treeview)

这可能是有道理的,因为文档指出:

如果一个小部件具有原生滚动能力,例如 Gtk.TextView、Gtk.TreeView 或 Gtk.IconView,则可以使用 Gtk.Container.add() 将其添加到 Gtk.ScrolledWindow。如果小部件没有,您必须先将小部件添加到 Gtk.Viewport,然后将视口添加到滚动窗口。

(来源:https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Viewport.html

所以因为 Gtk.TreeView 已经是可滚动的,所以它不应该进入单独的 Gtk.Viewport 小部件。

工作示例

我清理了上面的示例,它现在可以正常运行了:

林间空地文件

<?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">800</property>
    <property name="default_height">600</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="GtkBox" id="box2">
            <property name="visible">True</property>
            <property name="can_focus">False</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>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox" id="box3">
            <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">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Python 文件

#!/usr/bin/python3

from gi.repository import Gtk
import random

class LayerTreeView(Gtk.TreeView):
    def __init__(self, store):
        Gtk.TreeView.__init__(self, store)
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Name", renderer, text=0)
        self.append_column(column)

class DataTreeView(Gtk.TreeView):
    def __init__(self, store):
        Gtk.TreeView.__init__(self, 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 = 1

        #Initializing top TreeStore and TreeView
        #First object is the store, second is the view of the bottom data-view
        self.topstore = Gtk.TreeStore(str, object, object)
        self.topview = LayerTreeView(self.topstore)
        self.sw1.add(self.topview)

        #Adding some testdata
        for x in range(3):
            datastore = Gtk.TreeStore(str)
            dataview = DataTreeView(datastore)
            layername = "Layer {0}".format(self.layernumber)
            self.layernumber += 1
            self.topstore.append(None, [layername, datastore, dataview])

        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):
        datastore = Gtk.TreeStore(str)
        dataview = DataTreeView(datastore)
        layername = "Layer {0}".format(self.layernumber)
        self.layernumber += 1
        self.topstore.append(None, [layername, datastore, dataview])

    def on_add_data_clicked(self, widget):
        self.selection = self.topview.get_selection()
        model, treeiter = self.selection.get_selected()
        if treeiter != None:
            data_object = model[treeiter][1]
            data_object.append(None, [str(random.random())])

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

    def on_tree_selection_changed(self, selection):
        model, treeiter = selection.get_selected()
        if treeiter != None:
            treeview_object = model[treeiter][2]
            child = self.sw2.get_child()
            if child != None:
                self.sw2.remove(self.sw2.get_child())
            self.sw2.add(treeview_object)
            self.main_window.show_all()

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

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

if __name__ == "__main__":
     main()

【讨论】:

  • "如果一个小部件具有原生滚动能力,例如 Gtk.TextView、Gtk.TreeView 或 Gtk.IconView,则可以使用 Gtk.Container.add() 将其添加到 Gtk.ScrolledWindow。如果小部件没有,您必须先将小部件添加到 Gtk.Viewport,然后将视口添加到滚动窗口。”简而言之,这是不真实的。 Gtk.TreeView 没有“本机滚动能力”。 Gtk+3 文档存在一个巨大的问题,特别是(但不限于)Gtk.ScrolledWindow。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
相关资源
最近更新 更多