Python PyQt Tab Completion example
Here is an example Python GUI that implements tab completion. It uses the open source Qt 4.3 toolkit and PyQt 4.3 Python bindings.
A list of words is presented in a list box. As the user types, the list is shortened to show possible matches. If the user presses TAB, the input text is "completed" to the longest possible string match. This may be a whole word or a common substring of multiple words.
This example consists of two basic elements:
MyLineEdit
is a subclass of theQLineEdit
class. It is used as an input box to enter text. I needed to subclassQLineEdit
because I needed to capture the TAB key press event for tab completion. (See this previous post.)QListView
andMyListModel
implement a list with a simple model/view architechture.MyListModel
is a subclass ofQAbstractListModel
. I implemented the requiredrowCount
anddata
methods as well as a method calledsetAllData
which replaces the entire existing data with a new list of data.
This example makes use of two SIGNAL
s:
- The
textChanged
signal is emitted each time the user types a letter inside theQLineEdit
box. It is connected to thetext_changed
method which updates the list of words in theQListView
.MyListModel
'ssetAllData
method is used to update the data. - The
tabPressed
signal is a custom signal I added to myQLineEdit
subclass. It is emitted each time the user presses the TAB key. This signal is connected thetab_pressed
method which completes the input to the longest matching substring of the available words.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
LIST_DATA = ['a', 'aardvark', 'aardvarks', 'aardwolf', 'aardwolves',
'abacus', 'babel', 'bach', 'cache',
'daggle', 'facet', 'kabob', 'kansas']
####################################################################
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
####################################################################
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# create objects
self.la = QLabel("Start typing to match items in list:")
self.le = MyLineEdit()
self.lm = MyListModel(LIST_DATA, self)
self.lv = QListView()
self.lv.setModel(self.lm)
# layout
layout = QVBoxLayout()
layout.addWidget(self.la)
layout.addWidget(self.le)
layout.addWidget(self.lv)
self.setLayout(layout)
# connections
self.connect(self.le, SIGNAL("textChanged(QString)"),
self.text_changed)
self.connect(self.le, SIGNAL("tabPressed"),
self.tab_pressed)
def text_changed(self):
""" updates the list of possible completions each time a key is
pressed """
pattern = str(self.le.text())
self.new_list = [item for item in LIST_DATA if item.find(pattern) == 0]
self.lm.setAllData(self.new_list)
def tab_pressed(self):
""" completes the word to the longest matching string
when the tab key is pressed """
# only one item in the completion list
if len(self.new_list) == 1:
newtext = self.new_list[0] + " "
self.le.setText(newtext)
# more than one remaining matches
elif len(self.new_list) > 1:
match = self.new_list.pop(0)
for word in self.new_list:
match = string_intersect(word, match)
self.le.setText(match)
####################################################################
class MyLineEdit(QLineEdit):
def __init__(self, *args):
QLineEdit.__init__(self, *args)
def event(self, event):
if (event.type()==QEvent.KeyPress) and (event.key()==Qt.Key_Tab):
self.emit(SIGNAL("tabPressed"))
return True
return QLineEdit.event(self, event)
####################################################################
class MyListModel(QAbstractListModel):
def __init__(self, datain, parent=None, *args):
""" datain: a list where each item is a row
"""
QAbstractTableModel.__init__(self, parent, *args)
self.listdata = datain
def rowCount(self, parent=QModelIndex()):
return len(self.listdata)
def data(self, index, role):
if index.isValid() and role == Qt.DisplayRole:
return QVariant(self.listdata[index.row()])
else:
return QVariant()
def setAllData(self, newdata):
""" replace all data with new data """
self.listdata = newdata
self.reset()
####################################################################
def string_intersect(str1, str2):
newlist = []
for i,j in zip(str1, str2):
if i == j:
newlist.append(i)
else:
break
return ''.join(newlist)
####################################################################
if __name__ == "__main__":
main()
Related posts
- PyQt: How to pass arguments while emitting a signal — posted 2008-01-29
- PyQt4 QItemDelegate example with QListView and QAbstractListModel — posted 2008-01-23
- How to install pyqt4 on ubuntu linux — posted 2008-01-15
- How to capture the Tab key press event with PyQt 4.3 — posted 2008-01-03
- PyQt 4.3 Simple QAbstractListModel/ QlistView example — posted 2008-01-03
Comments
just add
self.new_list = []
after line 26 to avoid "AttributeError: 'MyWindow' object has no attribute 'new_list'" if tab is used before any data entering