GTK+和C語言實現的哈夫曼文件壓縮工具

/*************************************************************************
    > File Name: compressor.c
    > Author: hepan
    > Mail: hepan@xiyoulinux.org
    > Created Time: 20161223日 星期五 111500秒
 ************************************************************************/

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>

#define     MAXLEN          256             //文件名最大長度
#define     INFINITY        2147483647      //Long的最大值

//long file_len;
//float read_len = 0.0, write_len = 0.0;
long sumlength, FileLength, FileLength1;    //記錄源文件和壓縮文件的長度
float rate, speed;                          //記錄壓縮率和速度
double duration;                            //記錄壓縮用時
int flag;                                   //判斷文件是否壓縮解壓完成

typedef struct {
    unsigned char ch;                   //記錄不同字符在數組中的位置
    long weight;                        //字符出現頻率(權值) 
    long parent, lchild, rchild;        //定義哈夫曼樹指針變量
    char code[MAXLEN];                  //定義存儲哈夫曼編碼的數組
    int CodeLength;                     //編碼長度
} HuffNode, HuffmanTree[512];

//將窗體獨立成一個結構體
struct widget {
    GtkWidget *window;
    GtkWidget *window1;
    GtkWidget *window2;
    GtkWidget *label_tips;
    GtkWidget *button_cancel; 
    GtkWidget *entry;
    GtkWidget *entry1;
    GtkWidget *entry2;
    GtkWidget *pbar;
    GtkWidget *text1;
    GtkWidget *text2;
    GtkWidget *vbox;
    GtkWidget *hbox_button;
    int timer;
} wgt;

void compressor();                                          //哈夫曼壓縮器圖形主界面
void *compress(void *arg);                                  //壓縮函數
void *decompress(void *arg);                                //解壓函數
void complete_compress();                                   //壓縮完成,打印壓縮信息
void complete_decompress();                                 //解壓完成,打印解壓信息
void choose_file1(GtkWidget *widget, gpointer data);        //選擇壓縮文件函數
void file_compress(GtkWidget *widget,gpointer data);        //文件壓縮窗口
void choose_file2(GtkWidget *widget,gpointer data);         //選擇解壓文件函數
void file_decompress(GtkWidget *widget,gpointer data);      //文件解壓窗口
void send_file(GtkWidget *widget, gpointer data);           
void file_ok_sel(GtkWidget *w, GtkFileSelection *fs);
void gtk_dialog_destroy(GtkWidget *widget, gpointer data);      //窗口銷燬函數
void my_err(const char *err_string, int line);                  //錯誤處理函數
int my_itoa(int val, char* buf, int r);                         //整型值轉r進制字符串函數
void Select(HuffmanTree ht, int pos, int *s1, int *s2);         //建立哈弗曼樹中用於選擇最小和次小權值結點的函數
void hfmcoding(HuffmanTree ht,int n);                           //哈夫曼編碼函數
gboolean update_progress_bar_compress(gpointer data);           //壓縮進度條更新函數
gboolean update_progress_bar_decompress(gpointer data);         //解壓縮進度條更新函數
gint progress_timeout(gpointer data);                           //進度條循環函數
void destroy_progress(GtkWidget *widget, struct widget *pdata); // 清除分配的內存,刪除定時器(timer) 

GtkTextBuffer *text_buffer;     //文本框緩衝區

char pathname[MAXLEN];          //保存文件的絕對路徑名

//建立哈弗曼樹中用於選擇最小和次小權值結點的函數
void Select(HuffmanTree ht, int pos, int *s1, int *s2)
{ 
    //m1存放最小權值,m2存放次小權值
    //s1存放m1在數組中的下標,s2存放m2在數組中的下標
    int i, j;
    long m1, m2; 
    m1 = m2 = INFINITY;
    for (j=0; j <= pos; j++) {
        if (ht[j].weight < m1 && ht[j].parent == -1) {
            m2 = m1;
            *s2 = *s1;
            *s1 = j;
            m1 = ht[j].weight;
        } else if (ht[j].weight < m2 && ht[j].parent == -1) {
            m2 = ht[j].weight;
            *s2 = j;
        }
    }
    //使 s1 小於 s2
    if (*s1 > *s2) {
        i = *s1; 
        *s1 = *s2;
        *s2 = i;
    }
}

//哈弗曼編碼函數
void hfmcoding(HuffmanTree ht, int n)
{   
    //從葉子向根求每個字符的哈弗曼編碼
    int start;
    int i, f, c;
    char codes[MAXLEN];
    codes[n-1] = '\0';  //編碼結束符
    for (i=0; i<n; i++) {
        //逐個字符求哈弗曼編碼
        start = n - 1;
        for (c=i,f=ht[i].parent; f != -1; c=f,f=ht[f].parent) {
           start--;
           if (ht[f].lchild == c) {
               codes[start] = '0';
           } else {
               codes[start] = '1';
           }
        }
        strcpy(ht[i].code,&codes[start]);
        ht[i].CodeLength = strlen(ht[i].code);  
    }
}

//壓縮函數
void *compress(void *arg) 
{
    int i, j;
    FILE  *ifp, *ofp; 
    HuffmanTree ht;
    unsigned char c;
    int n, m;                   //葉子數和結點數
    int s1, s2;                 //權值最小的兩個結點的標號
    char buf[MAXLEN], name[MAXLEN];
    char codes[MAXLEN];
    long filelength = 0;
    int count = 0;
    clock_t start1, start2, finish1, finish2;
    double  duration1, duration2;
    ifp = fopen(pathname,"rb");
    if (ifp == NULL) {
        my_err("fopen",__LINE__);
    }
    bzero(buf,sizeof(buf));
    bzero(name,sizeof(name));
    for (i=0,j=0; i < strlen(pathname); i++) {
        if (pathname[i] == '/') {
            j = 0;
            continue;
        }
        name[j++] = pathname[i];
    }
    name[j] = '\0';
    for (i=0; i<strlen(name); i++) {
        if (name[i] == '.') {
            break;
        }
    }
    strncpy(buf, name, i);
    //printf("\n-->%s",buf);
    ofp = fopen(strcat(buf,".code"),"wb");
    if (ofp==NULL) {
        my_err("fopen",__LINE__);
    }
    start1 = clock() ;          //開始計時1

    FileLength = 0;
    while (!feof(ifp)) {
        fread(&c,1,1,ifp);
        ht[c].weight++; 
        FileLength++;
        //printf("字符 %c : %ld 個; ", c, ht[c].weight);
    }
    FileLength--;
    ht[c].weight--;
    //再將ASCII轉換爲字符存入到結點的ch成員裏,同時給雙親、孩子賦初值-1
    n = 0;
    for (i=0; i<256; i++) {
        if (ht[i].weight != 0) {
            //printf("\n****<>****%ld",ht[i].weight);
            ht[i].ch = (unsigned char)i;
            n++;    //葉子數
            ht[i].lchild = -1;
            ht[i].rchild = -1;
            ht[i].parent = -1;      
        } 
    }
    m = 2 * n - 1;          //哈弗曼樹結點總數
    j = 0;
    for (i=0; i<256; i++) {
        //去掉權值爲0的結點
        if (ht[i].weight != 0) {
            ht[j] = ht[i];
            j++;
        }
    }

    for (i=n; i<m; i++) {
        //初始化根結點
        ht[i].lchild = -1;
        ht[i].rchild = -1;
        ht[i].parent = -1;
    }
    //建立哈弗曼樹
    for (i=n; i<m; i++) {
        Select(ht,i-1,&s1,&s2);
        ht[i].lchild = s1;
        ht[i].rchild = s2;
        ht[s1].parent = i;
        ht[s2].parent = i;
        ht[i].weight = ht[s1].weight + ht[s2].weight;
        //printf("\n---->s1 = %d, \ts2 = %d", s1, s2);
        //printf("\n****>w1 =  %ld, \tw2 = %ld, \tsum = %ld", ht[s1].weight, ht[s2].weight, ht[i].weight);
    }
    hfmcoding(ht,n);
    finish1 = clock();
    duration1 = (double)(finish1 - start1) / CLOCKS_PER_SEC;
    //  printf( "哈弗曼樹編碼用時爲:%f seconds\n", duration1 );
    //  printf("葉子數爲%d,結點數爲%d\n",n,m); 
    //  for(i=0;i<n;i++) {
    //      printf("%d號葉子結點的權值爲:%ld,雙親爲:%d,左右孩子:%d,編碼爲:%s\n",
    //          i,ht[i].weight,ht[i].parent,ht[i].lchild,ht[i].code);
    //  }
    start2 = clock() ;                  //開始計時2
    fseek(ifp,0,SEEK_SET);              //將ifp指針移到文件開頭位置
    fwrite(&FileLength,4,1,ofp);        //將FileLength寫入目標文件的前4個字節的位置
    fseek(ofp,8,SEEK_SET);              //再將目標文件指針ofp移到距文件開頭8個字節位置
    codes[0] = 0;
    //將編碼信息寫入目標文件
    while (!feof(ifp)) {
        //fread(&c,1,1,ifp);
        c = fgetc(ifp);
        filelength++;
        for (i=0; i<n; i++) {
            if (c == ht[i].ch) {
                break;
            }
        }
        strcat(codes,ht[i].code);
        c = 0;
        while (strlen(codes) >= 8) {
            //printf("\nbefore : c = %d", c);
            //printf("\ncodes: >>%s",codes);
            for (i=0; i<8; i++) {
                //將codes的前801代碼表示的字符存入c
                //printf("\n------->%c",codes[i]);
                if (codes[i] == '1') {
                    c = (c << 1) | 1; 
                    //printf("\n<?*?>%d",c);
                } else {
                    c = c << 1;  
                    //printf("\n<?-^-?>%d",c);
                }
            }
            //printf("\nafter: c = %d",c);
            fwrite(&c,1,1,ofp);             //將新的字符寫入目標文件
            sumlength++;
            strcpy(codes,codes+8);          //更新codes的值
        }
        if (filelength == FileLength) {
            break;
        }
    }
    //再將剩餘的不足8位的01代碼補全8位,繼續寫入
    if (strlen(codes) > 0) {
        strcat(codes,"00000000");
        for (i=0; i<8; i++) {
            if (codes[i] == '1') {
                c = (c << 1) | 1;
            } else {
                c = c << 1;
            }
        }
        fwrite(&c,1,1,ofp);
        sumlength++;
    }
    sumlength += 8;
    //printf("編碼區總長爲:%ld個字節\n",sumlength-8);
    //將sumlength和n的值寫入目標文件,爲的是方便解壓
    fseek(ofp,4,SEEK_SET);
    fwrite(&sumlength,4,1,ofp);             //把sumlength寫進目標文件的第5-8個字節裏         
    fseek(ofp,sumlength,SEEK_SET);
    fwrite(&n,4,1,ofp);                     //把葉子數n寫進編碼段後面的4個字節的位置
    //存儲方式爲:n*(字符值(1個字節)+該字符的01編碼的位數(1個字節)+編碼(字節數不確定,用count來計算總值))
    for (i=0; i<n; i++) {
        fwrite(&(ht[i].ch),1,1,ofp);
        c = ht[i].CodeLength;               //編碼最長爲MAXLEN位,因此只需用一個字節存儲
        //printf("\n---->%d",c);
        //printf("\n---->%s",ht[i].code);
        fwrite(&c,1,1,ofp);
        //寫入字符的編碼
        if (ht[i].CodeLength % 8 != 0) { 
            for (j = ht[i].CodeLength % 8; j < 8; j++) {
                //把編碼不足8位的在低位補0,賦值給C,再把C寫入
                strcat(ht[i].code,"0");
            }
        }
        while (ht[i].code[0] != 0) {
            //開始存入編碼,每8位二進制數存入一個字節
            c = 0;
            for (j=0; j<8; j++) {
                if (ht[i].code[j] == '1') {
                    c = (c << 1) | 1;
                } else {
                    c = c << 1;
                }
            }
            //printf("\n***>%s",ht[i].code);
            strcpy(ht[i].code,ht[i].code+8);        //編碼前移8位,繼續存入編碼
            //printf("\n--->%s",ht[i].code);
            count++;                                //編碼佔的字節數的總值
            //printf("\n---->%d",count);
            fwrite(&c,1,1,ofp);
        }
    }
    sumlength = sumlength + 4 + n * 2 + count;   //計算壓縮後文件的長度
    //printf("\nafter compress: %ld Byte\n", sumlength);
    finish2 = clock();
    duration2 = (double)(finish2- start2) / CLOCKS_PER_SEC;
    duration = duration1 + duration2;
    /*printf( "寫入目標文件用時爲:%f seconds\n", duration2);*/
    //printf( "壓縮用時爲:%f seconds\n", duration1+duration2);
    speed = (float)FileLength/(duration1+duration2)/1000;
    //printf("\n壓縮速率爲:%5.4f KB/S\n",speed);
    //printf("源文件長度爲:%ld Byte\n",FileLength);
    rate = (float)sumlength / (float)FileLength;
    //printf("壓縮率(百分比)爲:%4.2f%%\n",rate*100);
    flag = 1;
    complete_compress();
    fclose(ifp);
    fclose(ofp);  
    pthread_exit(0);
}

//整型值轉r進制字符串函數
int my_itoa(int val, char* buf, int r)
{
    const unsigned int radix = r;
    char* p;
    unsigned int a;     //every digit
    int len;
    char* b;            //start of the digit char
    char temp;
    unsigned int u;
    p = buf;
    if (val < 0) {
        *p++ = '-';
        val = 0 - val;
    }
    u = (unsigned int)val;
    b = p;
    do {
        a = u % radix;
        u /= radix;
        *p++ = a + '0';
    } while (u > 0);
    len = (int)(p - buf);
    *p-- = 0;
    //swap
    do {
        temp = *p;
        *p = *b;
        *b = temp;
        --p;
        ++b;
    } while (b < p);
    return len;
}

//解壓縮
void *decompress(void *arg)
{
    HuffmanTree ht;
    clock_t start, finish;
    FILE *ifp, *ofp;  
    long filelength;
    int n, m;  
    int i, j, k;
    char buf[MAXLEN], codes[MAXLEN];
    char buffer[MAXLEN], name[MAXLEN];
    unsigned char c;
    int maxlength;
    ifp = fopen(pathname,"rb");
    if (ifp == NULL) {
        my_err("fopen",__LINE__);
    }

    bzero(buf,sizeof(buffer));
    bzero(name,sizeof(name));
    for (i=0,j=0; i < strlen(pathname); i++) {
        if (pathname[i] == '/') {
            j = 0;
            continue;
        }
        name[j++] = pathname[i];
    }
    name[j] = '\0';
    for (i=0; i<strlen(name); i++) {
        if (name[i] == '.') {
            break;
        }
    }
    strncpy(buffer, name, i);

    ofp = fopen(strcat(buffer,".decode"),"wb");
    if (ofp==NULL) {
        my_err("fopen",__LINE__);
    }
    start = clock() ;                   //開始計時
    fread(&FileLength,4,1,ifp);         //從壓縮文件讀出FileLength、sumlength
    //printf("\n源文件爲 %ld Byte",FileLength);
    fread(&sumlength,4,1,ifp);
    //printf("\n壓縮文件爲 %ld Byte",sumlength);
    fseek(ifp,sumlength,SEEK_SET);      //利用sumlength讀出n的值
    fread(&n,4,1,ifp);
    //printf("\n解碼信息:源文件長度爲%d個字節,字符種類n=%d\n",FileLength,n);
    for (i=0; i<n; i++) {
        //讀結點信息
        fread(&ht[i].ch,1,1,ifp);       //字符
        //printf("\n***>%d",ht[i].ch);
        fread(&c,1,1,ifp);              //編碼長度
        //printf("\n--->%d",c);
        ht[i].CodeLength = c;
        ht[i].code[0] = 0;
        if (ht[i].CodeLength % 8 > 0) {
            m = ht[i].CodeLength / 8 + 1;     //m爲編碼佔的字節數
        } else {
            m = ht[i].CodeLength / 8;
        }
        for (j=0; j<m; j++) {
            //根據字節長度m讀出編碼
            fread(&c,1,1,ifp);              //此處c爲01編碼轉換成的字符
            my_itoa(c,buf,2);               //字符型編碼轉換成二進制型(首位爲1//如果編碼不夠8位,則說明缺少了8-k位0,因此應先在前面空缺位寫0
            for (k=8; k>strlen(buf); k--) {
                strcat(ht[i].code,"0");
            }
            //再把二進制編碼存進ht.code中
            strcat(ht[i].code,buf);
            //printf("\n----->%s",ht[i].code);
        }
        ht[i].code[ht[i].CodeLength] = 0;   //去掉編碼中多餘的0  
    }
    //找出編碼長度的最大值
    maxlength = 0;
    for (i=0; i<n; i++) {
        if (ht[i].CodeLength > maxlength) {
            maxlength = ht[i].CodeLength;
        }
    }
    //開始寫入目標文件
    fseek(ifp,8,SEEK_SET);                  //指針指向編碼區,開始解碼
    filelength = 0;
    codes[0] = 0;
    buf[0] = 0;
    while (1) {
        //codes小於編碼長度的最大值時,繼續讀碼
        while (strlen(codes) < maxlength) {
            fread(&c,1,1,ifp);
            //printf("\n----->%d",c);
            my_itoa(c,buf,2);       //還原編碼
            for (k=8; k>strlen(buf); k--) {
                strcat(codes,"0");          //把缺掉的0補上
            }
            strcat(codes,buf);              //codes中此時存的爲一串01編碼
        }
        for (i=0; i<n; i++) {  
            //在codes中查找能使其前weight位和ht.code相同的i值,weight即爲codelength
            if (memcmp(ht[i].code,codes,(unsigned int)ht[i].CodeLength) == 0) {
                break;
            }
        }
        strcpy(codes,codes+ht[i].CodeLength);       //更新codes的值
        c = ht[i].ch;
        fwrite(&c,1,1,ofp);
        filelength++;
        if (filelength == FileLength) {
            break;                                  //寫入結束
        }
    }
    finish = clock();
    duration = (double)(finish - start) / CLOCKS_PER_SEC;
    //printf( "\n解壓完成,解壓用時爲:%f seconds\n", duration );

    fseek(ifp,0,SEEK_SET);  
    while (!feof(ifp)) {
        fread(&c,1,1,ifp);
        FileLength1++;
    }
    FileLength1--; 
    speed = (float)FileLength/duration/1000;
    //printf("此文件長度爲:%ld Byte\n",FileLength);
    //printf("\n解壓速度爲:%5.4f KB/S\n",speed);
    flag = 1;
    complete_decompress();

    fclose(ifp);
    fclose(ofp);
}

//錯誤處理函數
void my_err(const char *err_string, int line)
{
    fprintf(stderr, "line: %d ",line);
    perror(err_string);
    exit(1);
}

//窗口銷燬函數
void gtk_dialog_destroy(GtkWidget *widget, gpointer data)
{
    gtk_widget_destroy((GtkWidget *)data);
}

/*
//壓縮進度條更新函數
gboolean update_progress_bar_compress(gpointer data)
{
    FILE *fp = NULL;
    char buff[8] = {0};
    long size = 351920;
    unsigned char *pbuff;

    GtkProgressBar *pbar = (GtkProgressBar *)data;
    if((fp = fopen(pathname,"r")) == NULL) {
        my_err("fopen",__LINE__);
    }
    fseek(fp,0,SEEK_END);
    file_len = ftell(fp);

    pbuff = (unsigned char *)malloc((size+1)*sizeof(unsigned char));
    memset(pbuff,0,size*sizeof(unsigned char));
    if(read_len <= file_len) {
        fseek(fp,(long int)read_len,SEEK_SET);
        fread(pbuff,sizeof(unsigned char),size,fp);
        // fwrite(pbuff,size*sizeof(unsigned char),1,fp);

        gtk_progress_bar_set_fraction(pbar, read_len / file_len);
        sprintf(buff,"%d%s",(int)((read_len / file_len) * 100),"%");
        gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pbar),buff);
        read_len += size;
        return TRUE;   //必須返回TRUE;
    }

    if(read_len > file_len) {
        gtk_widget_hide(wgt.window1);
        return TRUE;
    }

    fclose(fp);
    free(pbuff);
    return TRUE;     //必須返回TRUE;
}
//解壓縮進度條更新函數
gboolean update_progress_bar_decompress(gpointer data)
{
    FILE *fp = NULL;
    char buff[8] = {0};
    long size = 61097;
    unsigned char *pbuff;

    GtkProgressBar *pbar = (GtkProgressBar *)data;
    if((fp = fopen(pathname,"r")) == NULL) {
        my_err("fopen",__LINE__);
    }
    fseek(fp,0,SEEK_END);
    file_len = ftell(fp);

    pbuff = (unsigned char *)malloc((size+1)*sizeof(unsigned char));
    memset(pbuff,0,size*sizeof(unsigned char));
    if(write_len <= file_len) {
        fseek(fp,(long int)write_len,SEEK_SET);
        fread(pbuff,sizeof(unsigned char),size,fp);

        gtk_progress_bar_set_fraction(pbar,write_len/file_len);
        sprintf(buff,"%d%s",(int)((write_len/file_len)*100),"%");
        gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pbar),buff);
        write_len += size;
        return TRUE;   //必須返回TRUE;
    }

    if(write_len > file_len) {
        gtk_widget_hide(wgt.window1);
        return TRUE;
    }

    complete_compress();

    fclose(fp);
    free(pbuff);
    return TRUE;     //必須返回TRUE;
}
*/

//進度條循環函數
gint progress_timeout(gpointer data)
{
    struct widget *pdata = (struct widget *)data;
    gdouble new_val; 

    //使用在調整對象中設置的取值範圍計算進度條的值
    new_val = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR(pdata->pbar)) + 0.2; 

    if (new_val > 1.0) {
        new_val = 0.0; 
    }
    //設置進度條的新值
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR (pdata->pbar),new_val); 

    if (flag == 1) {
        //gtk_widget_hide(pdata->window1);
        destroy_progress(pdata->window1,pdata);
        return TRUE;
    }

    //這是一個timeout函數,返回 TRUE,這樣它就能夠繼續被調用
    return TRUE;  
}

//清除分配的內存,刪除定時器(timer) 
void destroy_progress(GtkWidget *widget, struct widget *pdata) 
{ 
    gtk_timeout_remove(pdata->timer); 
    pdata->timer = 0; 
    pdata->window = NULL; 
    g_free (pdata); 
    gtk_widget_hide(pdata->window1);
} 

//哈夫曼壓縮器圖形主界面
void compressor()
{
    GtkWidget *window;
    GtkWidget *button1;
    GtkWidget *button2;
    GtkWidget *hbox;
    GtkWidget *vbox;
    GtkWidget *image;
    GdkColor color1;
    GdkColor color2;

    gdk_color_parse("LawnGreen",&color1);
    gdk_color_parse("DarkMagenta",&color2);
    gtk_widget_hide(wgt.window);
    image = gtk_image_new_from_file("file.jpg");
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 150, 70);
    gtk_container_set_border_width(GTK_CONTAINER(window),10);

    gtk_window_set_title(GTK_WINDOW(window),"Compress");
    gtk_window_set_resizable(GTK_WINDOW(window),TRUE);

    hbox = gtk_hbox_new(FALSE,20);
    vbox = gtk_vbox_new(FALSE,20);

    button1 = gtk_button_new_with_label("壓縮");
    button2 = gtk_button_new_with_label("解壓");
    gtk_widget_modify_fg(GTK_BIN(button1)->child,GTK_STATE_PRELIGHT,&color1);
    gtk_widget_modify_fg(GTK_BIN(button2)->child,GTK_STATE_PRELIGHT,&color2);

    gtk_widget_modify_bg(button1, GTK_STATE_PRELIGHT, &color1);
    gtk_widget_modify_bg(button2, GTK_STATE_PRELIGHT, &color2);

    gtk_box_pack_start(GTK_BOX(vbox),image,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(hbox),button1,FALSE,FALSE,10);
    gtk_box_pack_end(GTK_BOX(hbox),button2,FALSE,FALSE,10);

    gtk_container_add(GTK_CONTAINER(vbox),hbox);
    gtk_container_add(GTK_CONTAINER(window),vbox);
    gtk_widget_show_all(window); 

    g_signal_connect(GTK_OBJECT(button1),"clicked",GTK_SIGNAL_FUNC(choose_file1),(void *)window);
    g_signal_connect(GTK_OBJECT(button2),"clicked",GTK_SIGNAL_FUNC(choose_file2),(void *)window);
    g_signal_connect(GTK_OBJECT(window),"delete_event",GTK_SIGNAL_FUNC(gtk_main_quit),NULL);
    gtk_widget_show_all(window);
}

//選擇壓縮文件函數
void choose_file1(GtkWidget *widget, gpointer data)
{
    GtkWidget *scroll_box;
    GtkWidget *hbox;
    GtkWidget *button1;
    GtkWidget *button2;
    GtkWidget *button3;
    GtkWidget *tmp;
    GtkWidget *label;
    GdkColor color1;
    GdkColor color2;
    GdkColor color3;
    GdkColor color4;

    gdk_color_parse("DarkBlue",&color1);
    gdk_color_parse("LawnGreen",&color2);
    gdk_color_parse("gray",&color3);
    gdk_color_parse("red",&color4);

    tmp = (GtkWidget *)data;
    gtk_widget_hide(tmp);

    wgt.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(wgt.window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(wgt.window), 400, 150);
    gtk_container_set_border_width(GTK_CONTAINER(wgt.window),10);

    gtk_window_set_title(GTK_WINDOW(wgt.window),"文件壓縮");
    gtk_window_set_resizable(GTK_WINDOW(wgt.window),TRUE);

    wgt.vbox = gtk_vbox_new(FALSE,20);
    hbox = gtk_hbox_new(FALSE,20);

    scroll_box = gtk_scrolled_window_new(NULL,NULL);
    wgt.text1 = gtk_text_view_new();
    gtk_widget_modify_base(wgt.text1, GTK_STATE_NORMAL, &color3);
    gtk_widget_modify_text(wgt.text1, GTK_STATE_NORMAL, &color1);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_box), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(scroll_box), wgt.text1);
    gtk_widget_set_size_request(scroll_box,390,50);

    button1 = gtk_button_new_with_label("選擇文件");
    button2 = gtk_button_new_with_label("確認壓縮");
    button3 = gtk_button_new_with_label("取消壓縮"); 
    label = gtk_label_new("待壓縮文件位置");
    gtk_widget_modify_fg(label,GTK_STATE_NORMAL,&color1);
    gtk_widget_modify_fg(GTK_BIN(button1)->child,GTK_STATE_PRELIGHT,&color1);
    gtk_widget_modify_fg(GTK_BIN(button2)->child,GTK_STATE_PRELIGHT,&color2);
    gtk_widget_modify_fg(GTK_BIN(button3)->child,GTK_STATE_PRELIGHT,&color4);

    //gtk_widget_modify_bg(button1, GTK_STATE_PRELIGHT, &color1);
    //gtk_widget_modify_bg(button2, GTK_STATE_PRELIGHT, &color2);
    //gtk_widget_modify_bg(button3, GTK_STATE_PRELIGHT, &color3);

    gtk_box_pack_start(GTK_BOX(wgt.vbox),button1,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(wgt.vbox),label,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(wgt.vbox),scroll_box,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(hbox),button2,FALSE,FALSE,10);
    gtk_box_pack_end(GTK_BOX(hbox),button3,FALSE,FALSE,10);

    gtk_container_add(GTK_CONTAINER(wgt.window),wgt.vbox);
    gtk_container_add(GTK_CONTAINER(wgt.vbox),hbox);
    gtk_widget_show_all(wgt.window);      

    g_signal_connect(GTK_OBJECT(button1),"clicked",GTK_SIGNAL_FUNC(send_file),NULL);
    g_signal_connect(GTK_OBJECT(button2),"clicked",GTK_SIGNAL_FUNC(file_compress),NULL);
    g_signal_connect(GTK_OBJECT(button3),"clicked",GTK_SIGNAL_FUNC(compressor),(void *)wgt.window);
    g_signal_connect(GTK_OBJECT(wgt.window),"delete_event",GTK_SIGNAL_FUNC(compressor),(void *)wgt.window);
}

//文件壓縮窗口
void file_compress(GtkWidget *widget,gpointer data)
{
    GtkWidget *hbox, *vbox1, *vbox2, *button;
    struct widget *pdata;
    GdkColor color1;
    int status;
    pthread_t thid;

    gdk_color_parse("red",&color1);
    pdata = g_malloc(sizeof(struct widget));

    pdata->window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(pdata->window1),GTK_WIN_POS_CENTER);
    gtk_window_set_title(GTK_WINDOW(pdata->window1),"Process bar");
    gtk_window_set_default_size(GTK_WINDOW(pdata->window1),300,100);

    hbox = gtk_hbox_new(FALSE,0);
    vbox1 = gtk_vbox_new(FALSE,0);
    vbox2 = gtk_vbox_new(FALSE,0);
    pdata->pbar = gtk_progress_bar_new();
    button = gtk_button_new_with_label("取消");
    gtk_widget_set_size_request(button,55,30);
    gtk_widget_set_size_request(pdata->pbar,240,30);
    gtk_widget_modify_fg(GTK_BIN(button)->child,GTK_STATE_PRELIGHT,&color1);

    gtk_box_pack_start(GTK_BOX(vbox1),pdata->pbar,FALSE,FALSE,50);
    gtk_box_pack_end(GTK_BOX(vbox2),button,FALSE,FALSE,50);
    gtk_box_pack_start(GTK_BOX(hbox),vbox1,FALSE,FALSE,0);
    gtk_box_pack_end(GTK_BOX(hbox),vbox2,FALSE,FALSE,5);
    gtk_container_add(GTK_CONTAINER(pdata->window1),hbox);

    pthread_create(&thid, NULL, compress, NULL);
    pdata->timer = g_timeout_add(120,progress_timeout,pdata);
    //printf("\n----->%d",pdata->timer);
    //pthread_join(thid, (void *)&status);

    g_signal_connect(pdata->window1,"destroy",G_CALLBACK(destroy_progress),(void *)pdata);
    g_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(destroy_progress),(void *)pdata);
    gtk_widget_show_all(pdata->window1);
}

//壓縮完成,打印壓縮信息
void complete_compress()
{
    GtkWidget *vbox, *vbox1, *hbox, *label, *button;
    GdkColor color1, color2;
    gdk_color_parse("LawnGreen",&color1);
    gdk_color_parse("DarkBlue",&color2);
    wgt.window2 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(wgt.window2),GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(wgt.window2),300,150);
    char buf[MAXLEN];

    vbox = gtk_vbox_new(FALSE,0);
    vbox1 = gtk_vbox_new(FALSE,0);
    hbox = gtk_hbox_new(FALSE,0);
    button = gtk_button_new_with_label("確定");
    gtk_widget_set_size_request(button,100,30);

    bzero(buf,sizeof(buf));
    sprintf(buf,"源文件大小:  %ld  Byte\n壓縮後文件大小:  %ld  Byte\n壓縮用時:  %.2lf seconds\n壓縮率:  %.2f  %%\n",FileLength,sumlength,duration,rate*100);
    label = gtk_label_new(buf);
    gtk_widget_modify_fg(label,GTK_STATE_NORMAL,&color2);
    gtk_widget_modify_fg(GTK_BIN(button)->child,GTK_STATE_PRELIGHT,&color1);

    gtk_box_pack_start(GTK_BOX(vbox1),label,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(hbox),button,FALSE,FALSE,150);
    gtk_box_pack_start(GTK_BOX(vbox),vbox1,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,15);
    gtk_container_add(GTK_CONTAINER(wgt.window2),vbox);

    g_signal_connect(wgt.window2,"destroy",G_CALLBACK(gtk_dialog_destroy),(void *)wgt.window2);
    g_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(gtk_dialog_destroy),(void *)wgt.window2);
    gtk_widget_show_all(wgt.window2);
}

//選擇解壓文件函數
void choose_file2(GtkWidget *widget,gpointer data)
{
    GtkWidget *scroll_box;
    GtkWidget *hbox;
    GtkWidget *button1;
    GtkWidget *button2;
    GtkWidget *button3;
    GtkWidget *tmp;
    GtkWidget *label;
    GdkColor color1;
    GdkColor color2;
    GdkColor color3;
    GdkColor color4;

    gdk_color_parse("DarkBlue",&color1);
    gdk_color_parse("LawnGreen",&color2);
    gdk_color_parse("gray",&color3);
    gdk_color_parse("red",&color4);

    tmp = (GtkWidget *)data;
    gtk_widget_hide(tmp);

    wgt.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(wgt.window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(wgt.window), 400, 150);
    gtk_container_set_border_width(GTK_CONTAINER(wgt.window),10);

    gtk_window_set_title(GTK_WINDOW(wgt.window),"文件解壓");
    gtk_window_set_resizable(GTK_WINDOW(wgt.window),TRUE);

    wgt.vbox = gtk_vbox_new(FALSE,20);
    hbox = gtk_hbox_new(FALSE,20);

    scroll_box = gtk_scrolled_window_new(NULL,NULL);
    wgt.text1 = gtk_text_view_new();
    gtk_widget_modify_base(wgt.text1, GTK_STATE_NORMAL, &color3);
    gtk_widget_modify_text(wgt.text1, GTK_STATE_NORMAL, &color1);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_box), GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(scroll_box), wgt.text1);
    gtk_widget_set_size_request(scroll_box,390,50);

    button1 = gtk_button_new_with_label("選擇文件");
    button2 = gtk_button_new_with_label("確認解壓");
    button3 = gtk_button_new_with_label("取消解壓");   
    label = gtk_label_new("待解壓文件位置");
    gtk_widget_modify_fg(label,GTK_STATE_NORMAL,&color1);
    gtk_widget_modify_fg(GTK_BIN(button1)->child,GTK_STATE_PRELIGHT,&color1);
    gtk_widget_modify_fg(GTK_BIN(button2)->child,GTK_STATE_PRELIGHT,&color2);
    gtk_widget_modify_fg(GTK_BIN(button3)->child,GTK_STATE_PRELIGHT,&color4);

    //gtk_widget_modify_bg(button1, GTK_STATE_PRELIGHT, &color1);
    //gtk_widget_modify_bg(button2, GTK_STATE_PRELIGHT, &color2);
    //gtk_widget_modify_bg(button3, GTK_STATE_PRELIGHT, &color3);

    gtk_box_pack_start(GTK_BOX(wgt.vbox),button1,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(wgt.vbox),label,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(wgt.vbox),scroll_box,FALSE,FALSE,10);
    gtk_box_pack_start(GTK_BOX(hbox),button2,FALSE,FALSE,10);
    gtk_box_pack_end(GTK_BOX(hbox),button3,FALSE,FALSE,10);

    gtk_container_add(GTK_CONTAINER(wgt.window),wgt.vbox);
    gtk_container_add(GTK_CONTAINER(wgt.vbox),hbox);
    gtk_widget_show_all(wgt.window);      

    g_signal_connect(GTK_OBJECT(button1),"clicked",GTK_SIGNAL_FUNC(send_file),NULL);
    g_signal_connect(GTK_OBJECT(button2),"clicked",GTK_SIGNAL_FUNC(file_decompress),NULL);
    g_signal_connect(GTK_OBJECT(button3),"clicked",GTK_SIGNAL_FUNC(compressor),(void *)wgt.window);
    g_signal_connect(GTK_OBJECT(wgt.window),"delete_event",GTK_SIGNAL_FUNC(compressor),(void *)wgt.window);
}

//文件解壓窗口
void file_decompress(GtkWidget *widget,gpointer data)
{
    GtkWidget *hbox, *vbox1, *vbox2, *pbar, *button;
    struct widget *pdata;
    GdkColor color1;
    pthread_t thid;

    gdk_color_parse("red",&color1);
    pdata = g_malloc(sizeof(struct widget));

    pdata->window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(pdata->window1),GTK_WIN_POS_CENTER);
    gtk_window_set_title(GTK_WINDOW(pdata->window1),"Process bar");
    gtk_window_set_default_size(GTK_WINDOW(pdata->window1),300,100);

    hbox = gtk_hbox_new(FALSE,0);
    vbox1 = gtk_vbox_new(FALSE,0);
    vbox2 = gtk_vbox_new(FALSE,0);
    pdata->pbar = gtk_progress_bar_new();
    button = gtk_button_new_with_label("取消");
    gtk_widget_set_size_request(button,55,30);
    gtk_widget_set_size_request(pdata->pbar,240,30);
    gtk_widget_modify_fg(GTK_BIN(button)->child,GTK_STATE_PRELIGHT,&color1);

    gtk_box_pack_start(GTK_BOX(vbox1),pdata->pbar,FALSE,FALSE,50);
    gtk_box_pack_end(GTK_BOX(vbox2),button,FALSE,FALSE,50);
    gtk_box_pack_start(GTK_BOX(hbox),vbox1,FALSE,FALSE,0);
    gtk_box_pack_end(GTK_BOX(hbox),vbox2,FALSE,FALSE,5);
    gtk_container_add(GTK_CONTAINER(pdata->window1),hbox);

    pthread_create(&thid, NULL, decompress, NULL);
    pdata->timer = g_timeout_add(120,progress_timeout,pdata);

    g_signal_connect(pdata->window1,"destroy",G_CALLBACK(destroy_progress),(void *)pdata);
    g_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(destroy_progress),(void *)pdata);
    gtk_widget_show_all(pdata->window1);
}

//解壓縮完成,打印解壓信息
void complete_decompress()
{
    GtkWidget *window, *vbox, *vbox1, *hbox, *label, *button;
    GdkColor color1, color2;
    gdk_color_parse("LawnGreen",&color1);
    gdk_color_parse("DarkBlue",&color2);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window),250,150);
    char buf[MAXLEN];

    vbox = gtk_vbox_new(FALSE,0);
    vbox1 = gtk_vbox_new(FALSE,0);
    hbox = gtk_hbox_new(FALSE,0);
    button = gtk_button_new_with_label("確定");
    gtk_widget_set_size_request(button,100,30);

    bzero(buf,sizeof(buf));
    sprintf(buf,"壓縮文件大小:  %ld  Byte\n解壓後文件大小:  %ld  Byte\n解壓用時:  %.2lf seconds\n解壓速率:  %.2f  KB/s\n",FileLength1,FileLength,duration,speed);
    label = gtk_label_new(buf);
    gtk_widget_modify_fg(label,GTK_STATE_NORMAL,&color2);
    gtk_widget_modify_fg(GTK_BIN(button)->child,GTK_STATE_PRELIGHT,&color1);

    gtk_box_pack_start(GTK_BOX(vbox1),label,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(hbox),button,FALSE,FALSE,150);
    gtk_box_pack_start(GTK_BOX(vbox),vbox1,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,15);
    gtk_container_add(GTK_CONTAINER(window),vbox);

    g_signal_connect(window,"destroy",G_CALLBACK(gtk_dialog_destroy),(void *)window);
    g_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(gtk_dialog_destroy),(void *)window);
    gtk_widget_show_all(window);
}

void file_ok_sel(GtkWidget *w, GtkFileSelection *fs)
{
    GtkTextIter start, end;
    const char *buf;
    //g_print ("%s\n", gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));

    buf = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs));
    strcpy(pathname,buf);
    text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wgt.text1));
    gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(text_buffer),&start,&end);
    gtk_text_buffer_insert(GTK_TEXT_BUFFER(text_buffer),&end,buf,strlen(buf));

    gtk_widget_destroy((GtkWidget *)fs);
}

void send_file(GtkWidget *widget, gpointer data)
{
    GtkWidget *filew;

    filew = gtk_file_selection_new ("文件選擇");

    g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),"clicked",G_CALLBACK(file_ok_sel), (void *)filew);

    g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(filew)->cancel_button),"clicked",G_CALLBACK (gtk_dialog_destroy), (void *)filew);

    gtk_widget_show (filew);
    g_signal_connect (G_OBJECT (filew), "destroy",G_CALLBACK (gtk_dialog_destroy), (void *)filew);
}

//主函數
int main(int argc,char *argv[])
{
    gtk_init(&argc,&argv);

    //調用壓縮器
    compressor();

    gtk_main();

    return 0;
}

運行步驟:

1.安裝gtk+2.0環境
    Debian: sudo apt-get install libgtk2.0-dev
    RedHat: sudo yum install gtk2-devel-docs
2.編譯
    gcc -o compressor compressor.c -g -lpthread `pkg-config --cflags --libs gtk+-2.0`
3.運行
    ./compressor

程序運行部分截圖

壓縮器主窗口

選擇壓縮文件窗口

選擇文件窗口

選擇解壓文件窗口

進度條

文件壓縮信息

文件解壓信息

發佈了48 篇原創文章 · 獲贊 141 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章