2010-07-02 23 views
17

Estoy buscando mostrar un gran conjunto de datos a través de una vista de lista en GTK # y el rendimiento es un problema aquí. Actualmente estoy usando un TreeView respaldado con un ListStore, pero agregar todos mis datos al ListStore dura para siempre. ¿Existe algún tipo de widget de vista de lista en GTK que admita la carga de datos de forma diferida? En Winforms, puede usar la propiedad VirtualMode de DataGridView para manejar esto, pero no veo nada por el estilo de GTK.Vista de lista de carga lenta en GTK #

+0

¿Está su 'ListStore' ordenado? – doublep

+0

Además, no necesita un widget diferente, sino una implementación diferente de list store. No sé nada sobre GTK #, pero si le apetece escribir código usted mismo, probablemente pueda adaptar [Py-gtktree] (https://launchpad.net/py-gtktree) (Python). – doublep

+0

Los datos que entran en el ListStore están ordenados (por dirección, es una vista del editor hexadecimal), pero no estoy haciendo ninguna clasificación en la lista. Mirando Py-gtktree ahora. Gracias. –

Respuesta

0

Quizás pueda agregar los datos usando un hilo diferente, para que la aplicación actual no se "congele", sino que siga funcionando. Probablemente aún tome la misma cantidad de tiempo, pero al menos el usuario puede trabajar con el resto de la aplicación en el tiempo medio.

2

Hace una gran diferencia si inserta los datos en el modelo mientras está conectado a la vista, o inserta todos los datos mientras está "fuera de línea", y solo lo conecta a la vista cuando termina. El último es mucho más rápido, porque de lo contrario TreeView tiene que regenerar sus estructuras internas todo el tiempo.

Si la inserción no es el problema principal, pero recuperar los datos de su fuente de datos es la parte realmente lenta, entonces lo que ayuda mucho es si puede recuperar la cantidad de filas para el modelo rápidamente. Si ese es el caso, sugiero crear primero una lista de distribución con todas las filas asignadas con contenido vacío que puede enganchar a la vista y luego completar el contenido real desde una línea de retorno o subproceso inactivo. Lamentablemente, no hay una API de proceso por lotes para actualizar el modelo, por lo que varias filas podrían actualizarse a la vez.

9

Sin embargo, hasta donde yo sé, no hay ningún widget para hacer lo que desee en Gtk, sin embargo, puede hacer algo similar, en el resultado final, a la propiedad VirtualMode, en el TreeView.

El problema con el control TreeView es que recuperará todos los datos de su modelo con antelación. Si no fuera por esto, sugeriría un enfoque modelo único para este problema, pero lamentablemente el TreeView es codicioso a la hora de buscar los datos, por lo que es necesario controlar cuándo se cargan los datos de la vista, de lo contrario, ¿cómo más va? poder saber cuándo una fila es visible y así informar al modelo, o un proxy para obtener los datos de la fila a medida que se vuelve visible.

Se necesitan 3 cosas para conseguir que esto funcione

1) un modelo para su uso en la vista de árbol, que inicialmente tiene todas las filas, pero no existen datos en cualquiera de los campos
2) una manera de ir a buscar la datos de cualquier base de datos que use
3) un medio para determinar qué filas recuperar datos para

Los primeros dos elementos se pueden hacer en el nivel del modelo. La determinación de qué filas buscar requiere el widget Treeview y una forma de determinar qué filas se muestran. El método que uso a continuación no es óptimo, pero funciona, y puede ser arreglado y/o adaptado para cualquier uso que tenga en mente.

Estoy utilizando una clase de proxy para almacenar en el modelo y se utiliza para obtener datos específicos de esa fila. En mi ejemplo, se llama ProxyClass. Busca y retiene los datos de una fila, que inicialmente es nula. En este caso, el método Fetch solo crea y devuelve una cadena "algunos datos" + id

Esto se mantendría dentro de una instancia de MyNode, que hereda de TreeNode, que representa una fila de datos. la primera columna devuelve los datos contenidos en el proxy y la segunda columna, que nunca se muestra, contiene la instancia de la clase proxy.

Luego crea su NodeStore, su modelo, llenándola con instancias de MyNode (id) como se puede ver a continuación en el ejemplo.

El control sobre cuándo se cargan los datos se controla desde CellDataFunc. Este método es la clave para hacer que esto funcione. El CellDataFunc es responsable de establecer el texto en el CellRendererText para una columna específica en una fila identificada por el iterador que se le pasa. Se invoca cada vez que la vista de árbol revela una fila y solo para la fila recién revelada. Por lo tanto, solo obtendrá datos de las celdas que se representan en la pantalla. Esto le proporciona un medio para controlar cuándo se obtienen los datos, y así obtenerlos solo cuando los necesite.

Puede hacer que TreeView use el CellDataFunc para cargar sus datos según sea necesario al aplicarlo a una de las columnas con TreeViewColumn.SetCellDataFunc. Solo necesita hacer esto en una columna, ya que puede recuperar los datos de toda la fila.

Para detener todas las filas visibles menos que se obtengan sus datos, se puede hacer comprobando si la celda actual está en el rango visible o no. Para hacerlo, llame a TreeView.GetVisibleRange (out start, out end) y luego vea si el iterador actual transferido a esta función se encuentra dentro del rango inicial y final, que son objetos TreePath, por lo que primero deben cambiarse a TreeIters. Model.GetIter (fuera iter_start, inicio). Luego verifique si iter.UserData.ToInt32()> = iter_start.UserData.ToInt32() y menos que iter_end. Si el iter actual cae en el rango de iter_start a iter_end, entonces busca los datos, de lo contrario déjalo.

Aquí está mi ejemplo.

El ProxyClass ejemplo TreeNode

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

La costumbre

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

La ventana que incluye la ventana de desplazamiento, y TreeView. Aquí también se define CellDataFunc, aunque podría ubicarse en cualquier lugar.

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


} 

Espero que es lo que su después.

Consulte la documentación c para obtener una mejor explicación de lo que hacen cada uno de los métodos y sus parámetros. Los documentos de Mono dejan mucho que desear.

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

Alternativamente, se puede ver en la implementación de su propia Gtk.TreeModelImplementor como se describe en el Implementing GInterfaces el sitio web de Mono Project. Puede ver un ejemplo here.

Debería ser bastante trivial hacer una implementación así de "floja".