PDA

View Full Version : How to change completion rule of QCompleter



freud
24th August 2009, 15:13
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?

numbat
25th August 2009, 14:39
Using this QCompleter example (http://doc.trolltech.com/4.5/tools-customcompleter.html) and this custom QCompleter example (http://john.nachtimwald.com/2009/07/04/qcompleter-and-comma-separated-tags/), I implemented what you want.

Header:


#ifndef LINEEDIT_H
#define LINEEDIT_H

#include <QLineEdit>
#include <QStringList>
#include <QStringListModel>
#include <QString>
#include <QCompleter>

class MyCompleter : public QCompleter
{
Q_OBJECT

public:
inline MyCompleter(const QStringList& words, QObject * parent) :
QCompleter(parent), m_list(words), m_model()
{
setModel(&m_model);
}

inline void update(QString word)
{
// 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();
}

inline QString word()
{
return m_word;
}

private:
QStringList m_list;
QStringListModel m_model;
QString m_word;
};

class MyLineEdit : public QLineEdit
{
Q_OBJECT

public:
MyLineEdit(QWidget *parent = 0);
~MyLineEdit();

void setCompleter(MyCompleter *c);
MyCompleter *completer() const;

protected:
void keyPressEvent(QKeyEvent *e);

private slots:
void insertCompletion(const QString &completion);

private:
MyCompleter *c;
};

#endif // LINEEDIT_H


CPP:


MyLineEdit::MyLineEdit(QWidget *parent)
: QLineEdit(parent), c(0)
{
}

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();
}


void MyLineEdit::keyPressEvent(QKeyEvent *e)
{
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:


MyLineEdit * te = new MyLineEdit;
MyCompleter * completer = new MyCompleter(QStringList() << "oneone" << "Twotwo", this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
te->setCompleter(completer);

freud
25th August 2009, 16:40
:D
Many thanks,numbat,i just translated your code into Python,it works fine.


from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys

class TextEdit(QTextEdit):

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.PopupComplet ion)
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.movePosition(QTextCursor.StartOfWord,QTextCurso r.KeepAnchor)
tc.insertText(string)
self.setTextCursor(tc)

def textUnderCursor(self):
tc=self.textCursor()
tc.select(QTextCursor.WordUnderCursor)
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_T ab,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.completi onModel().index(0, 0))

cr = self.cursorRect()
cr.setWidth(self.cmp.popup().sizeHintForColumn(0)
+ self.cmp.popup().verticalScrollBar().sizeHint().wi dth())
self.cmp.complete(cr)

class Completer(QCompleter):
def __init__(self,stringlist,parent=None):
super(Completer,self).__init__(parent)
self.stringlist=stringlist
self.setModel(QStringListModel())

def update(self,completionText):
filtered=self.stringlist.filter(completionText,Qt. CaseInsensitive)
self.model().setStringList(filtered)
self.popup().setCurrentIndex(self.model().index(0, 0))


app=QApplication(sys.argv)
li=QStringList()
li<<'The'<<'that'<<'Red'<<'right'<<'what'
cmp=Completer(li)
window=QMainWindow()
edit=TextEdit()
edit.setCompleter(cmp)
window.setCentralWidget(edit)
window.show()

app.exec_()

jungle
26th September 2009, 22:27
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.

jungle
2nd October 2009, 01:41
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

medved6
10th December 2010, 07:44
Thank you.
Ir was usefull for me as well

oximer
29th December 2011, 12:02
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

amleto
30th December 2011, 14:17
make your own thread instead of digging up old, relatively un-connected threads.

PilipHannah
14th July 2021, 18:22
Using this QCompleter example (http://doc.trolltech.com/4.5/tools-customcompleter.html) and this employee monitoring (https://www.worktime.com/employee-monitoring), I implemented what you want.

Header:


#ifndef LINEEDIT_H
#define LINEEDIT_H

#include <QLineEdit>
#include <QStringList>
#include <QStringListModel>
#include <QString>
#include <QCompleter>

class MyCompleter : public QCompleter
{
Q_OBJECT

public:
inline MyCompleter(const QStringList& words, QObject * parent) :
QCompleter(parent), m_list(words), m_model()
{
setModel(&m_model);
}

inline void update(QString word)
{
// 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();
}

inline QString word()
{
return m_word;
}

private:
QStringList m_list;
QStringListModel m_model;
QString m_word;
};

class MyLineEdit : public QLineEdit
{
Q_OBJECT

public:
MyLineEdit(QWidget *parent = 0);
~MyLineEdit();

void setCompleter(MyCompleter *c);
MyCompleter *completer() const;

protected:
void keyPressEvent(QKeyEvent *e);

private slots:
void insertCompletion(const QString &completion);

private:
MyCompleter *c;
};

#endif // LINEEDIT_H


CPP:


MyLineEdit::MyLineEdit(QWidget *parent)
: QLineEdit(parent), c(0)
{
}

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();
}


void MyLineEdit::keyPressEvent(QKeyEvent *e)
{
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:


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