一个忽略音标符号的QLineEdit/QComboBox搜索

3
我有一个应用程序,人们可以在表单中输入地名。由于这里是欧洲,我们必须处理包含变音符号的名称,例如Orléans、Köln、Liège和Châteauroux。当人们输入名称时,我希望他们能够输入没有变音符号的字符,但仍然能够列出包含变音符号的名称列表,以便他们可以选择正确带重音符号的名称。该程序有一个长但不完整的名称列表(人们总是可以输入任何他们喜欢的名称)。
我已经有一个函数,可以基于非变音符号匹配找到名称。因此,“orle”将返回“Orléans”,“kol”将找到“Köln”等等。
我尝试了两件事:
1:一个带有QCompleter的QLineEdit,它使用QStringListModel填充completer中的列表与匹配项。不幸的是,这并不起作用,因为列表将包含名称的带重音符号的版本,这与用户输入的值不匹配,因此QLineEdit不会显示弹出窗口中的名称(如果有的话)。
我还尝试了一个QAbstractItemModel,直到我意识到QCompleter对模型返回的数据进行字符串匹配,所以再次“orle”!=“orlé”。
2:可编辑的QComboBox,其列表根据到目前为止输入的文本动态填充。以下代码从QComboBox::editTextChanged(QString)连接()。
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]弹出窗口,但这会使焦点从行编辑器中移除。点击行编辑器会关闭弹出窗口,所以我陷入了一个进退两难的境地(人们应该能够继续输入字符,缩小搜索范围)。

如果您有任何关于如何使此方法起作用的建议,将不胜感激。我更喜欢使用QLineEdit来解决问题。版本是Qt 5.4。

[1] 应该是在找到匹配项时触发,但是很遗憾没有。


我找到了一个可能有帮助的问题:QCompleter自定义完成规则 - Mel
2个回答

3
这应该可以运行:
使用QCompleter解决方案。
创建一个继承自QCompleter的类,并重新实现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();
}

现在创建一个包含所有城市(带有变音符号的单词)的数据模型,并在一些自定义角色数字下返回不带变音符号的字符串(子类化QStringListModel可能是最简单的方法,只需重新实现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);
}

我已经测试过它可以完美工作。请注意,实际上我在这里粘贴了完整的代码(只省略了一些显而易见的东西),因此解决方案非常简单。

0

谢谢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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接