C++ 下载文件时未更新进度

C++ 下载文件时未更新进度,c++,libcurl,gtk2,C++,Libcurl,Gtk2,我正在尝试使用GTK2和curl创建一个简单的文件下载程序 正如您在下面的示例中所看到的,正在使用单独的线程计算和更新进度,但由于某些原因,它无法正常工作。即使进程没有移动,我也可以看到文件正在下载 任何人都可以检查下面的代码,让我知道有什么问题吗 #include <stdio.h> #include <gtk/gtk.h> #include <curl/curl.h> size_t my_write_func(void *ptr, size_t size

我正在尝试使用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;
}
#包括
#包括
#包括
大小\我的写入\函数(void*ptr、大小\ t size、大小\ t nmemb、文件*流)
{
返回fwrite(ptr、大小、nmemb、流);
}
大小为my\u read\u func(无效*ptr,大小为,大小为nmemb,文件为*流)
{
返回fread(ptr、大小、nmemb、流);
}
GtkWidget*条;
int my_progress_func(GtkWidget*bar,
双t,/*dltotal*/
双d,/*dlnow*/
双ultotal,
双ulnow)
{
printf(“进度:%f\n”,(d*100.0)/t);
gdk_threads_enter();
gtk_进度设置值(gtk_进度(巴),(d*100.0)/t);
gdk_线程_离开();
返回0;
}
void*my_线程(void*ptr)
{
卷曲*卷曲;
文件*输出文件;
gchar*url=(gchar*)ptr;
curl=curl_easy_init();
if(curl)
{
卷曲编码;
outfile=fopen(“test.exe”,“w”);
curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);///跟随重定向////
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\u easy\u setopt(curl,CURLOPT\u WRITEFUNCTION,my\u write\u func);
curl\u easy\u setopt(curl,CURLOPT\u READFUNCTION,my\u read\u func);
curl\u easy\u setopt(curl,CURLOPT\u NOPROGRESS,0L);
curl\u easy\u setopt(curl,CURLOPT\u PROGRESSFUNCTION,my\u progress\u func);
curl_easy_setopt(curl,CURLOPT_PROGRESSDATA,Bar);
res=旋度(curl)\u容易执行(curl);
如果(res!=卷曲(OK)
fprintf(stderr,“%s\n”,curl\u easy\u strerror(res));
fclose(输出文件);
/*总是清理*/
旋度\轻松\清洁(旋度);
}
返回NULL;
}
///窗口事件////
静态void destroy_事件(GtkWidget*小部件,gpointer数据)
{
gtk_main_quit();
}
静态gboolean delete_事件(GtkWidget*小部件、GdkEvent*事件、gpointer数据)
{
return FALSE;///必须返回FALSE才能触发窗口的销毁事件
}
/// ///////////// ////
int main(int argc,字符**argv)
{
GtkWidget*窗口,*框架,*框架2;
GTK调整*adj;
/*必须在启动任何线程之前初始化libcurl*/
curl\u global\u init(curl\u global\u ALL);
/*初始化线程*/
g_thread_init(NULL);
gtk_init(&argc,&argv);
///窗口///////
窗口=gtk_窗口_新建(gtk_窗口_顶层);
gtk_窗口设置标题(gtk_窗口(窗口),“下载程序”);
///Widnow信号/////
//g_信号连接(g_对象(窗口),“删除_事件”,g_回调(删除_事件),NULL);
//g_信号连接(g_对象(窗口),“销毁”,g_回调(销毁事件),NULL);
///Widnow信号/////
Frame=gtk_Frame_new(空);
gtk_帧_设置_阴影_类型(gtk_帧(帧),gtk_阴影_输出);
gtk_容器添加(gtk_容器(窗口),框架);
Frame2=gtk_frame_new(空);
gtk_帧_集_阴影_类型(gtk_帧(帧2),gtk_阴影_-IN);
gtk_容器添加(gtk_容器(框架),框架2);
gtk_容器_设置_边框_宽度(gtk_容器(框架2),5);
调整=(gtk调整*)gtk调整新(0,0,100,0,0,0);
Bar=gtk_进度_Bar_新_,带调整(调整);
gtk_容器添加(gtk_容器(框架2),条形);
gtk_小部件_全部显示(窗口);
如果(!g_thread_create(&my_thread,argv[1],FALSE,NULL)!=0){
g_警告(“无法创建线程”);
}
gdk_threads_enter();
gtk_main();
gdk_线程_离开();
返回0;
}

必须从主线程更新GTK小部件

一段时间前,我实现了一个进度条,但是在计时器的帮助下

在调用gtk_main()之前安装计时器,并在计时器回调中更新小部件

它也可以通过线程来完成,但是需要遵循生产者-消费者模型,而且它更复杂

这里有一个很好的链接:
[

我是否应该将此添加到我的线程以允许GTK刷新UI?
而(GTK_events_pending()){GTK_main_iteration()}
但即使在线程中使用此选项,它也不能正常工作。。