Я пытаюсь создать простой загрузчик файлов, используя GTK2 и curl.
Как видно из приведенного ниже примера, сам прогресс вычисляется и обновляется с использованием отдельного потока, но по какой-то причине он не работает должным образом. Несмотря на то, что прогресс не движется, я вижу, что файл загружается.
Может ли кто-нибудь просмотреть приведенный ниже код и сообщить, что там не так?
#include <stdio.h>
#include <gtk/gtk.h>
#include <curl/curl.h>
size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fwrite(ptr, size, nmemb, stream);
}
size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fread(ptr, size, nmemb, stream);
}
GtkWidget *Bar;
int my_progress_func(GtkWidget *bar,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
printf("progress : %f \n", (d*100.0)/t);
gdk_threads_enter();
gtk_progress_set_value(GTK_PROGRESS(bar), (d*100.0)/t);
gdk_threads_leave();
return 0;
}
void *my_thread(void *ptr)
{
CURL * curl;
FILE *outfile;
gchar *url = (gchar *) ptr;
curl = curl_easy_init();
if(curl)
{
CURLcode res;
outfile = fopen("test.exe", "w");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); /// Follow redirections ////
curl_easy_setopt(curl, CURLOPT_PROXY, "172.16.3.3:3128");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, Bar);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "%s\n", curl_easy_strerror(res));
fclose(outfile);
/* always cleanup */
curl_easy_cleanup(curl);
}
return NULL;
}
/// Window events ////
static void destroy_event(GtkWidget *widget, gpointer data)
{
gtk_main_quit();
}
static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
return FALSE; /// must return false to trigger destroy event for window
}
/// ///////////// ////
int main(int argc, char **argv)
{
GtkWidget *Window, *Frame, *Frame2;
GtkAdjustment *adj;
/* Must initialize libcurl before any threads are started */
curl_global_init(CURL_GLOBAL_ALL);
/* Init thread */
g_thread_init(NULL);
gtk_init(&argc, &argv);
/// Window ///////
Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (Window), "Downloader");
/// Widnow signals /////
//g_signal_connect(G_OBJECT(Window), "delete_event", G_CALLBACK(delete_event), NULL);
//g_signal_connect(G_OBJECT(Window), "destroy", G_CALLBACK(destroy_event), NULL);
/// Widnow signals /////
Frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(Frame), GTK_SHADOW_OUT);
gtk_container_add(GTK_CONTAINER(Window), Frame);
Frame2 = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(Frame2), GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(Frame), Frame2);
gtk_container_set_border_width(GTK_CONTAINER(Frame2), 5);
adj = (GtkAdjustment*)gtk_adjustment_new(0, 0, 100, 0, 0, 0);
Bar = gtk_progress_bar_new_with_adjustment(adj);
gtk_container_add(GTK_CONTAINER(Frame2), Bar);
gtk_widget_show_all(Window);
if (!g_thread_create(&my_thread, argv[1], FALSE, NULL) != 0){
g_warning("can't create the thread");
}
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
return 0;
}
GTK виджеты должны быть обновлены из основного потока.
Некоторое время назад я реализовал индикатор выполнения, но с помощью таймера.
Установите таймер перед вызовом gtk_main () и обновите виджет в обратном вызове таймера.
Это может быть сделано и с потоками, но нужно следовать модели производитель-потребитель, и она более сложная.
Вот хорошая ссылка:
[http://www.cc.gatech.edu/data_files/public/doc/gtk/tutorial/gtk_tut-10.html][1]
Других решений пока нет …