2010-07-02 48 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

使這樣的實現「懶」應該是相當微不足道的。