天天生活在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函数一些简单了许多啊。并且传递参数也简单了许多。