GtkEntry更改用户输入的文本

时间:2013-05-14 09:07:43

标签: user-interface gtk

在gui中我想修改用户在GtkEntry中插入的文本。例如,如果用户输入'joHn doe',我的gui应该看到这不是一个格式很好的名称,并将其更改为'John Doe'。

我将处理程序连接到“已更改”信号,如GtkEntry text change signal中所述。出现的问题是,如果我更改信号处理程序中的条目,则会一次又一次地发出“已更改”信号,直到王国来临。

我目前通过进行字符串比较来防止这种情况,如果文本“namified”版本与条目内的文本不相等,我只会更改GtkEntryBuffer中的文本。但是我觉得作为程序员我应该能够更改条目中的文本,而不会一遍又一遍地调用更改的处理程序。

更改的信号处理程序是:

void nameify_entry ( GtkEditable* editable, gpointer data )
{
    gchar* nameified;
    const gchar *entry_text;

    entry_text = gtk_entry_get_text( GTK_ENTRY(editable) );
    nameified = nameify(entry_text);

    /*is it possible to change the buffer without this using this string
      comparison, without the "change" signal being emitted over and over again?*/
    if ( g_strcmp0(entry_text, nameified) != 0 ){
        GtkEntryBuffer* buf = gtk_entry_get_buffer(GTK_ENTRY(editable) );
        gtk_entry_buffer_set_text( buf, nameified, -1 );
    }
    g_free(nameified);
}

我的nameify功能是:

/*removes characters that should not belong to a name*/
gchar*
nameify ( const char* cstr )
{
    const char* c;
    gchar* ret_val;
    GString* s = g_string_new("");

    gboolean uppercase_next = TRUE;
    g_debug( "string = %s", cstr);

    for ( c = cstr; *c != '0'; c = g_utf8_next_char(c) ) {
        gunichar cp = g_utf8_get_char(c); 
        if ( cp == 0 ) break;
        if ( g_unichar_isalpha( cp ) ){
            if ( uppercase_next ){
                g_string_append_unichar( s, g_unichar_toupper(cp) );
                uppercase_next = FALSE;
            }
            else{
                g_string_append_unichar(s,g_unichar_tolower(cp));
            }
        }
        if ( cp == '-' ){
            g_string_append_unichar( s, cp);
            uppercase_next = TRUE;
        }
        if ( cp == ' '){
            g_string_append_unichar( s, cp);
            uppercase_next = TRUE;
        }
    }

    ret_val = s->str;
    g_string_free(s, FALSE);
    return ret_val;
}

非常欢迎任何帮助。

5 个答案:

答案 0 :(得分:3)

连接到“已更改”信号并不是很方便,但更适合连接到“插入文本”信号。更好的是使用默认的'insert-text'处理程序更新条目。在“插入文本”信号上使用g_signal_connect_after来更新条目中的文本,这可以防止更改的信号无限运行。这也应该对'delete-text'信号进行,因为如果用户删除大写字母,则应删除资本,第二个应该大写。

所以创建运行:

g_signal_connect_after( entry, "insert-text", G_CALLBACK(name_insert_after), NULL );
g_signal_connect_after( entry, "delete-text", G_CALLBACK(name_delete_after), NULL );

然后你可以拥有这些信号处理程序:

void
name_insert_after (GtkEditable* edit,
                   gchar* new_text,
                   gint new_length,
                   gpointer position,
                   gpointer data)
{
    /*prevent compiler warnings about unused variables*/
    (void) new_text; (void) new_length; (void) position; (void) data;
    const gchar* content = gtk_entry_get_text( GTK_ENTRY(edit) );
    gchar* modified = nameify( content);
    gtk_entry_set_text(GTK_ENTRY(edit),modified);
    g_free(modified);
}

void
name_delete_after (GtkEditable* edit,
                   gint start_pos,
                   gint end_pos,
                   gpointer data)
{
    /*no op cast to prevent compiler warnings*/
    (void) start_pos; (void) end_pos; (void) data;
    /*get text and modify the entry*/
    int cursor_pos = gtk_editable_get_position(edit);
    const gchar* content = gtk_entry_get_text( GTK_ENTRY(edit) );
    gchar* modified = nameify( content);
    gtk_entry_set_text(GTK_ENTRY(edit),modified);
    gtk_editable_set_position(edit, cursor_pos);
    g_free(modified);
}

这些可以与原帖中的nameify函数一起使用。 你甚至可以在数据上提供一个函数指针而不是'NULL'来使用这个处理程序,它具有能够修改条目中字符串的不同函数。

答案 1 :(得分:1)

根据您的要求insert-text信号似乎更合适。 insert-text可用于在输入之前对文本进行更改。您可以使用GtkEditable描述的回调函数模板insert_text_handler。您可以使用nameify对函数进行更改(因为您不会获得整个文本,而是文本或字符的一部分;最简单的修改可能是声明uppercase_next 静态 )修改文本。
希望这有帮助!

答案 2 :(得分:0)

我认为最快的解决方案是暂时阻止你的回调被叫。

g_signal_connect个函数组各自返回gulong类型的“handler_id”。您必须存储此ID,使用“userdata”参数将其传递给回调(或者仅使用全局静态变量),然后将文本操作代码放在g_signal_handler_block / g_signal_handler_unblock之间对

答案 3 :(得分:0)

连接到insert-textdelete-text是正确的想法,但您想使用g_signal_connect进行连接。如果您使用g_signal_connect_after,则在更正之前已经显示了错误的文本,这可能会导致显示闪烁。此外,您需要在调用gtk_entry_set_text时阻止您的信号处理程序,因为这会发出delete-text后跟insert-text。如果您不阻止信号,您将递归调用信号处理程序。记住GObject信号只是函数调用。发出信号与直接从代码中调用处理程序相同。

我建议为insert-text设置一个处理程序,以查看是否需要更改新输入。如果是,则创建一个新字符串,并按照GtkEditable文档

执行此操作
g_signal_handlers_block_by_func (editable, insert_text_handler, data);
gtk_editable_insert_text (editable, new-text, g_strlen(new_text) , position);
g_signal_handlers_unblock_by_func (editable, insert_text_handler, data);

g_signal_stop_emission_by_name (editable, "insert_text");

如果您不需要更改输入,只需返回。

对于delete-text处理程序,我会查看是否需要更改文本(记住还没有删除任何内容),如果是这样,请用

更新整个字符串
g_signal_handlers_block_by_func (editable, insert_text_handler, data);
g_signal_handlers_block_by_func (editable, delete_text_handler, data);
gtk_entry_set_text (GKT_ENTRY (editable), new-text);
g_signal_handlers_unblock_by_func (editable, delete_text_handler, data);
g_signal_handlers_unblock_by_func (editable, insert_text_handler, data);

g_signal_stop_emission_by_name (editable, "delete_text");

如果您不需要更改文本,请再次返回。

答案 4 :(得分:0)

比阻塞和解锁信号更简单只需要一个布尔值:

myHandler(...){
static int recursing=0;
if(recursing){
    recursing=0;
    return;
}
... logic to decide if a change is needed
recursing=1;
gtk_entru_set_text(...);
... will recurse to your hander, which will clear the recursing variable and resume here
}