博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接
自制一款简单的抽奖软件
一、参与抽奖同事信息准备
工号和姓名之间使用水平制表符分隔,每行只显示一个同事的信息。
二、准备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;
创建单向循环链表:
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);
}
当某个同事中奖后,我们需要把他从抽奖池中移除,避免二次中奖,所以还需要一个从链表中删除节点的功能:
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);
}
当程序退出的时候,我们需要释放所有动态申请的资源:
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);
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));
g_signal_connect (window,"realize",G_CALLBACK(realize_window),NULL);
g_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);
label= 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();
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){
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” 即可!
源代码和可执行程序下载链接
这里是从善若水的博客,感谢您的阅读💰💰💰
|