|
Packit |
1470ea |
|
|
Packit |
1470ea |
xmlns:its="http://www.w3.org/2005/11/its"
|
|
Packit |
1470ea |
type="topic"
|
|
Packit |
1470ea |
id="image-viewer.cpp">
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<info>
|
|
Packit |
1470ea |
<link type="guide" xref="cpp#examples"/>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<desc>A little bit more than a simple "Hello world" GTKmm application.</desc>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<revision pkgversion="0.1" version="0.1" date="2011-03-18" status="review"/>
|
|
Packit |
1470ea |
<credit type="author">
|
|
Packit |
1470ea |
<name>GNOME Documentation Project</name>
|
|
Packit |
1470ea |
<email its:translate="no">gnome-doc-list@gnome.org</email>
|
|
Packit |
1470ea |
</credit>
|
|
Packit |
1470ea |
<credit type="author">
|
|
Packit |
1470ea |
<name>Johannes Schmid</name>
|
|
Packit |
1470ea |
<email its:translate="no">jhs@gnome.org</email>
|
|
Packit |
1470ea |
</credit>
|
|
Packit |
1470ea |
<credit type="editor">
|
|
Packit |
1470ea |
<name>Marta Maria Casetti</name>
|
|
Packit |
1470ea |
<email its:translate="no">mmcasetti@gmail.com</email>
|
|
Packit |
1470ea |
<years>2013</years>
|
|
Packit |
1470ea |
</credit>
|
|
Packit |
1470ea |
</info>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<title>Image viewer</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<synopsis>
|
|
Packit |
1470ea |
In this tutorial, you will learn:
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>Some basic concepts of C++/GObject programming </item>
|
|
Packit |
1470ea |
<item>How to write a Gtk application in C++ </item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</synopsis>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<media type="image" mime="image/png" src="media/image-viewer.png"/>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="anjuta">
|
|
Packit |
1470ea |
<title>Create a project in Anjuta</title>
|
|
Packit |
1470ea |
Before you start coding, you'll need to set up a new project in Anjuta. This will create all of the files you need to build and run the code later on. It's also useful for keeping everything together.
|
|
Packit |
1470ea |
<steps>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Start Anjuta and click <guiseq><gui>File</gui><gui>New</gui><gui>Project</gui></guiseq> to open the project wizard.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Choose <gui>GTKmm (Simple)</gui> from the <gui>C++</gui> tab, click <gui>Forward</gui>, and fill out your details on the next few pages. Use <file>image-viewer</file> as project name and directory.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Make sure that <gui>Use GtkBuilder for user interface</gui> is disabled as we will
|
|
Packit |
1470ea |
create the UI manually in this tutorial. Check the <link xref="guitar-tuner.cpp">Guitar-Tuner</link>
|
|
Packit |
1470ea |
tutorial if you want to learn how to use the interface builder.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Click <gui>Apply</gui> and the project will be created for you. Open <file>src/main.cc</file> from the <gui>Project</gui> or <gui>File</gui> tabs. You should see some code which starts with the lines:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
#include <gtkmm.h>
|
|
Packit |
1470ea |
#include <iostream>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
#include "config.h">]]>
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</steps>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="build">
|
|
Packit |
1470ea |
<title>Build the code for the first time</title>
|
|
Packit |
1470ea |
This is a very basic C++ code setting up GTKmm. More details are given below; skip this list if you understand the basics:
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The three #include lines at the top include the config (useful autoconf build defines), gtkmm (user interface) and iostream (C++-STL) libraries. Functions from these libraries are used in the rest of the code.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The main function creates a new (empty) window and sets the window title.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The kit::run() call starts the GTKmm main loop, which runs the user interface and starts listening for events (like clicks and key presses). As we give the window
|
|
Packit |
1470ea |
as an argument to that function, the application will automatically exit when
|
|
Packit |
1470ea |
that window is closed.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
This code is ready to be used, so you can compile it by clicking <guiseq><gui>Build</gui><gui>Build Project</gui></guiseq> (or press <keyseq><key>Shift</key><key>F7</key></keyseq>).
|
|
Packit |
1470ea |
Press <gui>Execute</gui> on the next window that appears to configure a debug build. You only need to do this once, for the first build.
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="ui">
|
|
Packit |
1470ea |
<title>Creating the user interface</title>
|
|
Packit |
1470ea |
Now we will bring life into the empty window. GTKmm organizes the user interface
|
|
Packit |
1470ea |
with Gtk::Container s that can contain other widgets and even other containers. Here we
|
|
Packit |
1470ea |
will use the simplest available container, a Gtk::Box :
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
int
|
|
Packit |
1470ea |
main (int argc, char *argv[])
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
Gtk::Main kit(argc, argv);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Gtk::Window main_win;
|
|
Packit |
1470ea |
main_win.set_title ("image-viewer-cpp");
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Gtk::Box* box = Gtk::manage(new Gtk::Box());
|
|
Packit |
1470ea |
box->set_orientation (Gtk::ORIENTATION_VERTICAL);
|
|
Packit |
1470ea |
box->set_spacing(6);
|
|
Packit |
1470ea |
main_win.add(*box);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
image = Gtk::manage(new Gtk::Image());
|
|
Packit |
1470ea |
box->pack_start (*image, true, true);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Gtk::Button* button = Gtk::manage(new Gtk::Button("Open Imageā¦"));
|
|
Packit |
1470ea |
button->signal_clicked().connect (
|
|
Packit |
1470ea |
sigc::ptr_fun(&on_open_image));
|
|
Packit |
1470ea |
box->pack_start (*button, false, false);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
main_win.show_all_children();
|
|
Packit |
1470ea |
kit.run(main_win);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
return 0;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
]]>
|
|
Packit |
1470ea |
<steps>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The first lines create the widgets we want to use: a button for opening up an image, the image view widget itself and the box we will use as a container.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The calls to pack_start add the two widgets to the box and define their behaviour. The image will
|
|
Packit |
1470ea |
expand into any available space while the button will just be as big as needed. You will notice that we don't set
|
|
Packit |
1470ea |
explicit sizes on the widgets. In GTKmm this is usually not needed as it makes it much easier to have a layout that
|
|
Packit |
1470ea |
looks good in different window sizes. Next, the box is added to the window.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
We need to define what happens when the user clicks on the button. GTKmm uses the concept of signals. When the button is clicked, it fires the clicked signal, which we can connect to some action. This is done using the signal_clicked().connect
|
|
Packit |
1470ea |
method which tells GTKmm to call the on_open_image function when the button is clicked. We will define the callback in the next section.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The last step is to show all widgets in the window using
|
|
Packit |
1470ea |
show_all_children() . This is equivalent to using the show()
|
|
Packit |
1470ea |
method on all our child widgets.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</steps>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="show">
|
|
Packit |
1470ea |
<title>Showing the image</title>
|
|
Packit |
1470ea |
We will now define the signal handler for the clicked signal or the
|
|
Packit |
1470ea |
button we mentioned before. Add this code before the main
|
|
Packit |
1470ea |
method.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Gtk::Image* image = 0;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
on_open_image ()
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
Gtk::FileChooserDialog dialog("Open image",
|
|
Packit |
1470ea |
Gtk::FILE_CHOOSER_ACTION_OPEN);
|
|
Packit |
1470ea |
dialog.add_button (Gtk::Stock::OPEN,
|
|
Packit |
1470ea |
Gtk::RESPONSE_ACCEPT);
|
|
Packit |
1470ea |
dialog.add_button (Gtk::Stock::CANCEL,
|
|
Packit |
1470ea |
Gtk::RESPONSE_CANCEL);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Glib::RefPtr<Gtk::FileFilter> filter =
|
|
Packit |
1470ea |
Gtk::FileFilter::create();
|
|
Packit |
1470ea |
filter->add_pixbuf_formats();
|
|
Packit |
1470ea |
filter->set_name("Images");
|
|
Packit |
1470ea |
dialog.add_filter (filter);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
const int response = dialog.run();
|
|
Packit |
1470ea |
dialog.hide();
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
switch (response)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
case Gtk::RESPONSE_ACCEPT:
|
|
Packit |
1470ea |
image->set(dialog.get_filename());
|
|
Packit |
1470ea |
break;
|
|
Packit |
1470ea |
default:
|
|
Packit |
1470ea |
break;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
]]>
|
|
Packit |
1470ea |
This is a bit more complicated than anything we've attempted so far, so let's break it down:
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The dialog for choosing the file is created using the
|
|
Packit |
1470ea |
Gtk::FileChooserDialog constructor. This takes the title and type of the dialog. In our case, it is an Open dialog.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The next two lines add an Open and a Close button to the dialog.
|
|
Packit |
1470ea |
Notice that we are using stock button names from Gtk, instead of manually typing "Cancel" or "Open". The advantage of using stock names is that the button labels will already be translated into the user's language.
|
|
Packit |
1470ea |
The second argument to the add_button() method is a value to identify
|
|
Packit |
1470ea |
the clicked button. We use predefined values provided by GTKmm here, too.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
The next two lines restrict the <gui>Open</gui> dialog to only display files which can be opened by Gtk::Image . A filter object is created first; we then add all kinds of files supported by Gdk::Pixbuf (which includes most image formats like PNG and JPEG) to the filter. Finally, we set this filter to be the <gui>Open</gui> dialog's filter.
|
|
Packit |
1470ea |
Glib::RefPtr is a smart pointer used here, that makes sure that the filter is
|
|
Packit |
1470ea |
destroyed when there is no reference to it anymore.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
dialog.run displays the <gui>Open</gui> dialog. The dialog will wait for the user to choose an image; when they do, dialog.run will return the value Gtk::RESPONSE_ACCEPT (it would return Gtk::RESPONSE_CANCEL if the user clicked <gui>Cancel</gui>). The switch statement tests for this.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
We hide the <gui>Open</gui> dialog because we don't need it any more. The dialog would be hidden later anyway, as it is only a local variable and is
|
|
Packit |
1470ea |
destroyed (and therefore hidden) when the scope ends.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>Assuming that the user did click <gui>Open</gui>, the next line loads the
|
|
Packit |
1470ea |
file into the Gtk::Image so that it is displayed.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="build2">
|
|
Packit |
1470ea |
<title>Build and run the application</title>
|
|
Packit |
1470ea |
All of the code should now be ready to go. Click <guiseq><gui>Build</gui><gui>Build Project</gui></guiseq> to build everything again, and then <guiseq><gui>Run</gui><gui>Execute</gui></guiseq> to start the application.
|
|
Packit |
1470ea |
If you haven't already done so, choose the <file>Debug/src/image-viewer</file> application in the dialog that appears. Finally, hit <gui>Run</gui> and enjoy!
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="impl">
|
|
Packit |
1470ea |
<title>Reference Implementation</title>
|
|
Packit |
1470ea |
If you run into problems with the tutorial, compare your code with this <link href="image-viewer/image-viewer.cc">reference code</link>.
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="next">
|
|
Packit |
1470ea |
<title>Next steps</title>
|
|
Packit |
1470ea |
Here are some ideas for how you can extend this simple demonstration:
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Have the user select a directory rather than a file, and provide controls to cycle through all of the images in a directory.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Apply random filters and effects to the image when it is loaded and allow the user to save the modified image.
|
|
Packit |
1470ea |
<link href="http://www.gegl.org/api.html">GEGL</link> provides powerful image manipulation capabilities.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Allow the user to load images from network shares, scanners, and other more complicated sources.
|
|
Packit |
1470ea |
You can use <link href="http://library.gnome.org/devel/gio/unstable/">GIO</link> to handle network file transfers and the like, and <link href="http://library.gnome.org/devel/gnome-scan/unstable/">GNOME Scan</link> to handle scanning.
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</page>
|