4

我想派生自己的小部件类并将标准小部件添加到此类以创建复合小部件。有没有人有关于如何做到这一点的例子或建议?例如,假设我想创建 4 个按钮的自定义复合小部件。我猜它类似于下面的代码:

//First Question:  Is this the best way to create composite widget? (see below)

//Second Question:  How do you make a widget container expand in 
// the horizontal direction while at the same time shrink in 
// the vertical direction?  because i wanted the boxes to expand horizontally
// to fill the window, and at the same time shrink to minimum width in the vertical
// direction


#include <iostream>

using namespace std;
#include <gtkmm.h>

class MyWidget : public Gtk::Frame {
  public:
    MyWidget() {
        add(m_hbox1);
        m_hbox1.pack_start  (m_vbox1,    Gtk::PackOptions::PACK_SHRINK);
        m_vbox1.pack_start  (m_hbox2,    Gtk::PackOptions::PACK_EXPAND_WIDGET);
        m_hbox2.pack_start  (m_btn_fwd,  Gtk::PackOptions::PACK_EXPAND_WIDGET);
        m_hbox2.pack_start  (m_btn_play, Gtk::PackOptions::PACK_EXPAND_WIDGET);
        m_hbox2.pack_start  (m_btn_stop, Gtk::PackOptions::PACK_EXPAND_WIDGET);
        m_hbox2.pack_start  (m_btn_back, Gtk::PackOptions::PACK_EXPAND_WIDGET);

        show_all_children();
    }
    ~MyWidget() {
    }

  private:

    Gtk::Box    m_vbox1    {Gtk::ORIENTATION_VERTICAL};
    Gtk::Box    m_hbox1    {Gtk::ORIENTATION_HORIZONTAL};
    Gtk::Box    m_hbox2    {Gtk::ORIENTATION_HORIZONTAL};
    Gtk::Button m_btn_fwd  {"Fwd"};
    Gtk::Button m_btn_back {"Back"};
    Gtk::Button m_btn_play {"Play"};
    Gtk::Button m_btn_stop {"Stop"};
};

class MyWindow : public Gtk::Window {
  public:
    MyWindow(string name) {
       set_title(name);
       add(m_vbox);

       // Shrink in Vertical Direction
       m_vbox.pack_start(m_mywidget, Gtk::PackOptions::PACK_SHRINK);

       show_all_children();
    }

  private:
    Gtk::Box    m_vbox      {Gtk::ORIENTATION_VERTICAL};
    MyWidget    m_mywidget;
};

int main(int argc, char *argv[])
{
  auto app = Gtk::Application::create(argc, argv,
     "org.gtkmm.example.actionbar");

  MyWindow window {"Testing Custom Composite Widget"};

  // Shows the window and returns when it is closed.
  return app->run(window);
}
4

2 回答 2

3

经过几周的实验,我的结论是最好避免使用 c++ 从 Gtk::Widget 继承以创建复合小部件。相反,最好将 gktmm 中的复合小部件作为纯容器类,即。不是从任何类派生的,并重载 C++11 Functor 运算符以返回一个 Gtk::Box 小部件,该小部件由对象的构造函数预先打包,其中包含制作复合组件所需的所有小部件。例子:

using namespace std;
#include <gtkmm.h>
#include <iostream>

//======================================================
// SearchBar: An Example GTKMM Composite Widget / wmoore
//======================================================
class SearchBar {
  public:
     SearchBar();
     Gtk::Widget& operator()();

  public:
     Gtk::Box    box {Gtk::ORIENTATION_HORIZONTAL};
     Gtk::Label  label {"search: "};
     Gtk::Entry  entry;
     Gtk::Button BtnOk{"find"};
     Gtk::Button BtnNext{">"};
     Gtk::Button BtnPrev{"<"};
};

inline SearchBar::SearchBar() {
  box.pack_start(label);
  box.pack_start(entry, Gtk::PACK_EXPAND_WIDGET);
  box.pack_end(BtnNext);
  box.pack_end(BtnPrev);
  box.pack_end(BtnOk);
}

inline Gtk::Widget& SearchBar::operator()() {
  return box;
}

class MyWindow : public Gtk::Window {
  public:
    MyWindow(string name) {
       set_title(name);
       add(m_vbox);

       // Shrink in Vertical Direction
       m_vbox.pack_start(m_searchbar(), Gtk::PackOptions::PACK_SHRINK);  
                        // ^^^ NOTE use of C++11 functor operator "()"
                        // added to end of object name
                        // that makes it easy to tell difference between 
                        // Gtk::Widget and Composite widget's built 
                       // from many Gtk::Widget's

      ////Example Connecting of Signals to composite widget:
      // m_searchbar.BtnOk.signal_clicked.connect([]() {
      //     cout << "clicked button!\n";})

       show_all_children();
    }

  private:
    Gtk::Box    m_vbox      {Gtk::ORIENTATION_VERTICAL};
    SearchBar   m_searchbar;
};

int main(int argc, char *argv[])
{
  auto app = Gtk::Application::create(argc, argv,
     "org.gtkmm.example.actionbar");

  MyWindow window {"Testing Custom Composite Widget"};

  // Shows the window and returns when it is closed.
  return app->run(window);
}

使用纯容器类而不是从 Gtk::Widget 继承来创建复合小部件的原因如下:

(1) 用于 gtkmm 的 Widget api 非常多,并且倾向于将您添加到复合小部件的任何新公共方法隐藏在您可能不会使用的 gtkmm 小部件方法的混乱中。为此原因。最好将所有容器小部件对象添加到该类的公共部分,包括顶级框小部件。您仍然可以访问每个对象小部件的所有方法,而不会出现 gtkmm api 混乱。

(2) 从 Gtk::Widget 继承你的类在多态行为方面并没有真正给你带来任何有用的东西,因为复合小部件的小部件已经是从 Gtk::Widget 派生的多态对象。当您将顶级小部件框添加到父小部件时,复合小部件的所有子小部件(在框下)都将添加到父小部件的子列表中。因此,实际上没有必要为复合小部件容器提供多态行为。

(3) 如果您需要从 Gtk:Widget 派生的复合小部件,可以轻松地在其周围放置一个类包装器,将其转换回从 Gtk::Widget 派生的类。事实上,这一步很容易通过一个模板类包装器的单行实例化来完成。我能想到这样做的唯一原因是将组件重新插入到 glade 中(缺少 Glade Gui 将 C++11 仿函数运算符识别为返回复合小部件的顶级小部件的标准方法(glade 可能支持未来?愿望清单...))

于 2017-01-22T13:53:51.310 回答
0

实际上可以从gtkmm中的 Gtk::Widget 派生自定义 C++ 小部件,尽管该库需要相当多的额外代码,不仅用于合成复合对象,而且为了遵守 Gtk::Widget 接口规范。

我指的是一组应该在你的实现中被覆盖的虚拟方法,特别是:

  • get_request_mode_vfunc():(可选)返回小部件首选的 Gtk::SizeRequestMode。
  • get_preferred_width_vfunc():计算小部件的最小和自然宽度。
  • get_preferred_height_vfunc():计算小部件的最小和自然
    高度。
  • get_preferred_width_for_height_vfunc():计算小部件的最小和自然宽度,如果它被赋予指定的高度。
  • get_preferred_height_for_width_vfunc():计算小部件的最小和自然高度,如果它被赋予指定的宽度。
  • on_size_allocate():根据实际给出的高度和宽度定位小部件。
  • on_realize():将 Gdk::Window 与小部件相关联。
  • on_unrealize():(可选)打破与 Gdk::Window 的关联。
  • on_map():(可选)
  • on_unmap():(可选)
  • on_draw() : 在提供的 Cairo::Context 上绘图。

gtkmm 文档在 gtkmm-tutorial 部分中有一个可靠的示例。

于 2020-10-20T10:24:19.477 回答