文章目录
前言
学习如何使用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();
}
效果图