参考资料
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.
不用急,程序至少已经正常运行了。
这个警告告诉我们:
- You didn’t implement
g_application_activate()
- And no handlers connected to the
activate
signal - You should do one of these.
这两个错误原因都与信号有关。因此,先解释一下。
发生某些事情时会发出信号,比如创建/销毁一个窗口。 当程序被activated时(被激活),会发出activate
信号。(激活并不等于启动,但是可以暂且认为相同)
如果信号连接到一个函数,这个函数被称为信号处理程序。
流程是这样的:
- 有事情发生。
- 如果它与某个信号有关,那么该信号就会被发射。
- 如果信号已经预先连接到处理程序,则调用处理程序。
信号在对象中定义。例如,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个参数:
- 信号所属的实例。
- 信号的名称。
- 处理函数(也称为回调),需要用
G_CALLBACK
来强制转换. - 要传递给处理程序的数据。如果不需要数据,则应给出 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_window_new定义如下。
GtkWidget *
gtk_window_new (void);
根据这个定义,它返回一个指向 GtkWidget
的指针,而不是 GtkWindow
。它实际上创建了一个新的 GtkWindow
实例(不是 GtkWidget
),但返回一个指向 GtkWidget
的指针。但是,该指针指向 GtkWidget
,同时它也指向其中包含 GtkWidget
的 GtkWindow
。
如果要用作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_show
,gtk_widget_hide
自 GTK 4.10 起已弃用。你应该改用gtk_widget_set_visible
。
4. GtkApplicationWindow
GtkApplicationWindow
是 GtkWindow
的子对象。它有一些额外的功能可以更好地与 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
。
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;
}
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;
}
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 !!!");
else
gtk_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;
}
gtkmm
学到这里,已经感觉出来不对劲了,使用成本太高了,要写一堆强制转换宏真的很烦。
虽然不加宏实际上也没什么,但是编译器会警告,会淹没重要信息。
弃了,后面学gtkmm了,用C++。
gtkmm参考资料:
官方文档(这个是英文的):https://gnome.pages.gitlab.gnome.org/gtkmm-documentation
GNOME的文档(中文的):https://developer-old.gnome.org/gtkmm-tutorial/stable/
一、基础知识
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::Button
到Gtk::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);
}
3.信号
与大多数 GUI 工具包一样,gtkmm是事件驱动的。
- 当事件发生时,例如按下鼠标按钮,相应的信号将由被按下的 组件发出。
- 每个组件都有一组不同的信号可以发出。
- 为了使按钮点击产生动作,我们需要设置一个信号处理程序来捕捉按钮的“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++ 包装器:
- 它是一个组件,或继承自
Gtk::Object
的类。 - the C instance has a floating reference when the wrapper is created
Gtk::manage()
还没有被调用(包括它是否是用Gtk::make_managed()
创建的)Gtk::manage()
was called on it, but it was never added to a parent.
Glib::wrap()
将 C 和 C++ 实例相互绑定。在您希望 C 实例死亡之前,不要删除 C++ 实例。
在所有其他情况下,当删除对 C 实例的最后一个引用时,C++ 实例将自动删除。这包括所有返回 Glib::RefPtr
的 Glib::wrap()
重载。
6. hello world
有了前面的知识足够了,现在看一个例子:
sixqaq@debian:~/gtkmm$ tree
.
├── build
├── CMakeLists.txt
├── helloworld.cpp
├── helloworld.h
└── main.cpp
2 directories, 4 files
-
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; };
-
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; }
-
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); }
-
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})
二、按钮
gtkmm提供四种基本类型的按钮:
-
Gtk::Button
. 标准按钮,通常标有标签或图片。按下一个会触发一个动作。请参阅按钮部分。 -
Gtk::ToggleButton
. 与弹回的普通Button
不同,ToggleButton
会保持按下状态,直到您再次按下它。它可能用作开/关开关。 -
Gtk::CheckButton
. 它们的作用类似于ToggleButton
,但在小方块中显示它们的状态,并在旁边带有标签。它们应该用于大多数需要开/关设置的情况。请参阅CheckButton
部分。 -
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);
}
2. ToggleButton
用法和普通按钮差不多。但是单击后会保持激活/按下状态,直到再次单击。
-
可以使用
get_active()
方法获取它的状态,如果按钮处于按下状态,就会返回true
。 -
可以用
set_active()
方法来设置状态,注意,这也会发出"clicked"信号。 -
可以用
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);
}
4. Radio Button
没有单独的Radio Button类,而是用ToggleButton
和CheckButton
。把它们放到一个组时,每组只能有一个按钮被选中,这样就是单选按钮了。
例如:
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()
来告诉其他按钮,它们和第一个按钮共享一个分组。
当创建时,CheckButton
和ToggleButton
都是"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);
//单选按钮放进Box1
m_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);
//关闭按钮放进Box2
m_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);
}
三、Range Widgets
Gtk::Scale
继承自Gtk::Range
,Gtk::Scrollbar
并非继承自Gtk::Range
,但它和Gtk::Scale
共享很多功能。
它们都包含一个槽和滑块,拖动滑块可以让其在槽内移动(或者单击槽的某个位置来移动)。
所有的Range Widgets都与一个Adjustment
对象关联。
要更改小部件使用的下限、上限和当前值,需要使用它的Adjustment
的方法,可以用get_adjustment()
方法来获取这些方法。
Range Widgets的默认构造函数会自动创建一个Adjustment
,或者你可以为它指定一个现有的Adjustment
——为了与其他组件共享调节值。
详细信息见章节Adjustment。
1. Scrollbar Widgets
这些这是标准的滚动条,它们应该仅仅用来滚动另一个组件,例如一个Gtk::Entry
、Gtk::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. 例子
#include <gtkmm/application.h>
#include <gtkmm.h>
class Demo : public Gtk::Window {
public:
Demo();
virtual ~Demo() = default;
protected:
//signal handlers
void 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);
//VScale
m_VScale.set_digits (1);
m_VScale.set_value_pos (Gtk::PositionType::TOP);
m_VScale.set_draw_value();
m_VScale.set_inverted();
//HScale
m_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);
//Scrollbar
m_VBox_HScale.append (m_Scrollbar);
m_Scrollbar.set_expand (true);
//CheckButton
m_CheckButton.set_active();
m_CheckButton.signal_toggled().connect (
sigc::mem_fun (*this, &Demo::on_checkbutton_toggled)
);
m_VBox2.append (m_CheckButton);
//Position DropDown
auto 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);
}
四、杂项小组件
- Label
- Entry
- SpinButton
- ProgressBar
- InfoBar
- Tooltips
1. Label
Label
是用来添加不可编辑文本的主要方式。
- 可以在构造时指定文本,也可稍后用
set_text()
或set_markup()
方法。 - 标签的宽度会自动调整,可以使用
"\n"
来换行。 - 可以使用
set_justify()
方法使得文本对齐。 - 可以使用
set_wrap()
设置自动换行
Gtk::Label
可以设置粗体、颜色、大小,通过向set_markup()
传递Pango Markup语法的字符串来实现。
例如,<b>bold text</b> and <s>strikethrough text</s>
例子:
#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允许用户输入文本。
- 使用
set_text()
更改内容,get_text()
设置内容 set_editable(false)
设为只读set_visibility(false)
设为文本隐藏(比如密码)。- gtkmm4.8起,
activate
在文本输入组件中按下Enter时触发,changed
在组件文本更改时触发。当键盘焦点移到另一个组件时,可能表面用户输入完毕,可以用Gtk::EventControllerFocus
的leave
信号来捕获这一信息,后面章节会介绍。 set_activate_default(true)
会使得在Gtk::Entry
按下Enter时激活窗口的默认组件。这在对话框中很有用,通常默认组件是对话框的关闭按钮。 使用Gtk::Window::set_default_widget()
可以设置默认组件。
例子:
#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
信号。
例子:
#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()
方法在文本下方显示进度条。
#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
允许用户从一系列值中选择一个值。
SpinButton
用Adjustment
对象来保存`值的范围信息:
-
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
。
例子:(这个例子有点难理解,先不细究)
#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());
else
sprintf (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);
}
- ProgressBar
进度条,