首页 > 解决方案 > 在 C 中使用 GTK 的 GUI 不显示输入的实时值

问题描述

我正在尝试为 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);       
    }
}

标签: cgtkglade

解决方案


使用带有阻塞的循环sleep(1)或任何阻塞的东西总是不行的,因为这意味着您实际上是在阻止 UI 线程执行任何实际工作。正常的工作流程如下:

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

推荐阅读