QLineEdit/QComboBox 忽略变音符号的搜索
A QLineEdit/QComboBox search that ignores diacritics
我有一个应用程序,人们可以在其中输入地名。这是欧洲,我们必须处理包含变音符号的名称,例如 Orléans、Köln、Liège、Châteauroux。当人们输入名字时,我希望他们能够在没有变音符号的情况下键入字符,但仍然会列出包含这些名字的名字,这样他们就可以 select 正确重音的名字。该程序有一个很长但并不详尽的名称列表(人们可以随时输入他们喜欢的任何名称)。
我已经有了一个基于非变音符号匹配查找名字的函数。所以 'orle' 会 return 'Orléans','kol' 会找到 'Köln',等等
我尝试了两件事:
1:一个带有 QCompleter 的 QLineEdit,该 QCompleter 使用 QStringListModel 用匹配项填充完成器中的列表。不幸的是,这不起作用,因为列表将包含名称的重音版本,它与用户输入的值不匹配,因此 QLineEdit 不会在弹出窗口中显示名称(如果有的话)。
我还玩过 QAbstractItemModel,直到我意识到 QCompleter 对模型 return 编辑的数据进行了字符串匹配,所以再次 'orle' != 'orlé'.
2:一个可编辑的 QComboBox 列表根据目前输入的文本动态填充。以下代码是从 QComboBox::editTextChanged(QString):
connected() 得到的
void TripFormCargoHelper::fromEdited (const QString &str)
{
if (str.length () >= 3)
{
QStringList flist = m_database->findLocationStrings (str);
flist.push_front (str); // add the text we're editing first
bool b = box->blockSignals (true); // prevent recursive signals
box->clear ();
box->addItems (flist);
box->blockSignals (b);
box->showPopup ();
}
else
{
box->clear ();
box->hidePopup ();
}
}
这行得通,但只有一半...我希望在输入某些字符时弹出窗口 [1] 但这会从行编辑中移除焦点。单击行编辑关闭弹出窗口,所以我得到了一个 catch-22(人们应该能够继续输入字符,缩小搜索范围)。
如有任何关于如何完成这项工作的建议,我们将不胜感激。我更喜欢 QLineEdit 的解决方案。版本是 Qt 5.4.
[1] 应该是找到匹配的时候才对,唉
这应该有效:
使用 QCompleter
解决方案。
创建继承此 QCompleter
的 class 并重新实现 QCompleter::splitPath
:
DiacriticFreeCompleter::DiacriticFreeCompleter(QObject *parent)
: QCompleter(parent)
{
}
QStringList DiacriticFreeCompleter::splitPath(const QString &path) const
{
return QStringList() << ClearedFromDiacritic(path);
}
QString DiacriticFreeCompleter::pathFromIndex(const QModelIndex &index) const
{
// needed to use original value when value is selected
return index.data().toString();
}
现在创建包含所有城市(带有变音符号的单词)和一些自定义角色编号 return 字符串的数据模型,该字符串没有变音符号(sub-classing QStringListModel
could be the easiest way, just re implement data
特别对待这个角色值):
DiactricFreeStringListModel::DiactricFreeStringListModel(QObject *parent)
: QStringListModel(parent)
{
setDiactricFreeRole(Qt::UserRole+10);
}
QVariant DiactricFreeStringListModel::data(const QModelIndex &index, int role) const
{
if (role==diactricFreeRole()) {
QString value = QStringListModel::data(index, Qt::DisplayRole).toString();
return ClearedFromDiacritic(value);
} else {
return QStringListModel::data(index, role);
}
}
void DiactricFreeStringListModel::setDiactricFreeRole(int role)
{
mDiactricFreeRole = role;
}
int DiactricFreeStringListModel::diactricFreeRole() const
{
return mDiactricFreeRole;
}
现在将此模型与 QCompleter
连接,将此特殊角色值设置为 completionRole,一切都会正常运行。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
DiacriticFreeCompleter *completer = new DiacriticFreeCompleter(this);
DiactricFreeStringListModel *model = new DiactricFreeStringListModel(this);
completer->setModel(model);
completer->setCompletionRole(model->diactricFreeRole());
model->setStringList(QStringList()
<< "Kraków"
<< "Łba"
<< "Żarów"
<< "Źródło"
<< "Łęg"
<< "London"
<< "München"
<< "Orléans"
<< "Köln"
<< "Liège"
<< "Châteauroux");
ui->lineEdit->setCompleter(completer);
}
我测试过它运行良好。注意实际上我在这里粘贴了完整的代码(只是省略了一些明显的东西),所以解决方案非常简单。
谢谢 Marek,我一直在拼命地寻找这个,只为 Python (PyQT5),所以我重写了这个并且它很有魅力。这是代码:
import unicodedata
from PyQt5.QtCore import QStringListModel
from PyQt5.QtWidgets import QCompleter
from PyQt5.QtCore import Qt
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
class DiacriticFreeCompleter(QCompleter):
def splitPath(self, path):
return [strip_accents(path).lower()]
def pathFromIndex(self, index):
return index.data()
class DiactricFreeStringListModel(QStringListModel):
def __init__(self, *args, **kwargs):
super(DiactricFreeStringListModel, self).__init__(*args, **kwargs)
self.setDiactricFreeRole(Qt.UserRole+10)
def data(self, index, role):
if role == self.diactricFreeRole():
value = super(DiactricFreeStringListModel, self).data(index, Qt.DisplayRole)
return strip_accents(value).lower()
else:
return super(DiactricFreeStringListModel, self).data(index, role)
def setDiactricFreeRole(self, role):
self.mDiactricFreeRole = role
def diactricFreeRole(self):
return self.mDiactricFreeRole;
我有一个应用程序,人们可以在其中输入地名。这是欧洲,我们必须处理包含变音符号的名称,例如 Orléans、Köln、Liège、Châteauroux。当人们输入名字时,我希望他们能够在没有变音符号的情况下键入字符,但仍然会列出包含这些名字的名字,这样他们就可以 select 正确重音的名字。该程序有一个很长但并不详尽的名称列表(人们可以随时输入他们喜欢的任何名称)。
我已经有了一个基于非变音符号匹配查找名字的函数。所以 'orle' 会 return 'Orléans','kol' 会找到 'Köln',等等
我尝试了两件事:
1:一个带有 QCompleter 的 QLineEdit,该 QCompleter 使用 QStringListModel 用匹配项填充完成器中的列表。不幸的是,这不起作用,因为列表将包含名称的重音版本,它与用户输入的值不匹配,因此 QLineEdit 不会在弹出窗口中显示名称(如果有的话)。
我还玩过 QAbstractItemModel,直到我意识到 QCompleter 对模型 return 编辑的数据进行了字符串匹配,所以再次 'orle' != 'orlé'.
2:一个可编辑的 QComboBox 列表根据目前输入的文本动态填充。以下代码是从 QComboBox::editTextChanged(QString):
connected() 得到的void TripFormCargoHelper::fromEdited (const QString &str)
{
if (str.length () >= 3)
{
QStringList flist = m_database->findLocationStrings (str);
flist.push_front (str); // add the text we're editing first
bool b = box->blockSignals (true); // prevent recursive signals
box->clear ();
box->addItems (flist);
box->blockSignals (b);
box->showPopup ();
}
else
{
box->clear ();
box->hidePopup ();
}
}
这行得通,但只有一半...我希望在输入某些字符时弹出窗口 [1] 但这会从行编辑中移除焦点。单击行编辑关闭弹出窗口,所以我得到了一个 catch-22(人们应该能够继续输入字符,缩小搜索范围)。
如有任何关于如何完成这项工作的建议,我们将不胜感激。我更喜欢 QLineEdit 的解决方案。版本是 Qt 5.4.
[1] 应该是找到匹配的时候才对,唉
这应该有效:
使用 QCompleter
解决方案。
创建继承此 QCompleter
的 class 并重新实现 QCompleter::splitPath
:
DiacriticFreeCompleter::DiacriticFreeCompleter(QObject *parent)
: QCompleter(parent)
{
}
QStringList DiacriticFreeCompleter::splitPath(const QString &path) const
{
return QStringList() << ClearedFromDiacritic(path);
}
QString DiacriticFreeCompleter::pathFromIndex(const QModelIndex &index) const
{
// needed to use original value when value is selected
return index.data().toString();
}
现在创建包含所有城市(带有变音符号的单词)和一些自定义角色编号 return 字符串的数据模型,该字符串没有变音符号(sub-classing QStringListModel
could be the easiest way, just re implement data
特别对待这个角色值):
DiactricFreeStringListModel::DiactricFreeStringListModel(QObject *parent)
: QStringListModel(parent)
{
setDiactricFreeRole(Qt::UserRole+10);
}
QVariant DiactricFreeStringListModel::data(const QModelIndex &index, int role) const
{
if (role==diactricFreeRole()) {
QString value = QStringListModel::data(index, Qt::DisplayRole).toString();
return ClearedFromDiacritic(value);
} else {
return QStringListModel::data(index, role);
}
}
void DiactricFreeStringListModel::setDiactricFreeRole(int role)
{
mDiactricFreeRole = role;
}
int DiactricFreeStringListModel::diactricFreeRole() const
{
return mDiactricFreeRole;
}
现在将此模型与 QCompleter
连接,将此特殊角色值设置为 completionRole,一切都会正常运行。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
DiacriticFreeCompleter *completer = new DiacriticFreeCompleter(this);
DiactricFreeStringListModel *model = new DiactricFreeStringListModel(this);
completer->setModel(model);
completer->setCompletionRole(model->diactricFreeRole());
model->setStringList(QStringList()
<< "Kraków"
<< "Łba"
<< "Żarów"
<< "Źródło"
<< "Łęg"
<< "London"
<< "München"
<< "Orléans"
<< "Köln"
<< "Liège"
<< "Châteauroux");
ui->lineEdit->setCompleter(completer);
}
我测试过它运行良好。注意实际上我在这里粘贴了完整的代码(只是省略了一些明显的东西),所以解决方案非常简单。
谢谢 Marek,我一直在拼命地寻找这个,只为 Python (PyQT5),所以我重写了这个并且它很有魅力。这是代码:
import unicodedata
from PyQt5.QtCore import QStringListModel
from PyQt5.QtWidgets import QCompleter
from PyQt5.QtCore import Qt
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
class DiacriticFreeCompleter(QCompleter):
def splitPath(self, path):
return [strip_accents(path).lower()]
def pathFromIndex(self, index):
return index.data()
class DiactricFreeStringListModel(QStringListModel):
def __init__(self, *args, **kwargs):
super(DiactricFreeStringListModel, self).__init__(*args, **kwargs)
self.setDiactricFreeRole(Qt.UserRole+10)
def data(self, index, role):
if role == self.diactricFreeRole():
value = super(DiactricFreeStringListModel, self).data(index, Qt.DisplayRole)
return strip_accents(value).lower()
else:
return super(DiactricFreeStringListModel, self).data(index, role)
def setDiactricFreeRole(self, role):
self.mDiactricFreeRole = role
def diactricFreeRole(self):
return self.mDiactricFreeRole;