GTK+ Forums

Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
It is currently Fri Dec 19, 2014 12:13 pm

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Outputting data from two threads into two non-modal dialogs.
PostPosted: Sun Apr 22, 2012 12:36 am 
Offline
Familiar Face

Joined: Sun Apr 15, 2012 1:09 am
Posts: 6
Hello,

I am new to GTK, in fact it's my first GTK app. Briefly, I have two child threads that generate data, these data are shown in two non-modal dialogs' textviews. I have a couple of problems:
1. I am getting a run-time error on lines 138 and 158: "IA__gtk_text_view_get_buffer: assertion `GTK_IS_TEXT_VIEW (text_view)' failed"
2. The numbers are not automatically scrolled, to see the latest number I have to scroll manually.
3. I can't find to how display parent window and dialogs at a certain position on the screen.
4. Is the app generally alright?

I'd appreciate your input. Thanks in advance!

EDIT: I am using C++ and Ubuntu.

Code:
// g++ -Wall -g -pthread -std=c++0x `pkg-config --cflags --libs gtk+-2.0` gui.cpp -o gui

#include <gtk/gtk.h>
#include <iostream>
using namespace std;

static void destroy (GtkWidget*, gpointer);
static void create_dialog_box1(GtkButton *button, GtkWindow *parent);
static void create_dialog_box2(GtkButton *button, GtkWindow *parent);

pthread_t thread_one, thread_two;
void* thread_one_fun(void* arg);
void* thread_two_fun(void* arg);

GtkWidget* textview1;
GtkTextBuffer *buffer1;

GtkWidget* textview2;
GtkTextBuffer *buffer2;

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);

    // Create a parent window.
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW (window), "MyGUI");
    gtk_container_set_border_width (GTK_CONTAINER (window), 25);
    gtk_widget_set_size_request (window, 400, 100);
    g_signal_connect(G_OBJECT (window), "destroy", G_CALLBACK(destroy), NULL);

    // Create horizontal box.
    GtkWidget* hbox = gtk_hbox_new (TRUE, 5);

    // Create a new button1 with label
    GtkWidget* button1 = gtk_button_new_with_mnemonic("Button One");
    gtk_box_pack_start_defaults(GTK_BOX(hbox), button1);
    g_signal_connect(G_OBJECT (button1), "clicked", G_CALLBACK (create_dialog_box1), (gpointer) window);

    // Create a new button2 with label
    GtkWidget* button2 = gtk_button_new_with_label("Button Two");
    gtk_box_pack_start_defaults (GTK_BOX (hbox), button2);
    g_signal_connect(G_OBJECT (button2), "clicked", G_CALLBACK (create_dialog_box2), (gpointer) window);

    // Add buttons to the container.
    gtk_container_add (GTK_CONTAINER (window), hbox);
    gtk_widget_show_all(window);

    // Start data threads.
    if ( pthread_create(&thread_one, NULL,  thread_one_fun, NULL) != 0 )
        cerr << ("Run() error creating thread 1.") << endl << flush;

    if ( pthread_create(&thread_two, NULL,  thread_two_fun, NULL) != 0 )
        cerr << ("Run() error creating thread 2.") << endl << flush;

    gtk_main ();
    return 0;
}

/* Stop the GTK+ main loop function. */
static void destroy(GtkWidget *window, gpointer data)
{
    g_print("KK destroy(): distroy window was pressed.\n");
    gtk_main_quit ();
}

static void create_dialog_box1(GtkButton *button, GtkWindow *parent)
{
    /* Create a non-modal dialog with one OK button. */
    GtkWidget *dialog = gtk_dialog_new_with_buttons("Information 1", parent, GTK_DIALOG_DESTROY_WITH_PARENT,
                                                    GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);

    // Create textview.
    textview1 = gtk_text_view_new();

    gtk_widget_set_size_request (textview1, 400, 100);
    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW (textview1), GTK_WRAP_WORD);
    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview1), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview1), FALSE);

    buffer1 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview1));
    gtk_text_buffer_set_text(buffer1, "Default value one.\n", -1);
    // end of textviews
   
    // Create scrolled window.
    GtkWidget* scrolled_win = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);

    // Add widgets to containter.
    gtk_container_add(GTK_CONTAINER (scrolled_win), textview1);
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),  scrolled_win);
    gtk_widget_show_all(dialog);

    /* Call gtk_widget_destroy() when the dialog emits the response signal. */
    g_signal_connect(G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL);
}

static void create_dialog_box2(GtkButton *button, GtkWindow *parent)
{
    /* Create a non-modal dialog with one OK button. */
    GtkWidget *dialog = gtk_dialog_new_with_buttons("Information 2", parent, GTK_DIALOG_DESTROY_WITH_PARENT,
                                                    GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);

    // Create textview
    textview2 = gtk_text_view_new();

    gtk_widget_set_size_request (textview2, 400, 100);
    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW (textview2), GTK_WRAP_WORD);
    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview2), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview2), FALSE);

    buffer2 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview2));
    gtk_text_buffer_set_text(buffer2, "Default value two.\n", -1);
    // end of textviews

    // Create scrolled window.
    GtkWidget* scrolled_win = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);

    // Add widgets to containter.
    gtk_container_add(GTK_CONTAINER (scrolled_win), textview2);
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),  scrolled_win);
    gtk_widget_show_all(dialog);

    /* Call gtk_widget_destroy() when the dialog emits the response signal. */
    g_signal_connect(G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL);
}

void* thread_one_fun(void* arg)
{
    string str;
    for(int i=0;; i++)
    {
        str = "thread one: " + to_string(i) + "\n";
        cout << str << endl << flush;

        buffer1 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview1));   // I GET RUN TIME ERROR HERE
        GtkTextIter iter;
        GtkTextMark* mark = gtk_text_buffer_get_insert(buffer1);
        gtk_text_buffer_get_iter_at_mark(buffer1, &iter, mark);
        gtk_text_buffer_insert(buffer1, &iter, str.c_str(), -1);

        sleep(1);
    }

    return ((void *) 0);
}

void* thread_two_fun(void* arg)
{
    string str;
    for(int i=0;; i++)
    {
        str = "thread two: " + to_string(i) + "\n";
        cout << str << endl << flush;

        buffer2 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview2));    // I GET RUN TIME ERROR HERE
        GtkTextIter iter;
        GtkTextMark* mark = gtk_text_buffer_get_insert(buffer2);
        gtk_text_buffer_get_iter_at_mark(buffer2, &iter, mark);
        gtk_text_buffer_insert(buffer2, &iter, str.c_str(), -1);

        sleep(1);
    }

    return ((void *) 0);
}


Top
 Profile  
 
 Post subject: Re: Outputting data from two threads into two non-modal dial
PostPosted: Sun Apr 22, 2012 7:26 am 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 799
Location: UK
Hi,

I will answer your questions in the order you have given them

1. You are getting these errors because GTK+ is not thread safe although it is thread aware. See http://developer.gnome.org/gtk-faq/stable/x481.html Note that the use of gdk_threads_enter()/gdk_threads_leave() will soon be deprecated in the GTK+ 3 series and you will have to call GTK from within a single thread.

2. You have not told GTK to scroll, hence it did not do it. See the documentation at http://developer.gnome.org/gtk/stable/GtkTextView.html#gtk-text-view-scroll-to-iter

3. This can be done using the function gtk_window_move(). See the documentation at http://developer.gnome.org/gtk/stable/GtkWindow.html#gtk-window-move. Note the window manager is free to ignore requests to move a window, so this function may note always appear to function.

4. Sadly there are many mistakes in your code.

- Avoid using "using namespace std;" or any of its equivalents in a C++ application. I know it is used a lot in examples to reduce the amount of typing though in real applications it can cause problems with name space conflicts. Even the creator of C++ thinks it was a bad idea to include it in the language.
- You have not included all the needed headers.
- You have not done any initialisation to allow threads to be used.
- You have race conditions in your threads. So with your dialog windows you also create a thread. You can then destroy a window, which invalidates all references to the data but the threads are still running and trying to access the now invalid data.
- You are using GTK features that have been deprecated for many years now.
- You are not really using the features of C++ that make it useful.

Now for the pluses

+ Good to see that you are learning new tools. I do hope it will be useful to you.
+ I do hope you are willing to learning from your own mistakes, as that is partly how I learn new things.
+ Did you know that GTK has many bindings for other languages so you are not tied to the C interface. There is even an official binding for C++ called gtkmm. Although GTK is fairly object orientated already gtkmm makes access to it even easier, so you may want to consider using gtkmm instead of the C interface. Note that not everything is wrapped with the C++ binding, although you can still called the C interface for the very few that are not done. (I don't think I have ever needed to do that myself).
+ Your application can be written without using threads, with updates being done using a call back via a timer. The timer is added to the GTK main loop using g_timeout_add() or g_timeout_add_seconds(). Also without threads your code is easier to write in some ways as you do not have to worry about locking, thread safety and thread related strange bugs. This way you leave threads to what they are best for. Long running background tasks.

You do not say which versions of GTK you are using. I assumed it is GTK 2, but which one I can not tell. Note GTK is now in the version 3 with the latest being 3.4

_________________
E.


Top
 Profile  
 
 Post subject: Re: Outputting data from two threads into two non-modal dial
PostPosted: Sun Apr 22, 2012 3:41 pm 
Offline
Familiar Face

Joined: Sun Apr 15, 2012 1:09 am
Posts: 6
errol wrote:
1. You are getting these errors because GTK+ is not thread safe although it is thread aware. See http://developer.gnome.org/gtk-faq/stable/x481.html Note that the use of gdk_threads_enter()/gdk_threads_leave() will soon be deprecated in the GTK+ 3 series and you will have to call GTK from within a single thread.


This is my biggest concern. The app that I posted is a pilot. I have a multi-threaded command line application that downloads data from a remote server over tcp sockets in one thread (receive_thread), processes those data in another thread (process_thread), and sends results to another remote server in a third thread (post_thread). Download, process, and post are done asynchronously and in real time. Each thread outputs data and error messages into stdout and stderr, so the messages from all threads are intermixed in the shell. Now I want to develop a GUI that will display all those messages in separate windows.

If GTK+ will deprecate support for multi-threaded apps altogether, what is my alternative?

What comes to my mind is to create a parent window for each thread, essentially separate GTK app for each of my thread and launch everything using script. Not very elegant but what I do not know my options. I am using v. 2.


Thank you for your help, errol.


Top
 Profile  
 
 Post subject: Re: Outputting data from two threads into two non-modal dial
PostPosted: Sun Apr 22, 2012 5:04 pm 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 799
Location: UK
You may be using GTK+ 2, but which one? There are 13 releases of GTK+ 2 not including the minor releases to fix bugs.

To find out which version enter on the command line
Code:
pkg-config --modversion gtk+-2.0
to display the actual release.

GTK+ does not deprecate multi-threaded applications, just the opposite. The Glib library, GIO and some other core libraries are fully multi-thread and include functions to create threads, atomic variable access, locks, multi-threaded disc and network IO etc. The only part that is not multi-thread safe is the user interface section and this can only be called from one thread at a time using gdk_threads_enter()/gdk_threads_leave(). I presume gdk_threads_enter()/gdk_threads_leave() is being deprecated is that it is not portable and will fail on some systems (namely MS Windows).

As a note having a thread for each open window will make your application slower. This is due to higher resource usage (more memory for separate stacks, context saving area etc.), extra time needed to do context switching, extra time needed for locking of internal data structures within GTK+, extra calls needed to X11 to flush out the drawing queues.

Everything you want to do is possible to do using GTK+, it just requires some thought and planning.

For example in an application that I maintain I have 3 threads. The threads are
1 - A thread that deals with the user interface only and communicates with the other threads. This user interface does at times have many windows open.
2 - A thread that read data from many audio files from the hard disc and writes it to a buffer for each audio track
3 - A thread that reads the audio data from the buffers, processes, mixes and outputs to an audio device

Thread 3 MUST BE REAL TIME otherwise I would have audio drop outs. Thread 2 must be just quick enough to keep up with thread 3, data can be read in large blocks and does not have to be real time. Thread 1 which is the user interface just has to keep up with the user.

So for your application you could have the following threads, remember to lock memory access between them or use some sort of Inter Process Communication (there are many available so you may want to ask for a particular solution) :-

1 - The user interface
2 - TCP receive thread
3 - Data process thread
4 - TCP send thread

You say that all the threads must be in real time. I would have a little think about that and work out where the priorities should be. So the fastest most visual display units are updated are 14ms (70Hz) display units and most being at 16.7ms (60Hz). This is too fast to read anything if you are writing and then scrolling the displayed information. So the user interface thread need not be real time, just fast enough for the user. Are your TCP connections real time? or can you go read, process and send as much data as I can, but do not mind waiting if needs be. Real time has a well defined meaning in computer science.

Although you can keep adding threads, don't just add them just because you can. Each time you add a thread you add another layer of complexity and at a certain point your application will start rapidly slowing down.

_________________
E.


Top
 Profile  
 
 Post subject: Re: Outputting data from two threads into two non-modal dial
PostPosted: Sun Apr 22, 2012 6:05 pm 
Offline
Familiar Face

Joined: Sun Apr 15, 2012 1:09 am
Posts: 6
It's version 2.24.4.

To have only one, parent thread in my case, to output to the GTK GUI would require me to re-work my console app, which I'd like to avoid. Thank you for all your help, errol, I need to think it over.


Top
 Profile  
 
 Post subject: Re: Outputting data from two threads into two non-modal dial
PostPosted: Sun Apr 22, 2012 6:16 pm 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 799
Location: UK
As another thought, using GTK within every thread would stop each thread being able to work in real time due to having to lock the data structures within GTK+.

With a bit of thought you can get your application to work with the GUI code in one thread. In your other threads you just need to do a little bit of inter process communication (IPC).

_________________
E.


Top
 Profile  
 
 Post subject: Re: Outputting data from two threads into two non-modal dial
PostPosted: Thu Apr 26, 2012 8:46 pm 
Offline
Familiar Face

Joined: Sun Apr 15, 2012 1:09 am
Posts: 6
hey errol thank you for your help!!

I went with using POSIX message queue to send messages to the parent thread/window.

EDIT: I do not know how to mark thread as solved.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group