C语言实现CD管理程序


前言

学习如何使用C语言编写一个gui程序,并掌握编写程序的一般步骤。

一、编程七步法

  • 第一步:定义程序的目标
  • 第二步:设计程序
  • 第三步:编写代码
  • 第四步:编译程序
  • 第五步:运行程序
  • 第六步:程序测试与调试
  • 第七步:维护与更新程序

第三步到第六步是一个 “运行–调试–修改代码” 循环过程,直到项目的正式结束。

二、编程实践

1.编写此程序的目的

顾名思义CD管理程序就是管理CD数据的,具体有查找、添加、删除、修改CD,还要可以对CD的track进行增删改查。

2.设计程序

主界面就是一个输入框查询和展示查找的数据。一个按钮用来实现添加功能。数据保存到MySQL数据库中。通过一个添加按钮打开CD添加界面,完成CD添加操作。

3.编写-运行-调试代码

项目的文件结构

头文件CDManager.h:保存主要函数的声明,以及全局变量定义。 UI源文件Interface.c:实现生成UI的函数。 回调函数源文件Callback.c:存放所有用到的回掉函数。 程序入口Main.c:包含main函数

开发环境
操作系统:Linux Mint 20
gcc:v9.3.0 (命令 gcc --version
gtk:v3.24.20 (查看的命令 pkg-config --modversion gtk+-3.0
glade: v3.22.2
vscode:v1.48.2

(1)使用glade设计主窗体UI

推荐先学习这个课程:gtk glade programming
打开glade,软件的界面如下
在这里插入图片描述
新建一个gui项目
在这里插入图片描述
最后完成的预览图如下
在这里插入图片描述
添加treeview的时候要注意:
model是liststore,这是数据格式
view对应的是treeview,添加显示的列的方法是在treeview上右键选择edit,然后就可以添加了。

关于treeview的详细教程参考链接:http://scentric.net/tutorial/treeview-tutorial.html

最后附上ui的数据:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkListStore" id="liststoreCD">
    <columns>
      <!-- column-name Tittle -->
      <column type="gchararray"/>
      <!-- column-name Artist -->
      <column type="gchararray"/>
      <!-- column-name Catalogue -->
      <column type="gchararray"/>
    </columns>
  </object>
  <object class="GtkWindow" id="wndMain">
    <property name="width_request">800</property>
    <property name="height_request">600</property>
    <property name="can_focus">False</property>
    <property name="resizable">False</property>
    <property name="window_position">center</property>
    <child type="titlebar">
      <placeholder/>
    </child>
    <child>
      <object class="GtkFixed">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkButton" id="btnAdd">
            <property name="label">gtk-add</property>
            <property name="width_request">80</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">20</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="btnQuit">
            <property name="label">gtk-close</property>
            <property name="width_request">80</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
          </object>
          <packing>
            <property name="x">120</property>
            <property name="y">20</property>
          </packing>
        </child>
        <child>
          <object class="GtkSeparator">
            <property name="width_request">760</property>
            <property name="height_request">1</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">80</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="width_request">100</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="halign">start</property>
            <property name="label" translatable="yes">Search String: </property>
            <attributes>
              <attribute name="font-desc" value="Sans 12"/>
            </attributes>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">100</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="txtSearch">
            <property name="width_request">545</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
          </object>
          <packing>
            <property name="x">140</property>
            <property name="y">100</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="btnSearch">
            <property name="label">gtk-find</property>
            <property name="width_request">80</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
          </object>
          <packing>
            <property name="x">700</property>
            <property name="y">100</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow">
            <property name="width_request">760</property>
            <property name="height_request">420</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkTreeView" id="tvCD">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="model">liststoreCD</property>
                <property name="search_column">0</property>
                <child internal-child="selection">
                  <object class="GtkTreeSelection"/>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="colTitle">
                    <property name="sizing">fixed</property>
                    <property name="fixed_width">200</property>
                    <property name="title" translatable="yes">Tittle</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellTitle"/>
                      <attributes>
                        <attribute name="text">0</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="colArtist">
                    <property name="sizing">fixed</property>
                    <property name="fixed_width">300</property>
                    <property name="title" translatable="yes">Artist</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellArtist"/>
                      <attributes>
                        <attribute name="text">1</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="colCatalogue">
                    <property name="sizing">autosize</property>
                    <property name="title" translatable="yes">Catalogue</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellCatalogue"/>
                      <attributes>
                        <attribute name="text">2</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">160</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

(2)编写后台C代码

CDManager.h 的源代码:

#include <gtk/gtk.h>

// UI fuctions
GtkWidget *CreatMainWindow(void);
GtkWidget *CreatAddCDWindow(void);

// Callback functions
void on_btnAdd_clicked(GtkWidget *button);
void on_btnQuit_clicked(GtkWidget *button);
void on_btnSearch_clicked(GtkWidget *button,gpointer *data);

Interface.c 的源代码:

#include "CDManager.h"

GtkWidget *CreatMainWindow(void)
{
    GtkBuilder *builder = gtk_builder_new_from_file("Main.ui");
    GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "wndMain"));
    gtk_builder_connect_signals(builder, NULL);
    g_signal_connect(window,"destroy",G_CALLBACK(gtk_main_quit),NULL);
    return window;
}

GtkWidget *CreatAddCDWindow(void)
{
}

Callback.c 的源代码:

// 暂时没有代码

Main.c 的源代码:

#include "CDManager.h"

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    GtkWidget *mainWindow = CreatMainWindow();
    gtk_widget_show_all(mainWindow);
    gtk_main();

    return EXIT_SUCCESS;
}

(3)运行程序

先编写一个makefile来编译整个项目,内如如下

all: app

app: CDManager.h Callback.c Interface.c Main.c
	gcc -o CDManager Callback.c Interface.c Main.c `pkg-config --cflags --libs gtk+-3.0`

clean:
	rm -f CDManager

在项目目录中运行

make

最后就可以运行程序了

./CDManager

(4)实现其他功能

添加CD功能
UI功能的实现

添加的UI

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="wndAddCD">
    <property name="width_request">400</property>
    <property name="height_request">300</property>
    <property name="can_focus">False</property>
    <property name="resizable">False</property>
    <property name="window_position">center-on-parent</property>
    <child type="titlebar">
      <placeholder/>
    </child>
    <child>
      <object class="GtkFixed">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkLabel">
            <property name="width_request">360</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Add CD</property>
            <attributes>
              <attribute name="font-desc" value="Sans 14"/>
              <attribute name="weight" value="semibold"/>
            </attributes>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">20</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="width_request">100</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Tittle:</property>
            <property name="justify">right</property>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">80</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="txtTittle">
            <property name="width_request">250</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
          </object>
          <packing>
            <property name="x">120</property>
            <property name="y">80</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="width_request">100</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Catalogue:</property>
            <property name="justify">right</property>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">180</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="width_request">100</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Artist:</property>
            <property name="justify">right</property>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">130</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="txtCatalogue">
            <property name="width_request">250</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
          </object>
          <packing>
            <property name="x">120</property>
            <property name="y">180</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="txtArtist">
            <property name="width_request">250</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
          </object>
          <packing>
            <property name="x">120</property>
            <property name="y">130</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="btnAddCD">
            <property name="label">gtk-add</property>
            <property name="width_request">80</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_btnAddCD_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="x">120</property>
            <property name="y">230</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="btnClose">
            <property name="label">gtk-close</property>
            <property name="width_request">80</property>
            <property name="height_request">40</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_btnClose_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="x">290</property>
            <property name="y">230</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

主窗体添加窗口

GtkWidget *CreatAddCDWindow(void)
{
    GtkBuilder *builder=gtk_builder_new_from_file("AddCD.ui")   ;
    GtkWidget *window=GTK_WIDGET(gtk_builder_get_object(builder,"wndAddCD"));
    
    gtk_builder_connect_signals(builder,NULL);
    g_signal_connect(window,"destroy",G_CALLBACK(on_btnClose_clicked),NULL);
    g_object_unref(builder);
    return window;
}

编写回调函数

#include "CDManager.h"

static GtkWidget *addCDWindow;

void on_btnAdd_clicked(GtkButton *button)
{
    if (addCDWindow)
    {
        gtk_widget_hide(addCDWindow);
    }
    else
    {
        addCDWindow = CreatAddCDWindow();
    }
    gtk_widget_show_all(addCDWindow);
}

void on_btnQuit_clicked(GtkButton *button)
{
    gtk_main_quit();
}

void on_btnSearch_clicked(GtkButton *button, gpointer *data);

void on_btnAddCD_clicked(GtkButton *button);
void on_btnClose_clicked(GtkButton *button)
{
    gtk_widget_hide(addCDWindow);
    addCDWindow=NULL;
}

再次编译测试

make

./CDManager
安装Mariadb数据库

Linux Mint中执行下面的命令

sudo apt-get install mariadb-server

这条命令将安装server和client

然后安装一个有命令行提示的MySQL工具

sudo apt-get install mycli

创建数据库和数据表

mycli
> creat database CDManagerDb;

> creat table Artists
  (
    Id int auto_increment primary key,
    Name varchar(20) not null
  );
  
> creat table CD
  (
    Id int auto_increment primary key
  	Title varchar(30) not null,
  	ArtistId int not null,
  	Catalogue varchar(20) not null,
  	notes varchar(100)
  );
  
> creat table Track
  (
    CdId int not null,
    TrackId int not null,
    Title varchar(100),
    primary key(CdId,TrackId)
  )

授权用户

grant all on CDManagerDb.* to test@'%' identified by 'test';

安装MySQL开发包

sudo apt-get install libmysqlclient-dev
添加后台代码实现

添加一个访问数据库的头文件CDManagerMysql.h,源代码如下:
这是《Beginning Linux Progamming》书中的代码

// A simplistic structure to represent current CD
struct CurrentCDSt
{
    int ArtistId;
    int CDId;
    char Title[100];
    char ArtistName[100];
    char Catalogue[100];
};

// A simplistic track detailed structure
struct CurrentTracksSt
{
    int CDId;
    char Track[20][100];
};

#define MaxCDResult 10
struct CDSearchSt
{
    int CDId[MaxCDResult];
};


// Database backend functions
int DatabaseStart(char *name,char *password);
void DatabaseEnd();

// Functions for adding a CD
int AddCD(char *artist,char *title,char *catalogue,int *cdId);
int AddTrack(struct CurrentTracksSt *tracks);

// Functions for finding and retrieving a CD
int FindCDs(char *searchStr,struct CDSearchSt *results);
int GetCD(int CDId,struct CurrentCDSt *dest);
int GetCDTracks(int CDId,struct CurrentTracksSt *dest);

// Function for deleting items
int DeleteCD(int CDId);

数据库操作的函数实现CDManagerMysql.c,源代码如下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
#include "CDManagerMysql.h"

static MYSQL myConnection;
static int dbConnected = 0;

static int GetArtistId(char *artist);

int DatabaseStart(char *name, char *password)
{
    if (dbConnected)
    {
        return 1;
    }
    mysql_init(&myConnection);
    if (!mysql_real_connect(&myConnection, "localhost", name, password, "CDManagerDb", 0, NULL, CLIENT_FOUND_ROWS))
    {
        fprintf(stderr, "Database connection failed: %d, %s", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    dbConnected = 1;
    return 1;
}

void DatabaseEnd()
{
    if (dbConnected)
    {
        mysql_close(&myConnection);
    }
    dbConnected = 0;
}

int AddCD(char *artist, char *title, char *catalogue, int *cdId)
{
    if (!dbConnected)
    {
        return 0;
    }
    int artistId = -1;
    artistId = GetArtistId(artist);

    char es[250];
    char is[250];
    mysql_escape_string(es, title, strlen(title));
    sprintf(is, "insert into CD(Title,ArtistId,Catalogue) values('%s',%d,'%s')", title, artistId, catalogue);
    int result = mysql_query(&myConnection, is);
    if (result)
    {
        fprintf(stderr, "Insert error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }

    result = mysql_query(&myConnection, "select last_insert_id()");
    MYSQL_RES *resPtr;
    MYSQL_ROW row;
    int newCDId = -1;
    if (!result)
    {
        resPtr = mysql_use_result(&myConnection);
        if (resPtr)
        {
            row = mysql_fetch_row(resPtr);
            if (row)
            {
                sscanf(row[0], "%d", &newCDId);
            }
            mysql_free_result(resPtr);
        }
    }
    else
    {
        fprintf(stderr, "Select error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    *cdId = newCDId;
    return newCDId == -1 ? 0 : 1;
}

int GetArtistId(char *artist)
{
    char es[100];
    char qs[250];
    mysql_escape_string(es, artist, strlen(artist));
    sprintf(qs, "select Id from Artist where name='%s'", es);
    MYSQL_RES *resPtr;
    MYSQL_ROW row;
    int result = mysql_query(&myConnection, qs);
    if (result)
    {
        fprintf(stderr, "Select error %d: %s", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    resPtr = mysql_store_result(&myConnection);
    int artistId = -1;
    if (resPtr)
    {
        if (mysql_num_rows(resPtr) > 0)
        {
            if (row = mysql_fetch_row(resPtr))
            {
                sscanf(row[0], "%d", &artistId);
            }
        }
    }
    mysql_free_result(resPtr);
    if (artistId != -1)
    {
        return artistId;
    }

    char is[250];
    sprintf(is, "insert into Artist(Name) values('%s')", es);
    result = mysql_query(&myConnection, is);
    if (result)
    {
        fprintf(stderr,"Insert error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    result = mysql_query(&myConnection, "select last_insert_id()");
    if (result)
    {
        fprintf(stderr,"Select error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    resPtr = mysql_use_result(&myConnection);
    if (resPtr)
    {
        if (row = mysql_fetch_row(resPtr))
        {
            sscanf(row[0], "%d", &artistId);
        }
    }
    mysql_free_result(resPtr);
    return artistId;
}

int AddTrack(struct CurrentTracksSt *tracks)
{
    if (!dbConnected)
    {
        return 0;
    }

    int i = 0;
    char es[100];
    char is[250];
    int result = -1;
    while (tracks->Track[i][0])
    {
        mysql_escape_string(es, tracks->Track[i], strlen(tracks->Track[i]));
        sprintf(is, "insert into Track(CdId,TrackId,Title) values(%d,%d,'%s')", tracks->CDId, i + 1, es);
        result = mysql_query(&myConnection, is);
        if (result)
        {
            fprintf(stderr,"Add track error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
            return 0;
        }
        i++;
    }
    return 1;
}

int FindCDs(char *searchStr, struct CDSearchSt *results)
{
    MYSQL_RES *resPtr;
    MYSQL_ROW row;
    int res = -1;
    char qs[250];
    char es[30];
    int count = 0;

    if (!dbConnected)
    {
        return 0;
    }

    memset(results, -1, sizeof(*results));
    mysql_escape_string(es, searchStr, strlen(searchStr));
    sprintf(qs, "select CD.Id from CD,Artist where CD.ArtistId=Artist.Id and (Artist.Name like '%%%s%%' or CD.Title like '%%%s%%' or CD.Catalogue like '%%%s%%')", es, es, es);

    res = mysql_query(&myConnection, qs);
    if (res)
    {
        fprintf(stderr, "Find CDs error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    else
    {
        resPtr = mysql_store_result(&myConnection);
        if (resPtr)
        {
            count = mysql_num_rows(resPtr);
            if (count > 0)
            {
                int i = 0;
                while ((row = mysql_fetch_row(resPtr)) && (i < MaxCDResult))
                {
                    sscanf(row[0], "%d", &results->CDId[i]);
                    i++;
                }
            }
        }
        mysql_free_result(resPtr);
    }
    return count;
}

int GetCD(int CDId, struct CurrentCDSt *dest)
{
    if (!dbConnected)
    {
        return 0;
    }

    MYSQL_RES *resPtr;
    MYSQL_ROW row;
    int res = 0;
    char qs[250];

    memset(dest, 0, sizeof(*dest));
    sprintf(qs, "select CD.Id,Artist.Id,CD.Title,CD.Catalogue,Artist.Name from CD,Artist where CD.ArtistId=Artist.Id and CD.Id=%d", CDId);
    res = mysql_query(&myConnection, qs);
    if (res)
    {
        fprintf(stderr, "Get CD error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    else
    {
        resPtr = mysql_store_result(&myConnection);
        if (resPtr)
        {
            if (mysql_num_rows(resPtr) > 0)
            {
                if (row = mysql_fetch_row(resPtr))
                {
                    sscanf(row[0], "%d", &dest->CDId);
                    sscanf(row[1], "%d", &dest->ArtistId);
                    strcpy(dest->Title, row[2]);
                    strcpy(dest->Catalogue, row[3]);
                    strcpy(dest->ArtistName, row[4]);
                }
            }
        }
        mysql_free_result(resPtr);
    }
    return dest->ArtistId == -1 ? 0 : 1;
}

int GetCDTracks(int CDId, struct CurrentTracksSt *dest)
{
    if (!dbConnected)
    {
        return 0;
    }

    MYSQL_RES *resPtr;
    MYSQL_ROW row;
    char qs[250];
    int result;
    int count = 0, i = 0;
    memset(dest, 0, sizeof(*dest));
    dest->CDId = -1;
    sprintf(qs, "select TrackId,Title from Track where CDId=%d order by TrackId", CDId);
    result = mysql_query(&myConnection, qs);
    if (result)
    {
        fprintf(stderr, "Get Tracks error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    else
    {
        resPtr = mysql_store_result(&myConnection);
        if (resPtr)
        {
            count = mysql_num_rows(resPtr);
            if (count > 0)
            {
                while (row = mysql_fetch_row(resPtr))
                {
                    strcpy(dest->Track[i], row[1]);
                    i++;
                }
                dest->CDId = CDId;
            }
        }
        mysql_free_result(resPtr);
    }
    return count;
}

int DeleteCD(int CDId)
{
    if (!dbConnected)
    {
        return 0;
    }

    MYSQL_RES *resPtr;
    MYSQL_ROW row;
    char qs[250];
    int artistId = -1, count = 0;
    sprintf(qs, "select ArtistId from CD where ArtistId=(select ArtistId from CD where Id=%d)", CDId);
    int res = mysql_query(&myConnection, qs);
    if (res)
    {
        fprintf(stderr, "Select artistId error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }
    else
    {
        resPtr = mysql_store_result(&myConnection);
        if (resPtr)
        {
            count = mysql_num_rows(resPtr);
            if (count == 1)
            {
                row = mysql_fetch_row(resPtr);
                sscanf(row[0], "%d", &artistId);
            }
            mysql_free_result(resPtr);
        }
    }
    sprintf(qs, "delete from Track where CdId=%d", CDId);
    res = mysql_query(&myConnection, qs);
    if (res)
    {
        fprintf(stderr, "Select artistId error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }

    sprintf(qs, "delete from CD where Id=%d", CDId);
    res = mysql_query(&myConnection, qs);
    if (res)
    {
        fprintf(stderr, "Select artistId error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
        return 0;
    }

    if (artistId != -1)
    {
        sprintf(qs, "delete from Artist where Id=%d", artistId);
        res = mysql_query(&myConnection, qs);
        if (res)
        {
            fprintf(stderr, "Select artistId error %d: %s\n", mysql_errno(&myConnection), mysql_error(&myConnection));
            return 0;
        }
    }
    return 1;
}

获取文本输入框对象

#include "CDManager.h"

GtkWidget *TittleEntry;
GtkWidget *ArtistEntry;
GtkWidget *CatalogueEntry;

GtkWidget *CreatMainWindow(void)
{
    GtkBuilder *builder = gtk_builder_new_from_file("Main.ui");
    GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "wndMain"));
    gtk_builder_connect_signals(builder, NULL);
    g_object_unref(builder);
    return window;
}

GtkWidget *CreatAddCDWindow(void)
{
    GtkBuilder *builder=gtk_builder_new_from_file("AddCD.ui")   ;
    GtkWidget *window=GTK_WIDGET(gtk_builder_get_object(builder,"wndAddCD"));
    TittleEntry=GTK_WIDGET(gtk_builder_get_object(builder,"txtTittle"));
    ArtistEntry=GTK_WIDGET(gtk_builder_get_object(builder,"txtArtist"));
    CatalogueEntry=GTK_WIDGET(gtk_builder_get_object(builder,"txtCatalogue"));
    
    gtk_builder_connect_signals(builder,NULL);
    g_signal_connect(window,"destroy",G_CALLBACK(on_btnClose_clicked),NULL);
    g_object_unref(builder);
    return window;
}

UI中添加功能的回掉函数

#include "CDManagerMysql.h"  //包含头文件

extern GtkWidget *TittleEntry;
extern GtkWidget *ArtistEntry;
extern GtkWidget *CatalogueEntry;

// Add CD Callbacks
void on_btnAddCD_clicked(GtkButton *button)
{
    const gchar *tittleConst,*artistConst,*catalogueConst;
    tittleConst= gtk_entry_get_text(GTK_ENTRY(TittleEntry));
    artistConst= gtk_entry_get_text(GTK_ENTRY(ArtistEntry));
    catalogueConst= gtk_entry_get_text(GTK_ENTRY(CatalogueEntry));

    gchar tittle[200], artist[200], catalogue[200];
    strcpy(tittle, tittleConst);
    strcpy(artist, artistConst);
    strcpy(catalogue, catalogueConst);

    DatabaseStart("jlee", "ljj"); //用户名和密码
    int cdId, result;
    result = AddCD(artist, tittle, catalogue, &cdId);
    GtkWidget *msgDialog;
    if (result == 1)
    {
        msgDialog = gtk_message_dialog_new(GTK_WINDOW(AddCDWindow),
                                           GTK_DIALOG_DESTROY_WITH_PARENT,
                                           GTK_MESSAGE_INFO,
                                           GTK_BUTTONS_CLOSE,
                                           "Add CD successfully!");
    }
    else
    {
        msgDialog = gtk_message_dialog_new(GTK_WINDOW(AddCDWindow),
                                           GTK_DIALOG_DESTROY_WITH_PARENT,
                                           GTK_MESSAGE_INFO,
                                           GTK_BUTTONS_CLOSE,
                                           "Add CD failed!");
    }
    gtk_dialog_run(GTK_DIALOG(msgDialog));
    gtk_widget_destroy(msgDialog);
    DatabaseEnd();
}

makefile文件需要修改如下:

all: app

app: CDManager.h Callback.c Interface.c Main.c CDManagerMysql.h CDManagerMysql.c
	gcc -rdynamic -o CDManager Callback.c Interface.c CDManagerMysql.c Main.c -lmysqlclient `pkg-config --cflags --libs gtk+-3.0`

clean:
	rm -f CDManager

最后实现的效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

搜索CD功能
UI关于Treeview修改

由于要展示Track的信息,我们将Treeview的Model 改成TreeStore,而不是简单使用Liststore。
新建一个Treestore

在这里插入图片描述

在这里插入图片描述

绑定到Treeview上

在这里插入图片描述

后台代码实现

CDManager.h头文件中添加一个枚举:

enum {
    ColumnTittle,
    ColumnArtist,
    ColumnCatalogue,
    NumColumns
};

Callback.c文件中添加搜索按钮的回掉函数,源代码如下:

extern GtkWidget *CDTreeView;
extern GtkTreeStore *TreeStore;

void on_btnSearch_clicked(GtkButton *button, gpointer *data)
{
    DatabaseStart("jlee","ljj");
    gchar searchString[200];
    strcpy(searchString, gtk_entry_get_text(GTK_ENTRY(data)));
    struct CDSearchSt cdSearchResult;
    gint foundedCDCount = FindCDs(searchString, &cdSearchResult);

    gint i, j, trackCount;
    struct CurrentCDSt cd;
    struct CurrentTracksSt ct;
    GtkTreeIter parent, child;
    gchar trackTittle[110];
    for (i = 0; i < foundedCDCount && i < MaxCDResult; i++)
    {
        GetCD(cdSearchResult.CDId[i], &cd);
        gtk_tree_store_append(TreeStore, &parent, NULL);
        gtk_tree_store_set(TreeStore, &parent,
                           ColumnTittle, cd.Title,
                           ColumnArtist, cd.ArtistName,
                           ColumnCatalogue, cd.Catalogue,
                           -1);

        trackCount = GetCDTracks(cdSearchResult.CDId[i], &ct);
        memset(trackTittle, 0, sizeof(trackTittle));
        for (j = 0; j < trackCount; j++)
        {
            sprintf(trackTittle, " Track %d. ", j + 1);
            strcat(trackTittle, ct.Track[j]);
            gtk_tree_store_append(TreeStore, &child, &parent);
            gtk_tree_store_set(TreeStore, &child, ColumnTittle, trackTittle);
        }
    }

    gtk_tree_view_set_model(GTK_TREE_VIEW(CDTreeView),GTK_TREE_MODEL(TreeStore));
    DatabaseEnd();
}
效果图

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值