在 gui 中,我想修改用户在 GtkEntry 中插入的文本。例如,如果用户输入“joHn doe”,我的 gui 应该会看到这不是一个格式良好的名称,并将其更改为“John Doe”。
我将处理程序连接到“已更改”信号,如GtkEntry 文本更改信号中所述。出现的问题是,如果我更改信号处理程序中的条目,则会一次又一次地发出“已更改”信号,直到王国到来。
我目前通过进行字符串比较来防止这种情况,并且仅当文本“nameified”版本与条目内的文本不相等时才更改 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;
}
非常欢迎任何帮助。
连接到“changed”信号并不是很方便,但连接到“insert-text”信号更合适。更好的是让默认的“插入文本”处理程序更新条目。比在“插入文本”信号上使用 g_signal_connect_after 来更新条目中的文本,这可以防止更改后的信号无限运行。这也应该对“删除文本”信号执行,因为如果用户删除大写字母,则应删除大写字母,并将第二个字母大写。
创建运行时:
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”,以将这个处理程序与能够修改条目中的字符串的不同函数一起使用。
insert-text
信号似乎更合适。 insert-text
可用于在输入之前对文本进行可能的更改。您可以使用回调函数模板insert_text_handler
部分描述GtkEditable
。您可以使用 nameify
来更改函数(因为您不会获得整个文本,而是获得部分文本或字符;最简单的修改可能是声明 uppercase_next
static)来对文本进行修改。我认为最快的解决方案是暂时阻止您的回调被调用。
g_signal_connect
函数组每个都返回一个 gulong
类型的“handler_id”。您必须存储此 id,使用“userdata”参数将其传递给回调(或者仅使用全局静态变量),然后将文本操作代码放在 g_signal_handler_block
/g_signal_handler_unblock
之间一对。
连接到
insert-text
和 delete-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");
如果不需要更改文本,只需返回即可。
比阻塞和解除阻塞信号更简单,只需一个布尔值:
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
}
回顾 nameify() 函数 它返回已释放字符串的位置。 在某些情况下,这可能会导致SEGV。
ret_val = s->str;
g_string_free(s, FALSE);
return ret_val;