效果如下:
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), 下图是角度示意:
要绘制内存填充区域,需要知道内存占用比例,根据这个比例,计算圆弧的开始角度和结束角度。
假设内存占用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,这里先给出下载链接
网友评论