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_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

在这里插入图片描述

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

在这里插入图片描述

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::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);
	
}

在这里插入图片描述

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.cpp

2 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})
    

在这里插入图片描述

二、按钮

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);
}

在这里插入图片描述

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);
}

在这里插入图片描述

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);
	
	//单选按钮放进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::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. 例子

在这里插入图片描述

#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);
}

四、杂项小组件

  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>

例子:

在这里插入图片描述

#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()可以设置默认组件。

例子:
在这里插入图片描述

#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允许用户从一系列值中选择一个值。

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

例子:(这个例子有点难理解,先不细究)
在这里插入图片描述

#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);
}
  1. ProgressBar
    进度条,
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值