What

这几天用Gtk写Mysql的GUI前台,今天终于写得差不多了,在博客里记记,源码在这https://godorz.info/wp-content/uploads/2010/04/mysqlgui.zip, 1000多行几乎都是手打,可以自由复制,但其中的致谢信息不可去掉,因为贪图方便,连基本的头文件和实现都没分开,大汗..编译命令为: gcc -o main main.c -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient `pkg-config --cflags --libs gtk+-2.0` ,截图如下:

How

程序分为几个部分,各函数功能如下:

  • cb_openmenuitem()用来显示子对话框,输入服务器,端口,用户名和密码,点击Connect按钮后调用cb_connectbutton()函数,得到用户输入,初始化myconnection,连接服务器.
  • cb_aboutmenuitem()是一些版权信息.其实是用来练手对话框的.
  • cb_executebutton()用于得到用户在主面板的输入(mysql script命令),调用make_tree_view( )将执行结果显示在showresultvbox中.
  • cb_savebutton()将用户修改过的数据表保存下来.想法可以参考这里https://godorz.info/2010/04/display-mysql-data-with-gtkclist/
  • GType getdatatype(MYSQL_FIELD *field)将数据表每一列的数据类型MYSQL_TYPE转化为对应的Gtk中的数据类型G_TYPE.函数在这里没有用上,其实是为了以后修改代码时做准备的.比如说要提供一个时间类型的加减功能,就必须事先知道该列中存储的数据类型为吗MYSQL_TYPE_TIME而不是MYSQL_TYPE_STRING.
  • SETLIST()这是一个宏命令,为了使treeview每一列的类型为editable,需要对每一列绑定不同的回调函数,在回调函数中指名列号.在程序中,我认定数据表列数不超过30列,30个基本一样的回调函数写起来很麻烦,因此用宏写了.
  • setup_tree_view()根据传入的MYSQL conn新建显示数据表需要用到的treeview,指定列名和每列的数据类型(这里全部指定为G_TYPE_STRING),然后设置每列均为可编辑.
  • make_tree_view()有了调用setup_tree_view()得到的treeview后,将数据写在treeview上,然后放在滚动窗口scrolled_win,之后将scrolled_win放在showresultvbox上,显示数据表的内容.
  • make_clist()和setup_tree_view() + make_tree_view()的功能类似,显示数据表的内容,只不过这里改成了gtk_clist实现,而不是gtk_tree_view,在程序中这个函数也没有被使用,仅仅是做个比较罢了.
  • create_and_fill_model()设置数据库名称和数据表名称的显示类型treestore--树形显示,而不是数据表内容的treeview--列表显示.
  • create_view_and_model()将数据库名称和数据表名称以树形形式dbsandtablestreeview放至滚动窗口scrolled_win_dbs_tables,再将滚动窗口显示在showdbsandtablesvbox中.
  • on_changed()接受用户选择数据库(表)的事件,将选择的数据库和数据表分别存为selecteddatabase和selectedtable

Problems

遇到的比较恶心的问题有好几个,好在大概都解决了:

  1. 回调函数,SETLIST()就是很恶心的一个,很认真的查了资料,却没有发现好的解决方法,貌似每列一个回调函数指定该列为可编辑是必须的.还有一个是树形显示中接受用户选择的GtkTreeSelection *selection的回调函数,g_signal_connect可以,但g_signal_connect_swapped就不行了,printf()的方式调式了好久才发现这个问题,悲剧的是到现在还是搞不清两者的区别..
  2. vbox的刷新,其实这么说是不正确的,因为new出来的一个vbox,是无法再在面板上显示出来的,唯一的方法是将vbox内的widget全部remove掉,然后再gtk_container_add进去,remove时需要注意的是事先需要得到需要remove的widget的引用(gtk_widget_ref)..假如该widget之后不再需要使用了,还需要将引用去掉(gtk_widget_unref).
  3. 修改后的数据表的保存,当一个单元cell为空时,将gtk_tree_model_get得到的字符串s以printf打印出来是(null),但理所当然地以为s == NULL是个大错,没有值与值为空在界面上看来都是一个空的单元,但它们还是存在区别的,这也是为什么我将对空的单元内容的判断从if( s == NULL)改成了if( s == NULL || strlen(s) < 1 )..不过,虽然知道这一点,我写程序时还是偷懒了,一律将空的单元以""的形式写回数据表..其实,负责任点的话,是需要在insert时仅仅写明需要插入的列的,至于空的单元,不理就是了.顺便提一下数据表的问题,我没有对字符编码做判断,因此数据出现中文时程序显示乱码,并非判断很难,而是我对用数据库存储中文或多或少存在一些偏见.

To be continued..

前面解决了一些问题,但是,程序没有解决的问题还有很多:

  1. 为了数据的安全,每次对数据表的操作,不管是选择,显示,修改还是保存,其实在操作之前都是需要重新连接数据库的,而在操作完之后将连接关闭.而且保存是其实还是有一些数据要判断的,因为affected_row很不靠谱.这个真的很简单,但是我没改,因为有太多的bug了.:-)
  2. 程序主面板中还有一个命令输入框scriptentry和执行按钮executebutton,其实都是测试程序时留下的,本该去掉的,如果不去掉的话,至少应该在cb_executionbutton里加入一些判断语句,禁止用户执行create table/database和drop table.database等语句,因为真的有太多bug了.一旦程序出现问题把数据库搞坏就对不起自己了.也正因为如此,凡是涉及到删除的功能程序中全部没有提供,在数据表中新建一列都被和谐了..
  3. 然后,虽然clist比起treeview真的太简单了,连单元可编辑都步支持,更别提在单元里放入widget了..但想了几天后,我觉得是可以曲线救国的,用gtk_clist_get_text()得到鼠标点击的行列,然后新建一个输入框,大小等同于clist中一个单元,让它悬浮在鼠标点击单元之上,将其完全覆盖,当用户停止输入后,将得到的结果覆盖单元原值,这么依赖,用户看起来的效果就和单元可编辑没什么两样啦.
  4. ...

Reference

最主要的参考资料有:

  • Gtk+ 2.0 tutorial
  • Dev help

然后是gtk+的邮件列表(gtk-devel-list@gnome.org),老外真的很热心,有什么问题一问基本上都有人回,但是,为了节省大家的时间,不妨先看完How To: Ask Questions The Smart Way..这里是邮件列表的archive,有什么不懂的在这先搜搜..http://www.mail-archive.com/gtk-app-devel-list@gnome.org/info.html

EOF

想起一个笑话,从前有个太监,下面呢..下面没了..

其实下面还有的,测试一下动态签名图: