> 文章列表 > gtk / gtkmm学习记录

gtk / gtkmm学习记录

gtk / gtkmm学习记录

参考资料

https://github.com/ToshioCP/Gtk4-tutorial/blob/main/gfm

第一个程序

1. 创建一个程序

#include <gtk/gtk.h>int main (int argc, char **argv)
{GtkApplication *app = gtk_application_new ("com.hello", G_APPLICATION_DEFAULT_FLAGS);int stat = g_application_run (app, argc, argv);g_object_unref (app);return stat;
}

这样就是最简程序了:创建一个程序并运行它。

其中"com.hello"是app的id,规则就是域名倒过来,和java的包名规则一样。

然而,编译运行后你会看到这样的警告:

(main:34508): GLib-GIO-WARNING **: 17:21:46.809: Your application does not implement g_application_activate() and has no handlers connected to the 'activate' signal.  It should do one of these.

不用急,程序至少已经正常运行了。
这个警告告诉我们:

  1. You didn’t implement g_application_activate()
  2. And no handlers connected to the activate signal
  3. You should do one of these.

这两个错误原因都与信号有关。因此,先解释一下。
发生某些事情时会发出信号,比如创建/销毁一个窗口。 当程序被activated时(被激活),会发出activate信号。(激活并不等于启动,但是可以暂且认为相同)

如果信号连接到一个函数,这个函数被称为信号处理程序。

流程是这样的:

  1. 有事情发生。
  2. 如果它与某个信号有关,那么该信号就会被发射。
  3. 如果信号已经预先连接到处理程序,则调用处理程序。
    信号在对象中定义。例如,activate信号属于 GApplication 对象,它是 GtkApplication 对象的父对象。

GApplication 对象是 GObject 对象的子对象。GObject 是所有对象层次结构中的顶级对象。

GObject -- GApplication -- GtkApplication
<---parent                      --->child

子对象从其父对象继承信号、函数、属性等。所以,GtkApplication 也有activate信号。

2. 处理activate信号

#include <gtk/gtk.h>static void
app_activate (GApplication *app, gpointer *user_date)
{g_print ("hello world\\n");
}
int main (int argc, char **argv)
{GtkApplication *app =gtk_application_new ("com.hello", G_APPLICATION_DEFAULT_FLAGS);g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);int stat = g_application_run (G_APPLICATION (app), argc, argv);g_object_unref (app);return stat;
}

g_signal_connect()有4个参数:

  1. 信号所属的实例。
  2. 信号的名称。
  3. 处理函数(也称为回调),需要用G_CALLBACK来强制转换.
  4. 要传递给处理程序的数据。如果不需要数据,则应给出 NULL。

3. GtkWindow

前面还是没有图形话界面。
所以这里加上:

#include <gtk/gtk.h>static void
on_app_activate (GApplication *app, gpointer user_data)
{GtkWidget *win = gtk_window_new();gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));gtk_window_present (GTK_WINDOW (win));
}int main (int argc, char **argv)
{GtkApplication *app =gtk_application_new ("com.hello", G_APPLICATION_DEFAULT_FLAGS);g_signal_connect (G_APPLICATION (app), "activate", G_CALLBACK (on_app_activate), NULL);int stat = g_application_run (G_APPLICATION (app), argc, argv);g_object_unref (app);return stat;
}

Widget 是一个抽象的概念,包括所有的GUI 界面,如窗口、对话框、按钮、多行文本、容器等。GtkWidget 是一个基础对象,所有 GUI 对象都从中派生。

parent <-----> child
GtkWidget -- GtkWindow

GtkWindow 在其对象的顶部包含 GtkWidget

gtk / gtkmm学习记录

该函数gtk_window_new定义如下。

GtkWidget *
gtk_window_new (void);

根据这个定义,它返回一个指向 GtkWidget 的指针,而不是 GtkWindow。它实际上创建了一个新的 GtkWindow 实例(不是 GtkWidget),但返回一个指向 GtkWidget 的指针。但是,该指针指向 GtkWidget,同时它也指向其中包含 GtkWidgetGtkWindow

如果要用作win指向 GtkWindow 类型实例的指针,则需要对其进行强制转换。

(GtkWindow *) win

它有效,但通常不使用。相反,使用宏GTK_WINDOW

GTK_WINDOW (win)

建议使用宏,因为它不仅会转换指针,还会检查类型。

您可以使用gtk_widget_set_visible (win, true)而不是gtk_window_present。但是这两者的行为是不同的。假设屏幕上有win1和win2两个窗口,win1在win2的后面。两个窗口都可见。该函数gtk_widget_set_visible (win1, true)不执行任何操作,因为 win1 已经可见。因此,win1 仍然落后于 win2。另一个函数gtk_window_present (win1)将 win1 移动到窗口堆栈的顶部。因此,如果你想呈现窗口,你应该使用gtk_window_present.

两个函数gtk_widget_showgtk_widget_hide自 GTK 4.10 起已弃用。你应该改用gtk_widget_set_visible

gtk / gtkmm学习记录

4. GtkApplicationWindow

GtkApplicationWindowGtkWindow 的子对象。它有一些额外的功能可以更好地与 GtkApplication 集成。建议将其用作应用程序的顶层窗口,而不是 GtkWindow

1 static void
2 app_activate (GApplication *app, gpointer user_data) {
3   GtkWidget *win;
4 
5   win = gtk_application_window_new (GTK_APPLICATION (app));
6   gtk_window_set_title (GTK_WINDOW (win), "pr4");
7   gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
8   gtk_window_present (GTK_WINDOW (win));
9 }

当创建 GtkApplicationWindow 时,需要将 GtkApplication 实例作为参数提供。然后它会自动连接这两个实例。所以不需要再调用gtk_window_set_application

gtk / gtkmm学习记录

GtkLabel, GtkButton, GtkBox

1.GtkLabel

显示一个标签

#include <gtk/gtk.h>static void
app_activate (GApplication *app)
{GtkWindow *win = GTK_WINDOW (gtk_application_window_new (GTK_APPLICATION (app)));gtk_window_set_title (win, "hello window");gtk_window_set_default_size (win, 400, 300);GtkLabel *label = GTK_LABEL (gtk_label_new ("hello label"));gtk_window_set_child (win, GTK_WIDGET (label));gtk_window_present (win);
}int main (int argc, char **argv)
{GtkApplication *app =gtk_application_new ("top.hello", G_APPLICATION_DEFAULT_FLAGS);g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);int stat = g_application_run (G_APPLICATION (app), argc, argv);g_object_unref (app);return stat;
}

gtk / gtkmm学习记录

2. GtkButton

点击按钮关闭窗口。

#include <gtk/gtk.h>static void
click_btn (GtkButton *btn, GtkWindow *win)
{gtk_window_destroy (win);
}static void
app_activate (GtkApplication *app)
{GtkWindow *win = GTK_WINDOW (gtk_application_window_new (app));gtk_window_set_title (win, "hello window");gtk_window_set_default_size (win, 400, 300);GtkWidget *btn = gtk_button_new();gtk_button_set_label (GTK_BUTTON (btn), "button");gtk_window_set_child (win, btn);g_signal_connect (btn, "clicked", G_CALLBACK (click_btn), win);gtk_window_present (win);
}int main (int argc, char  **argv)
{GtkApplication *app =gtk_application_new ("top.hello", G_APPLICATION_DEFAULT_FLAGS);g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);int stat = g_application_run (G_APPLICATION (app), argc, argv);g_object_unref (app);return stat;
}

gtk / gtkmm学习记录

3. GtkBox

点击hello button会变为hello button!!!,点击close会关闭。

这里需要明白的是,一个window只能有一个孩子,如果要添加多个组件,就要用布局容器。

#include <gtk/gtk.h>
#include <string.h>static void
btn_1_clicked (GtkButton *btn)
{const char *text = gtk_button_get_label (btn);if (strcmp (text, "hello button") == 0)gtk_button_set_label (btn, "hello button !!!");elsegtk_button_set_label (btn, "hello button");
}static void
btn_2_clicked (GtkButton *btn, GtkWindow *win)
{gtk_window_destroy (win);
}static void
app_activate (GtkApplication *app)
{GtkWidget *win = gtk_application_window_new (app);gtk_window_set_title (GTK_WINDOW (win), "hello window");gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);GtkWidget *btn_1,*btn_2,*box;btn_1 = gtk_button_new_with_label ("hello button");btn_2 = gtk_button_new_with_label ("close");box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);gtk_box_set_homogeneous (GTK_BOX (box), TRUE);gtk_box_append (GTK_BOX (box), btn_1);gtk_box_append (GTK_BOX (box), btn_2);gtk_window_set_child (GTK_WINDOW (win), box);g_signal_connect (GTK_BUTTON (btn_1), "clicked", G_CALLBACK (btn_1_clicked), NULL);g_signal_connect (GTK_BUTTON (btn_2), "clicked", G_CALLBACK (btn_2_clicked), win);gtk_window_present (GTK_WINDOW (win));
}int main (int argc, char **argv)
{GtkApplication *app =gtk_application_new ("com.hello", G_APPLICATION_DEFAULT_FLAGS);g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);int stat = g_application_run (G_APPLICATION (app), argc, argv);g_object_unref (app);return stat;
}

gtk / gtkmm学习记录



gtkmm

学到这里,已经感觉出来不对劲了,使用成本太高了,要写一堆强制转换宏真的很烦。

虽然不加宏实际上也没什么,但是编译器会警告,会淹没重要信息。

弃了,后面学gtkmm了,用C++。

gtkmm参考资料:
官方推荐书:https://gnome.pages.gitlab.gnome.org/gtkmm-documentation

一、基础知识

1. 简单例子

#include <gtkmm.h>class MyWindow : public Gtk::Window {
public:MyWindow() {set_title ("Hello world window");set_default_size (200, 200);}
};int main (int argc, char **argv)
{auto app = Gtk::Application::create ("com.hello");return app->make_window_and_run<MyWindow> (argc, argv);
}
g++ simple.cc -o simple `pkg-config --cflags --libs gtkmm-4.0` -std=c++17

运行就显示一个空白窗口,标题是hello world window

需要注意的是,直接用#include <gtkmm.h>会引入gtkmm的所有套件,这个头文件有1MB。

更好的方案是引入需要的部分,这里为了学习,我们直接全部包含就行了。

多妙啊,比tmd C语言看起来舒服多了。

2. 组件(控件)

除了可见的组件,还有一些不可见的组件用于布局。例如Gtk::Box, Gtk::Frame
下面是一个添加Gtk::ButtonGtk::Box的例子:

#include <gtkmm/application.h>
#include <gtkmm/box.h>
#include <gtkmm/window.h>
#include <gtkmm/frame.h>
#include <gtkmm/button.h>class MyWindow : public Gtk::Window {
public:MyWindow() {set_title ("Hello world window");set_default_size (200, 200);set_child (m_box);m_box.append (m_button_1);m_box.append (m_button_2);m_box.set_orientation (Gtk::Orientation::VERTICAL);}
private:Gtk::Button m_button_1;Gtk::Button m_button_2;Gtk::Box m_box;
};int main (int argc, char **argv)
{auto app = Gtk::Application::create ("com.hello");return app->make_window_and_run<MyWindow> (argc, argv);}

gtk / gtkmm学习记录

3.信号

与大多数 GUI 工具包一样,gtkmm是事件驱动的。

  1. 当事件发生时,例如按下鼠标按钮,相应的信号将由被按下的 组件发出。
  2. 每个组件都有一组不同的信号可以发出。
  3. 为了使按钮点击产生动作,我们需要设置一个信号处理程序来捕捉按钮的“clicked”信号。

gtkmm使用 libsigc++ 库来实现信号。下面是连接 Gtk::Button"clicked"信号和名为“on_button_clicked”的信号处理程序的示例代码行

m_button1.signal_clicked().connect( sigc::mem_fun(*this,&HelloWorld::on_button_clicked) );

信号的更多内容见附录:https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/chapter-signals.html

4. Glib::ustring

您可能会惊讶地发现gtkmm并未std::string在其界面中使用。相反,它使用Glib::ustring,它是如此相似且不引人注目,以至于您实际上可以假设每个Glib::ustring都是 std::string并忽略本节的其余部分。但如果您想在申请中使用英语以外的语言,请继续阅读。

std::string 每个字符使用 8 位,但 8 位不足以对阿拉伯语、中文和日语等语言进行编码。尽管这些语言的编码已由Unicode Consortium指定,但 C 和 C++ 语言尚未为 UTF-8 编码提供任何标准化的 Unicode 支持。GTK 和 GNOME 选择使用 UTF-8 实现 Unicode,这就是 Glib::ustring 所包装的内容。它提供与 std::string 几乎完全相同的接口,以及与 std::string 的自动转换。

UTF-8 的好处之一是除非您愿意,否则您不需要使用它,因此您不需要一次改造所有代码。std::string仍然适用于 7 位 ASCII 字符串。但是,当您尝试将您的应用程序本地化为中文等语言时,您将开始看到奇怪的错误,并可能出现崩溃。然后你需要做的就是开始使用Glib::ustring

请注意,UTF-8 与 8 位编码(如 ISO-8859-1)不兼容。例如,德语变音符号不在 ASCII 范围内,在 UTF-8 编码中需要超过 1 个字节。如果您的代码包含 8 位字符串文字,则必须将它们转换为 UTF-8(例如,巴伐利亚问候语“Grüß Gott”将是“Gr\\xC3\\xBC\\xC3\\x9F Gott”)。

您应该避免使用 C 风格的指针算法和 strlen() 等函数。在 UTF-8 中,每个字符可能需要 1 到 6 个字节,因此不能假设下一个字节是另一个字符。Glib::ustring为您担心这方面的细节,因此您可以使用诸如 Glib::ustring::substr() 之类的方法,同时仍然根据字符而不是字节来思考。

与 Windows UCS-2 Unicode 解决方案不同,这不需要任何特殊的编译器选项来处理字符串文字,并且不会导致与 ASCII 不兼容的 Unicode 可执行文件和库。

5. 混合使用C和C++ API

有时可能gtkmm还没有方便的接口可以使用,可以用C的接口。

如果要将gtkmm的对象实例和C接口(需要一个C的GOBject对象)一起使用,请使用C++对象实例的gobjc()函数来得到一个底层的C实例指针。

例如:

Gtk::Button button("example");
gtk_button_do_something_that_gtkmm_cannot(button.gobj());

反过来,如果要从C实例获取一个C++的实例,请使用重载的Glib::wrap()函数。C 实例的引用计数不会递增,除非您将可选 take_copy参数设置为true。

举个例子:

GtkButton *cbutton = GTK_BUTTON (gtk_button_new());
Gtk::Button *button = Glib::wrap (cbutton);
button->set_label ("Now I speak C++ too!");

如果出现以下情况,则应显式删除 C++ 包装器:

  1. 它是一个组件,或继承自Gtk::Object的类。
  2. the C instance has a floating reference when the wrapper is created
  3. Gtk::manage()还没有被调用(包括它是否是用Gtk::make_managed()创建的)
  4. Gtk::manage() was called on it, but it was never added to a parent.

Glib::wrap()将 C 和 C++ 实例相互绑定。在您希望 C 实例死亡之前,不要删除 C++ 实例。

在所有其他情况下,当删除对 C 实例的最后一个引用时,C++ 实例将自动删除。这包括所有返回 Glib::RefPtrGlib::wrap() 重载。

6. hello world

有了前面的知识足够了,现在看一个例子:

sixqaq@debian:~/gtkmm$ tree
.
├── build
├── CMakeLists.txt
├── helloworld.cpp
├── helloworld.h
└── main.cpp2 directories, 4 files
  1. helloworld.h

    #pragma once#include <gtkmm/button.h>
    #include <gtkmm/window.h>class HelloWorld : public Gtk::Window {
    public:HelloWorld();~HelloWorld() override;
    protected:void on_button_clicked();Gtk::Button m_button;
    };
    
  2. helloworld.cpp

    #include "helloworld.h"
    #include <iostream>HelloWorld::HelloWorld(): m_button ("Hello world")
    {//设置按钮外边距,前端都懂m_button.set_margin (10);//当按钮收到 clicked 信号时会调用 on_button_clicked() 方法m_button.signal_clicked().connect (sigc::mem_fun (*this, &HelloWorld::on_button_clicked));set_child (m_button);
    }HelloWorld::~HelloWorld()
    { }void HelloWorld::on_button_clicked()
    {std::cout << "Hello World" << std::endl;
    }
    
  3. main.cpp

    #include "helloworld.h"
    #include <gtkmm/application.h>int main (int argc, char **argv)
    {auto app = Gtk::Application::create ("com.hello");return app->make_window_and_run<HelloWorld> (argc, argv);
    }
    
  4. CMakeLists.txt

    cmake_minimum_required(VERSION 3.1)
    project(main)
    set(EXE main)aux_source_directory(. SRC)find_package(PkgConfig REQUIRED)
    pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)add_compile_options(${GTKMM_CFLAGS_OTHER})add_executable(${EXE} ${SRC})target_include_directories(${EXE} PUBLIC ${GTKMM_INCLUDE_DIRS})
    target_link_libraries(${EXE} PUBLIC ${GTKMM_LIBRARIES})
    

gtk / gtkmm学习记录

二、按钮

gtkmm提供四种基本类型的按钮:

  1. Gtk::Button. 标准按钮,通常标有标签或图片。按下一个会触发一个动作。请参阅按钮部分。

  2. Gtk::ToggleButton. 与弹回的普通 Button 不同,ToggleButton 会保持按下状态,直到您再次按下它。它可能用作开/关开关。

  3. Gtk::CheckButton. 它们的作用类似于 ToggleButton,但在小方块中显示它们的状态,并在旁边带有标签。它们应该用于大多数需要开/关设置的情况。请参阅CheckButton部分。

  4. Radio Button成组用于相互排斥的选项。按下一个会导致其组中的所有其他人关闭。它们类似于 ToggleButtons 或 CheckButtons(侧面带有标签的小部件),但通常看起来不同。没有单独的Radio Button类,但是Check Buttons和Toggle Buttons都可以作为Radio Buttons。

请注意,由于 GTK 的主题系统,这些小部件的外观会有所不同。对于radio按钮和check按钮,它们可能会有很大差异。

1. Button

一个带图片标签的例子,标签还试了一下Unicode的Emoji,效果挺好。

make_managed<>()的意思,看下注释文档。意思是这样创建的组件会被标记为由父组件接管,无需手动管理。

#include "helloworld.h"
#include <gtkmm/application.h>#include <gtkmm/button.h>
#include <gtkmm/image.h>
#include <gtkmm/window.h>
#include <gtkmm/box.h>
#include <gtkmm/label.h>
#include <iostream>class MyButton : public Gtk::Window {
public:MyButton();private:void on_button_clicked();
};MyButton::MyButton()
{Gtk::Button *button = Gtk::make_managed<Gtk::Button> ();Gtk::Image *img = Gtk::make_managed<Gtk::Image> ("1.png");Gtk::Label *label = Gtk::make_managed<Gtk::Label> ("💘 MyButton");Gtk::Box *hbox = Gtk::make_managed<Gtk::Box> (Gtk::Orientation::HORIZONTAL);hbox->append (*img);hbox->append (*label);button->set_child (*hbox);button->set_margin (10);button->signal_clicked().connect (sigc::mem_fun (*this, &MyButton::on_button_clicked));set_child (*button);set_title ("I Love You!");
}void MyButton::on_button_clicked()
{std::cout << "Hello world" << std::endl;
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<MyButton> (argc, argv);
}

gtk / gtkmm学习记录

2. ToggleButton

用法和普通按钮差不多。但是单击后会保持激活/按下状态,直到再次单击。

  1. 可以使用get_active()方法获取它的状态,如果按钮处于按下状态,就会返回true

  2. 可以用set_active()方法来设置状态,注意,这也会发出"clicked"信号。

  3. 可以用toggled()方法来切换状态 。

3. CheckButton

这个按钮类直接继承自Gtk::Widget
它和ToggleButton很像,只是外观不一样。可以使用和ToggleButton相同的方法来设置和检测它。

#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include <gtkmm/checkbutton.h>
#include <iostream>class Demo : public Gtk::Window {
public:Demo();virtual ~Demo() = default;
protected:void on_button_toggled();Gtk::CheckButton m_button;
};Demo::Demo(): m_button ("a toggle button")
{set_title ("checkbutton demo");m_button.signal_toggled().connect (sigc::mem_fun (*this, &Demo::on_button_toggled));m_button.set_margin (10);set_child (m_button);
}void Demo::on_button_toggled()
{std::cout << "the button was toggled, state = "<< (m_button.get_active() ? "true" : "false")<< std::endl;
}
int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<Demo> (argc, argv);
}

gtk / gtkmm学习记录

4. Radio Button

没有单独的Radio Button类,而是用ToggleButtonCheckButton。把它们放到一个组时,每组只能有一个按钮被选中,这样就是单选按钮了。
例如:

auto rb1 = Gtk::make_managed<Gtk::CheckButton>("button1");
auto rb2 = Gtk::make_managed<Gtk::CheckButton>("button2");
auto rb3 = Gtk::make_managed<Gtk::CheckButton>("button3");
rb2->set_group(*rb1);
rb3->set_group(*rb1);

使用了set_group()来告诉其他按钮,它们和第一个按钮共享一个分组。

当创建时,CheckButtonToggleButton都是"off"的,没有任何一个被选中。用set_active()来开启它们中的一个。


#include <gtkmm/application.h>
#include <gtkmm/box.h>
#include <gtkmm/window.h>
#include <gtkmm/button.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/separator.h>class Demo : public Gtk::Window {
public:Demo();virtual ~Demo() = default;
protected:void on_button_clicked();Gtk::Box m_Box_Top, m_Box_1, m_Box_2;Gtk::CheckButton m_RadioButton_1, m_RadioButton_2, m_RadioButton_3;Gtk::Separator m_Separator;Gtk::Button m_Button_Close;
};Demo::Demo() :m_Box_Top (Gtk::Orientation::VERTICAL),m_Box_1 (Gtk::Orientation::VERTICAL, 10),m_Box_2 (Gtk::Orientation::VERTICAL, 10),m_RadioButton_1 ("button1"),m_RadioButton_2 ("button2"),m_RadioButton_3 ("button3"),m_Button_Close ("close")
{set_title ("radio buttons demo");//把按钮1,2,3放进同一个分组m_RadioButton_2.set_group (m_RadioButton_1);m_RadioButton_3.set_group (m_RadioButton_1);//把外部盒子设为窗口子元素set_child (m_Box_Top);//内部盒子放到外部盒子里m_Box_Top.append (m_Box_1);m_Box_Top.append (m_Box_2);m_Separator.set_expand();//设置内部盒子外边距m_Box_1.set_margin (10);m_Box_2.set_margin (10);//单选按钮放进Box1m_Box_1.append (m_RadioButton_1);m_Box_1.append (m_RadioButton_2);m_Box_1.append (m_RadioButton_3);m_RadioButton_1.set_expand();m_RadioButton_2.set_expand();m_RadioButton_3.set_expand();//设置第2个按钮开启m_RadioButton_2.set_active (true);//关闭按钮放进Box2m_Box_2.append (m_Button_Close);m_Button_Close.set_expand();//连接槽函数m_Button_Close.signal_clicked().connect (sigc::mem_fun (*this, &Demo::on_button_clicked));
}void Demo::on_button_clicked()
{set_visible (false);
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<Demo> (argc, argv);
}

gtk / gtkmm学习记录

三、Range Widgets

Gtk::Scale继承自Gtk::RangeGtk::Scrollbar并非继承自Gtk::Range,但它和Gtk::Scale共享很多功能。

它们都包含一个槽和滑块,拖动滑块可以让其在槽内移动(或者单击槽的某个位置来移动)。

所有的Range Widgets都与一个Adjustment对象关联。
要更改小部件使用的下限、上限和当前值,需要使用它的Adjustment的方法,可以用get_adjustment()方法来获取这些方法。

Range Widgets的默认构造函数会自动创建一个Adjustment,或者你可以为它指定一个现有的Adjustment——为了与其他组件共享调节值。

详细信息见章节Adjustment。

1. Scrollbar Widgets

这些这是标准的滚动条,它们应该仅仅用来滚动另一个组件,例如一个Gtk::EntryGtk::Viewport,尽管往往大多数情况下使用Gtk::ScrolledWindow组件更容易。

Gtk::Scrollbar的方向可以是横 / 纵。

2. Scale Widgets

Gtk::Scale组件允许用户可视化的选择/操纵特定范围的值。

例如,你可以用它来调节一个图片预览程序的放大倍率,或者是控制颜色亮度,以及指定无操作则息屏的时间。

Scrollbar一样,方向可以是水平或树值的。默认构造函数会创建一个值全为0.0Adjustment。这没有什么用,所以你需要设置Adjustment的细节来获得有意义的行为。

有用的方法

Scale小部件可以将它们的当前值显示为槽旁边的数字。默认情况下,它们会显示值,可以用set_draw_value()方法更改它。

默认情况下,Scale组件显示的值会四舍五入到小数点后1位,这是Gtk::Adjustment中的字段。可以使用set_digits()方法更改它。

此外,值可以显示在槽的不同位置,由方法set_value_pos()指定。

3. 例子

gtk / gtkmm学习记录

#include <gtkmm/application.h>
#include <gtkmm.h>class Demo : public Gtk::Window {
public:Demo();virtual ~Demo() = default;
protected://signal handlersvoid on_checkbutton_toggled();void on_dropdown_position();void on_adjustment1_value_changed();void on_adjustment2_value_changed();void on_button_quit();Gtk::Box m_VBox_Top, m_VBox2, m_VBox_HScale;Gtk::Box m_HBox_Scales, m_HBox_DropDown, m_HBox_Digits, m_HBox_PageSize;Glib::RefPtr<Gtk::Adjustment> m_adjustment, m_adjustment_digits, m_adjustment_pagesize;Gtk::Scale m_VScale;Gtk::Scale m_HScale, m_Scale_Digits, m_Scale_PageSize;Gtk::Separator m_Separator;Gtk::CheckButton m_CheckButton;Gtk::Scrollbar m_Scrollbar;Gtk::DropDown m_DropDown_Position;Gtk::Button m_Button_Quit;
};struct PositionTypeStruct {Gtk::PositionType position;Glib::ustring text;
};const std::array<PositionTypeStruct, 4>positionTypes = {PositionTypeStruct{Gtk::PositionType::TOP, "Top"},PositionTypeStruct{Gtk::PositionType::BOTTOM, "Button"},PositionTypeStruct{Gtk::PositionType::LEFT, "Left"},PositionTypeStruct{Gtk::PositionType::RIGHT, "Right"}
};Demo::Demo() :m_VBox_Top (Gtk::Orientation::VERTICAL, 0),m_VBox2 (Gtk::Orientation::VERTICAL, 20),m_VBox_HScale (Gtk::Orientation::VERTICAL, 10),m_HBox_Scales (Gtk::Orientation::HORIZONTAL, 10),m_HBox_DropDown (Gtk::Orientation::HORIZONTAL, 10),m_HBox_Digits (Gtk::Orientation::HORIZONTAL, 10),m_HBox_PageSize (Gtk::Orientation::HORIZONTAL, 10),m_adjustment (Gtk::Adjustment::create (0, 0, 101, 0.1, 1, 1)),m_adjustment_digits (Gtk::Adjustment::create (1, 0, 5, 1, 2)),m_adjustment_pagesize (Gtk::Adjustment::create (1, 1, 101)),m_VScale (m_adjustment, Gtk::Orientation::VERTICAL),m_HScale (m_adjustment, Gtk::Orientation::HORIZONTAL),m_Scale_Digits (m_adjustment_digits),m_Scale_PageSize (m_adjustment_pagesize),m_CheckButton ("Display value on scale wigets", false),m_Scrollbar (m_adjustment),m_Button_Quit ("Quit")
{set_title ("range controls");set_default_size (300, 350);//VScalem_VScale.set_digits (1);m_VScale.set_value_pos (Gtk::PositionType::TOP);m_VScale.set_draw_value();m_VScale.set_inverted();//HScalem_HScale.set_digits (1);m_HScale.set_value_pos (Gtk::PositionType::TOP);m_HScale.set_draw_value();set_child (m_VBox_Top);m_VBox_Top.append (m_VBox2);m_VBox2.set_expand (true);m_VBox2.set_margin (10);m_VBox2.append (m_HBox_Scales);m_HBox_Scales.set_expand (true);//把VScale和HScale紧靠一起m_HBox_Scales.append (m_VScale);m_VScale.set_expand (true);m_HBox_Scales.append (m_VBox_HScale);m_VBox_HScale.set_expand (true);m_VBox_HScale.append (m_HScale);m_HScale.set_expand (true);//Scrollbarm_VBox_HScale.append (m_Scrollbar);m_Scrollbar.set_expand (true);//CheckButtonm_CheckButton.set_active();m_CheckButton.signal_toggled().connect (sigc::mem_fun (*this, &Demo::on_checkbutton_toggled));m_VBox2.append (m_CheckButton);//Position DropDownauto string_list = Gtk::StringList::create ({});m_DropDown_Position.set_model (string_list);//Fill the DropDown's list model:for (const auto &i : positionTypes)string_list->append (i.text);m_VBox2.append (m_HBox_DropDown);m_HBox_DropDown.append (*Gtk::make_managed<Gtk::Label> ("Scale Value Position", false));m_HBox_DropDown.append (m_DropDown_Position);m_DropDown_Position.property_selected().signal_changed().connect (sigc::mem_fun (*this, &Demo::on_dropdown_position));m_DropDown_Position.set_selected (0);m_DropDown_Position.set_expand (true);//Digits:m_HBox_Digits.append (*Gtk::make_managed <Gtk::Label> ("Scale Digits: ", false));m_Scale_Digits.set_digits (0);m_Scale_Digits.set_expand (true);m_adjustment_digits->signal_value_changed().connect (sigc::mem_fun (*this, &Demo::on_adjustment1_value_changed));m_HBox_Digits.append (m_Scale_Digits);//Page Size:m_HBox_PageSize.append (*Gtk::make_managed<Gtk::Label> ("Scrollbar Page Size:", false));m_Scale_PageSize.set_digits (0);m_Scale_PageSize.set_expand (true);m_adjustment_pagesize->signal_value_changed().connect (sigc::mem_fun (*this, &Demo::on_adjustment2_value_changed));m_HBox_PageSize.append (m_Scale_PageSize);m_VBox2.append (m_HBox_Digits);m_VBox2.append (m_HBox_PageSize);m_VBox_Top.append (m_Separator);m_VBox_Top.append (m_Button_Quit);set_default_widget (m_Button_Quit);m_Button_Quit.signal_clicked().connect (sigc::mem_fun (*this, &Demo::on_button_quit));m_Button_Quit.set_margin (10);
}void Demo::on_checkbutton_toggled()
{m_VScale.set_draw_value (m_CheckButton.get_active());m_HScale.set_draw_value (m_CheckButton.get_active());
}void Demo::on_dropdown_position()
{const auto selected = m_DropDown_Position.get_selected();if (selected == GTK_INVALID_LIST_POSITION)return;const auto postype = positionTypes[selected].position;m_VScale.set_value_pos (postype);m_HScale.set_value_pos (postype);
}void Demo::on_adjustment1_value_changed()
{const double val = m_adjustment_digits->get_value();m_VScale.set_digits (val);m_HScale.set_digits (val);
}void Demo::on_adjustment2_value_changed()
{const double val = m_adjustment_pagesize->get_value();m_adjustment->set_page_size (val);m_adjustment->set_page_increment (val);
}void Demo::on_button_quit()
{set_visible (false);
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<Demo> (argc, argv);
}

四、杂项小组件

  1. Label
  2. Entry
  3. SpinButton
  4. ProgressBar
  5. InfoBar
  6. Tooltips

1. Label

Label是用来添加不可编辑文本的主要方式。

  1. 可以在构造时指定文本,也可稍后用set_text()set_markup()方法。
  2. 标签的宽度会自动调整,可以使用"\\n"来换行。
  3. 可以使用set_justify()方法使得文本对齐。
  4. 可以使用set_wrap()设置自动换行

Gtk::Label可以设置粗体、颜色、大小,通过向set_markup()传递Pango Markup语法的字符串来实现。

例如,<b>bold text</b> and <s>strikethrough text</s>

例子:

gtk / gtkmm学习记录

#include <gtkmm/application.h>
#include <gtkmm.h>#define NDEBUG
class Demo : public Gtk::Window {
public:Demo();virtual ~Demo() = default;
protected:Gtk::Box m_HBox;Gtk::Box m_VBox, m_VBox_2;Gtk::Frame m_Frame_Normal, m_Frame_Multi, m_Frame_Left, m_Frame_Right,m_Frame_LineWrapped, m_Frame_FilledWrapped, m_Frame_Underlined;Gtk::Label m_Label_Normal, m_Label_Multi, m_Label_Left, m_Label_Right,m_Label_LineWrapped, m_Label_FilledWrapped, m_Label_Underlined;
};Demo::Demo() :m_HBox (Gtk::Orientation::HORIZONTAL, 5),m_VBox (Gtk::Orientation::VERTICAL, 5),m_VBox_2 (Gtk::Orientation::VERTICAL, 5),m_Frame_Normal ("Normal Label"),m_Frame_Multi ("Multi-line Label"),m_Frame_Left ("Left Justified Label"),m_Frame_Right ("Right Justified Label"),m_Frame_LineWrapped ("Line wrapped label"),m_Frame_FilledWrapped ("Filled, wrapped label"),m_Frame_Underlined ("Under lined label"),m_Label_Normal ("_This is a Normal label", true),m_Label_Multi ("this is a Multi-line label.\\nSecond line\\nThird line"),m_Label_Left ("This is a Left-Justified\\nMulti-line label.\\nThird line"),m_Label_Right ("This is a Right-Justified\\nmulti-line label.\\nThird line"),m_Label_Underlined ("<u>This label is underlined!</u>\\n""<u>T</u>h<u>is one is</u> <u>u</u>n<u>derlin</u>ed ""in<u> q<u>u</u>ite a f</u>u<u>nky</u> fashion")
{set_title ("Label");m_HBox.set_margin (5);set_child (m_HBox);set_default_size (1, 1);m_HBox.append (m_VBox);m_Frame_Normal.set_child (m_Label_Normal);m_VBox.append (m_Frame_Normal);m_Frame_Multi.set_child (m_Label_Multi);m_VBox.append (m_Frame_Multi);m_Label_Right.set_justify (Gtk::Justification::LEFT);m_Frame_Left.set_child (m_Label_Left);m_VBox.append (m_Frame_Left);m_Label_Right.set_justify (Gtk::Justification::RIGHT);m_Frame_Right.set_child (m_Label_Right);m_VBox.append (m_Frame_Right);m_HBox.append (m_VBox_2);m_Label_LineWrapped.set_text ("This is an example of a line-wrapped label.  It "/* add a big space to the next line to test spacing */"should not be taking up the entire             ""width allocated to it, but automatically ""wraps the words to fit.  ""The time has come, for all good men, to come to ""the aid of their party.  ""The sixth sheik's six sheep's sick.\\n""     It supports multiple paragraphs correctly, ""and  correctly   adds ""many          extra  spaces. ");m_Label_LineWrapped.set_wrap();m_Frame_LineWrapped.set_child (m_Label_LineWrapped);m_VBox_2.append (m_Frame_LineWrapped);m_Label_FilledWrapped.set_text ("This is an example of a line-wrapped, filled label.  ""It should be taking ""up the entire              width allocated to it.  ""Here is a sentence to prove ""my point.  Here is another sentence. ""Here comes the sun, do de do de do.\\n""    This is a new paragraph.\\n""    This is another newer, longer, better ""paragraph.  It is coming to an end, ""unfortunately.");m_Label_FilledWrapped.set_justify (Gtk::Justification::FILL);m_Label_FilledWrapped.set_wrap();m_Frame_FilledWrapped.set_child (m_Label_FilledWrapped);m_VBox_2.append (m_Frame_FilledWrapped);m_Label_Underlined.set_justify (Gtk::Justification::LEFT);m_Label_Underlined.set_use_markup (true);m_Frame_Underlined.set_child (m_Label_Underlined);m_VBox_2.append (m_Frame_Underlined);
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<Demo> (argc, argv);
}

2. Entry

Enrty允许用户输入文本。

  1. 使用set_text()更改内容,get_text()设置内容
  2. set_editable(false)设为只读
  3. set_visibility(false)设为文本隐藏(比如密码)。
  4. gtkmm4.8起,activate在文本输入组件中按下Enter时触发,changed在组件文本更改时触发。当键盘焦点移到另一个组件时,可能表面用户输入完毕,可以用Gtk::EventControllerFocusleave信号来捕获这一信息,后面章节会介绍。
  5. set_activate_default(true)会使得在Gtk::Entry按下Enter时激活窗口的默认组件。这在对话框中很有用,通常默认组件是对话框的关闭按钮。 使用Gtk::Window::set_default_widget()可以设置默认组件。

例子:
gtk / gtkmm学习记录

#include <gtkmm/application.h>
#include <gtkmm.h>class Demo : public Gtk::Window {
public:Demo();virtual ~Demo() = default;
protected:void on_checkbox_editable_toggled();void on_checkbox_visibility_toggled();void on_button_close();Gtk::Box m_HBox;Gtk::Box m_VBox;Gtk::Entry m_Entry;Gtk::Button m_Button_Close;Gtk::CheckButton m_CheckButton_Editable, m_CheckButton_Visible;
};void Demo::on_checkbox_editable_toggled()
{m_Entry.set_editable (m_CheckButton_Editable.get_active());
}void Demo::on_checkbox_visibility_toggled()
{m_Entry.set_visibility (m_CheckButton_Visible.get_active());
}void Demo::on_button_close()
{set_visible (false);
}Demo::Demo() :m_VBox (Gtk::Orientation::VERTICAL),m_Button_Close ("Close"),m_CheckButton_Editable ("Editable"),m_CheckButton_Visible ("Visible")
{set_size_request (200, 100);set_title ("Gtk::Entry");set_child (m_VBox);m_Entry.set_max_length (50);m_Entry.set_text ("hello");m_Entry.set_text (m_Entry.get_text() + " world");m_Entry.select_region (0, m_Entry.get_text_length());m_Entry.set_expand (true);m_VBox.append (m_Entry);m_VBox.append (m_HBox);m_HBox.append (m_CheckButton_Editable);m_CheckButton_Editable.set_expand (true);m_CheckButton_Editable.signal_toggled().connect (sigc::mem_fun (*this, &Demo::on_checkbox_editable_toggled));m_CheckButton_Editable.set_active (true);m_HBox.append (m_CheckButton_Visible);m_CheckButton_Visible.set_expand (true);m_CheckButton_Visible.signal_toggled().connect (sigc::mem_fun (*this, &Demo::on_checkbox_visibility_toggled));m_CheckButton_Visible.set_active (true);m_Button_Close.signal_clicked().connect (sigc::mem_fun (*this, &Demo::on_button_close));m_VBox.append (m_Button_Close);m_Button_Close.set_expand();set_default_widget (m_Button_Close);
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<Demo> (argc, argv);
}

输入补全,自gtkmm4.10已经弃用,这里就先不学它了。

可以使用set_icon_from_paintable()或者set_icon_from_name()指定图标,图标可以位于文本区域开头/结尾。用户点击图标时会发出signal_icon_press信号。

例子:
gtk / gtkmm学习记录

#include <gtkmm/application.h>
#include <gtkmm.h>
#include <iostream>class Demo : public Gtk::Window {
public:Demo();~Demo() override = default ;
protected:void on_icon_pressed (Gtk::Entry::IconPosition icon_pos);void on_button_close();Gtk::Box m_VBox;Gtk::Entry m_Entry;Gtk::Button m_Button_Close;
};Demo::Demo() :m_VBox (Gtk::Orientation::VERTICAL),m_Button_Close ("Close")
{set_title ("Gtk::Entry");set_child (m_VBox);m_Entry.set_max_length (50);m_Entry.set_text ("Hello world");m_Entry.set_activates_default (true);m_VBox.append (m_Entry);m_Entry.set_icon_from_icon_name ("edit-find");m_Entry.signal_icon_press().connect (sigc::mem_fun (*this, &Demo::on_icon_pressed));m_Button_Close.signal_clicked().connect (sigc::mem_fun (*this, &Demo::on_button_close));m_VBox.append (m_Button_Close);set_default_widget (m_Button_Close);
}void Demo::on_button_close()
{set_visible (false);
}
void Demo::on_icon_pressed (Gtk::Entry::IconPosition icon_pos)
{std::cout << "Icon presed." << std::endl;
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<Demo> (argc, argv);
}

可以使用set_progress_faction()或者set_progress_pulse_step()方法在文本下方显示进度条。
gtk / gtkmm学习记录

#include <gtkmm/application.h>
#include <gtkmm.h>class Demo : public Gtk::Window {
public:Demo();~Demo() override = default;
protected:bool on_timeout();void on_button_close();Gtk::Box m_VBox;Gtk::Entry m_Entry;Gtk::Button m_Button_Close;
};Demo::Demo() :m_VBox (Gtk::Orientation::VERTICAL),m_Button_Close ("Close")
{set_title ("Gtk::Entry");set_child (m_VBox);m_Entry.set_max_length (50);m_Entry.set_text ("Hello world");m_VBox.append (m_Entry);Glib::signal_timeout().connect (sigc::mem_fun (*this, &Demo::on_timeout), 100);m_Button_Close.signal_clicked().connect (sigc::mem_fun (*this, &Demo::on_button_close));m_VBox.append (m_Button_Close);set_default_widget (m_Button_Close);
}bool Demo::on_timeout()
{static double fraction = 0;m_Entry.set_progress_fraction (fraction);fraction += 0.01;if (fraction > 1)fraction = 0;return true;
}void Demo::on_button_close()
{set_visible (false);
}int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<ExampleWindow> (argc, argv);
}

3. SpinButton

SpinButton允许用户从一系列值中选择一个值。

SpinButtonAdjustment对象来保存`值的范围信息:

  • value:当前值

  • lower:下限值

  • upper:上限值

  • step_increment: 按下鼠标按钮 1 时要增加/减少的值

  • page_increment: 按下鼠标按钮 2 时要增加/减少的值

  • page_size: 未被使用。

此外,鼠标按钮 3 可用于直接跳转到 upper或lower值。

默认会创建一个Adjustment,使用get_adjustment访问它。也可以在构造函数中为它指定一个。

方法

  • set_digits更改小数位数。
  • set_value(), get_value()
  • spin()转动按钮,如同单击按钮一样。需要一个Gtk::SpinType来指定方向和新的位置。
  • set_numeric(true)来阻止用户输入非数字字符。
  • set_wrap(),在上限和下限之间换行。
  • set_snap_to_ticks(),强制捕捉距离最近step_increment

例子:(这个例子有点难理解,先不细究)
gtk / gtkmm学习记录

#include <gtkmm/application.h>
#include <gtkmm.h>class ExampleWindow : public Gtk::Window {
public:ExampleWindow();virtual ~ExampleWindow();protected://Signal handlers:void on_checkbutton_snap();void on_checkbutton_numeric();void on_spinbutton_digits_changed();void on_button_close();enum enumValueFormats {VALUE_FORMAT_INT,VALUE_FORMAT_FLOAT};void on_button_getvalue (enumValueFormats display);//Child widgets:Gtk::Frame m_Frame_NotAccelerated, m_Frame_Accelerated;Gtk::Box m_HBox_NotAccelerated, m_HBox_Accelerated,m_HBox_Buttons;Gtk::Box m_VBox_Main, m_VBox, m_VBox_Day, m_VBox_Month, m_VBox_Year,m_VBox_Accelerated, m_VBox_Value, m_VBox_Digits;Gtk::Label m_Label_Day, m_Label_Month, m_Label_Year,m_Label_Value, m_Label_Digits,m_Label_ShowValue;Glib::RefPtr<Gtk::Adjustment> m_adjustment_day, m_adjustment_month, m_adjustment_year,m_adjustment_value, m_adjustment_digits;Gtk::SpinButton m_SpinButton_Day, m_SpinButton_Month, m_SpinButton_Year,m_SpinButton_Value, m_SpinButton_Digits;Gtk::CheckButton m_CheckButton_Snap, m_CheckButton_Numeric;Gtk::Button m_Button_Int, m_Button_Float, m_Button_Close;
};ExampleWindow::ExampleWindow():m_Frame_NotAccelerated ("Not accelerated"),m_Frame_Accelerated ("Accelerated"),m_VBox_Main (Gtk::Orientation::VERTICAL, 5),m_VBox (Gtk::Orientation::VERTICAL),m_VBox_Day (Gtk::Orientation::VERTICAL),m_VBox_Month (Gtk::Orientation::VERTICAL),m_VBox_Year (Gtk::Orientation::VERTICAL),m_VBox_Accelerated (Gtk::Orientation::VERTICAL),m_VBox_Value (Gtk::Orientation::VERTICAL),m_VBox_Digits (Gtk::Orientation::VERTICAL),m_Label_Day ("Day: ", Gtk::Align::START),m_Label_Month ("Month: ", Gtk::Align::START),m_Label_Year ("Year: ", Gtk::Align::START),m_Label_Value ("Value: ", Gtk::Align::START),m_Label_Digits ("Digits: ", Gtk::Align::START),m_adjustment_day (Gtk::Adjustment::create (1.0, 1.0, 31.0, 1.0, 5.0, 0.0)),m_adjustment_month (Gtk::Adjustment::create (1.0, 1.0, 12.0, 1.0, 5.0, 0.0)),m_adjustment_year (Gtk::Adjustment::create (2012.0, 1.0, 2200.0, 1.0, 100.0, 0.0)),m_adjustment_value (Gtk::Adjustment::create (0.0, -10000.0, 10000.0, 0.5, 100.0, 0.0)),m_adjustment_digits (Gtk::Adjustment::create (2.0, 1.0, 5.0, 1.0, 1.0, 0.0)),m_SpinButton_Day (m_adjustment_day),m_SpinButton_Month (m_adjustment_month),m_SpinButton_Year (m_adjustment_year),m_SpinButton_Value (m_adjustment_value, 1.0, 2),m_SpinButton_Digits (m_adjustment_digits),m_CheckButton_Snap ("Snap to 0.5-ticks"),m_CheckButton_Numeric ("Numeric only input mode"),m_Button_Int ("Value as Int"),m_Button_Float ("Value as Float"),m_Button_Close ("Close")
{set_title ("SpinButton");m_VBox_Main.set_margin (10);set_child (m_VBox_Main);m_VBox_Main.append (m_Frame_NotAccelerated);m_VBox.set_margin (5);m_Frame_NotAccelerated.set_child (m_VBox);/* Day, month, year spinners */m_VBox.set_spacing (5);m_VBox.append (m_HBox_NotAccelerated);m_Label_Day.set_expand();m_VBox_Day.append (m_Label_Day);m_SpinButton_Day.set_wrap();m_SpinButton_Day.set_expand();m_VBox_Day.append (m_SpinButton_Day);m_HBox_NotAccelerated.set_spacing (5);m_HBox_NotAccelerated.append (m_VBox_Day);m_Label_Month.set_expand();m_VBox_Month.append (m_Label_Month);m_SpinButton_Month.set_wrap();m_SpinButton_Month.set_expand();m_VBox_Month.append (m_SpinButton_Month);m_HBox_NotAccelerated.append (m_VBox_Month);m_Label_Year.set_expand();m_VBox_Year.append (m_Label_Year);m_SpinButton_Year.set_wrap();m_SpinButton_Year.set_expand();m_SpinButton_Year.set_size_request (55, -1);m_VBox_Year.append (m_SpinButton_Year);m_HBox_NotAccelerated.append (m_VBox_Year);//Accelerated:m_VBox_Main.append (m_Frame_Accelerated);m_VBox_Accelerated.set_margin (5);m_Frame_Accelerated.set_child (m_VBox_Accelerated);m_VBox_Accelerated.set_spacing (5);m_VBox_Accelerated.append (m_HBox_Accelerated);m_HBox_Accelerated.append (m_VBox_Value);m_Label_Value.set_expand();m_VBox_Value.append (m_Label_Value);m_SpinButton_Value.set_wrap();m_SpinButton_Value.set_expand();m_SpinButton_Value.set_size_request (100, -1);m_VBox_Value.append (m_SpinButton_Value);m_HBox_Accelerated.append (m_VBox_Digits);m_VBox_Digits.append (m_Label_Digits);m_Label_Digits.set_expand();m_SpinButton_Digits.set_wrap();m_adjustment_digits->signal_value_changed().connect (sigc::mem_fun (*this,&ExampleWindow::on_spinbutton_digits_changed));m_VBox_Digits.append (m_SpinButton_Digits);m_SpinButton_Digits.set_expand();//CheckButtons:m_VBox_Accelerated.append (m_CheckButton_Snap);m_CheckButton_Snap.set_expand();m_CheckButton_Snap.set_active();m_CheckButton_Snap.signal_toggled().connect (sigc::mem_fun (*this,&ExampleWindow::on_checkbutton_snap));m_VBox_Accelerated.append (m_CheckButton_Numeric);m_CheckButton_Numeric.set_expand();m_CheckButton_Numeric.set_active();m_CheckButton_Numeric.signal_toggled().connect (sigc::mem_fun (*this,&ExampleWindow::on_checkbutton_numeric));//Buttons:m_VBox_Accelerated.append (m_HBox_Buttons);m_Button_Int.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this,&ExampleWindow::on_button_getvalue), VALUE_FORMAT_INT));m_HBox_Buttons.set_spacing (5);m_HBox_Buttons.append (m_Button_Int);m_Button_Int.set_expand();m_Button_Float.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this,&ExampleWindow::on_button_getvalue), VALUE_FORMAT_FLOAT));m_HBox_Buttons.append (m_Button_Float);m_Button_Float.set_expand();m_VBox_Accelerated.append (m_Label_ShowValue);m_Label_ShowValue.set_expand();m_Label_ShowValue.set_text ("0");//Close button:m_Button_Close.signal_clicked().connect (sigc::mem_fun (*this,&ExampleWindow::on_button_close));m_VBox_Main.append (m_Button_Close);
}ExampleWindow::~ExampleWindow()
{
}void ExampleWindow::on_button_close()
{set_visible (false);
}void ExampleWindow::on_checkbutton_snap()
{m_SpinButton_Value.set_snap_to_ticks (m_CheckButton_Snap.get_active());
}void ExampleWindow::on_checkbutton_numeric()
{m_SpinButton_Value.set_numeric (m_CheckButton_Numeric.get_active());
}void ExampleWindow::on_spinbutton_digits_changed()
{m_SpinButton_Value.set_digits (m_SpinButton_Digits.get_value_as_int());
}void ExampleWindow::on_button_getvalue (enumValueFormats display)
{gchar buf[32];if (display == VALUE_FORMAT_INT)sprintf (buf, "%d", m_SpinButton_Value.get_value_as_int());elsesprintf (buf, "%0.*f", m_SpinButton_Value.get_digits(),m_SpinButton_Value.get_value());m_Label_ShowValue.set_text (buf);
}
int main (int argc, char **argv)
{auto app = Gtk::Application::create();return app->make_window_and_run<ExampleWindow> (argc, argv);
}
  1. ProgressBar
    进度条,