How to change completion rule of QCompleter
The standard QCompleter classs uses completionPrefix to search for all completions, so all the completions have the same prefix which is completionPrefix, what i want to achieve is this:if the string in the completion model contains the chars under the cursor,it should appear in the completion, and i also want to use different rules to decide which should appear in the completions.
For example:
the completion model:QListModel<<"manual"<<"anual"
When i type 'nual', i want both "manual" and "anual" appear in the completion, because they both contains "nual",so how can i achieve this?
I don't know how to change the completion rule,it seems that the standard QCompleter use prfix matching to find completions.
Any suggestions?
Re: How to change completion rule of QCompleter
Using this QCompleter example and this custom QCompleter example, I implemented what you want.
Header:
Code:
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <QLineEdit>
#include <QStringList>
#include <QStringListModel>
#include <QString>
#include <QCompleter>
{
Q_OBJECT
public:
inline MyCompleter
(const QStringList
& words,
QObject * parent
) : {
setModel(&m_model);
}
{
// Do any filtering you like.
// Here we just include all items that contain word.
QStringList filtered
= m_list.
filter(word, caseSensitivity
());
m_model.setStringList(filtered);
m_word = word;
complete();
}
{
return m_word;
}
private:
};
{
Q_OBJECT
public:
~MyLineEdit();
void setCompleter(MyCompleter *c);
MyCompleter *completer() const;
protected:
private slots:
void insertCompletion
(const QString &completion
);
private:
MyCompleter *c;
};
#endif // LINEEDIT_H
CPP:
Code:
MyLineEdit
::MyLineEdit(QWidget *parent
){
}
MyLineEdit::~MyLineEdit()
{
}
void MyLineEdit::setCompleter(MyCompleter *completer)
{
if (c)
QObject::disconnect(c,
0,
this,
0);
c = completer;
if (!c)
return;
c->setWidget(this);
connect(completer, SIGNAL(activated(const QString&)), this, SLOT(insertCompletion(const QString&)));
}
MyCompleter *MyLineEdit::completer() const
{
return c;
}
void MyLineEdit::insertCompletion(const QString& completion)
{
setText(completion);
selectAll();
}
{
if (c && c->popup()->isVisible())
{
// The following keys are forwarded by the completer to the widget
switch (e->key())
{
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return; // Let the completer do default behavior
}
}
bool isShortcut = (e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E;
if (!isShortcut)
QLineEdit::keyPressEvent(e
);
// Don't send the shortcut (CTRL-E) to the text edit.
if (!c)
return;
bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!isShortcut && !ctrlOrShift && e->modifiers() != Qt::NoModifier)
{
c->popup()->hide();
return;
}
c->update(text());
c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
}
Usage:
Code:
MyLineEdit * te = new MyLineEdit;
MyCompleter
* completer
= new MyCompleter
(QStringList() <<
"oneone" <<
"Twotwo",
this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
te->setCompleter(completer);
Re: How to change completion rule of QCompleter
:D
Many thanks,numbat,i just translated your code into Python,it works fine.
Code:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
def __init__(self,parent=None):
super(TextEdit,self).__init__(parent)
self.cmp=None
def setCompleter(self,completer):
if self.cmp:
self.disconnect(self.cmp,0,0)
self.cmp=completer
if (not self.cmp):
return
self.cmp.setWidget(self)
self.
cmp.
setCompletionMode(QCompleter.
PopupCompletion) self.cmp.setCaseSensitivity(Qt.CaseInsensitive)
self.connect(self.cmp,SIGNAL('activated(QString)'),self.insertCompletion)
def completer(self):
return self.cmp
def insertCompletion(self,string):
tc=self.textCursor()
tc.insertText(string)
self.setTextCursor(tc)
def textUnderCursor(self):
tc=self.textCursor()
return tc.selectedText()
def keyPressEvent(self,e):
print(e.text())
if (self.cmp and self.cmp.popup().isVisible()):
if e.key() in (Qt.Key_Enter,Qt.Key_Return,Qt.Key_Escape,Qt.Key_Tab,Qt.Key_Backtab):
e.ignore()
return
isShortcut=((e.modifiers() & Qt.ControlModifier) and e.key()==Qt.Key_E)
if (not self.cmp or not isShortcut):
super().keyPressEvent(e)
ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)
if (not self.cmp or (ctrlOrShift and e.text().isEmpty())):
return
eow
=QString("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=") hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
completionPrefix = self.textUnderCursor()
if (not isShortcut and (hasModifier or e.text().isEmpty() or completionPrefix.length()<2
or eow.contains(e.text().right(1)))):
self.cmp.popup().hide()
return
self.cmp.update(completionPrefix)
self.cmp.popup().setCurrentIndex(self.cmp.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self.cmp.popup().sizeHintForColumn(0)
+ self.cmp.popup().verticalScrollBar().sizeHint().width())
self.cmp.complete(cr)
def __init__(self,stringlist,parent=None):
super(Completer,self).__init__(parent)
self.stringlist=stringlist
def update(self,completionText):
filtered=self.stringlist.filter(completionText,Qt.CaseInsensitive)
self.model().setStringList(filtered)
self.popup().setCurrentIndex(self.model().index(0, 0))
li<<'The'<<'that'<<'Red'<<'right'<<'what'
cmp=Completer(li)
edit=TextEdit()
edit.setCompleter(cmp)
window.setCentralWidget(edit)
window.show()
app.exec_()
Re: How to change completion rule of QCompleter
After a lot of studying up on how this works, I've finally figured it out. I've implemented my own version as well, but I have a question.
My problem is that when the first letter entered into the editable combobox is actually the first letter of an entry, that entry is automatically selected. This bypasses completion of a single letter.
For example. The list is:
chicken broth
cinnamon
boneless chicken breast
I want "c" to show all of these as possible completions. However, "c" will cause the combobox to select "chicken broth" instead. I've looked, but I can't figure out how to disable this behavior. Any suggestions?
Thanks
Bill
edit: btw, hitting "c" will make the edittext read "chicken broth." And hitting backspace will remove all but the "c." Then it completes like i want it to. Obviously, I don't want to have to hit backspace every time.
Re: How to change completion rule of QCompleter
I'll just give this one bump to see if anyone missed it. I have looked into the issue more, and I still can't figure out exactly what is setting the text. But it definitely happens without any completer attached.
bill
Re: How to change completion rule of QCompleter
Thank you.
Ir was usefull for me as well
Re: How to change completion rule of QCompleter
There is a way of config the space between lines in the popup list?
I don find a way to choose the row height.
Thanks,
Urbano
Re: How to change completion rule of QCompleter
make your own thread instead of digging up old, relatively un-connected threads.
Re: How to change completion rule of QCompleter
Quote:
Originally Posted by
numbat
Using this
QCompleter example and this
employee monitoring, I implemented what you want.
Header:
Code:
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <QLineEdit>
#include <QStringList>
#include <QStringListModel>
#include <QString>
#include <QCompleter>
{
Q_OBJECT
public:
inline MyCompleter
(const QStringList
& words,
QObject * parent
) : {
setModel(&m_model);
}
{
// Do any filtering you like.
// Here we just include all items that contain word.
QStringList filtered
= m_list.
filter(word, caseSensitivity
());
m_model.setStringList(filtered);
m_word = word;
complete();
}
{
return m_word;
}
private:
};
{
Q_OBJECT
public:
~MyLineEdit();
void setCompleter(MyCompleter *c);
MyCompleter *completer() const;
protected:
private slots:
void insertCompletion
(const QString &completion
);
private:
MyCompleter *c;
};
#endif // LINEEDIT_H
CPP:
Code:
MyLineEdit
::MyLineEdit(QWidget *parent
){
}
MyLineEdit::~MyLineEdit()
{
}
void MyLineEdit::setCompleter(MyCompleter *completer)
{
if (c)
QObject::disconnect(c,
0,
this,
0);
c = completer;
if (!c)
return;
c->setWidget(this);
connect(completer, SIGNAL(activated(const QString&)), this, SLOT(insertCompletion(const QString&)));
}
MyCompleter *MyLineEdit::completer() const
{
return c;
}
void MyLineEdit::insertCompletion(const QString& completion)
{
setText(completion);
selectAll();
}
{
if (c && c->popup()->isVisible())
{
// The following keys are forwarded by the completer to the widget
switch (e->key())
{
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return; // Let the completer do default behavior
}
}
bool isShortcut = (e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E;
if (!isShortcut)
QLineEdit::keyPressEvent(e
);
// Don't send the shortcut (CTRL-E) to the text edit.
if (!c)
return;
bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!isShortcut && !ctrlOrShift && e->modifiers() != Qt::NoModifier)
{
c->popup()->hide();
return;
}
c->update(text());
c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
}
Usage:
Code:
MyLineEdit * te = new MyLineEdit;
MyCompleter
* completer
= new MyCompleter
(QStringList() <<
"oneone" <<
"Twotwo",
this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
te->setCompleter(completer);
Thank you, Numbat
It`s works for me