GTKMM / C ++ 11:如何从其他小部件创建自定义复合小部件?

时间:2016-12-27 11:22:02

标签: c++ c++11 widget gtkmm gtkmm3

我想派生自己的窗口小部件类并向此类添加标准窗口小部件以创建复合窗口小部件。有没有人有关于如何做到这一点的例子或建议?例如,假设我想创建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);
}

2 个答案:

答案 0 :(得分:1)

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

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小部件方法&#39 ;无论如何都要使用。为此原因。最好将所有容器窗口小部件对象添加到类的公共部分,包括顶层框窗口小部件。在没有gtkmm api杂乱的情况下,您仍然可以访问每个对象窗口小部件的所有方法。

(2)从Gtk :: Widget继承你的类并不能在多态行为方面获得任何有用的东西,因为复合小部件的小部件已经是从Gtk :: Widget派生的多态对象。将顶层窗口小部件框添加到父窗口小部件时,复合窗口小部件的所有子窗口小部件(在框下)都会添加到父窗口小部件的子窗口列表中。因此,实际上不需要具有复合窗口小部件容器的多态行为。

(3)如果你需要从Gtk:Widget派生的复合小部件,很容易在它周围放置一个类包装器,将它转换回一个派生自Gtk :: Widget的类。实际上,通过模板类包装器的oneline实例化可以轻松完成此步骤。我可以想到这样做的唯一原因是将组件插回到林间空地中(缺少Glade Gui,将C ++ 11仿函数操作符识别为返回复合小部件的顶层窗口小部件的标准方法(可能用于支持glade)未来?愿望清单......))

答案 1 :(得分: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部分中有一个可靠的示例。