Gtkmm:如何将静态列标题添加到Gtk :: ListBox

时间:2018-08-18 15:57:40

标签: c++ gtk gtkmm

我的应用程序中有一个Gtk::ListBox,当前包含Gtk::ListBoxRow个对象,这些对象本身拥有:

  1. 一个Gtk::Entry(也称为编辑框)。
  2. 一个Gtk::ColorButton

小部件的当前视觉布局如下:

-------------------------
| Entry1 | ColorButton1 |
-------------------------
| Entry2 | ColorButton2 |
-------------------------
...

我想在两个列中都添加一个 static 标头(在执行期间不需要更改)。像这样:

-------------------------
| Title1 |    Title2    | <-- Notice how the headers are aligned to the columns below.
-------------------------
| Entry1 | ColorButton1 |
-------------------------
| Entry2 | ColorButton2 |
-------------------------
...

其中Title1Title2只是某种标签。我没有找到任何在线操作示例,关于Gtk::ListBox es,文档(我认为)不够清晰。

我该怎么做?

P.S。我也将接受用C或Python给出的答案,或者在线获得一个清晰示例的链接。

1 个答案:

答案 0 :(得分:1)

我有同样的问题。我使用vala弄清楚了这一点,并且我确信可以使用c ++轻松实现相同的方法,因为此处使用的所有oop功能也可以在c ++中使用:

ListBoxRow可以具有标题,因此我们可以在第一行添加标题。定义一个从ListBoxRow派生的类,并在其中填充行小部件。

public class ListBoxRowWithData: ListBoxRow{

    public int id;
    public int email;
    public int name;

    public Gtk.Label label_id;
    public Gtk.Entry entry_email;
    public Gtk.Entry entry_name;

    // these static variables will hold widths of your widgets in your row
    public static int id_width;
    public static int em_width;
    public static int nm_width;
    ...

    public ListBoxRowWithData (int _id, string _email, string _name) {
        id = _id; // you can also use an initializer list in c++
        email = _email;
        name = _name;

        //create your widgets
        label_id = new Gtk.Label(id.to_string());
        entry_email = new Gtk.Entry(); entry_email.set_text(email);
        entry_name = new Gtk.Entry(); entry_name.set_text(name);

        // pack your widgets in a box
        Gtk.Box rbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
        rbox.pack_start (label_id, true, true, 0); // trues and falses must match with headers' trues and falses
        rbox.pack_start (entry_email, true, true, 0);
        rbox.pack_start (entry_name, true, true, 0);
        ...
        this.add(rbox);
    }
    // using below method, we will get widths of the first row, and set them
    // as widths of the header labels.

    public void init_widths(){
        // in vala and c++ static ints are automatically set to 0 when you declare them
        if(id_width == 0){
            id_width = label_id.get_allocated_width();
            em_width = entry_email.get_allocated_width();
            nm_width = entry_name.get_allocated_width();
        }
    }
}

并在包含列表框的窗口类中定义以下方法:

        public void align_my_header(){
        // listbox has no built-in something like a table header
        // my solution is to set a row header to the first row,
        // by updating title sizes based on row contents.

        // an arrow function is used here. you can use class methods.
        listbox.set_header_func((_row, _before)=>{
            // Dynamic Type Casting from base class to derived class
            var row = _row as ListBoxRowWithData;

            // create your "fake" header here.
            var fake_header = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);

            // those below are header labels
            var id = new Gtk.Label("id");
            var em = new Gtk.Label("email");
            var nm = new Gtk.Label("name");

            fake_header.pack_start (id, true, true, 0); // trues and falses must match with row widgets' trues and falses
            fake_header.pack_start (em, true, true, 0);
            fake_header.pack_start (nm, true, true, 0);

            //check if the row is the first one (invariant to sort operations which is nice), we don't want other rows having headers
            if(row.get_index()==0){
                            // When realize signal is emmitted, widgets allocates their widths already
                fake_header.realize.connect(()=>{ 
                    // we get widgets' widths, and set them as header widths here
                    row.init_widths();
                    id.set_size_request(ListBoxRowWithData.id_width, -1);// access static varibles here
                    em.set_size_request(ListBoxRowWithData.em_width, -1);
                    nm.set_size_request(ListBoxRowWithData.nm_width, -1);

                });

                row.set_header(fake_header);
                fake_header.show_all();

            }else{
                row.set_header(null);
            }

        });
    }
// populate your listbox in your window class where you need, in a loop for instance:
listbox.add (new ListBoxRowWithData(id, email, name));

最后在创建列表框后在窗口类的构造函数中调用align_my_header()方法。

完整的实现可以在这里看到:https://gitlab.com/aferust/sqlitewtview/blob/master/src/window2.vala https://gitlab.com/aferust/sqlitewtview/blob/master/win_res/sshot2.png