Blog (9)
Komentarze (7)
Recenzje (0)

Generator wykresów w Gnuplot w GTKmm

@skrzeczol755Generator wykresów w Gnuplot w GTKmm08.09.2012 22:31

Tematem tego wpisu jest opracowanie podstawowych założeń takiego generatora i napisanie go z użyciem GTKmm 3+. Na początek parę informacji związanych z Gnuplotem:

  • Zanim wygenerujesz ładny wykes, musisz się namęczyć z poleceniami
  • „Klepanie poleceń” i tego typu rzeczy dzieją się głównie w trybie interaktywnym, w którym ustala się najwięcej parametrów takiego wykresu
  • Możesz wyskalować osie dzięki włączeniu automatycznego skalowania
  • Możesz włączyć skalę logarytmiczną na osi X lub Y
  • Możesz nazwać linie i punkty na wykresie, osie, ustawić tytuł wykresu itd.

Może dla niektórych wyżej wymienione rzeczy wydają się oczywiste, ale mają znaczenie przy naszym zadaniu. Naszym zadaniem jest przyporządkowanie tych poleceń odpowiednim zdarzeniom w naszym generatorze. Aby ułatwić sobie zadanie, będziemy generować skrypt, który będzie przechowywać te wszystkie polecenia niezbędne do utworzenia wykresu. Ten skrypt wczytamy jako parametr gnuplota i dostaniemy wykres w jego oknie.

Po co nam ten generator?!

Ten program ma ułatwić generowanie wykresów na komputerkach z gnuplotem i ułatwić wykorzystanie jego potencjału tym wszystkim, którzy potrzebują wykresów, np. do zrobienia sprawozdania z laborek z (tu wstawcie nazwę przedmiotu), a nie mają czasu lub nie mogą bądź nie chcą uczyć się kolejnego języka programowania (tu skryptów).

Co będzie potrzebne?

  • Zainstalowany w systemie i działający Gnuplot,
  • Kompilator i biblioteka Gtkmm
  • Edytor tekstu
  • Do sprawdzenia poprawności generowania wykresów: plik z danymi do wykresu

Jeśli chodzi o wymagania dotyczące pliku z danymi do wykresu, to jedną kolumnę trzeba zarezerwować na oś X, drugą na oś Y, ewentualnie trzecią na błędy pomiarowe. Kolumny oddzielamy spacją lub tabulatorem, a dane dla kolejnych punktów są w wierszach.

Przykładowy plik z danymi będzie dostępny pod tym linkiem.

Interfejs

Opis interfejsu zacznę od najwyższego poziomu, a skończę na najniższych. Wykorzystamy układ Box (Pudełko) w rozmieszczeniu pionowym. Użyjemy 16 elementów. Pierwsze trzy będą w układzie Box w rozmieszczeniu poziomym i zawierają dwa elementy: etykietę i liniowe wejście tekstowe (GtkEntry).

Następnie umieszczamy etykietę tekstową, a pod nią w następnej komórce układu układ poziomy (GtkBox) z dwiema etykietami i liniowymi polami tekstowymi (GtkEntry) ułożonymi na przemian. Powtarzamy dwukronie.

Kolejnym elementem jest przycisk, a następnie pole do zaznaczenia (GtkCheckBox) wraz z położonym poniżej liniowym wejściem tekstowym.

Następne dwa elementy są takie jak te trzy pierwsze, a następnie dwa pola do zaznaczenia (ptaszkiem) ułożone kolejno po sobie w pionie.

Na koniec idzie przycisk.

Etykiety tekstowe po kolei: Graph title, X axis label, Y axis label, X range, Begin, End, Y Range, Begin, End, Load data file, Graph label, Fit function below to loaded data, Fitting function label, Parameters, Draw linear regression, Show grid, Generate graph

Kod

Zanim zaczniemy dalsze programowanie, należy dorzucić nagłówki fstream oraz cstdlib. UWAGA: jeśli ktoś użyje innego sposobu uruchomienia Gnuplota z poziomu tego programu, użyć innego nagłówka, który zapewnia odpowiednią funkcję.

Jeżeli projekt tworzymy z pomocą Anjuta, pod linijką [code=cpp]#define UI_FILE "src/gnuplot_graph__generator.ui"[/code]

umieszczamy deklaracje zmiennych. Użyjemy typów Gtk::Entry, Gtk::Button, Gtk::CheckButton, Glib::ustring, bool.


Gtk::Entry *title, *xlabel, *ylabel, *xstart, *ystart, *xend,
*yend, *graphlabel, *fitfun, *fitfunlab, *vars;
Gtk::Button *load_data, *generate;
Gtk::CheckButton *enfitting, *linreg, *grid;
Glib::ustring data_file_filename;
bool with_linreg = false;
bool with_fitting = false;

Po deklaracji zmiennych, możemy przystąpić do deklaracji funkcji. Najpierw przyjrzymy się funkcji odpowiedzialnej za włączenie dopasowania funkcji do danych:


void enable_fitting()
{
	if(!fitfun->get_sensitive())
	{
		fitfun->set_sensitive();
		fitfunlab->set_sensitive();
		vars->set_sensitive();
		with_fitting = true;
	}
	else
	{
		with_fitting = false;
		fitfun->set_sensitive(false);
		fitfunlab->set_sensitive(false);
		vars->set_sensitive(false);
	}
}

W funkcji sprawdza się, czy jest dostępne dla użytkownika pole zawierające wzór funkcji dopasowania do wykresu. Jeśli nie, zostaje włączone (właściwość sensitive == true), a wraz z nim pole pozwalające na definicję etykiety dla takiej funkcji (będzie widoczna w legendzie wykresu) oraz pole zawierające parametry funkcji dopasowania do danych — bez parametrów taka funkcja nie zadziała prawidłowo w Gnuplot. Ponadto wartość with_fitting zostanie ustawiona na „true”

Jeśli jest inaczej, wszystkie wskazane wyżej elementy interfejsu będą całkowicie wyłączone z użytku, a wartość with_fitting zostanie ustawiona na „false”.

Następna funkcja ma za zadanie zmienić wartość with_linreg z „false” na „true” lub na odwrót:


void enable_linreg()
{
	if(!with_linreg)
		with_linreg = true;
	else
		with_linreg = false;
}

Ta funkcja obsługuje zdarzenie kliknięcia na przycisk z etykietą „Load data file” i jej zadaniem jest otwarcie okna dialogowego otwierania pliku i ustalenie nazwy wybranego pliku z danymi.


void open_data_file()
{
	Gtk::FileChooserDialog open_file("Open data file");
	open_file.add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
	open_file.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);

	if(open_file.run())
	{
		data_file_filename = open_file.get_filename();
	}
}

Metoda add_button() pozwala na dodanie do dialogu dwóch przycisków. Pierwszy argument to etykieta (tu z zestawu standardowych), a drugi to sygnał wysyłany po kliknięciu.

Ostatnia funkcja, którą dodamy to poniższa funkcja:


void generate_graph()
{
	double xs = std::strtod(xstart->get_text().c_str(),NULL);
	double xe = std::strtod(xend->get_text().c_str(),NULL);
	double ys = std::strtod(ystart->get_text().c_str(),NULL);
	double ye = std::strtod(yend->get_text().c_str(),NULL);
	
	Glib::ustring xrange = Glib::ustring::compose("set xrange[%1:%2]\n",
	                                            Glib::ustring::format(xs),
	                                            Glib::ustring::format(xe));
	Glib::ustring yrange = Glib::ustring::compose("set yrange[%1:%2]\n",
	                                            Glib::ustring::format(ys),
	                                            Glib::ustring::format(ye));
	Glib::ustring gtitle = Glib::ustring::compose("set title \"%1\"\n", title->get_text());
	Glib::ustring gxlabel = Glib::ustring::compose("set xlabel \"%1\"\n", xlabel->get_text());
	Glib::ustring gylabel = Glib::ustring::compose("set ylabel \"%1\"\n", ylabel->get_text());
	Glib::ustring plot = Glib::ustring::compose("plot '%1' using 1:2 title '%2'",
	                                            data_file_filename, graphlabel->get_text());
	Glib::ustring ffun = Glib::ustring::compose("f(x)=%1;\nfit f(x) '%2' using 1:2 via %3\n",
	                                            fitfun->get_text(), data_file_filename, vars->get_text());
	Glib::ustring glinreg = Glib::ustring::compose("g(x)=aa*x+bb;\nfit g(x) '%1' using 1:2 via aa, bb\n", data_file_filename);
	
	std::ofstream script("/tmp/generating-script");
	script << "set autoscale\n";
	script << "unset log\n";
	script << "unset label\n";
	script << gtitle;
	script << gxlabel;
	script << gylabel;
	script << xrange;
	script << yrange;

	if(grid->get_active())
	{
		script << "set grid\n";
	}

	if(with_fitting == true && with_linreg == true)
	{
		script << ffun;
		script << glinreg;
		script << Glib::ustring::compose("%1, \\\nf(x) title '%2', \\\ng(x) title 'Linear regression'\n",
		                                 plot, fitfunlab->get_text());
	}

	if(with_fitting == true && with_linreg == false)
	{
		script << ffun;
		script << Glib::ustring::compose("%1, \\\nf(x) title '%2'\n",
		                                 plot, fitfunlab->get_text());
	}

	if(with_fitting == false && with_linreg == true)
	{
		script << glinreg;
		script << Glib::ustring::compose("%1, \\\ng(x) title 'Linear regression'\n", plot);
	}                                           

	if(with_fitting == false && with_linreg == false)
	{
		script << plot << std::endl;
	}                                           

	script.close();
	system("gnuplot -persist /tmp/generating-script");
	
}

Pierwsza część: zamieniamy na liczby zakres osi X i Y. Druga część: tworzymy linijki tekstu zawierające polecenia dla programu Gnuplot. Wykorzystujemy tutaj funkcję Glib::ustring::compose(const Glib::ustring format, ...) do formatowania tekstu. Do formatowania liczb (zamiany ich na Glib::ustring) używana jest funkcja Glib::ustring::format().

Trzecia część: Tworzymy strumień wyjściowy w pliku „/tmp/generating-script”, a następnie zapisujemy do niego linie tekstu. Używamy również funkcji sprawdzających włączenie określonych opcji i zawarcie ich w generowanym pliku. Na koniec zostaje zamknięty.

Czwarta część: Wywołujemy program Gnuplot funkcją system(const char*) i używamy trybu wsadowego (pliku skryptu, który utworzyliśmy). Parametr „-persist” oznacza, że nie zostanie zamknięte okno z wykresem, który Gnuplot wygeneruje, dzięki temu możemy obejrzeć wykres i zapisać.

Kompilacja i uruchomienie

Kompilacja jest typowa dla prostych programów wykorzystujących GTKmm, więc w Anjuta wystarczy domyślna konfiguracja dla tego typu projektów. Po uruchomieniu powinno pojawić się takie okno:

Okno działającego programu z danymi do wygenerowania przykładowego wykresu
Okno działającego programu z danymi do wygenerowania przykładowego wykresu

Dla przykładowych danych można wygenerować następujący wykres:

Otrzymany wykres w terminalu X11 Gnuplota
Otrzymany wykres w terminalu X11 Gnuplota

Podsumowanie

Taki generator co prawda nie zapewnia pełnej obsługi funkcji oferowanych przez Gnuplota, ale pozwala na wykonanie wykresów opierających się na danych doświadczalnych z pliku wraz z nazwami osi, tytułem, możliwością dopasowania funkcji wprowadzonej przez użytkownika do wczytanych danych oraz włączenia rysowania regresji liniowej — aproksymacji liniowej funkcji z punktami danymi w pliku.

Przy okazji pozwoli na opanowanie podstaw tworzenia skryptów dla Gnuplota w celu usprawnienia generowania wykresów.

Szanowna Użytkowniczko! Szanowny Użytkowniku!
×
Aby dalej móc dostarczać coraz lepsze materiały redakcyjne i udostępniać coraz lepsze usługi, potrzebujemy zgody na dopasowanie treści marketingowych do Twojego zachowania. Twoje dane są u nas bezpieczne, a zgodę możesz wycofać w każdej chwili na podstronie polityka prywatności.

Kliknij "PRZECHODZĘ DO SERWISU" lub na symbol "X" w górnym rogu tej planszy, jeżeli zgadzasz się na przetwarzanie przez Wirtualną Polskę i naszych Zaufanych Partnerów Twoich danych osobowych, zbieranych w ramach korzystania przez Ciebie z usług, portali i serwisów internetowych Wirtualnej Polski (w tym danych zapisywanych w plikach cookies) w celach marketingowych realizowanych na zlecenie naszych Zaufanych Partnerów. Jeśli nie zgadzasz się na przetwarzanie Twoich danych osobowych skorzystaj z ustawień w polityce prywatności. Zgoda jest dobrowolna i możesz ją w dowolnym momencie wycofać zmieniając ustawienia w polityce prywatności (w której znajdziesz odpowiedzi na wszystkie pytania związane z przetwarzaniem Twoich danych osobowych).

Od 25 maja 2018 roku obowiązuje Rozporządzenie Parlamentu Europejskiego i Rady (UE) 2016/679 (określane jako "RODO"). W związku z tym chcielibyśmy poinformować o przetwarzaniu Twoich danych oraz zasadach, na jakich odbywa się to po dniu 25 maja 2018 roku.

Kto będzie administratorem Twoich danych?

Administratorami Twoich danych będzie Wirtualna Polska Media Spółka Akcyjna z siedzibą w Warszawie, oraz pozostałe spółki z grupy Wirtualna Polska, jak również nasi Zaufani Partnerzy, z którymi stale współpracujemy. Szczegółowe informacje dotyczące administratorów znajdują się w polityce prywatności.

O jakich danych mówimy?

Chodzi o dane osobowe, które są zbierane w ramach korzystania przez Ciebie z naszych usług, portali i serwisów internetowych udostępnianych przez Wirtualną Polskę, w tym zapisywanych w plikach cookies, które są instalowane na naszych stronach przez Wirtualną Polskę oraz naszych Zaufanych Partnerów.

Dlaczego chcemy przetwarzać Twoje dane?

Przetwarzamy je dostarczać coraz lepsze materiały redakcyjne, dopasować ich tematykę do Twoich zainteresowań, tworzyć portale i serwisy internetowe, z których będziesz korzystać z przyjemnością, zapewniać większe bezpieczeństwo usług, udoskonalać nasze usługi i maksymalnie dopasować je do Twoich zainteresowań, pokazywać reklamy dopasowane do Twoich potrzeb. Szczegółowe informacje dotyczące celów przetwarzania Twoich danych znajdują się w polityce prywatności.

Komu możemy przekazać dane?

Twoje dane możemy przekazywać podmiotom przetwarzającym je na nasze zlecenie oraz podmiotom uprawnionym do uzyskania danych na podstawie obowiązującego prawa – oczywiście tylko, gdy wystąpią z żądaniem w oparciu o stosowną podstawę prawną.

Jakie masz prawa w stosunku do Twoich danych?

Masz prawo żądania dostępu, sprostowania, usunięcia lub ograniczenia przetwarzania danych. Możesz wycofać zgodę na przetwarzanie, zgłosić sprzeciw oraz skorzystać z innych praw wymienionych szczegółowo w polityce prywatności.

Jakie są podstawy prawne przetwarzania Twoich danych?

Podstawą prawną przetwarzania Twoich danych w celu świadczenia usług jest niezbędność do wykonania umów o ich świadczenie (tymi umowami są zazwyczaj regulaminy). Podstawą prawną przetwarzania danych w celu pomiarów statystycznych i marketingu własnego administratorów jest tzw. uzasadniony interes administratora. Przetwarzanie Twoich danych w celach marketingowych realizowanych przez Wirtualną Polskę na zlecenie Zaufanych Partnerów i bezpośrednio przez Zaufanych Partnerów będzie odbywać się na podstawie Twojej dobrowolnej zgody.