我正在尝试用C语言构建一个活动监视器的GUI应用程序,并决定使用GTK库和Glade来帮助设计。(使用Ubuntu 20.04)
按下按钮,数值显示在相应位置,每次点击更新一次。唯一的问题是我需要它自己实时更新,所以我将代码转换为sleep(1)的无限循环,所以它每隔1秒更新一次。但是值现在甚至没有显示在GUI上。为了测试代码是否正在执行,我尝试在控制台上打印代码不同部分的值,并且它们确实被打印出来了。
我尝试过的事情,但没有成功:
- 在循环和递归之间切换,都失败了。
- 使用time.h库将sleep()函数替换为自制计时器
- 将gui显示代码封装到一个函数中,并在循环中调用整个函数。
- 使用GDK函数强制刷新GUI,因此它在每次迭代中手动更新GUI。
- 在代码的不同部分使用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线程执行任何实际工作。正常的工作流程如下:
- 你想有一个主循环运行,要么使用
gtk_main()
或gtk_application_new()
,并连接到"激活"信号(当你调用gtk_application_run()
时将被调用)。 初始化后端代码初始化UI代码,在其中为每个进程创建必要的小部件。此时,您可能已经希望使用 - 对于周期性更新,您应该向主循环发布一个周期性事件,您可以使用
g_timeout_add_seconds ()
之类的API。在回调中,您可以对上一步中创建的标签调用gtk_label_set_text()
。只要回调函数返回G_SOURCE_CONTINUE
,回调函数就会以指定的时间间隔 定期被调用。
gtk_widget_show()
和friends使其可见。要详细说明@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);
}
这只是给你一个主要的想法。上面的代码有一些问题,最重要的是,如果你点击两次,它会愉快地启动两个合作循环。