微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决在 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,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. 初始化您的后端代码
  3. 初始化您的 UI 代码,您可以在其中为每个进程创建必要的小部件。此时,您可能已经希望使用 gtk_widget_show() 和朋友
  4. 使其可见
  5. 对于定期更新,您应该将定期事件发布到主循环,您可以使用 g_timeout_add_seconds () 之类的 API。在回调中,您可以在上一步中创建的标签上调用 gtk_label_set_text()。只要回调返回G_SOURCE_CONTINUE,就会按照指定的时间间隔定期调用回调
,

为了扩展@nielsdg 所说的正确,基于事件循环(例如 GTK)的 UI 代码必须将阻塞代码限制在最低限度。

/* 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);
}

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。