2010-07-02 42 views
17

我正在寻找通过GTK#中的列表视图显示大型数据集,并且性能是一个问题。我目前正在使用一个支持ListStore的TreeView,但将所有数据添加到ListStore需要永久。在GTK中是否有一个支持延迟加载数据的列表视图窗口小部件?在Winforms中,可以使用DataGridView的VirtualMode属性来处理这个问题,但我没有看到GTK的排序。GTK惰性加载列表视图#

+0

你的'ListStore'排序了吗? – doublep 2010-07-02 16:51:32

+0

此外,您不需要一个不同的小部件,而是一个不同的列表存储实现。我对GTK#一无所知,但如果您想自己编写代码,您可能可以调整[Py-gtktree](https://launchpad.net/py-gtktree)(Python)。 – doublep 2010-07-02 16:53:38

+0

进入ListStore的数据进行排序(通过地址 - 它是一个十六进制编辑器视图),但我没有在列表本身进行任何排序。现在看看Py-gtktree。谢谢。 – 2010-07-02 22:49:16

回答

0

也许你可以使用不同的线程添加数据,以便当前的应用程序不会“冻结”,而是继续运行。它可能仍然需要相同的时间,但至少用户可以在平时与其他应用程序一起工作。

2

如果您在将数据连接到视图时将数据插入到模型中,或者在“脱机”时插入所有数据,并且只有在完成后才将其连接到视图,它会产生巨大的差异。后者是很多更快,因为否则树视图必须始终重新生成它的内部结构。

如果插入不是主要问题,但从数据源获取数据是实际的慢速部分,那么如果您可以快速检索模型的行数,那会有多大帮助。如果是这样的话,那么我会建议首先创建一个liststore,其中的所有行都分配有空白内容,您可以挂钩到视图,然后从空闲回调或线程中填充实际内容。不幸的是,没有用于更新模型的批处理API,因此可以一次更新多行。

9

在那里,据我所知,没有小工具可以在Gtk中做你想做的事情,但是,你可以在TreeView中做类似的工作,最终的结果是VirtualMode属性。

TreeView控件的问题是它会提前从它的模型中提取所有数据。如果不是这样,那么我会建议一个模型只能解决这个问题,但不幸的是,TreeView在获取数据时非常贪婪,所以需要控制何时从视图加载数据,否则还会如何呢?以便能够知道行何时可见并因此通知模型或代理在行可见时获取数据。

你需要三样东西来得到这个工作

1)模型在TreeView,最初具有的所有行使用,但在任何领域
2)取的的方式没有数据来自您使用的任何数据库的数据
3)确定哪些行要获取数据的方法

前两项可以在模型级完成。确定要提取哪些行需要Treeview小部件以及确定显示哪些行的方法。我在下面使用的方法不是最优的,但它确实有效,并且可以根据您的想法进行整理和/或调整。

我正在使用代理类存储在模型中,并用于获取特定于该行的数据。在我的例子中,它被称为ProxyClass。它提取并保存一行最初为空的数据。在这种情况下,Fetch方法只创建并返回一个字符串“some data”+ id

这将保存在MyNode的一个实例中,该实例从TreeNode继承,表示一行数据。第一列返回代理中保存的数据,而第二列(从未显示)保存代理类实例。

然后,您可以创建NodeStore和您的模型,并使用MyNode(id)的实例来填充它,如下面的示例中所示。

数据加载时的控制由CellDataFunc控制。这种方法是使其发挥作用的关键。 CellDataFunc负责将CellRendererText中的文本设置为由传递给它的迭代器标识的行中的特定列。每当treeview显示一行并仅显示新显示的行时,都会调用它。因此只会获取显示器中呈现的单元格的数据。这为您提供了一种控制何时获取数据的手段,因此只在需要时才提取数据。

通过将TreeViewColumn.SetCellDataFunc应用到其中一列,可以使TreeView使用CellDataFunc根据需要加载数据。您只需要在一列上执行此操作,因为它可以获取整行的数据。

要停止除了可见行之外的所有行,以使其获取数据,可以通过检查当前单元格是否在可见范围内来完成。为此,可以调用TreeView.GetVisibleRange(out start,out end),然后查看传递给此函数的当前迭代器是否在开始和结束范围内,它们是TreePath对象,因此需要先将它们更改为TreeIters。 Model.GetIter(out iter_start,start)。然后检查iter.UserData.ToInt32()> = iter_start.UserData.ToInt32()是否小于iter_end。如果当前iter落在从iter_start到iter_end的范围内,则获取数据,否则保持不变。

这里是我的榜样。

的ProxyClass

namespace LazyTree 
{ 

    public class ProxyClass 
    { 
     int id; 
     string data; 

     public ProxyClass (int id) 
     { 
     this.id = id; 
     data = null; 
     } 


     public void Fetch() 
     { 
     data = "some data " + id; 
     } 


     public int Id 
     { 
     get { return id; } 
     } 

     public string Data 
     { 
     get {return data;} 
     } 
    } 
} 

定制树节点实例

namespace LazyTree 
{ 
    [Gtk.TreeNode (ListOnly=true)] 
    public class MyNode : Gtk.TreeNode 
    { 
     protected ProxyClass proxy; 

     public MyNode (int id) 
     { 
      proxy = new ProxyClass(id); 
     } 

     [Gtk.TreeNodeValue (Column=1)] 
     public ProxyClass Proxy 
     { 
      get {return proxy;} 
     } 

     [Gtk.TreeNodeValue (Column=0)] 
     public string Data 
     { 
      get { return proxy.Data; } 
     } 
    } 
} 

其包括滚动窗口,和树视图的窗口。这也是CellDataFunc定义的地方,尽管可以放在任何地方。

namespace LazyTree 
{ 

    public class MyWindow : Gtk.Window 
    { 
     int NUMBER_COLUMNS = 10000; 
     Gtk.NodeStore store; 
     Gtk.NodeStore Store { 
      get { 
       if (store == null) { 
        store = new Gtk.NodeStore (typeof (MyNode)); 
        for(int i = 0; i < NUMBER_COLUMNS; i++) 
        { 
         store.AddNode (new MyNode (i)); 
        } 
       } 
       return store; 
      } 
     } 


     protected void CellDataFunc(Gtk.TreeViewColumn column, 
            Gtk.CellRenderer cell, 
            Gtk.TreeModel model, 
            Gtk.TreeIter iter) 
     { 
      try { 
       string data = (string)model.GetValue(iter, 0); 
       ProxyClass proxy = (ProxyClass)model.GetValue(iter, 1); 
       Gtk.TreeView view = (Gtk.TreeView)column.TreeView; 
       Gtk.TreePath start, end; 
       bool go = view.GetVisibleRange(out start,out end); 
       Gtk.TreeIter iter_start, iter_end; 
       if(go) 
       { 
        model.GetIter(out iter_start, start); 
        model.GetIter(out iter_end, end); 
       } 
       if (go && 
        data == null && 
        iter.UserData.ToInt32() >= iter_start.UserData.ToInt32() && 
        iter.UserData.ToInt32() <= iter_end.UserData.ToInt32()) 
       { 
        Console.WriteLine("Lazy Loading " + proxy.Id + ", Visible: " + cell.Visible); 
        proxy.Fetch(); 
       } 

       ((Gtk.CellRendererText)cell).Text = data; 
      } catch(Exception e) { 
       Console.WriteLine("error: " + e); 
      } 
     } 


     public MyWindow() : base("Lazy Tree") 
     { 
      Gtk.NodeView view = new Gtk.NodeView(Store); 

      Gtk.ScrolledWindow scroll = new Gtk.ScrolledWindow(); 
      scroll.Add(view); 
      Add(scroll); 
      Gtk.CellRendererText cell = new Gtk.CellRendererText(); 
      view.AppendColumn ("Lazy Data", cell, "text", 0); 

      Gtk.TreeViewColumn column = view.GetColumn(0); 

      column.SetCellDataFunc(cell, CellDataFunc); 
     } 


     protected override bool OnDeleteEvent (Gdk.Event ev) 
     { 
      Gtk.Application.Quit(); 
      return true; 
     } 

     public static void Main() 
     { 
      Gtk.Application.Init(); 
       MyWindow win = new MyWindow(); 
      win.SetDefaultSize(200, 200); 
        win.ShowAll(); 
      Gtk.Application.Run(); 
     } 
    } 


} 

希望这是你的后。

请参阅c文档以更好地解释每种方法及其参数的作用。 Mono文档留下了许多不足之处。

SetCellDataFunc(C文档) http://developer.gnome.org/gtk/stable/GtkTreeViewColumn.html#gtk-tree-view-column-set-cell-data-func

(CeCellDataFunc) http://developer.gnome.org/gtk/stable/GtkTreeViewColumn.html#GtkTreeCellDataFunc

(DestroyFunc) http://developer.gnome.org/glib/unstable/glib-Datasets.html#GDestroyNotify

0

或者,你可以看看作为在Implementing GInterfaces描述实现自己的Gtk.TreeModelImplementor Mono项目网站。你可以看到一个例子here

使这样的实现“懒”应该是相当微不足道的。