博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接

文章目录

  • 自制一款简单的抽奖软件
    • 一、参与抽奖同事信息准备
    • 二、准备GTK4环境
    • 三、开始Coding
      • 3.1 创建抽奖资源池
      • 3.2 创建抽奖界面
    • 四、程序使用建议

抽奖软件视频

自制一款简单的抽奖软件

一、参与抽奖同事信息准备


工号和姓名之间使用水平制表符分隔,每行只显示一个同事的信息。

二、准备GTK4环境

安装传送门

三、开始Coding

3.1 创建抽奖资源池

我们首先创建一个单向循环链表,实现同事信息的记录。这里没有使用顺序表存储,主要是考虑到参与抽奖的同事人数可能增加,如果顺序表预分配的太小,还需要修改code。

基本数据结构:

struct employee_s{char name[50];   //同事姓名char number[20]; //同事工号struct employee_s *next;
};typedef struct{int employee_quantity;  //参与抽奖的人说struct employee_s *tail; //单向循环链表尾指针struct employee_s * luck_employee; //某一轮中获奖的同事
}employee_queue_t;static employee_queue_t * g_employee_queue; //全局变量,记录了参与抽奖的所有同事信息
#define get_employee_queue_head(q) (q)->tail->next;//获取链表头节点

创建单向循环链表:

/** input:* employee_queue:一个用于存放参与抽奖同事信息的结构体指针,程序内部分配内存*  filename:参与抽奖同事信息文件的路径*/
static void
create_employee_queue(employee_queue_t ** employee_queue,char *filename)
{g_assert(filename!=NULL);FILE *fp=NULL;//创建一个抽奖池*employee_queue = g_malloc0(sizeof(employee_queue_t));g_assert(employee_queue!=NULL);//初始化抽奖池状态信息(*employee_queue)->employee_quantity=0;//创建一个空链表头struct employee_s *head = g_malloc0(sizeof(struct employee_s));g_assert(head!=NULL);head->next = head;//构成一个单向循环链表(*employee_queue)->tail = head;//打开记录有同事信息的文件fp = fopen(filename,"r");g_assert(fp!=NULL);struct employee_s *employee;while(!feof(fp)){//为一个同事创建信息块employee = g_malloc0(sizeof(struct employee_s));g_assert(employee!=NULL);//从尾部插入到单向循环链表中employee->next=(*employee_queue)->tail->next;(*employee_queue)->tail->next=employee;(*employee_queue)->tail=employee;++(*employee_queue)->employee_quantity;//读取同事工号fscanf(fp,"%s\t",employee->number);//读取同事姓名fgets(employee->name,sizeof(employee->name),fp);//去掉结尾的换行符if(employee->name[strlen(employee->name)-1]=='\n')employee->name[strlen(employee->name)-1]='\0';}//关闭文件fclose(fp);
}

当某个同事中奖后,我们需要把他从抽奖池中移除,避免二次中奖,所以还需要一个从链表中删除节点的功能:

/** input:*  employee_queue:资源池指针*    employee:需要删除的同事信息节点*/
static void
remove_employee_from_queue(employee_queue_t *employee_queue,struct employee_s *employee)
{g_assert(employee_queue && employee);//定义一个临时的指针,用于遍历寻找需要删除的同事节点struct employee_s * prev = get_employee_queue_head(employee_queue);while(prev->next != employee)prev = prev->next;//将该同事从链表中删除prev->next = employee->next;employee->next=NULL;//如果删除的是尾节点,需要重新修改尾指针的值if(employee_queue->tail == employee)employee_queue->tail=prev;//抽奖池数量减一--employee_queue->employee_quantity;//安全释放资源g_clear_pointer(&employee,g_free);
}

当程序退出的时候,我们需要释放所有动态申请的资源:

/** input:*  employee_queue:资源池指针*/
static void
destory_employee_queue(employee_queue_t * employee_queue)
{g_assert(employee_queue);struct employee_s *remove_employee;struct employee_s *employee = get_employee_queue_head(employee_queue);employee = employee->next;//从头节点依次删除每个节点while(employee_queue->employee_quantity--){remove_employee = employee;employee = employee->next;g_clear_pointer(&remove_employee,g_free);}//释放链表头节点g_clear_pointer(&employee,g_free);//释放抽奖资源池g_clear_pointer(&employee_queue,g_free);
}

3.2 创建抽奖界面

三个全局变量:

//用于抽奖时抽奖人信息的滚动
static guint g_timer_id=0;//用于窗口背景的动态更新
static guint g_bg_timer_id=0;//窗口背景图缓存区
static GdkPixbuf *g_src_pixbuf=NULL;

Gtk4 运行主程序:

int
main(int argc, char *argv[])
{GtkApplication *app;int status;app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);//当程序启动后,执行activate函数,做进一步操作g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);//启动程序status = g_application_run (G_APPLICATION (app), argc, argv);g_object_unref (app);return status;
}

activate函数:

static void
activate (GtkApplication* app,gpointer user_data)
{GtkWidget *window;//创建抽奖资源池create_employee_queue(&g_employee_queue,"H:\\Downloads\\person.txt");//创建抽奖窗口window = gtk_application_window_new (app);gtk_window_set_title (GTK_WINDOW (window), "");gtk_window_maximize (GTK_WINDOW (window));//在窗口构建完成后,调用realize_window做一步操作g_signal_connect (window,"realize",G_CALLBACK(realize_window),NULL);//当窗口关闭后调用析构函数destroy_windowg_signal_connect (window, "destroy", G_CALLBACK (destroy_window), NULL);gtk_widget_show (window);
}

窗口析构函数:

static void
destroy_window(GtkWidget* self,gpointer user_data)
{//如果定时器开启,则关闭if(g_timer_id > 0)g_source_remove(g_timer_id);//如果定时器开启,则关闭if(g_bg_timer_id >0)g_source_remove(g_bg_timer_id);//释放资源池destory_employee_queue(g_employee_queue);//释放背景图片的缓存g_object_unref(g_src_pixbuf);
}

创建抽奖窗口布局:

static void
realize_window(GtkWidget* self,gpointer user_data)
{GtkWidget *box;GtkWidget *label;GtkWidget *overlay;GtkWidget *picture;overlay=gtk_overlay_new();gtk_widget_set_hexpand(overlay,TRUE);gtk_widget_set_vexpand(overlay,TRUE);gtk_window_set_child(GTK_WINDOW(self),overlay);//创建抽奖g_src_pixbuf = gdk_pixbuf_new_from_file("H:\\Downloads\\lottery.jpeg", NULL);picture= gtk_picture_new_for_pixbuf(g_src_pixbuf);//设置抽奖窗口背景图gtk_overlay_set_child(GTK_OVERLAY(overlay),picture);//开启定时器,当窗口改变时,动态调整背景图宽高比例g_bg_timer_id=g_idle_add(G_SOURCE_FUNC(update_background),picture);box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0);gtk_widget_set_hexpand(box,TRUE);gtk_widget_set_vexpand(box,TRUE);gtk_overlay_add_overlay(GTK_OVERLAY(overlay),box);//抽奖人信息显示Labellabel= gtk_label_new("");gtk_widget_set_margin_top(label,300);gtk_widget_set_hexpand(label,TRUE);gtk_widget_set_vexpand(label,TRUE);gtk_box_append(GTK_BOX(box),label);//定义一个控制器,用于响应鼠标点击事件GtkGesture* click_gesture = gtk_gesture_click_new();//当鼠标释放时,调用start_stop_lottery函数g_signal_connect(click_gesture,"released",G_CALLBACK(start_stop_lottery),label);gtk_widget_add_controller(box,GTK_EVENT_CONTROLLER(click_gesture));
}

鼠标事件响应回调函数:

static void
start_stop_lottery(GtkGestureClick* self,gint n_press,gdouble x,gdouble y,gpointer user_data)
{//双击if(n_press==2){//如果抽奖程序未开启,则开启抽奖,每隔10ms调用一次luck_circulation函数,动态//滚动参与抽奖人信息if(g_timer_id == 0)g_timer_id=g_timeout_add(10,G_SOURCE_FUNC(luck_circulation),user_data);//如果已经在抽奖中,则停止定时器,也就是公布中奖结果else{g_source_remove(g_timer_id);g_timer_id=0;//将中奖人从抽奖池中删除remove_employee_from_queue(g_employee_queue,g_employee_queue->luck_employee);}}
}

抽奖程序:

static gboolean
luck_circulation(gpointer user_data)
{GtkWidget *label=user_data;//如果抽奖池中没有同事了,则直接退出if(g_employee_queue->employee_quantity == 0){g_timer_id=0;return G_SOURCE_REMOVE;}//初始化随机种子srand(g_get_real_time());//随机生成一个抽奖人信息索引guint begin_pos = rand()%g_employee_queue->employee_quantity+1;//从链表头遍历找到这个抽奖人struct employee_s *employee = get_employee_queue_head(g_employee_queue);while(begin_pos--)employee=employee->next;//在抽奖窗口显示抽奖人信息char *markup = g_markup_printf_escaped("<span font=\"100\">%s %s</span>",employee->name,employee->number);gtk_label_set_markup (GTK_LABEL(label),markup);g_free(markup);//更新此时刻选中的同事信息g_employee_queue->luck_employee = employee;//进行下一轮抽奖人随机选择流程return G_SOURCE_CONTINUE;
}

抽奖窗口背景图自适应程序:

static gboolean
update_background(gpointer user_data)
{//记录上一次窗口长宽比static double prev_ratio =0.0;int width = gtk_widget_get_width(user_data);int height = gtk_widget_get_height(user_data);//计算当前窗口长宽比double curr_ratio = ((double)width)/((double)height);//如果初次运行或者窗口窗宽比发生变化,则更新窗口背景图长宽比if(prev_ratio==0.0 || prev_ratio!=curr_ratio){prev_ratio = curr_ratio;GtkPicture *picture=user_data;//将原始窗口背景图进行比例缩放GdkPixbuf *update_pixbuf = gdk_pixbuf_scale_simple(g_src_pixbuf,width,height,GDK_INTERP_NEAREST);gtk_picture_set_pixbuf(picture,update_pixbuf);g_object_unref(update_pixbuf);}return G_SOURCE_CONTINUE;
}

四、程序使用建议

程序设计原因,建议抽奖池人数小于500人。

背景图片随便换,不限于我提供的这个,只需要将使用的背景图命名为 “lottery.jpeg” 即可!


源代码可执行程序下载链接


这里是从善若水的博客,感谢您的阅读

【GTK4】又快年底了,100行代码教你做一款简单的年会抽奖软件相关推荐

  1. 100行代码教你爬取斗图网(Python多线程队列)

    100行代码教你爬取斗图网(Python多线程队列) 前言 根据之前写的两篇文章,想必大家对多线程和队列有了一个初步的了解,今天这篇文章就来实战一下,用多线程 + 队列 爬取斗图网的全网图片. 你还在 ...

  2. 100行代码教你教务系统自动抢课!

    帮助广大学生解决抢课问题!自动抢课!! 100行代码帮你实现抢课!   本项目使用了python中splinter的API接口用来操作页面交互,用了twilio用来给手机发送短信通知抢课成功.   欢 ...

  3. 【零基础强化学习】100行代码教你训练——基于SARSA的CliffWalking爬悬崖游戏

    基于SARSA的CliffWalking爬悬崖游戏

  4. 100行代码搞定实时视频人脸表情识别(附代码)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|OpenCV学堂 好就没有写点OpenCV4 + Open ...

  5. abaqus画一个球 python_简单几步,100行代码用Python画一个蝙蝠侠的logo

    蝙蝠侠作为DC漫画的核心人物之一,一直都受到广大粉丝的喜爱,而笔者作为DC的铁杆粉丝,自然也是老爷(粉丝对蝙蝠侠的昵称)的支持者.今天,笔者就用Python来画一个蝙蝠侠的logo,大概就是下图这个样 ...

  6. Android鬼点子 100行代码,搞定柱状图!

    最近,项目中遇到一个地方,要用到柱状图.所以这篇文章主要讲怎么搞一个柱子. 100行代码,搞定柱状图! 我的印象中柱子是这样的. 恩,简单,一个View直接放到xml,搞定! 但,设计师给的柱子是这样 ...

  7. SAP系统和微信集成的系列教程之八:100行代码在微信公众号里集成地图搜索功能

    本系列的英文版Jerry写作于2017年,这个教程总共包含十篇文章,发表在SAP社区上. 系列目录 (1) 微信开发环境的搭建 (2) 如何通过微信公众号消费API (3) 微信用户关注公众号之后,自 ...

  8. 100行代码让您学会JavaScript原生的Proxy设计模式

    面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了. 其实和Java一样,JavaScript从语言层面来讲,也提供了对代理这个设计模式的原生支持.我们用一个不到100行代码的 ...

  9. react hooks使用_我如何使用React Hooks在约100行代码中构建异步表单验证库

    react hooks使用 by Austin Malerba 奥斯汀·马勒巴(Austin Malerba) 我如何使用React Hooks在约100行代码中构建异步表单验证库 (How I bu ...

最新文章

  1. pg 主键系统信息_神仙打架:PG 和 MySQL 到底哪个更好用?
  2. 根据 JS 自动定义页面缩放比(根据分辨率进行适配)
  3. linux系统下载经验,linux系统的学习经验首篇
  4. HOMEBREW安装之后需要需要把他安装到路径之中
  5. IBatis 简易框架搭建
  6. 机试题型_2020年焊工(技师)新版试题及焊工(技师)试题及答案
  7. 快速入门 Jupyter notebook
  8. mysql 连接 iOS_iOS连接mysql数据库及基本操作
  9. [原创] 在XP/2K 下实现 Win+Ctrl+Del 等键的屏蔽的方法,附源码与演示程序下载。...
  10. 程序员的系统桌面应该是什么样的
  11. 虚拟均衡器:Producers Vault Baby Bass for Mac
  12. 什么是开源软件? 开源和FOSS解释
  13. vue获取内外网ip地址
  14. 修复win7本地服务器,win7开启本地服务器配置
  15. 路由器概述(作用功能、工作过程、内部组成【RAM、ROM区别】、接口)
  16. 计算机网络共享打不开,电脑只要打开共享提示“无法启用共享访问”如何解决...
  17. 灰色预测(MATLAB)
  18. hive:建库建表、表分区、内部表外部表、数据导入导出
  19. 计算机辅助绘图方式,计算机辅助绘图技巧
  20. PDMS二次开发产品Naki.CI(二):升级到1.0.1版本

热门文章

  1. 【FCC】Build a Tribute Page(html+css+bootstrap)
  2. 双系统平板刷linux系统下载软件,台电官方论坛 - Windows平板加装Ubuntu实现双系统的尝试,有图有真相 - 平板笔记本...
  3. ubuntu安装man中文帮助文档:man c/c++
  4. 刘强东继续动刀高管:多名VP被换,年底末位10%淘汰,之前薪资已打8折
  5. 在MSTR中使用ECharts作为VI模板(1)-- 创建第一个ECharts的VI模板
  6. 一般计算机电源都在多少压力,一般计算机电源都在多少电压 计算机电源一般都在多少电压...
  7. Java实现“梭哈”游戏
  8. for循环和while循环哪个效率更高
  9. (附源码)Node.js自我展示博客网站 毕业设计231547
  10. java课后习题答案_《java课后习题答案》.doc