在C中使用GTK的GUI不显示输入的实时值

  • 本文关键字:显示 实时 GUI GTK c gtk glade
  • 更新时间 :
  • 英文 :


我正在尝试用C语言构建一个活动监视器的GUI应用程序,并决定使用GTK库和Glade来帮助设计。(使用Ubuntu 20.04)

按下按钮,数值显示在相应位置,每次点击更新一次。唯一的问题是我需要它自己实时更新,所以我将代码转换为sleep(1)的无限循环,所以它每隔1秒更新一次。但是值现在甚至没有显示在GUI上。为了测试代码是否正在执行,我尝试在控制台上打印代码不同部分的值,并且它们确实被打印出来了。

我尝试过的事情,但没有成功:

  1. 在循环和递归之间切换,都失败了。
  2. 使用time.h库将sleep()函数替换为自制计时器
  3. 将gui显示代码封装到一个函数中,并在循环中调用整个函数。
  4. 使用GDK函数强制刷新GUI,因此它在每次迭代中手动更新GUI。
  5. 在代码的不同部分使用gtk_show_all强制它在每次迭代结束时显示。

我认为它与按钮触发器有关,并且输出仅在回调函数执行后才在GUI上更新(从我对控制台打印的观察来看)。因此,我试图以编程方式按下按钮,以避免每次都要点击它,但无法找到太多的主题。

如果你能想到任何方法使这项工作或替代我正在采取的方法,请帮助。其主要思想是输出GUI应该具有实时更新的值,而不管按钮是什么。

提前感谢!

这是用于在GUI上打印值的函数:

struct timespec tm;
tm.tv_sec  = 0;
tm.tv_nsec = 1000 * 1000 * 1000;
myproc_t* myprocs = NULL;
unsigned int myprocs_len = 0;
//call to function that will return the processes and their specifications 
sample_processes(&myprocs, &myprocs_len, tm);

if(s == 0){
        // sort by CPU usage 
        qsort(myprocs, myprocs_len, sizeof(myprocs[0]), myproc_comp_pcpu);
    }
else if(s == 1){
        // sort by Memory usage 
        qsort(myprocs, myprocs_len, sizeof(myprocs[0]), myproc_comp_rss);
    }

for (i = 0; i < myprocs_len && i < 5; i++) 
{
    if (strlen(myprocs[i].cmd) == 0) {
        break;
    }
    //convert specs read from /proc file to string format
    sprintf(pid, "%d", myprocs[i].tid);
    sprintf(cpu, "%.2f",myprocs[i].pcpu);
    sprintf(memory, "%lu", myprocs[i].vm_rss/1000);
    sprintf(cmd, "%s", myprocs[i].cmd); 
    
    switch(i)
    {
        case 0:
            gtk_label_set_text(GTK_LABEL(PID1), pid);
            gtk_label_set_text(GTK_LABEL(CPU1), cpu);
            gtk_label_set_text(GTK_LABEL(MEM1), memory);
            gtk_label_set_text(GTK_LABEL(CMD1), cmd);   
        case 1:
            gtk_label_set_text(GTK_LABEL(PID2), pid);
            gtk_label_set_text(GTK_LABEL(CPU2), cpu);
            gtk_label_set_text(GTK_LABEL(MEM2), memory);
            gtk_label_set_text(GTK_LABEL(CMD2), cmd);
        case 2:
            gtk_label_set_text(GTK_LABEL(PID3), pid);
            gtk_label_set_text(GTK_LABEL(CPU3), cpu);
            gtk_label_set_text(GTK_LABEL(MEM3), memory);
            gtk_label_set_text(GTK_LABEL(CMD3), cmd);
        case 3:
            gtk_label_set_text(GTK_LABEL(PID4), pid);
            gtk_label_set_text(GTK_LABEL(CPU4), cpu);
            gtk_label_set_text(GTK_LABEL(MEM4), memory);
            gtk_label_set_text(GTK_LABEL(CMD4), cmd);   
        case 4:
            gtk_label_set_text(GTK_LABEL(PID5), pid);
            gtk_label_set_text(GTK_LABEL(CPU5), cpu);
            gtk_label_set_text(GTK_LABEL(MEM5), memory);
            gtk_label_set_text(GTK_LABEL(CMD5), cmd);       
    }
}

使用sleep(1)或任何阻塞的循环始终是不允许的,因为这意味着您有效地阻止了UI线程执行任何实际工作。正常的工作流程如下:

  1. 你想有一个主循环运行,要么使用gtk_main()gtk_application_new(),并连接到"激活"信号(当你调用gtk_application_run()时将被调用)。
  2. 初始化后端代码初始化UI代码,在其中为每个进程创建必要的小部件。此时,您可能已经希望使用gtk_widget_show()和friends使其可见。
  3. 对于周期性更新,您应该向主循环发布一个周期性事件,您可以使用g_timeout_add_seconds ()之类的API。在回调中,您可以对上一步中创建的标签调用gtk_label_set_text()。只要回调函数返回G_SOURCE_CONTINUE,回调函数就会以指定的时间间隔
  4. 定期被调用。

要详细说明@nielsdg的正确说法,基于事件循环的UI代码(如GTK)必须将阻塞代码限制到最低限度。

/* Never do this: it will freeze the UI */
static void
on_button_clicked()
{
    do {
        /* Your code here */
        sleep(1);
    while (condition);
}
相反,展开代码并利用主事件循环:
static gboolean
iteration(gpointer user_data)
{
    /* Your code here */
    return condition ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
}
static void
on_button_clicked()
{
    g_timeout_add(1000, iteration, NULL);
}

这只是给你一个主要的想法。上面的代码有一些问题,最重要的是,如果你点击两次,它会愉快地启动两个合作循环。

相关内容

  • 没有找到相关文章

最新更新