* QCompleter and Comma-Separated Tags
Posted on July 4th, 2009 by John. Filed under programming.
Here is a python script demonstrating how to use QCompleter to complete multiple tags in a QLineEdit. A few features of this script are: removing tags from the drop down that already appear in the QLineEdit, caching the tags, and inserting a , after completion to ease adding more tags. There are a few parts of this script that I’m going to go into detail about.
#!/usr/bin/env python ''' Copyright (c) 2009 John Schember Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ''' import sys from PyQt4.Qt import Qt, QObject, QApplication, QLineEdit, QCompleter, \ QStringListModel, SIGNAL TAGS = ['Nature', 'buildings', 'home', 'City', 'country', 'Berlin'] class CompleterLineEdit(QLineEdit): def __init__(self, *args): QLineEdit.__init__(self, *args) QObject.connect(self, SIGNAL('textChanged(QString)'), self.text_changed) def text_changed(self, text): all_text = unicode(text) text = all_text[:self.cursorPosition()] prefix = text.split(',')[-1].strip() text_tags = [] for t in all_text.split(','): t1 = unicode(t).strip() if t1 != '': text_tags.append(t) text_tags = list(set(text_tags)) self.emit(SIGNAL('text_changed(PyQt_PyObject, PyQt_PyObject)'), text_tags, prefix) def complete_text(self, text): cursor_pos = self.cursorPosition() before_text = unicode(self.text())[:cursor_pos] after_text = unicode(self.text())[cursor_pos:] prefix_len = len(before_text.split(',')[-1].strip()) self.setText('%s%s, %s' % (before_text[:cursor_pos - prefix_len], text, after_text)) self.setCursorPosition(cursor_pos - prefix_len + len(text) + 2) class TagsCompleter(QCompleter): def __init__(self, parent, all_tags): QCompleter.__init__(self, all_tags, parent) self.all_tags = set(all_tags) def update(self, text_tags, completion_prefix): tags = list(self.all_tags.difference(text_tags)) model = QStringListModel(tags, self) self.setModel(model) self.setCompletionPrefix(completion_prefix) if completion_prefix.strip() != '': self.complete() def main(): app = QApplication(sys.argv) editor = CompleterLineEdit() completer = TagsCompleter(editor, TAGS) completer.setCaseSensitivity(Qt.CaseInsensitive) QObject.connect(editor, SIGNAL('text_changed(PyQt_PyObject, PyQt_PyObject)'), completer.update) QObject.connect(completer, SIGNAL('activated(QString)'), editor.complete_text) completer.setWidget(editor) editor.show() return app.exec_() if __name__ == '__main__': main()
Looking at the main() function the editor widget’s text_changed signal is connected to the completer’s update slot. This serves two purposes. It provides the completer with all tags that are in the editor. Also, it provides the completer with the prefix of the current text that is being entered. The prefix is used for listing matching tags that are stored in the completer’s cache.
editor’s complete_text function takes the text in the editor before the cursor and subtracts the length of the prefix from that position because the prefix will be included in the completed text. The text before, the completed text, a comma, a space, and the text after the cursor are combined. This becomes the text in the QLineEdit. The cursor is advanced the position it was at minus the length of the prefix and plus 2 characters (, ) so that typing can immediately continue.
TAGS can be replaced with a function that gets all relevant tags if caching is not wanted.
Also, note that completer.setWidget(editor) was used not QLineEdit’s setCompleter() function. If setCompleter is used completion will only take place at the beginning of the QLineEdit. Meaning it will match everything before as one string and ignore the , delimiter separating the tags.
One Response to “QCompleter and Comma-Separated Tags”
Leave a Reply
Tags
Archives
- July 2010 (4)
- June 2010 (1)
- May 2010 (2)
- March 2010 (1)
- January 2010 (8)
- December 2009 (5)
- November 2009 (6)
- October 2009 (4)
- September 2009 (2)
- August 2009 (6)
- July 2009 (6)
- June 2009 (4)
- May 2009 (6)
- April 2009 (4)
- March 2009 (2)
- February 2009 (4)
- January 2009 (4)
- December 2008 (7)
- November 2008 (2)
September 17th, 2009 at 11:41 am
THANK YOU!
This example was exactly what I needed. You saved me half a day of heartache. Thank you for working out the solution and taking the time to share it.
Keep kicking ass…