Re: TreeView speedup





On Mon, 2003-03-24 at 11:41, Andrew E. Makeev wrote:
2. Wrote our own simple TextRenderer with "cached" Pango::Layout where only text could be changed.
   Test case: 10000 rows with 4 string, 4 int, 4 double, 4 bool columns.
TreeView was filled with values in 3-4 x times faster. (8-9 sec against 30 sec).

Hi,

Could you post code for these improvements?
I just use CList, because of TreeView being so slow.
But I would like to use TreeView, and it would be easier for me to write
TextRenderer if I see something similar.

Well, here is test sample with simple text renderer (column size is not handled well though).

-andrew

#include <gtkmm.h>
#include <gtk/gtkliststore.h>

class CellRendererTextSimple : public Gtk::CellRenderer
{
public:
    CellRendererTextSimple ();
    virtual ~CellRendererTextSimple ()
    {
    }
    Glib::PropertyProxy<Glib::ustring> property_text();

protected:
    virtual void get_size_vfunc (Gtk::Widget& widget,
				 const Gdk::Rectangle* cell_area,
				 int* x_offset,
				 int* y_offset,
				 int* width,
				 int* height);

    virtual void render_vfunc (const Glib::RefPtr<Gdk::Window>& window,
			       Gtk::Widget& widget,
			       const Gdk::Rectangle& background_area,
			       const Gdk::Rectangle& cell_area,
			       const Gdk::Rectangle& expose_area,
			       Gtk::CellRendererState flags);
private:
    Glib::Property<Glib::ustring> property_text_;
    Glib::RefPtr< Pango::Layout > m_ptrLayout;
    bool m_needLayout;
};

CellRendererTextSimple::CellRendererTextSimple () :
Glib::ObjectBase  (typeid(CellRendererTextSimple)),
Gtk::CellRenderer (),
property_text_    (*this, "text", ""),
m_needLayout      (true)
{
    property_mode () = Gtk::CELL_RENDERER_MODE_INERT;
    property_xpad () = 2;
    property_ypad () = 2;
    property_xalign () = 0.0;
    property_yalign () = 0.5;
}

Glib::PropertyProxy<Glib::ustring> CellRendererTextSimple::property_text ()
{
    return property_text_.get_proxy();
}

void CellRendererTextSimple::get_size_vfunc
    (Gtk::Widget& widget,
     const Gdk::Rectangle* cell_area,
     int* x_offset,
     int* y_offset,
     int* width,
     int* height)
{
    if (m_needLayout)
    {
        m_ptrLayout = widget.create_pango_layout ("");
        m_needLayout = false;
    }

    Pango::Rectangle rect = m_ptrLayout->get_pixel_logical_extents ();

    if (width)
    {
	*width = property_xpad ()*2 + rect.get_width ();
    }
    if (height)
    {
	*height = property_ypad ()*2 + rect.get_height ();
    }
    if (cell_area)
    {
	if (x_offset)
	{
	    *x_offset = int (property_xalign ()*(cell_area->get_width ()
						 - rect.get_width ()
						 - 2*property_xpad ()));
	    *x_offset = std::max (*x_offset, 0);
	}
	if (y_offset)
	{
	    *y_offset = int (property_yalign ()*(cell_area->get_height ()
						 - rect.get_height ()
						 - 2*property_ypad ()));
	    *y_offset = std::max (*y_offset, 0);
	}
    }
}

void CellRendererTextSimple::render_vfunc
    (const Glib::RefPtr<Gdk::Window>& window,
     Gtk::Widget& widget,
     const Gdk::Rectangle& background_area,
     const Gdk::Rectangle& cell_area,
     const Gdk::Rectangle& expose_area,
     Gtk::CellRendererState flags)
{
    if (m_needLayout)
    {
        m_ptrLayout = widget.create_pango_layout ("");
        m_needLayout = false;
    }
    m_ptrLayout->set_text (property_text ());

    const unsigned int cell_xpad = property_xpad ();
    const unsigned int cell_ypad = property_ypad ();
    Gtk::StateType state;
    int x_offset = 0, y_offset = 0, width = 0, height = 0;

    get_size (widget, cell_area, x_offset, y_offset, width, height);

    if((flags & Gtk::CELL_RENDERER_SELECTED) != 0)
    {
        state = (widget.has_focus()) ? Gtk::STATE_SELECTED : Gtk::STATE_ACTIVE;
    }
    else
    {
        state = (widget.is_sensitive()) ?
            Gtk::STATE_NORMAL : Gtk::STATE_INSENSITIVE;
    }

    widget.get_style ()
        ->paint_layout (window,
                        state,
                        true,
                        cell_area,
                        widget,
                        "cellrenderertext",
                        cell_area.get_x () + x_offset + cell_xpad,
                        cell_area.get_y () + y_offset + cell_ypad,
                        m_ptrLayout);
}

class MyWindow : public Gtk::Window
{
    struct MyColumnModel : Gtk::TreeModel::ColumnRecord
    {
	Gtk::TreeModelColumn< int >  id;
	Gtk::TreeModelColumn< Glib::ustring >  label;
	Gtk::TreeModelColumn< double >  val;
	Gtk::TreeModelColumn< bool >  check;
	Gtk::TreeModelColumn< int >  id1;
	Gtk::TreeModelColumn< Glib::ustring >  label1;
	Gtk::TreeModelColumn< double >  val1;
	Gtk::TreeModelColumn< bool >  check1;
	Gtk::TreeModelColumn< int >  id2;
	Gtk::TreeModelColumn< Glib::ustring >  label2;
	Gtk::TreeModelColumn< double >  val2;
	Gtk::TreeModelColumn< bool >  check2;
	Gtk::TreeModelColumn< int >  id3;
	Gtk::TreeModelColumn< Glib::ustring >  label3;
	Gtk::TreeModelColumn< double >  val3;
	Gtk::TreeModelColumn< bool >  check3;
	MyColumnModel ()
	{
	    add (id); add (label); add (val); add (check);
	    add (id1); add (label1); add (val1); add (check1);
	    add (id2); add (label2); add (val2); add (check2);
	    add (id3); add (label3); add (val3); add (check3);
	}
    };
    MyColumnModel m_cols;
    Gtk::TreeView m_tree;
    Glib::RefPtr< Gtk::ListStore > m_refModel;

public:
    MyWindow ();
    void fill_tree ();
    void click_cb ();
    Glib::RefPtr< Gtk::ListStore > create_model ();
};

MyWindow::MyWindow ()
{
    set_default_size (-1, 500);
    Gtk::VBox *box = manage (new Gtk::VBox);
    add (*box);
    Gtk::ScrolledWindow *sw = manage (new Gtk::ScrolledWindow);
    sw->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

    box->add (*sw);
    m_refModel = create_model ();
    m_tree.set_model (m_refModel);

    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("ID",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.id);
    }
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("LABEL",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.label);
    }
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("VALUE",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.val);
    }
    m_tree.append_column ("CHECK", m_cols.check);
    
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("ID",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.id1);
    }
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("LABEL",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.label1);
    }
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("VALUE",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.val1);
    }
    m_tree.append_column ("CHECK", m_cols.check1);
    
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("ID",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.id2);
    }
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("LABEL",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.label2);
    }
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("VALUE",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.val2);
    }
    m_tree.append_column ("CHECK", m_cols.check2);
    
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("ID",
				     *Gtk::manage (render));
	  m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.id3);
	}
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("LABEL",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.label3);
	}
    {
	CellRendererTextSimple *render = new CellRendererTextSimple;
	Gtk::TreeViewColumn *col =
	    new Gtk::TreeViewColumn ("VALUE",
				     *Gtk::manage (render));
	m_tree.append_column (*Gtk::manage (col));
	col->add_attribute (render->property_text (), m_cols.val3);
    }
    m_tree.append_column ("CHECK", m_cols.check3);

    sw->add (m_tree);
    Gtk::Button *b = manage (new Gtk::Button("FILL"));
    box->pack_end (*b, Gtk::PACK_SHRINK);
    b->signal_clicked ().connect (SigC::slot (*this, &MyWindow::click_cb));
    show_all ();
}

void MyWindow::click_cb ()
{
    m_refModel->clear ();
    fill_tree ();
}

Glib::RefPtr< Gtk::ListStore > MyWindow::create_model ()
{
    Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create (m_cols);
    return store;
}

void MyWindow::fill_tree ()
{
    for (int i = 0; i < 10000; i++)
    {
	Gtk::TreeIter iter = m_refModel->append ();
	(*iter)[m_cols.id] = i;
	(*iter)[m_cols.label] = "LABEL";
	(*iter)[m_cols.val] = i/3.0;
	(*iter)[m_cols.check] = i%2;
	(*iter)[m_cols.id1] = i;
	(*iter)[m_cols.label1] = "LABEL";
	(*iter)[m_cols.val1] = i/3.0;
	(*iter)[m_cols.check1] = i%2;
	(*iter)[m_cols.id2] = i;
	(*iter)[m_cols.label2] = "LABEL";
	(*iter)[m_cols.val2] = i/3.0;
	(*iter)[m_cols.check2] = i%2;
	(*iter)[m_cols.id3] = i;
	(*iter)[m_cols.label3] = "LABEL";
	(*iter)[m_cols.val3] = i/3.0;
	(*iter)[m_cols.check3] = i%2;
    }
}

int main (int argc, char *argv[])
{
    Gtk::Main kit (&argc, &argv);
    MyWindow app;
    kit.run (app);
    return 0;
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]