raspberry pi 1でgtkmm その2

#概要

raspberry pi 1でgtkmmやってみた。
Threadやってみた。

#写真

![2018-12-22-111652_1440x900_scrot.png](https://qiita-image-store.s3.amazonaws.com/0/18104/4d3cdbc8-ed1f-892c-6f9d-c4ca93e244c5.png)


#サンプルコード

```
#include <gtkmm.h>
#include <gtkmm/application.h>
#include <sstream>
#include <chrono>
#include <iostream>
#include <thread>
#include <mutex>

class ExampleWindow;
class ExampleWorker
{
public:
	ExampleWorker();
	void do_work(ExampleWindow * caller);
	void get_data(double * fraction_done, Glib::ustring * message) const;
	void stop_work();
	bool has_stopped() const;
private:
	mutable std::mutex m_Mutex;
	bool m_shall_stop;
	bool m_has_stopped;
	double m_fraction_done;
	Glib::ustring m_message;
};
class ExampleWindow: public Gtk::Window
{
public:
	ExampleWindow();
	void notify();
private:
	void on_start_button_clicked();
	void on_stop_button_clicked();
	void on_quit_button_clicked();
	void update_start_stop_buttons();
	void update_widgets();
	void on_notification_from_worker_thread();
	Gtk::Box m_VBox;
	Gtk::ButtonBox m_ButtonBox;
	Gtk::Button m_ButtonStart;
	Gtk::Button m_ButtonStop;
	Gtk::Button m_ButtonQuit;
	Gtk::ProgressBar m_ProgressBar;
	Gtk::ScrolledWindow m_ScrolledWindow;
	Gtk::TextView m_TextView;
	Glib::Dispatcher m_Dispatcher;
	ExampleWorker m_Worker;
	std::thread * m_WorkerThread;
};


ExampleWorker::ExampleWorker():
	m_Mutex(),
	m_shall_stop(false),
	m_has_stopped(false),
	m_fraction_done(0.0),
	m_message()
{
}
void ExampleWorker::get_data(double * fraction_done, Glib::ustring * message) const
{
	std::lock_guard<std::mutex> lock(m_Mutex);
	if (fraction_done) * fraction_done = m_fraction_done;
	if (message) * message = m_message;
}
void ExampleWorker::stop_work()
{
	std::lock_guard<std::mutex> lock(m_Mutex);
	m_shall_stop = true;
}
bool ExampleWorker::has_stopped() const
{
	std::lock_guard<std::mutex> lock(m_Mutex);
	return m_has_stopped;
}
void ExampleWorker::do_work(ExampleWindow* caller)
{
	{
		std::lock_guard<std::mutex> lock(m_Mutex);
		m_has_stopped = false;
		m_fraction_done = 0.0;
		m_message = "";
	}
	for (int i = 0; ; ++i)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(250));
		{
			std::lock_guard<std::mutex> lock(m_Mutex);
			m_fraction_done += 0.01;
			if (i % 4 == 3)
			{
				std::ostringstream ostr;
				ostr << (m_fraction_done * 100.0) << "% done\n";
				m_message += ostr.str();
			}
			if (m_fraction_done >= 1.0)
			{
				m_message += "Finished";
				break;
			}
			if (m_shall_stop)
			{
				m_message += "Stopped";
				break;
			}
		}
		caller->notify();
	}
	{
		std::lock_guard<std::mutex> lock(m_Mutex);
		m_shall_stop = false;
		m_has_stopped = true;
	}
	caller->notify();
}
ExampleWindow::ExampleWindow():
	m_VBox(Gtk::ORIENTATION_VERTICAL, 5),
	m_ButtonBox(Gtk::ORIENTATION_HORIZONTAL),
	m_ButtonStart("Start work"),
	m_ButtonStop("Stop work"),
	m_ButtonQuit("_Quit", true),
	m_ProgressBar(),
	m_ScrolledWindow(),
	m_TextView(),
	m_Dispatcher(),
	m_Worker(),
	m_WorkerThread(nullptr)
{
	set_title("Multi-threaded example");
	set_border_width(5);
	set_default_size(300, 300);
	add(m_VBox);
	m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK);
	m_ProgressBar.set_text("Fraction done");
	m_ProgressBar.set_show_text();
	m_ScrolledWindow.add(m_TextView);
	m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	m_VBox.pack_start(m_ScrolledWindow);
	m_TextView.set_editable(false);
	m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);
	m_ButtonBox.pack_start(m_ButtonStart, Gtk::PACK_SHRINK);
	m_ButtonBox.pack_start(m_ButtonStop, Gtk::PACK_SHRINK);
	m_ButtonBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK);
	m_ButtonBox.set_border_width(5);
	m_ButtonBox.set_spacing(5);
	m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
	m_ButtonStart.signal_clicked().connect(sigc::mem_fun(* this, &ExampleWindow::on_start_button_clicked));
	m_ButtonStop.signal_clicked().connect(sigc::mem_fun(* this, &ExampleWindow::on_stop_button_clicked));
	m_ButtonQuit.signal_clicked().connect(sigc::mem_fun(* this, &ExampleWindow::on_quit_button_clicked));
	m_Dispatcher.connect(sigc::mem_fun(* this, &ExampleWindow::on_notification_from_worker_thread));
	auto buffer = m_TextView.get_buffer();
	buffer->create_mark("last_line", buffer->end(), true);
	update_start_stop_buttons();
	show_all_children();
}
void ExampleWindow::on_start_button_clicked()
{
	if (m_WorkerThread)
	{
		std::cout << "Can't start a worker thread while another one is running." << std::endl;
	}
	else
	{
		m_WorkerThread = new std::thread([this] {
			m_Worker.do_work(this);
		});
	}
	update_start_stop_buttons();
}
void ExampleWindow::on_stop_button_clicked()
{
	if (!m_WorkerThread)
	{
		std::cout << "Can't stop a worker thread. None is running." << std::endl;
	}
	else
	{
		m_Worker.stop_work();
		m_ButtonStop.set_sensitive(false);
	}
}
void ExampleWindow::update_start_stop_buttons()
{
	const bool thread_is_running = m_WorkerThread != nullptr;
	m_ButtonStart.set_sensitive(!thread_is_running);
	m_ButtonStop.set_sensitive(thread_is_running);
}
void ExampleWindow::update_widgets()
{
	double fraction_done;
	Glib::ustring message_from_worker_thread;
	m_Worker.get_data(&fraction_done, &message_from_worker_thread);
	m_ProgressBar.set_fraction(fraction_done);
	if (message_from_worker_thread != m_TextView.get_buffer()->get_text())
	{
		auto buffer = m_TextView.get_buffer();
		buffer->set_text(message_from_worker_thread);
		Gtk::TextIter iter = buffer->end();
		iter.set_line_offset(0);
		auto mark = buffer->get_mark("last_line");
		buffer->move_mark(mark, iter);
		m_TextView.scroll_to(mark);
	}
}
void ExampleWindow::on_quit_button_clicked()
{
	if (m_WorkerThread)
	{
		m_Worker.stop_work();
		if (m_WorkerThread->joinable()) m_WorkerThread->join();
	}
	hide();
}
void ExampleWindow::notify()
{
	m_Dispatcher.emit();
}
void ExampleWindow::on_notification_from_worker_thread()
{
	if (m_WorkerThread && m_Worker.has_stopped())
	{
		if (m_WorkerThread->joinable()) m_WorkerThread->join();
		delete m_WorkerThread;
		m_WorkerThread = nullptr;
		update_start_stop_buttons();
	}
	update_widgets();
}
int main(int argc, char * argv[])
{
	auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
	ExampleWindow window;
	return app->run(window);
}



```

以上。