美文网首页C语言我用 Linux
使用Gtk写一个360网速指示器

使用Gtk写一个360网速指示器

作者: 霡霂976447044 | 来源:发表于2019-05-29 15:49 被阅读2次

    效果如下:


    Peek 2019-05-29 14-57.gif

    使用Linux Mint很久了,其实在这,之前写了一个比较丑的网速指示器。


    image

    刚开始使用pygtk写,后来使用c语言写,Gtk资料少。其实也就写了不到两天,很多地方代码还没有优化,因为自己在实习,上班也没有什么事干,于是就想实现下比较好看的效果。总的来说,GTK+资料少,写起来还是比较费劲。下面说下思路。

    1. 实现悬浮窗

    实现悬浮窗的是使用GTK的GTK_WINDOW_POPUP类型,它可以实现没有任务窗口栏显示。

    window = gtk_window_new(GTK_WINDOW_POPUP);
    

    2. 获取网络、内存信息

    主要是读取/proc/net/dev/proc/meminfo

    int getMemPercentage() {
    
        FILE *fd_men = fopen("/proc/meminfo", "r");
        if(fd_men == NULL) {
            perror("open file /proc/meminfo error");
            exit(-1);
        }
    
        char tmp[255];
        fgets(tmp, 255, fd_men);
        unsigned long men = 0;
        unsigned long free = 0;
        sscanf(tmp, "MemTotal: %ld KB", &men);
        fgets(tmp, 255, fd_men);
        fgets(tmp, 255, fd_men);
        sscanf(tmp, "MemAvailable: %ld KB", &free);
    
        unsigned long use = men - free;
        int percent = ((float)use / (float)men) * 100;
    
    
        fclose(fd_men);
        return percent;
    }
    
    // 获取网速
    void getNetworkBandWidth(unsigned long long int *receiveBytes, unsigned long long int *sendBytes) {
        char *buf;
        const int bufSize = 255;
        FILE *devfd;
    
        buf = (char *) calloc(bufSize, 1);
    
        devfd = fopen("/proc/net/dev", "r");
        if (devfd == NULL) {
            perror("open file /proc/net/dev failure.");
            exit(-1);
        }
    
        // Ignore the first and second lines of the file.
        fgets(buf, bufSize, devfd); // fgets will return if reading a newline.
        fgets(buf, bufSize, devfd);
        *receiveBytes = 0;
        *sendBytes = 0;
    
        while (fgets(buf, bufSize, devfd)) {
            unsigned long long int rBytes, sBytes;
            char *line = strdup(buf);
    
            char *dev;
            dev = strtok(line, ":");
            gchar *is_lo = g_strrstr(dev, "lo");
            if (is_lo != NULL) { // if end with lo
                continue;
            }
            sscanf(buf + strlen(dev) + 2, "%llu %*d %*d %*d %*d %*d %*d %*d %llu", &rBytes, &sBytes);
            *receiveBytes += rBytes;
            *sendBytes += sBytes;
            free(line);
        }
    
        fclose(devfd);
        free(buf);
    }
    

    网速的逻辑参照deepin的任务管理器的网速获取。

    3. 设置透明

    需要桌面系统支持。

    // 配置透明
    static void tran_setup(GtkWidget *win) {
        GdkScreen *screen;
        GdkVisual *visual;
    
        gtk_widget_set_app_paintable(win, TRUE);
        screen = gdk_screen_get_default();
        visual = gdk_screen_get_rgba_visual(screen);
    
        if (visual != NULL && gdk_screen_is_composited(screen)) {
            gtk_widget_set_visual(win, visual);
            g_print("ok");
        }
    }
    

    4. 绘制界面

    绘制界面是使用的是cairo

    static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
                                  gpointer user_data) {
        GtkWidget *win = gtk_widget_get_toplevel(widget);
    
        cairo_t *first_cr, *second_cr;
        cairo_surface_t *first, *second;
    
        int width, height;
        char tmp[255];
    
        gtk_window_get_size(GTK_WINDOW(win), &width, &height);
    
        first = cairo_surface_create_similar(cairo_get_target(cr),
                                             CAIRO_CONTENT_COLOR_ALPHA, width, height);
    
        second = cairo_surface_create_similar(cairo_get_target(cr),
                                              CAIRO_CONTENT_COLOR_ALPHA, width, height);
    
    
    
        first_cr = cairo_create(first);
        second_cr = cairo_create(second);
    
    
        // 画左边的圆
        cairo_translate(first_cr, SIZE/2+6, height / 2); // 设置中心点
    
        cairo_set_line_width(first_cr, PEN_WIDTH); // 设置线条宽度
        cairo_set_source_rgb(first_cr, 0.9, 0.9, 0.9); // 设置圆的外边框颜色
        cairo_arc(first_cr, 0, 0, SIZE / 2, 0, 2 * M_PI);
        cairo_stroke_preserve(first_cr); // 画外边框
    
        cairo_set_source_rgb(first_cr, 0.85, 0.8, 0.8); // 设置圆的填充颜色(灰色)
        cairo_fill(first_cr); // 填充可用内存
    
        float start = 0.5 - ((float)memPercentage / 100);
        float end = 0.5 + ((float)memPercentage / 100);
        cairo_arc(first_cr, 0, 0, SIZE / 2, M_PI * start, M_PI * end);
    //    cairo_close_path(first_cr);
        if(memPercentage <=50) {
            cairo_set_source_rgb(first_cr, 0.1, 0.7, 0.1); // 设置圆的填充颜色 绿色
        } else if(memPercentage <= 79) {
            cairo_set_source_rgb(first_cr, 1, 0.6, 0); // 设置圆的填充颜色 黄色
        } else {
            cairo_set_source_rgb(first_cr, 0.9, 0, 0); // 设置圆的填充颜色 红色
        }
    
        cairo_fill(first_cr); // 填充已经使用的内存
    
        cairo_set_source_rgb(first_cr, 0.1, 0.1, 0.1);
        cairo_select_font_face(first_cr, "Ubuntu Mono",
                               CAIRO_FONT_SLANT_NORMAL,
                               CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size(first_cr, 16);
        cairo_move_to(first_cr, -12, 4);
        sprintf(tmp, "%d%%", memPercentage);
        cairo_show_text(first_cr, tmp);
    
    
        // 矩形
        cairo_set_source_rgb(second_cr, 0.9, 0.9, 0.9); // 右边的颜色
        cairo_rectangle(second_cr, 0, 0, RIGHT_CIRCLE_WIDTH, RIGHT_CIRCLE_SIZE); // 矩形
        cairo_fill(second_cr);
    
        // 右边的半圆
        cairo_set_source_rgb(second_cr, 0.9, 0.9, 0.9); // 右边的颜色
        cairo_translate(second_cr, 100, 25);
        cairo_arc(second_cr, 0, 0, RIGHT_CIRCLE_SIZE / 2, 0, 2 * M_PI);
        cairo_fill(second_cr);
        cairo_set_source_rgb(second_cr, 0.1, 0.1, 0.1);
        cairo_select_font_face(second_cr, "Serif",
                               CAIRO_FONT_SLANT_NORMAL,
                               CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size(second_cr, 11);
    
        cairo_move_to(second_cr, -(RIGHT_CIRCLE_WIDTH-SIZE/2), -8);
        sprintf(tmp, " ↑ %lu kb/s", csD);
        cairo_show_text(second_cr, tmp);
        cairo_move_to(second_cr, -(RIGHT_CIRCLE_WIDTH-SIZE/2), 15);
        sprintf(tmp, " ↓ %lu kb/s", crD);
        cairo_show_text(second_cr, tmp);
    
        // 添加
        cairo_set_operator(first_cr, CAIRO_OPERATOR_DEST_OVER);
        cairo_set_source_surface(first_cr, second, 0, -SIZE/2+5);
        cairo_paint(first_cr);
    
        cairo_set_source_surface(cr, first, 0, 0);
        cairo_paint(cr);
    
        cairo_destroy(first_cr);
        cairo_destroy(second_cr);
    
    
        return FALSE;
    }
    

    比较麻烦的是内存占用的绘制,这里是使用的绘制圆弧实现的,圆的中心点是(0, 0), 下图是角度示意:

    截图_2019-05-29_15-32-56.png
    要绘制内存填充区域,需要知道内存占用比例,根据这个比例,计算圆弧的开始角度和结束角度。
    假设内存占用50%,那么应该是 开始角度为:0 * M_PI,结束角度为 1 * M_PI, 等价于以下代码动态实现:
    float start = 0.5 - ((float)memPercentage / 100);
    float end = 0.5 + ((float)memPercentage / 100);
    

    start为负数的时候,自然会逆时针绘制。

    再者,绘制界面的时候,使用了两个surface,一个绘制圆+圆弧,一个绘制矩形+圆。这需要使用cairo的组合。

    自启动

    把二进制文件放到$PATH里面,加可执行权限。
    在自启动管理软件里面执行这个二进制文件,如果发现会边框黑,加上sleep 20。等你的桌面完全启动好再启动。

    下载

    有空会上传到github,这里先给出下载链接

    http://file.alonebo.top/360ball

    相关文章

      网友评论

        本文标题:使用Gtk写一个360网速指示器

        本文链接:https://www.haomeiwen.com/subject/tmnwzqtx.html