Учимся вводить только цифры в wxTextCtrl
September 8th, 2008 Begemot Posted in Программирование
Поразительная фигня, казалось бы простая задача и должно быть готовое решение. И правда есть, только поразительно кривое:(
Итак, задача – необходимо дать возможность пользователю указать число, например в настройках, количество записей для отображения где-нибудь… Первое что приходит в голову – wxSlider, ну знаете такой контрол со стрелочками. Так как у меня диапазон от нуля до 10 000, а возможность задать шаг инкрементации для стрелочек я не нашел, и равен он 1 – то этот вариант, оказывается издевательством над юзером.
Думаем дальше и вспоминаем, про wxTextCtrl и валидаторы. Радуемся и пишем код, что-то типа
m_Count = new wxTextCtrl( this, ID_MAXCLIPTEXT, _T(""), wxDefaultPosition, wxSize(50, -1), 0, wxTextValidator(wxFILTER_NUMERIC, &count));
Я ожидал что контрол будет давать вводить в себя только цифры. Я ошибся, он также пропускал точку и запятую, что еще хоть как-то можно понять, хотя его и не просили. Но еще оказались разрешенными: ‘+’ и ‘–’. Также он спокойно пропускал все буквы кирилицы. Похоже что контрол отфильтровывал только пробел, спецсимволы и латиницу. Хотя даже это можно ввести туда пользуясь драг анд дропом или простой вставкой. Вообщем я разочаровался.
Попробывал задать более конкретно wxFILTER_INCLUDE_CHAR_LIST и разрешил только цифры. “голосуй, не голосуй, все равно … “ (с). Теже траблы со вставкой текста из буфера и …. с кирилицей. Прямо наваждение какое-то.
Решил писать сам, вот что получилось
[sourcecode language=”cpp”]
h:
void OnMaxClipTextUpdated( wxCommandEvent& event );
wxTextCtrl* m_Count;
unsigned long maxCount;
long maxInsertionPoint;
wxString maxCountStr;
bool maxSetManually;
cpp:
EVT_TEXT( ID_MAXCLIPTEXT, COPDatabase::OnMaxClipTextUpdated )
m_Count = new wxTextCtrl( this, ID_MAXCLIPTEXT, _T(“”), wxDefaultPosition, wxSize(50, -1), 0);
m_Count->SetMaxLength(4);
void COPDatabase::OnMaxClipTextUpdated( wxCommandEvent& event )
{
if (maxSetManually) return;
wxString str=m_Count->GetValue();
if (str.empty() || (str==wxT(” “))) // вторая проверка нужна для случая когда юзер выделяет все в контроле и жмет пробел
{
maxCount=0;
str=wxT(“0”);
maxInsertionPoint=1;
// or if you want to let empty string – replace last 2 string to these – maxCountStr=wxEmptyString; maxInsertionPoint=0;
}
if (str.ToULong(&maxCount))
{ // successfully converted to ulong, check range now
if (maxCount>10000) maxCount=10000;
else if (maxCount<0) maxCount=0;
maxInsertionPoint=m_Count->GetInsertionPoint();
maxCountStr=wxString::Format(wxT(“%i”), maxCount); // we should do like this! иначе траблы с пробелами, нулями..
}
maxSetManually=true;
m_Count->SetValue(maxCountStr);
maxSetManually=false;
m_Count->SetInsertionPoint(maxInsertionPoint);
}
[/sourcecode]
Код выглядит извратом, но именно в такой он превратился после нескольких итераций, и такой заработал лучше всего.
Поведение (юзабилити) не идеально, но твердую 4-ку я бы поставил. Хотя в случае если диапазон снизу ограничен не нулем, тогда поведение хреново будет, но у меня такого случая пока не было. Как выход можно исключить динамическую проверку диапазона, и перенести ее в код сохраняющий значение.
Еще совет, лучше поставить ограничение на длину вводимых символов, типа m_Count->SetMaxLength(4); для верхней границы 10000 и т.д.
Пользуйтесь. Высказывайтесь.
September 9th, 2008 at 2:29 pm
wxSpinCtrl чем не подошел, по моему он для того и предназначен.
September 9th, 2008 at 3:55 pm
wxSpinCtrl не разрешает ввести число с клавиатуры, только стрелочками. У стрелочек шаг изменения = 1 и его нельзя изменить, у меня диапазон от 0 до 10 000, и я не хочу над юзерами издеватся:)
September 9th, 2008 at 9:20 pm
wxSpinCtrl позволяет вводить значение с клавиатуры. Что-то ты не так делаешь.
September 9th, 2008 at 10:45 pm
ну попробуй скомпили стандартный пример с wxSpinCtrl.
Хотя может я и путаю что-то, я сам был в шоке когда обнаружил что с клавы нельзя цифры ввести.
September 12th, 2008 at 2:41 am
wxSpinCtrl – вроде замечен баг в последней ветке (2.8.8), легко исправляется. Посмотрите в баг репортах.
September 12th, 2008 at 8:59 pm
спасибо, нашел.
Пропатчил, скомпили. Действительно позволяет вводить цифры теперь. А еще можно писать всякие нехорошие слова – буквы не фильтрует:( решение еще хуже чем wxTextCtrl с валидатором.
September 13th, 2008 at 2:27 am
можно еще посмотреть в сторону wxFormatValidator…
September 13th, 2008 at 9:47 am
Попробывал, тоже не устраивает поведенье. Но тут хоть понятно, он все-таки заточен немного для другой задачи.
В принципе меня мой изврат пока устраивает:)
September 16th, 2008 at 2:23 pm
# if (maxSetManually) return;
# …
# # maxSetManually=true;
# m_Count->SetValue(maxCountStr);
# maxSetManually=false;
Эту конструкцию можно заменить на
# m_Count->ChangeValue(maxCountString)
не вызывающую эвент EVT_TEXT
September 16th, 2008 at 5:45 pm
Да, действительно, здорово:)
Заменяем SetValue() на ChangeValue(), и выкидываем все что связанно с maxSetManually.
Я себе уже исправил, спасибо.
November 10th, 2008 at 3:57 pm
Если уж так хочется именно wxTextCtrl для ввода только цифр:
class wxNumberValidator : public wxTextValidator {
public:
wxNumberValidator:wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST)
{
wxArrayString a;
for (int i=0;i<10;i++) a.Add(wxString::Format(“%d”,i));
SetIncludes(a);
}
};
/* … */
wxTextCtrl* m_pNumberCtrl = new wxTextCtrl(this, wxID_ANY, wxT(“0”), wxDefaultPosition, wxDefaultSize, 0, wxNumberValidator());
November 10th, 2008 at 6:55 pm
Вариант с wxFILTER_INCLUDE_CHAR_LIST я пробывал, и о проблемах с ним писал выше
March 11th, 2010 at 12:42 pm
На самом деле если верить утверждениям некоторых написавших специальный класс валидатора для этого, делов там на 30 минут, и класс работает лучше чем у меня:(