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

Posted

技术标签:

【中文标题】GTKMM/C++11:如何从其他小部件中创建自定义复合小部件?【英文标题】:GTKMM/C++11: How to create a custom composite widget out of other widgets? 【发布时间】:2016-12-27 11:22:02 【问题描述】:

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

【问题讨论】:

【参考方案1】:

经过几周的实验,我的结论是最好避免使用 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 可能支持未来?愿望清单...))

【讨论】:

operator Gtk::Widget&amp;operator() 更合适【参考方案2】:

实际上可以从 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 教程部分中有一个可靠的示例。

【讨论】:

以上是关于GTKMM/C++11:如何从其他小部件中创建自定义复合小部件?的主要内容,如果未能解决你的问题,请参考以下文章

在 Qt 设计器中创建要提升的自定义小部件

如何在 R Markdown 中创建条件 selectInput 小部件?

如何在 Qt 中创建共享小部件的子目录?

如何在 Qt 的后续函数调用中访问在函数中创建的小部件

如何在 spotify 应用程序 API 中创建类似“轮播”的小部件?

如何在 WordPress 中创建自定义可拖动小部件