2010-07-02 15 views
17

Szukam wyświetlić duży zestaw danych za pośrednictwem widoku listy w GTK # i wydajność jest tutaj problemem. Obecnie używam TreeView wspieranego z ListStore, ale dodawanie wszystkich moich danych do ListStore trwa wieczność. Czy istnieje jakiś widżet widoku listy w GTK, który obsługuje leniwy ładowanie danych? W WinFormach można użyć właściwości VirtualMode obiektu DataGridView, aby obsłużyć to, ale nie widzę niczego podobnego do GTK.Lazy załadowany widok listy w GTK #

+0

Czy Twój "ListStore" jest posortowany? – doublep

+0

Nie potrzebujesz też innego widgetu, a raczej innej implementacji magazynu list. Nie wiem nic o GTK #, ale jeśli masz ochotę samemu napisać kod, prawdopodobnie mógłbyś zaadaptować [Py-gtktree] (https://launchpad.net/py-gtktree) (Python). – doublep

+0

Dane przechodzące do magazynu listStore są sortowane (według adresu - jest to widok edytora szesnastkowego), ale nie wykonuję żadnego sortowania na samej liście. Zajrzyj teraz do Py-gtktree. Dzięki. –

Odpowiedz

0

Być może możesz dodać dane za pomocą innego wątku, dzięki czemu bieżąca aplikacja nie "zamrozi", ale po prostu pozostanie uruchomiona. Prawdopodobnie zajmie to tyle samo czasu, ale przynajmniej użytkownik może pracować z resztą aplikacji w średnim czasie.

2

Wprowadza dużą różnicę, jeśli wstawia się dane do modelu, gdy jest podpięty do widoku, lub wstawia wszystkie dane, gdy jest "offline" i łączy je z widokiem dopiero po zakończeniu. Później jest on dużo szybszy, ponieważ w przeciwnym razie drzewko musi regenerować wewnętrzne struktury przez cały czas.

Jeśli wstawianie nie jest głównym problemem, ale pobieranie danych ze źródła danych jest rzeczywistą wolną częścią, to bardzo pomaga, jeśli można szybko pobrać liczbę wierszy dla modelu. Jeśli tak jest, to proponuję najpierw utworzyć magazyn listowy z wszystkimi wierszami przydzielonymi z pustą zawartością, które można podłączyć do widoku, a następnie wypełnić rzeczywistą zawartość z połączenia zwrotnego lub wątku. Niestety, nie ma apletu-pakietu, który aktualizowałby model, dzięki czemu kilka wierszy mogłoby zostać zaktualizowanych jednocześnie.

9

Tam, o ile mi wiadomo, nie ma widżetu do zrobienia tego, co chcesz w Gtk, jednak możesz zrobić coś podobnego, w wyniku końcowym, do właściwości VirtualMode, w TreeView.

Problem z kontrolką TreeView polega na tym, że wcześniej pobierze ona wszystkie dane z tego modelu. Gdyby nie było to, sugerowałbym podejście tylko do tego modelu, ale niestety TreeView jest chciwy, jeśli chodzi o pobieranie danych, więc kontrolowanie kiedy dane są ładowane z widoku jest potrzebne, inaczej jak to się dzieje aby móc stwierdzić, kiedy wiersz jest widoczny, a tym samym poinformować model lub proxy, aby pobrać dane dla wiersza, gdy stanie się on widoczny.

Trzeba 3 rzeczy, aby to zadziałało

1) model do wykorzystania w widoku drzewa, który początkowo miał wszystkie wiersze, ale nie ma danych w każdym z pól
2) sposób pobierania dane z dowolnej używanej bazy danych:
3) sposób określania, które wiersze do pobrania danych dla

Pierwsze dwie pozycje można wykonać na poziomie modelu. Określanie, które wiersze do pobrania wymaga widżetu Widok drzewa i sposobu określania, które wiersze są wyświetlane. Metoda, której używam poniżej, nie jest optymalna, ale działa i może być uporządkowana i/lub przystosowana do dowolnego wykorzystania, o którym myślisz.

Używam klasy proxy do przechowywania w modelu i jest używany do pobierania danych specyficznych dla tego wiersza. W moim przykładzie nazywa się ProxyClass. Pobiera i przechowuje dane dla wiersza, który jest początkowo pusty. W takim przypadku metoda Fetch właśnie tworzy i zwraca ciąg "niektóre dane" + identyfikator

Zostanie to zatrzymane wewnątrz instancji MyNode, która dziedziczy po TreeNode, reprezentując wiersz danych. pierwsza kolumna zwraca dane przechowywane w proxy, a druga kolumna, która nigdy nie jest wyświetlana, zawiera instancję klasy proxy.

Następnie należy utworzyć NodeStore, swój model, wypełniając go instancjami MyNode (id), jak widać poniżej w przykładzie.

Kontrola nad ładowaniem danych jest sterowana z CellDataFunc. Ta metoda jest kluczem do tego, aby to zadziałało. CellDataFunc jest odpowiedzialny za ustawienie tekstu w CellRendererText dla konkretnej kolumny w wierszu identyfikowanym przez iterator przekazany do niej. Nazywa się to za każdym razem, gdy widok drzewa pokazuje wiersz i tylko dla nowo odkrytego rzędu. W ten sposób pobierane będą tylko dane dla komórek renderowanych na ekranie. Daje to możliwość kontrolowania, kiedy dane są pobierane, a więc pobieranie go tylko wtedy, gdy jest to potrzebne.

Możesz sprawić, że TreeView użyje CellDataFunc do załadowania danych w razie potrzeby, stosując go do jednej z kolumn za pomocą TreeViewColumn.SetCellDataFunc. Trzeba to zrobić tylko w jednej kolumnie, ponieważ może pobrać dane dla całego wiersza.

Aby zatrzymać wszystkie widoczne wiersze oprócz widocznych po pobraniu danych, można to sprawdzić, sprawdzając, czy bieżąca komórka znajduje się w widocznym zakresie, czy nie. Aby to zrobić, wywołaj TreeView.GetVisibleRange (out start, out end), a następnie sprawdź, czy bieżący iterator przekazany do tej funkcji znajduje się w zakresie początkowym i końcowym, które są obiektami TreePath, więc muszą zostać najpierw zmienione w TreeIters. Model.GetIter (out iter_start, start). Następnie sprawdź, czy iter.UserData.ToInt32()> = iter_start.UserData.ToInt32() i mniej niż iter_end. Jeśli aktualny iter mieści się w zakresie od iter_start do iter_end, wówczas pobierz dane, w przeciwnym razie pozostaw je.

Oto mój przykład.

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;} 
     } 
    } 
} 

Zwyczaj przykład TreeNode

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; } 
     } 
    } 
} 

okno, które zawiera przewijane okna i TreeView. Tutaj także zdefiniowano CellDataFunc, choć można go umieścić w dowolnym miejscu.

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(); 
     } 
    } 


} 

Mam nadzieję, że to jest co po.

Zobacz dokumentację c, aby uzyskać dokładniejsze wyjaśnienie, co każda z metod i ich parametry mają. Dokumenty Mono pozostawiają wiele do życzenia.

SetCellDataFunc (C docs) 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

Alternatywnie, można spojrzeć na realizację własnego Gtk.TreeModelImplementor jak opisano w Implementing GInterfaces na strona internetowa Mono Project. Możesz zobaczyć jeden przykład: here.

Powinno być dość banalnie, aby takie wdrożenie było "leniwe".