利用GTK 和 libxml 编写词典软件

天天生活在linux下面,又是热岑于读英文文档的人,没有词典软件好悲剧啊。只能天天在浏览器里开有道来翻译。这个让人感觉太麻烦了。

虽然linux下也有几个不错的词典软件,如gold-dict之类的,gnome自带的那个软件我是没有用成功过,总是在卡死状态。

自己这一段也略懂点GTK,干脆自己写个算了。嘿嘿,当然功能还是很简单的啊。

主要还是通过有道词典的网页版来实现的。只不过将网页里面的信息进行提取,显示出来了。先上张效果图看看。


既然是linux下的软件,就要有点linux的风范。在单词查找框中输入ctrl + l的时候会清除原有的输入.

嗯。这个快捷键我是很喜欢啊。别人做的软件很多时候不会满足自己的一些需求啊。



贴完图就的贴代码,还是那样的,程序员之间代码才是最直接的交流语言。


#include <stdio.h>
#include <string.h>

#include <gtk/gtk.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>

#define KEY_WORD_XPATH "//div[@id='phrsListTab']"
#define KEY_URL_PREFIX "http://dict.youdao.com/search?q="
#define KEY_URL_SUFIX "&keyfrom=dict.index"

GPtrArray * dict_get_translate(const gchar * keyword)
{
	gchar * key_url = NULL;
	htmlDocPtr html_doc = NULL;
	xmlDocPtr doc = NULL;
	xmlNodePtr clone = NULL;
	xmlXPathContextPtr ctx = NULL;
	xmlXPathObjectPtr obj = NULL;
	xmlNodeSetPtr nodeset = NULL;
	gchar * tmp_str = NULL;
	GPtrArray * result = NULL;
	gint i;


	key_url = g_strjoin(NULL, KEY_URL_PREFIX, keyword, KEY_URL_SUFIX, NULL);
	g_debug("KEY-URL: %s", key_url);

	html_doc = htmlReadFile(key_url, NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
	if (!html_doc)
	{
		g_message("KEY-URL %s get failed", key_url);
		goto out;
	}

	ctx = xmlXPathNewContext((xmlDocPtr)html_doc);
	if (!ctx)
	{
		g_message("XPath context creat failed");
		goto out;
	}

	obj = xmlXPathEvalExpression(KEY_WORD_XPATH, ctx);
	if (!obj)
	{
		g_message("XPath eval key word xpath failed");
		goto out;
	}

	if (xmlXPathNodeSetIsEmpty(obj->nodesetval))
	{
		g_message("XPath search keyword failed");
		goto out;
	}

	nodeset = obj->nodesetval;
	g_debug("Key word node set have %d object", nodeset->nodeNr);

	clone = xmlCopyNode(nodeset->nodeTab[0], 1);
	if (!clone)
		goto out;

	doc = xmlNewDoc("1.0");
	if (!doc)
		goto out;

	xmlDocSetRootElement(doc, clone);

	xmlXPathFreeContext(ctx);
	ctx = NULL;

	xmlXPathFreeObject(obj);
	obj = NULL;

	ctx = xmlXPathNewContext(doc);
	if (!ctx)
		goto out;

	obj = xmlXPathEvalExpression("//span[@class='keyword']", ctx);
	if (!obj)
		goto out;

	nodeset = obj->nodesetval;
	tmp_str = xmlNodeGetContent(nodeset->nodeTab[0]->xmlChildrenNode);
	g_debug("The word to search %s", tmp_str);

	xmlFree(tmp_str);
	tmp_str = NULL;

	xmlXPathFreeObject(obj);
	obj = NULL;

	obj = xmlXPathEvalExpression("//ul/li", ctx);
	if (!obj)
		goto out;

	if (xmlXPathNodeSetIsEmpty(obj->nodesetval))
	{
		g_message("Result value is empty");
		goto out;
	}

	nodeset = obj->nodesetval;
	result = g_ptr_array_sized_new(nodeset->nodeNr + 1);
	for (i = 0; i < nodeset->nodeNr; ++i)
	{
		tmp_str = xmlNodeGetContent(nodeset->nodeTab[i]->xmlChildrenNode);
		g_ptr_array_add(result, tmp_str);
	}

	g_ptr_array_add(result, NULL);

out:
	if (doc)
		xmlFreeDoc(doc);

	if (key_url)
		g_free(key_url);

	if (html_doc)
		xmlFreeDoc((xmlDocPtr)html_doc);

	if (ctx)
		xmlXPathFreeContext(ctx);

	if (obj)
		xmlXPathFreeObject(obj);

	return result;
}

void dict_present_result(gpointer data, gpointer user_data)
{
	g_printf("result: %s\n", data);
}

void dict_free_the_result(gpointer data, gpointer user_data)
{
	if (data)
		xmlFree(data);
}

#define DICTIONARY_TYPE_PANEL (dictionary_panel_get_type())
#define DICTIONARY_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), DICTIONARY_TYPE_PANEL, DictionaryPanel))
#define DICTIONARY_IS_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), DICTIONARY_TYPE_PANEL))
#define DICTIONARY_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), DICTIONARY_TYPE_PANEL, DictionaryPanelClass))
#define DICTIONARY_IS_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), DICTIONARY_TYPE_PANEL))
#define DICTIONARY_PANEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), DICTIONARY_TYPE_PANEL, DictionaryPanelClass))

typedef struct _DictionaryPanel DictionaryPanel;
typedef struct _DictionaryPanelClass DictionaryPanelClass;

struct _DictionaryPanel
{
	GtkWindow window;

	GtkWidget * entry;
	GtkWidget * text_view;
	GtkWidget * search_button;
};

struct _DictionaryPanelClass
{
	GtkWindowClass parent_class;
};

GType dictionary_panel_get_type(void);

static void dictionary_panel_class_init(DictionaryPanelClass * klass)
{

}

void dict_translate_word(GtkWidget * button, DictionaryPanel * panel)
{
	GPtrArray * result;
	GtkTextBuffer * text_buffer;
	const gchar * keyword = gtk_entry_get_text(GTK_ENTRY(panel->entry));
	gchar * tip;

	//here we should check the keyword
	if (strlen(keyword) == 0)
		return;

	text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(panel->text_view));

	result = dict_get_translate(keyword);
	if (result)
	{
		tip = g_strjoinv("\n", (gchar **)result->pdata);

		g_ptr_array_foreach(result, dict_free_the_result, NULL);

		g_ptr_array_free(result, TRUE);
	}
	else
	{
		tip = g_strjoin(NULL, "Word ", keyword, " not found", NULL);
	}

	gtk_text_buffer_set_text(text_buffer, tip, -1);
	g_free(tip);
}

gboolean dict_filter_input_word(GtkWidget * entry, GdkEventKey * key, DictionaryPanel * panel)
{
	switch (key->keyval)
	{
		case GDK_KEY_Return:
			dict_translate_word(NULL, panel);
			return TRUE;
		case GDK_KEY_L:
		case GDK_KEY_l:
			if (key->state & GDK_CONTROL_MASK)
			{
				gtk_entry_set_text(GTK_ENTRY(panel->entry), "");
				return TRUE;
			}

			break;
		default:
			break;
	}

	return FALSE;
}

static void dictionary_panel_init(DictionaryPanel * self)
{
	GtkWidget * hbox, * vbox;
	GtkWidget * scrolled_window;
	GtkWidget * window = GTK_WIDGET(&self->window);

	gtk_widget_set_size_request(window, 500, 300);
	gtk_container_set_border_width(GTK_CONTAINER(window), 10);
	gtk_window_set_title(GTK_WINDOW(window), "Dictionary");
	g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);


	self->entry = gtk_entry_new();
	gtk_widget_add_events(self->entry, GDK_KEY_PRESS_MASK);
	g_signal_connect(window, "key-press-event", G_CALLBACK(dict_filter_input_word), self);

	self->text_view = gtk_text_view_new();
	gtk_text_view_set_editable(GTK_TEXT_VIEW(self->text_view), FALSE);

	self->search_button = gtk_button_new_from_stock(GTK_STOCK_FIND);
	g_signal_connect(self->search_button, "clicked", G_CALLBACK(dict_translate_word), self);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
	gtk_box_pack_start(GTK_BOX(hbox), self->entry, TRUE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), self->search_button, FALSE, FALSE, 0);

	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
	gtk_container_add(GTK_CONTAINER(scrolled_window), self->text_view);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);

	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);

	gtk_container_add(GTK_CONTAINER(window), vbox);
}

GType dictionary_panel_get_type(void)
{
	static GType type = 0;

	if (type == 0)
	{
		const GTypeInfo info = {
			sizeof (DictionaryPanelClass),
			NULL,
			NULL,
			(GClassInitFunc)dictionary_panel_class_init,
			NULL,
			NULL,
			sizeof(DictionaryPanel),
			0,
			(GInstanceInitFunc)dictionary_panel_init
		};

		type = g_type_register_static(GTK_TYPE_WINDOW, "Dictionary", &info, 0);
	}

	return type;
}

GtkWidget * dictionary_panel_new(void)
{
	DictionaryPanel * dict;

	dict = g_object_new(DICTIONARY_TYPE_PANEL, "type", GTK_WINDOW_TOPLEVEL, NULL);

	return GTK_WIDGET(dict);
}

int main(int ac, char ** av)
{
	GtkWidget * dict;
	gtk_init(&ac, &av);

	dict = dictionary_panel_new();

	gtk_widget_show_all(dict);
	gtk_main();

	return 0;
}

顺便把 Makefile里面的东西也弄出来吧。这个代码里面需要引用libxml的东西

dict:dict.c
	cc -g -o $@ $< `pkg-config --cflags --libs libxml-2.0` `pkg-config --cflags --libs gtk+-3.0`

在解析有道词典返回的页面的时候认识到了XPath的东西。这个是个好东西啊。有时间的话,大家可以了解一下,很简单,但是很有用啊。

另外,这回写的代码采用了gobject的对象管理功能,代码写起来就是不一样啊,main函数一些简单了许多啊。并且传递参数也简单了许多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值