Sei sulla pagina 1di 49

Constructing interface I tried to document each step in GUI creation with screenshot and instructions.

Video of the actual process can be found on ScreenToaster site. When you start Glade3 without opening project, you're greeted by two windows: main application window that we'll be using to create interface and preferences dialog that lets you set some project settings.

You can see from this screen that we'll be using GtkBuilder project format with object names being unique across the whole project. We won't be using any images in this project, so resource location is not important to us. Lastly, this project will be compatible with GTK+ >= 2.14, which should make it usable on most distributions out there. You can check your project for any incompatibilities from this dialog too, but since we're starting new poject, this is not needed. Last thing is to click Close and we're done with initial setup. Now it's time to place initial toplevel window into project. Just click "Window" icon in "Toplevels" category and you should see something like this:

Now we'll set window title to "Charter" and default size to 600 x 400 px.

Now we need to switch to "Common" tab in properties section and set "Border width" to 6 px.

Last thing we need to do with main window is to connect gtk_main_quit function to "destroy" signal. This will close our application when we'll click on close button. I'll talk more about signals in one of the following posts, so no more details will be given here.

With our main window finished, we need to add GtkVBox to it. In previous post, we planed to add four widgets inside this box, but since menu bar and tool bar will be created using GtkUIManager and manually inserted from code, we only need two. So when we're asked about number of elements, we enter 2.

Now we'll add status bar to application. Click status bar icon and insert it into bottom of the box. Your GUI should look something like this:

Now we need to make sure that status bar is packed from bottom to top. Why is this important? If we would to pack status bar from top to bottom, it would be impossible to add menu and tool bar at the top of the application. How to ensure that status bar is packed from bottom to top? Select "Packing tab" and select "End" as pack type and set "Position" to 0. What does this do? It instructs Glade to pack status bar as first element from bottom to top.

Next is horizontal box that will hold three widgets. Creating one is exactly the same as creating vertical box. Don't forget to adjust pack type of this box to "End" too!!!

According to our widget tree, we need to insert scrolled window into first compartment. And since we only want to have vertical scrollbar present when needed, we need to

adjust horizontal scrollbar policy too.

Inserting tree view into scrolled window is easy. Click tree view icon and insert it into scrolled window. When asked about TreeView model, just leave field empty and click OK (we'll be dealing with this when we'll add data storage to our application).

Inside second field of horizontal box goes vertical button box that will hold buttons for modifying data inside tree view. We'll need six buttons inside this box: "move to top", "move up", "add", "remove", "mode down" and "move to bottom". So when we're asked about number of fields when creating vertical button box, we enter 6 and click OK. Next, we need to modify some of the properties of the button box. We'll set "Layout style" to "Center", "Spacing" to 2 and "Homogeneous" to "Yes".

Under "Packing" tab, we set the "Expand" property to "No", since we want buttons to take as little space as possible.

Creating buttons is simple. Just click button icon and place it onto proper space. Now under "General" tab search for "Stock button" and set it to "Top" (Glade will convert icon you select to stock item name automatically, so don't worry about what is written in Stock Button field).

Using exactly the same steps create another five buttons. Icons for them should be "Up", "Add", "Remove", "Down" and "Bottom".

Last empty field in horizontal box should be, according to our widget tree, filled with vertical box that will house two children widgets. End result should look something like this:

Inside bottom compartment, we need to add horizontal button box for two toggle buttons. We'll also set "Layout style" to "Center", "Spacing" to 2 and "Homogeneous" to "Yes".

Under packing, set "Expand" property to "No", since we want this section to take as

little space as possible.

Adding buttons to this box is very similar to adding them to vertical box. But we'll add check buttons here instead of normal ones. Simply click Check button icon and insert it into empty space inside button box. After that, change the button's label to "Show points". Repeat those steps to create second check button and change label to "Show lines".

Upper part of the vertical box will house scrolled window. Add it exactly the same as we added the one for data part. Inside this scrolled window we need to insert viewport.

Next in widget tree is alignment which will take care of centering the drawing area. Creating one is simple: just click Alignment icon and insert it into viewport. We also

need to modify horizontal and vertical scale values. Those two control the expansion of the child widget. Value 1 means "Expand child as much as possible", 0 means "Do not expand child at all" while 0.4 means "Expand child to take 40% of allocated size". We'll set both values to 0, since we want our child to be constant in size.

Inserting frame takes some work in our case, since Glade3 does some things by default that we don't need. Click frame icon and insert it into alignment. Change "Frame shadow" to "In" and you should end up with something like this:

You can see that Glade automatically added label and alignment into frame. We don't need any, so we'll delete them both. This is how things look after removal:

And we are left with only one widget to pack: drawing area. Click icon and insert it into bottom compartment of frame (upper one will be left empty, since we don't want any

label). Under "Common", set widget's size request to 300 x 200 px and we're done.

There is only one thing left to do for today: save and test this GUI. We'll write really simple C application for this task. I won't explain much about code today, since we'll be dealing with coding in later posts, but most of the code should be easy to understand. ?
/* * Compile me with: * gcc -o tut tut.c $(pkg-config --cflags --libs gtk+-2.0 gmodule2.0) */ #include <gtk/gtk.h> int main( int argc, char **argv ) { GtkBuilder *builder; GtkWidget *window; GError *error = NULL; /* Init GTK+ */ gtk_init( &argc, &argv ); /* Create new GtkBuilder object */ builder = gtk_builder_new(); /* Load UI from file. If error occurs, report it and quit application. * Replace "tut.glade" with your saved project. */ if( ! gtk_builder_add_from_file( builder, "tut.glade", &error ) )

{ g_warning( "%s", error->message ); g_free( error ); return( 1 );

/* Get main window pointer from UI */ window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) ); /* Connect signals */ gtk_builder_connect_signals( builder, NULL ); /* Destroy builder, since we don't need it anymore */ g_object_unref( G_OBJECT( builder ) ); /* Show window. All other widgets are automatically shown by GtkBuilder */ gtk_widget_show( window ); /* Start main loop */ gtk_main(); return( 0 );

Now run this code and be amazed;). Not too bad, but nothing special either. Try resizing window to see how compartments behave when resized. Do you like it? I don't either, so join me next time when we'll be dealing with space alocation/requisition in detail. And we're done. If you need more detailed process of clicking/changing properties, head to ScreenToaster site, where you can watch video of the whole thing. Hope you'll find it useful. Finished glade file can be obtained from here: tut.glade. Stay healthy

Glade3 tutorial (3) - Size negotiation

Hello. Last time we managed to create initial GUI for our "Charter" application. Today we'll try to make it more flexible. I'll explain some fundamental things about size negotiation process first, and after this introduction, we'll play a bit with Glade. Contents

Glade3 tutorial (1) - Introduction Glade3 tutorial (2) - Constructing interface Glade3 tutorial (3) - Size negotiation Glade3 tutorial (4) - GtkTreeView data backend Glade3 tutorial (5) - Modifying widget tree Glade3 tutorial (6) - Signals

Size negotiation in GTK+ Knowing how GTK+ handles widget sizing is one of the most important things if you want to create clean and efficient GUIs. I called this process negotiation, since there are two processes that contribute to final result: size requisition and size allocation. Size requistion stage can be seen as recursive "How much space do you need?" question. Toplevel window ask this question it's child, child widgets asks their children, ... This process ends when all of the widgets in tree responded. In our case this process looks like this:

There are two important findings in this cascade: 1. Child widgets do not know anything about parent's size preferences 2. Parent bases it's size on sizes of it's children Now that toplevel window knows how much space is needed in ideal conditions, it decides how much space will actually be available. If requisition stage returned some sensible value, this is what is ususally used. But if we manually changed size request of toplevel window (using gtk_window_set_default_size for example) or for any other reason requested size cannot be used, window will discard it and use different one. And here is where the second stage begins: allocation. This can be also seen as a command: "Here you have some space, do whatever you need to do to fit into it." that is passed from parent to it's children. And if the widget has more than one child, it's also responsible to properly divide available space among it's children. Remember this, since this is very important for things that come next. Widget packing I won't talk about packing much here, since this topic is nicely represented in official GTK+ tutorial. And now it's time for you to go there and read packing section: GTK+ tutorial - Packing Widgets. Done reading? Good. Now how are all these different options placed inside Glade? They can be found on two separate places: 1. "General" tab of container widget contains options that are set on container widget itself (examples would be "Homogeneous" property of GtkVBox or "Column spacing" of GtkTable. 2. "Packing" tab of widget that is being added into container contains options that are set at insertion time (in code, those parameters are set using gtk_box_pack_* and gtk_table_attach family of functions. Are things relativelly clear now? If not, try recreating packing examples from tutorial using Glade3. This is a great practise to get code<->Glade3 connections properly set up. Widget packing and application resizing And we finally came to the point where we'll talk about the problem with our application - resizing is just wrong;). But before we get too exited and start editing our interface, let's put some of the newly acquired knowledge to work. We'll describe how central horizontal box determines sizes of it's children. I took a screenshot of two instances of our application, running at the same time. I resized them and measured parts of the horizontal box. You can see that the central part with buttons retained it's width, while other two parts gained equal amount of space. Why is this so? This is caused by second part of the size negotiation process. Parent widget allocated some extra space to the horizontal box and box then divided that extra space among it's

chilren with expand property on.

Now we're ready to start fixing our application by making left part of the application fixed in size. Start up glade, load project and click on left scrolled window.

Now go to "Common" tab and set width request to 150 px. This will ensure that scrolled widget always requests 150 px wide space from parent.

But this is not enough to make our left side fixed, since size requests only specify minimal amount of space that this widget needs. It's completely legal to allocate more space to it. To make sure that scrolled window gets exactly 150 px, we need to modify it's packing parameters too. Open "Packing" tab and set "Expand" property to "No".

With things set like this, any extra space that will be allocated to horizontal box will be added to display area. Save your project and run sample application again. BTW, you don't need to recompile it to see the changes, since we only changed glade file, which is loaded at runtime. See how our GUI reacts to resizing now?

And this concludes today's part of tutorial. Feel free to experiment with different packing options. Final glade file of today's tutorial can be downloaded from here (make sure you rename it to tut.glade before trying to run our sample application). You're all invited to join me next time, when we'll add data store to out tree view and connect some buttons to their signals. Stay healthy

Model-view-controller (MVC) design

Before we start doing anything, we need to know some things about how GtkTreeView operates. Information about this can be found in API documentation and in GtkTreeView tutorial. I would advice you to read those two references if you're just starting to code with GTK+. If you don't have time to read those two in entirety, I'll just quickly sum the contents: 1. GtkTreeView is basically just container that holds columns and provides surface on which cell renderers draw on. 2. GtkCellRenderer is object that draws inside tree view based on the data inside backend storage. 3. GtkTreeModel is interface that any data store needs to implement if it wants to be used as a data backend to tree view. 4. GtkTreeStore and GtkListStore are two data stores that implement GtkTreeModel interface and are part of GTK+ itself. Keep this in mind since it's crucial for understanding what exactly will we be doing with glade. Creating data backend Load project form last post into Glade. Now scroll down widget catalog until you see "Tree Model" part. Expand it and click on "List Store" icon. New entry will appear under "Objects" inside object tree.

You'll probably want to resize right panel to get more space for editing list store properties. We'll do two things now:

1. Define number of columns inside list store and their types 2. Add some sample data to list store for demonstration purposes Number of columns and their types are defined in upper part of the "General" tab. Simply start typing into proper field to add new column. What columns do we need? Since we're developing charting application, we'll need one column that will hold X coordinates of the points, one column to hold Y coordinates and one column for optional label that can be added to point. Types of the columns will be gdouble for X and Y coordinates and gchararray for label column (gchararray is just another name for "gchar *" that is registered using GType system).

Now we'll add some sample data to list store. Scroll down to expose data insertion part, add six rows using plus button and fill them with with data like this:
+-------+-------+---------------------+ | - 4.5 | - 2 | Start | | - 3 | - 1.5 | | | - 1.5 | - 1 | | | 0 | - 0.5 | Y axis intersection | | 1.5 | 0 | X axis intersection | | 3 | 0.5 | End | +-------+-------+---------------------+

Our list store is finished now. Connecting tree view with list store Next, we need to connect tree view and list store. Select tree view and in "General" tab click button with "..." next to the "TreeView model" field. Inside pop-up dialog select "liststore1" and click "OK".

Adding display components to tree view Now we need to add columns to out tree view that will hold cell renderers. When we select tree view, new icon labeled "Edit..."will appear at the end of the toolbar. Clicking it will open new window with tree view editor.

Inside "General" tab of Tree View Editor we can see some information we already entered before. To create display parts of tree view, we need to switch to "Hierarchy" tab, where we're presented with a lot of empty space;). To add new column, click "Add" button and new column will be added to left field, with it's properties displayed in rigth field. We'll change column's title to "X" and this is all that we'll do with this column.

Now right-click newly created column and select "Add child Spin item", which will add new GtkCellRendererSpin to hierarchy. Now we need to again change some properties. But since changing cell renderer's properties is a bit different that changing properties of normal objects, I'll explain this a bit more in detail. If you look at the properties editor, you'll notice that it is composed from 4 columns: first column holds check button, second column holds property name, third column holds spin button and last column holds combo box. Why are things so complex? Because cell renderers can have their properties set on two different ways: on a global basis, which means that this property will be the same for all lines that cell renderer draws; or on a per-line basis, which means that property is stored inside data backend and is read from there for each line. How do these two methods map onto property editor? Check button controls whether property is set per-line or global (active check button means per-line, inactive means

global). When we set some property on a per-line basis, we must inform cell renderer in which column of data store are values for this property stored, and we can use spin button to directly input column number or combo box to select column based on label that we assigned to it when constructing list store. When we set some property on a global scale, spin button and combo box are hidden and we get regular property editing widget instead. Now let's set up our spin cell renderer. First property that we're goint to set is "Text". We want to display values from data backend, so we'll leave the check button activated and set the column, from which this property will take it's values to X-coords column.

Second property that we'll set is "Adjustment" property. We'll set it on a global scale, since we want all lines to have the same range of available values. So we need to remove tick from check button and then click the button with "..." and create new adjustment (we don't have any yet, so we created it).

Last property that we'll set is "Editable". We'll again set it globaly to "Yes", since we want all our X coordinate cells to be editable.

Now we need to create another column for Y coordinates. Process is exactly the same as before:

right-click X column and select "Add Column item" change title to "Y" right click Y column and select "Add child Spin item" Adjust properties exactly as before (when setting "Text" property, slect Ycoords clumns as source of values and when you set "Adjustment" property, you don't create new one, just reuse adjustment1 that we created for X spin renderer)

Last column that we need to add will hold point labels. We again right-click on column Y and select "Add Column item". We set it's title property to "Label". Now right-click on Label columns and select "Add child Text item&qout; On the text renderer, set "Text" property to Label column adn "Editable" property globaly to "Yes".

There is one last thing that we need to do: adjusting properties of adjustment1 that we created when setting properties of spin button. Close tree view editor and select adjustment1 from object tree. Now set minimum value to -5000, maximum value to 5000, step increment to 0.1, page increment to 10 and page size to 0 (only adjustments that are connected to scroll bar should have non-zero page size).

You can see that values that we entered into list store are now displayed inside tree view. But we have a problem: labels are not visible because we set size request of the scrolled window too low. But how can we determine how much space will we need on different themes with different font sizes? The answer is: "We cannot.". And this is why we'll modify our GUI in next part of this tutorial and make it more flexible. As usual, you can get the latest glade file or watch screencast. Little Glade3-3.6.7 bug Glade3-3.6.7 has some problems with loading projects that use somewhat complex cell renderer settings. In our case, the problematic part is adjustment for spin buttons. You'll notice that when you load this project next time, adjustment property will be marked like it was set on per line basis (see image for details). All you have to do is remove those checks and you should be OK. This bug is already fixed in git version, so all that we have to do is wait for next release.

So long and stay healthy

Glade3 tutorial (5) - Modifying widget tree


Hi. Last time we added data backend to our application, but we discovered at the end that our GUI isn't flexible enough. We'll fix that today by adding paned widget into widget tree and replace image and label buttons with image only buttons. Contents

Glade3 tutorial (1) - Introduction Glade3 tutorial (2) - Constructing interface Glade3 tutorial (3) - Size negotiation Glade3 tutorial (4) - GtkTreeView data backend Glade3 tutorial (5) - Modifying widget tree Glade3 tutorial (6) - Signals

New widget tree We're going to do some changes to the widget tree. We'll do those changes on paper first and from that we'll create our modification plan. And here are old and new widget trees:

What steps do we need to make to transform old tree (on the left side of the image) into new tree (on the right side)? 1. Add GtkHPaned as parent of GtkHBox 2. Reparent display area GtkHBox from GtkHBox to GtkHPaned 3. Add GtkAlignment as parent of GtkVButtonBox

4. Replace GtkVButtonBox with GtkVBox 5. Replace stock items on buttons with stock images only We'll also need to adjust some widget and packing parameters, but to keep initial plan as simple as possible, I'll just describe those changes on-the-go. Inserting widget into widget tree So far, we were building our GUI in top-to-bottom linear manner, where we first constructed parent of the widget and than widget itself. Now I'll show you how to insert widget in the middle of the widget tree. Right click GtkHBox from widget tree and select "Add Parent -> Horizontal Panes". You should end up wih something similar to this:

Now we need to reparent display area vertical box to right pane. Right click in vertical box in widget tree and select "Cut". Now right click inside right pane and select "Paste". Simple.

You can see that we have some empty space on the right side of our buttons. To remove it, select horizontal box and decrese number of elements to 2.

Next on the list is adding GtkAlignment as parent of vertical button box. This process is analogous to adding panes. Right click vertical button box and select "Add Parent ->

Alignment". That's all there is to it.

Creating image-only buttons Last thing we need to do is replace vertical button box with normal vertical box and add only stock icons to the buttons. The simplest way of doing this is to delete the button box and recreate buttons from scratch. We could cut-paste each button, but creating them will be faster in this case. After deleting button box, insert vertical box with 6 elements into empty space and set element spacing to 2 px.

Now create new button, insert it into one of the fields in vertical box and set it's content to custom.

Inside empty space insert GtkImage and set it's stock ID to "e;Top"e;.

Now repeate this five more times, using stock ID's "Up", "Add", "Remove", "Down" and "Bottom". You should see something like this when you finish:

Final tweaks

All that is left now is to tweak our GUI properties a bit. First thing we need to do is make data scrolled window expandable and scrollable. Select scrolledwindow1 and under "General" tab set "Horizontal Scrollbar Policy" to "Automatic". Now open "Packing" tab and set "Expand" to "Yes". Now select alignment2 and under "General" tab set "Horizontal scale" and "Vertical Scale" to 0, which will make our buttons centered. And we're done. Save the interface and have fun. Get complete glade file or watch screencast as usual. Next time we'll connect some callbacs (and I mean it;). Stay healthy until then

Glade3 tutorial (6) - Signals


Hello.

As promised, we'll finally connect some signals to handlers and write some C code that will do something useful. Contents

Glade3 tutorial (1) - Introduction Glade3 tutorial (2) - Constructing interface Glade3 tutorial (3) - Size negotiation Glade3 tutorial (4) - GtkTreeView data backend Glade3 tutorial (5) - Modifying widget tree Glade3 tutorial (6) - Signals

Signals tab in Glade If you select one of the buttons, signals tab will look like this (I color-coded columns):

Inside "Signal" column are listed signals, grouped by widget type. "Handler" column is where you insert name of the function that should be hooked to this signal. "User data" column can hold object name from this glade file that should be sent as data parameter to callback function (note: if field is not empty, signal will be connected the same way as if you were to call g_signal_connect_swapped macro). Last, "After" column holds check button that controls how you callback is connected: if checked, your signal will be connected like you were to call g_signal_connect_after, which means that your function will be called after the default signal handler. For 99% of time, you can safely ignore last two columns, since they are rarely used, but

it's still nice to know why exactly they stand there for. Signal connection theory Signal connection is done in two stages: 1. Names of the functions that should be evoked when signal is emitted are specified in "Signals" tab in Glade. 2. Mapping of function name -> function address is done at runtime by either using GModule or manually by user. First stage is simple: just fill appropriate fields inside "Signals" tab and save the project. Second stage is more complex to understand, because there are two different ways of mapping function names to function addresses. First one (the simple one) is to use gtk_builder_connect_signals function, which will automatically search for function with proper names in your main executable. I won't go into details of how this is done, but keep in mind that GModule is required for this operation. Second method of mapping names to addresses is to use gtk_builder_connect_signals_full, where you must provide function that will do the mapping. In this tutorial, we'll use automatic method, since this is what most people will end up using in their applications. Passing custom data to callbacks Any modestly complex application will require some data exchange to and from callback functions. So how can we achieve this? As already mentioned, one way of passing data to callback function is to specify object name in Glade. This is simple, but fairly limited method, since only object from your UI file can be passed like this. More flexible way of passing data to callbacks is by using data parameter of gtk_builder_connect_signals. Whatever you specify as a last parameter will be passed to all of the connected callbacks as a last parameter. "But there is only one argument available, and I would need more!" Well, this problem is usually solved by defining a structure that holds all of the data that may be needed in any of the callbacks. Pointer to an instance of that structure is then passed as a last parameter to gtk_builder_connect_signals. Callback handler definition When using gtk_builder_connect_signals to connect signals, you most take some additional steps to ensure that functions are found by GModule. Exact steps needed are platform specific, but Glib developers kindly provided means to write portable code. Before function declaration/definition, you should place G_MODULE_EXPORT macro. So if we have callback handler for button's "clicked" signal defined like this in non-glade application:

static void cb_clicked( GtkButton *button, gpointer data ) { /* CODE HERE */ }

we need to modify it to this in order for GModule to be able to find it: ?


G_MODULE_EXPORT void cb_clicked( GtkButton *button, gpointer data ) { /* CODE HERE */ }

Other step needed to make code function properly on all platforms is to link executable with proper linker flags. Thanks to Glib developers, all that we need to do is add "gmodule-2.0" to pkg-config parameters. If you compiled your normal applications using compile lines similar to those: ?
gcc -c object1.c $(pkg-config --clfags gtk+-2.0) gcc -c object2.c $(pkg-config --cflags gtk+-2.0) gcc -o app object1.o object2.o $(pkg-config --libs gtk+-2.0)

then adopted new compile lines will look like this: ?


gcc -c object1.c $(pkg-config --clfags gtk+-2.0 gmodule-2.0) gcc -c object2.c $(pkg-config --cflags gtk+-2.0 gmodule-2.0) gcc -o app object1.o object2.o $(pkg-config --libs gtk+-2.0 gmodule2.0)

Preparing glade file Before we can start writing code, we need to do some minor modifications to glade file: rename "window1" to "main_window", rename "drawingarea1" to "chart_area" and connect "expose-event" to cb_expose_chart function.

Note though, renaming is not needed, but it's more convenient if we rename widgets that we need. With these changes in place, we're ready to start creating code. Code This is where Glade cannot help us anymore. Let's start by creating empty folder that will hold all of our files. Now copy latest glade file you saved here and rename it to "charter.glade". Our code will be initially divided into 3 source files:

1. charter.c will hold main function 2. support.h will hold some convenience macros and main data structure definition 3. callbacks.c will hold functions that we connected in Glade We'll start with support.h file. ?
#ifndef __SUPPORT_H__ #define __SUPPORT_H__ #include <gtk/gtk.h> /* Convenience macros for obtaining objects from UI file */ #define CH_GET_OBJECT( builder, name, type, data ) \ data->name = type( gtk_builder_get_object( builder, #name ) ) #define CH_GET_WIDGET( builder, name, data ) \ CH_GET_OBJECT( builder, name, GTK_WIDGET, data ) /* Main data structure definition */ typedef struct _ChData ChData; struct _ChData { /* Widgets */ GtkWidget *main_window; /* Main application window */ GtkWidget *chart_area; /* Chart drawing area */ }; #endif /* __SUPPORT_H__ */

First thing we do is include GTK+ header file, which will provide type and function declarations. Next we define two macros that will make obtaining object pointers less verbose. Consult your favourite C preprocessor manual for more information about how this two macros work. Last thing in this file is structure definition. This structure will serve as our main data storage. We'll add more fields to it gradually. For starters, main window and chart area pointers will suffice. Next file that we'll create is charter.c. ?
#include "support.h" #define UI_FILE "charter.glade" int main( int argc, char **argv ) { ChData *data; GtkBuilder *builder; GError *error = NULL; /* Init GTK+ */ gtk_init( &argc, &argv ); /* Create new GtkBuilder object */ builder = gtk_builder_new(); if( ! gtk_builder_add_from_file( builder, UI_FILE, &error ) ) { g_warning( "%s", error->message );

g_free( error ); return( 1 );

/* Allocate data structure */ data = g_slice_new( ChData ); /* Get objects from UI */ #define GW( name ) CH_GET_WIDGET( builder, name, data ) GW( main_window ); GW( chart_area ); #undef GW /* Connect signals */ gtk_builder_connect_signals( builder, data ); /* Destroy builder, since we don't need it anymore */ g_object_unref( G_OBJECT( builder ) ); /* Show window. All other widgets are automatically shown by GtkBuilder */ gtk_widget_show( data->main_window ); /* Start main loop */ gtk_main(); /* Free any allocated data */ g_slice_free( ChData, data ); return( 0 );

This file is almost exactly the same as tut.c file from second part of this tutorial. One important difference is creation of data structure that is passed to all of the callbacks. Last file is callbacks.c. We'll be adding callback handlers into this file as we connect them inside Glade. There is only one function that we need to write: cb_expose_chart. But before we start writing anything, we need to check what prototype expose handler should have. Navigate to GtkWidget API reference and search for "expose-event" signal. (Or, if you're too lazy to search for yourself, click this link.) In reference, you'll see that expose event handler should be defined as: ?
gboolean function( GtkWidget *widget, GdkEventExpose *event, gpointer user_data )

Having this knowledge, we can now create callback file. ?


#include "support.h" G_MODULE_EXPORT gboolean cb_expose_chart( GtkWidget *widget, GdkEventExpose *event, ChData *data ) { cairo_t *cr;

/* Create cairo context from GdkWindow */ cr = gdk_cairo_create( event->window ); /* Paint whole area in green color */ cairo_set_source_rgb( cr, 0, 1, 0 ); cairo_paint( cr ); /* Destroy cairo context */ cairo_destroy( cr ); /* Return TRUE, since we handled this event */ return( TRUE ); }

You can see that we added G_MODULE_EXPORT in front of function definition. We also modified last parameter to avoid some casting. You can ignore function body for now, since cairo drawing will be explained more when we'll start drawing our charts. Compiling application Last thing we need to do is compile application. This will in our case be done in three steps: 1. compile charter.c into charter.o 2. compile callbacks.c into callbacks.o 3. link both object files into charter This translates into terminal command lines: ?

gcc -c charter.c -o charter.o $(pkg-config --cflags gtk+-2.0 gmodule2.0) gcc -c callbacks.c -o callbacks.o $(pkg-config --cflags gtk+-2.0 gmodule-2.0) gcc -o charter charter.o callbacks.o $(pkg-config --libs gtk+-2.0 gmodule-2.0)

Since this can be quite demanding to write for each recompilation, I created Makefile that should make things a bit easier. ?
CC CFLAGS LIBS DEBUG = = = = gcc `pkg-config --cflags gtk+-2.0 gmodule-2.0` `pkg-config --libs gtk+-2.0 gmodule-2.0` -Wall -g

OBJECTS = charter.o callbacks.o .PHONY: clean all: charter charter: $(OBJECTS) $(CC) $(DEBUG) $(LIBS) $(OBJECTS) -o $@ charter.o: charter.c support.h $(CC) $(DEBUG) $(CFLAGS) -c $< -o $@

callbacks.o: callbacks.c support.h $(CC) $(DEBUG) $(CFLAGS) -c $< -o $@ clean: rm -f *.o charter

Consult you make manual for detailed information about this file. And we're done for today. Getting sources This is something new. I created a git repository at Gtihub.com to host files from this tutorial. I'll pack a code from each part of the tutorial and upload it on Github.com, but if you prefer to use git for obtaining code, I'll also tag commits. Here is my Github repository and here are files for this tutorial part. I also started converting posts from this tutorial into a DocBook format, which should hopefully make this tutorial more flexible and available in more formats. Stay healthy

Potrebbero piacerti anche