Tab over non-editable cells in QtableView - python

I am using PYQT5. I have a QtableView widget which I use for quick data entry similar to a spreadsheet. Some columns are editable while others (labels etc) are not. I have achieved this quite simply by subclassing QItemDelegate.
What I would like to do is when a user tabs from an editable cell, it will skip any non-editable cell and go to the next editable cell. I think I need to examine a keypress event after editing somewhere and determine which column is next. Alternatively, when I land in a non-editable cell, I should move immediately to the next editable cell. My code is:
class Rates_sheet(QDialog, Ui_rates_sheet):
"""Displays rates for the next x days for quick changes"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
QDialog.__init__(self)
self.ui = Ui_rates_sheet()
self.ui.setupUi(self)
self.ui.num_days.setValue(45)
self.ui.get_avail.clicked.connect(self.make_rate_sheet)
self.ui.publish_avail.clicked.connect(self.publish_rates)
self.populate()
def populate(self):
self.rates_model = QSqlTableModel()
self.rates_model.setTable("rates_sheet")
self.rates_model.select()
self.rates_view = self.ui.rates_grid
self.rates_view.setModel(self.rates_model)
self.rates_view.resizeColumnsToContents()
self.rates_view.setSortingEnabled(True)
self.rates_view.setColumnHidden(0, True)
for x in range(7,12):
self.rates_view.setColumnHidden(x, True)
self.rates_view.horizontalHeader().moveSection(3,10)
self.rates_view.horizontalHeader().moveSection(3,12)
self.rates_view.horizontalHeader().moveSection(13,2)
self.rates_view.setItemDelegate(Tmodel(self))
self.rates_model.setHeaderData(1, Qt.Horizontal, "Date")
self.rates_model.sort(1, Qt.AscendingOrder)
def make_rate_sheet(self):
pass
def publish_rates(self):
pass
class Tmodel(QItemDelegate):
"""Remplement rates_sheet table"""
def __init__(self, parent=None):
QItemDelegate.__init__(self)
def createEditor(self, parent, option, index):
if index.column() == 4:
spinbox = QSpinBox(parent)
spinbox.setRange(1,4)
return spinbox
elif index.column() in [5,6,11,12]:
spinbox = QSpinBox(parent)
spinbox.setRange(49,499)
return spinbox
EDIT:
I have tried to reimplement QSqlTableModel to change the value in flags but end up with a runtime error:
class MySqlTableModel(QSqlTableModel):
def __init__(self):
QSqlTableModel.__init__(self)
def flags(self, index):
if index.isValid():
if index.column() == 4:
flags ^= 1
return flags
The error I get is:
File "f:\Dropbox\pms\main-5.py", line 2658, in <module>
sys.exit(app.exec_())
builtins.TypeError: invalid result from MySqlTableModel.flags(), NoneType cannot be converted to PyQt5.QtCore.ItemFlags in this context
Now I am even more confused.

After sweating over this for days, the answer is that you can successfully reimplement QsqlTableModel. Here is how I arrived at the answer.
First I saw recommendations on the net mostly in the QT forums for C++ that you should reimplement QsqlQueryModel instead of QsqlTableModel. But then you have to create the methods for setdata, data, rowcount, columncount, insertrows, deleterows not to mention flags yourself. This seemed like a lot of work and prone to error. I could not get it to work.
Thinking that all of the above was pointless and a lot of work, I found a code snippet from stackoverflow where someone was trying to use the dataChanged signal from QsqlQueryModel and they were recommended to overide QsqlTableModel. Finally I saw an example of how to do precisely what I was attempting. Therefore I updated my code as follows:
class Rates_sheet(QDialog, Ui_rates_sheet):
"""Displays rates for the next x days for quick changes"""
def __init__(self):
"""Constructor"""
QDialog.__init__(self)
self.ui = Ui_rates_sheet()
self.ui.setupUi(self)
self.ui.num_days.setValue(45)
self.ui.get_avail.clicked.connect(self.make_rate_sheet)
self.ui.publish_avail.clicked.connect(self.publish_rates)
self.populate()
def populate(self):
self.rates_model = MySqlTableModel()
self.rates_model.setTable("rates_sheet")
self.rates_model.select()
self.rates_view = self.ui.rates_grid
self.rates_view.setModel(self.rates_model)
self.rates_view.resizeColumnsToContents()
self.rates_view.setSortingEnabled(True)
self.rates_view.setColumnHidden(0, True)
self.rates_view.setItemDelegate(Tmodel(self))
self.rates_model.setHeaderData(1, Qt.Horizontal, "Date")
self.rates_model.sort(1, Qt.AscendingOrder)
def make_rate_sheet(self):
pass
def publish_rates(self):
pass
class MySqlTableModel(QSqlTableModel):
"""Overides QSqlTableModel to make columns not selectable"""
def __init__(self):
QSqlTableModel.__init__(self)
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
value = value.strip() if type(value) == str else value
return super(MySqlTableModel, self).setData(index, value, role)
def flags(self, index):
flags = super(MySqlTableModel, self).flags(index)
if index.column() in (4, 5, 6, 11):
flags |= Qt.ItemIsEditable
else:
flags &= Qt.ItemIsSelectable
return flags
This was the solution. I am still testing but I haven't found any problems yet.

Related

QTableView dynamic row heigh for large QAbstractTableModel

I know there have been a lot of times question was answered on stackoverflow about how to set row height for QTableView. I'm asking one more time but my question is not exactly about "how", at least not so simple. I'm setting row height successfully with help of Qt.SizeHintRole in data method of my custom model derived from QAbstractTableModel - see code below. (Also tried very similar example but with help of sizeHint() method of QStyledItemDelegate - the result is exactly the same.)
It works pretty good when I have MODEL_ROW_COUNT about 100 as in example below. But my dataset has ~30-40 thousands of rows. As result this simple application starts about 30 seconds with MODEL_ROW_COUNT=35000 for example.
The reason of this big delay is this line of code:
self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
Everything works really fast with MODEL_ROW_COUNT=35000 if I would comment this line. But in this case data() method is not called with Qt.SizeHintRole and I can't manipulate row height.
So, my question is - how to set row height on a per row basis for dataset with thousands of rows? Below example works but takes 30 seconds to start with 35 000 rows (after window is shown everything is fluent)...
At the same time if I use QSqlTableModel it doesn't have this problem and I may use sizeHint() of QStyledItemDelegate without big problems. But it's a mess to have too many delegates... May I subclass QStyledItemDelegate instead of QAbstractTableModel to implement my custom model? (I'm not sure that it will work as every source recomment to subclass QAbstractTableModel for custom models...)
Or I did something wrong and there is a better way than usage of QHeaderView.ResizeToContents?
P.S. I really need different heights. Some rows in database have less data and I may show them in a couple of cells. But others have more data and I need extra space to display it. The same height for all rows will mean either waste of space (a lot of white space on a screen) or lack of essential details for some data rows. I'm using contant CUSTOM_ROW_HEIGHT only too keep example as much simple as possible and reproducible with ease - you may use any DB with any large table (I think I may re-create it even without DB... will try soon)
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QTableView, QHeaderView
from PySide2.QtSql import QSqlDatabase, QSqlQuery
from PySide2.QtCore import Qt, QAbstractTableModel, QSize
class MyWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("/home/db.sqlite")
self.db.open()
self.table_model = MyModel(parent=self, db=self.db)
self.table_view = QTableView()
self.table_view.setModel(self.table_model)
# SizeHint is not triggered without this line but it causes delay
self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
layout = QVBoxLayout(self)
layout.addWidget(self.table_view)
self.setLayout(layout)
class MyModel(QAbstractTableModel):
CUSTOM_ROW_HEIGHT = 300
MODEL_ROW_COUNT = 100
MODEL_COL_COUNT = 5
def __init__(self, parent, db):
QAbstractTableModel.__init__(self, parent)
self.query = QSqlQuery(db)
self.query.prepare("SELECT * FROM big_table")
self.query.exec_()
def rowCount(self, parent=None):
return self.MODEL_ROW_COUNT
def columnCount(self, parent=None):
return self.MODEL_COL_COUNT
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
if role == Qt.DisplayRole:
if self.query.seek(index.row()):
return str(self.query.value(index.column()))
if role == Qt.SizeHintRole:
return QSize(0, self.CUSTOM_ROW_HEIGHT)
return None
def main():
app = QApplication([])
win = MyWindow()
win.show()
app.exec_()
if __name__ == "__main__":
main()
Ok, Thanks to #musicamante I realized that I missed canFetchMore() and fetchMore() methods. So, I implemented dynamic size property and these methods in MyModel class. It was not hard at all and now I have better performance than QSqlTableModel and identical visual behavior with direct conrol of visible buffer size. Below is new code of MyModel class:
class MyModel(QAbstractTableModel):
CUSTOM_ROW_HEIGHT = 300
MODEL_ROW_COUNT = 37000
MODEL_COL_COUNT = 5
PAGE_SIZE = 500
def __init__(self, parent, db):
QAbstractTableModel.__init__(self, parent)
self.query = QSqlQuery(db)
self.query.prepare("SELECT * FROM big_table")
self.query.exec_()
self._current_size = self.PAGE_SIZE
def rowCount(self, parent=None):
return self._current_size
def columnCount(self, parent=None):
return self.MODEL_COL_COUNT
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
if role == Qt.DisplayRole:
if self.query.seek(index.row()):
return str(self.query.value(index.column()))
if role == Qt.SizeHintRole:
return QSize(0, self.CUSTOM_ROW_HEIGHT)
return None
def canFetchMore(self, index):
return self._current_size < self.MODEL_ROW_COUNT
def fetchMore(self, index):
self.beginInsertRows(index, self._current_size, self._current_size + self.PAGE_SIZE - 1)
self._current_size += self.PAGE_SIZE
self.endInsertRows()

How to make particular cell editable and leave the rest non-editable in QTableWidget?

I have QTableWidget which is non editable.(i had setup noEditTriggers while creating Ui file). I want to make particular cell editable from each row. how i can get this done?
I looked into several answers on SO and other platforms but didn't get anything working for me.
currently I am using this piece of code. it doesnt give an error but i still could not edit that cell value.
self.item = QTableWidgetItem('Hi')
flags = self.item.flags()
flags ^= QtCore.Qt.ItemIsEditable
self.item.setFlags(flags)
self.table.setItem(row, column, self.item)
EDIT::
Using the same fundament for the #musicamante answer is to create a delegate that only returns one editor in the specific column, the advantage is that you don't need to subclassify QTableWidget and the logic can be used in other types of views:
class Delegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
if index.column() == 2:
return super(Delegate, self).createEditor(parent, option, index)
delegate = Delegate(self.table)
self.table.setItemDelegate(delegate)
Update:
If you want the cells with NN to be editable then you must return the editor when it meets that condition: index.data() == "NN"
import random
import sys
from PyQt5 import QtWidgets
class Delegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
if index.data() == "NN":
return super(Delegate, self).createEditor(parent, option, index)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
texts = ["Hello", "Stack", "Overflow", "NN"]
table = QtWidgets.QTableWidget(10, 5)
delegate = Delegate(table)
table.setItemDelegate(delegate)
for i in range(table.rowCount()):
for j in range(table.columnCount()):
text = random.choice(texts)
it = QtWidgets.QTableWidgetItem(text)
table.setItem(i, j, it)
table.resize(640, 480)
table.show()
sys.exit(app.exec_())
You could set the flags for each item, while leaving the default edit triggers, but this is not very good approach, since you could have a very large table, some items could be changed/added/removed and you might forget to set/reset the flags.
A better approach could be to override the edit() method, and execute the default implementation (which creates the item editor and starts the editing) by manually setting the edit trigger.
This requires to leave the default edit triggers (or at least one trigger method) set.
class TableWidget(QtWidgets.QTableWidget):
def edit(self, index, trigger, event):
# editing is allowed only for the third column
if index.column() != 2:
trigger = self.NoEditTriggers
return super().edit(index, trigger, event)

Python - Deleting a selected row from a QSQLRelationalTableModel / QTableView

I have a QTableView class, and a QSQLRelationalTableModel class for displaying data from within an SQLITE database.
I have subclassed these myself as I need to overwrite various methods within the PyQt classes.
I am trying to implement a 'Delete Row' function which will then automatically redraw the TableView.
Unfortunately I'm at a loss. I have overwritten the data() method within the model, as I need to modify the data that's returned for some columns.
When I delete a row, this data() method obviously then has nothing to return for that row, which throws type errors (It's expecting to return data for a missing row).
For the moment I have it sat in a try/except block (I know that's bad practice), and to return "Deleted" if it doesn't work.
Here is my complete model:
class PayRateTableModel(QtSql.QSqlRelationalTableModel):
def __init__(self,master):
QtSql.QSqlRelationalTableModel.__init__(self,db=master.db)
self.header_labels = ['ID','From','To','Day Rate','Night Rate', 'Description']
self.editable_columns = [1,2,3,4,5]
self.setTable('payrates')
self.setEditStrategy(QtSql.QSqlRelationalTableModel.OnRowChange)
self.select()
def columnCount(self,master,parent=QtCore.QModelIndex()):
return 6
def flags(self, index):
flags=super(self.__class__, self).flags(index)
if index.column() not in self.editable_columns:
flags ^= QtCore.Qt.ItemIsEditable
return flags
def headerData(self, p_int, Qt_Orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole and Qt_Orientation == QtCore.Qt.Horizontal:
return self.header_labels[p_int]
return QtCore.QAbstractTableModel.headerData(self,p_int, Qt_Orientation,role)
def data(self, index, role):
row=index.row()
col=index.column()
data = super(PayRateTableModel, self).data(index, role)
if role==QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignCenter
elif role==QtCore.Qt.FontRole:
f=QtGui.QFont
return f
elif role==QtCore.Qt.ForegroundRole and index.column() not in self.editable_columns:
return QtGui.QColor(150,150,150)
try:
if (col in [1,2]) and (role==QtCore.Qt.DisplayRole) and data:
data=super(PayRateTableModel, self).data(index,role)
year=data[:4]
month=data[5:7]
day=data[8:10]
data=QtCore.QDate(int(year),int(month),int(day)).toString("dd/MM/yyyy")
return data
elif col in [3,4] and role ==QtCore.Qt.DisplayRole:
data=super(PayRateTableModel, self).data(index,role)
return '%.4f' % data
except:
return "Deleted"
return super(PayRateTableModel, self).data(index,role)
Here is my TableView
class PayRateTableView(QtWidgets.QTableView):
""" Display table of editable pay rates"""
def __init__(self, master):
QtWidgets.QTableView.__init__(self)
self.hideColumn(0)
self.setSortingEnabled(True)
self.model = PayRateTableModel(master)
self.sortmodel=DateSortProxyModel()
self.sortmodel.setSourceModel(self.model)
self.setModel(self.sortmodel)
self.sortByColumn(1,QtCore.Qt.AscendingOrder)
self.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
self.resizeRowsToContents()
self.resizeColumnsToContents()
self.setAlternatingRowColors(True)
self.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.setItemDelegateForColumn(1, CalendarDelegate(master))
self.setItemDelegateForColumn(2, CalendarDelegate(master))
self.setItemDelegateForColumn(3, PayRateDelegate(master))
self.setItemDelegateForColumn(4, PayRateDelegate(master))
self.vhead=self.verticalHeader()
self.vhead.setVisible(False)
self.vhead.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.vhead.customContextMenuRequested.connect(self.contextMenuEvent)
def sizeHintForRow(self, p_int):
return 20
def contextMenuEvent(self, event):
index = self.currentIndex()
menu=QtWidgets.QMenu(self)
menu.popup(QtGui.QCursor.pos())
deleteAction = menu.addAction("Delete Row")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == deleteAction:
self.model.removeRow(index.row())
self.model.submitAll()
This actually works but although it deletes the data, it leaves half a row at the top of the table like this (Row 3 has just been deleted):
Any changes I make that seem to successfully delete the row, make the app quit with a "Processed finished with exit code 11" which I cannot find any detailed explanation (Platform specific, I'm using PyCharm on Mac OS and Python 3.6)
Thanks
As it turns out, I seem to have figured it out. Kind of.
Although it's not a direct answer per se, I changed the deletion method from a context menu to a toolbar icon.
Select row, click toolbar icon, invoke the following Class method:
def deletePayRate(self):
index = self.currentIndex()
deleteconfirmation = QtWidgets.QMessageBox.critical(self.parent(), "Delete row", "Really delete the selected Pay Rate?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if deleteconfirmation == QtWidgets.QMessageBox.Yes:
self.model.removeRow(index.row())
self.model.submitAll()
return
else:
return
Simple but it works.

spanning multiple columns, using QTreeView and QAbstractItemModel

I want to make a TreeView where the top level entries span all columns (e.g. they have one row), while children span multiple columns (e.g. they have multiple rows.)
I was trying to accomplish this with QTreeView.setFirstColumnSpanned. However, I can't really figure out where/when to call this (and maybe I'm going about things completely wrong to begin with.)
I thought maybe I could make a function in my view that would be called after the view is populated, to check for any items that need to have their spans updated.
However, I couldn't find any signals to connect to that might help me do this (I tried several, but none of them ever seemed to fire.)
I also tried reimplementing insertRows in my model and/or rowsInserted in my view, but they never seem to get called.
Here's an extremely simplified version of what I'm doing:
class MyModel(QtCore.QAbstractItemModel):
def __init__(self, top_level_nodes):
QtCore.QAbstractItemModel.__init__(self)
self.top_level_nodes = top_level_nodes
self.columns = 5
def columnCount(self, parent):
return self.columns
def rowCount(self, parent):
total = len(self.top_level_nodes)
for node in self.top_level_nodes:
total += len(node.subnodes)
return total
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
if role == QtCore.Qt.DisplayRole:
obj = index.internalPointer()
return obj.name
return QtCore.QVariant()
def index(self, row, column, parent):
if not parent.isValid():
if row > (len(self.top_level_nodes) - 1):
return QtCore.QModelIndex()
return self.createIndex(row, column, self.top_level_nodes[row])
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
node = index.internalPointer()
if node.parent is None:
return QtCore.QModelIndex()
else:
return self.createIndex(node.parent.row, 0, node.parent)
class FakeEntry(object):
def __init__(self, name, row, children=[]):
self.parent = None
self.row = row
self.name = 'foo'
self.subnodes = children
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(800, 600)
self.layout = QtGui.QVBoxLayout(self)
self.tree_view = QtGui.QTreeView(self)
self.layout.addWidget(self.tree_view)
entry = FakeEntry('foo', 0, children=[])
self.model = MyModel([entry])
self.tree_view.setModel(self.model)
def main():
app = QtGui.QApplication(sys.argv)
frame = MainWindow()
frame.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
This code runs fine. What I need to know is:
How can I make the entry created by FakeEntry span all 5 columns instead of creating 5 identical cells?
Other questions:
Can I reimplement QAbstractItemModel.insertRows or QTreeView.rowsInserted? Is there a "trick" to doing this that I'm missing, which is causing my code to pick up the default implementation?
What actually handles creating the rows/columns in the view, once the model is populated with data and attached to the view?
This works for me if I add a call to setFirstColumnSpanned after the setModel() call in MainWindow.__init__().
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(800, 600)
self.layout = QtGui.QVBoxLayout(self)
self.tree_view = QtGui.QTreeView(self)
self.layout.addWidget(self.tree_view)
entry = FakeEntry('foo', 0, children=[])
self.model = MyModel([entry])
self.tree_view.setModel(self.model)
self.tree_view.setFirstColumnSpanned(0, QtCore.QModelIndex(), True)
I believe the view keeps track of the spanned columns internally, separate from the model. But it will reset the span information whenever setModel() is called. So, you should call setFirstColumnSpanned() after calling setModel() on the view. If your model is doing lazy loading with fetchMore(), you'll have to work out some way to inform the view that it should update the spans after the model has loaded new data.
With regard to what actually handles creating the rows/columns in the view, I think about that as a partnership between the model and the view. Once the view has a model set, it begins asking various questions to the model. For the top-most parent item (an invalid index), how many columns are there? Cool, there's 5. How many rows are there? Ok, 1. Just from that information the view's header can start to configure itself. At a certain point, the view iterates over the rows, columns, and children in the model, and asks each index for it's data in turn, for each data role. It can then start to create the delegates to display that data in the appropriate spot in the view.

PyQt - QTableView search by hiding rows

Here's my problem. I have a QTableView displaying some data (set up in a model) and a QLineEdit Widget that i want to use for searching text in all displayed rows. The expected behavior should be: i type some text in the QLineEdit and the QTableView update itself hiding all rows that not contain that data.
The question is, how should i implement that? I have found a QTableView member function named hideRows() that seems the right choice, but i can't figure out how should i iterate through all the data and where to put that method. It should be contained inside the model or the dialog? (this is actually my first time using models, so i just grasped how they work)
Plus i need to implement an export function (csv, html or whatever) but just with the currently displayed rows (those who are not hiding). Is this possible?
Thank you for any advice.
Here is my code till now:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
import Android_extractor
import ui_android_dialog
class recordsTableModel(QAbstractTableModel):
def __init__(self, records, parent = None):
QAbstractTableModel.__init__(self, parent)
self.__records = records
def rowCount(self, parent):
return len(self.__records)
def columnCount(self, parent):
return len(self.__records[0])
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
def data(self, index, role):
if role == Qt.EditRole:
row = index.row()
column = index.column()
return self.__colors[row][column].name()
if role == Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.__records[row][column]
return value
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return self.__records[0]._fields[section]
class AndroidDialog(QDialog, ui_android_dialog.Ui_androidDialog):
def __init__(self, parent=None):
super(AndroidDialog, self).__init__(parent)
self.setupUi(self)
self.buttonMapper = QSignalMapper(self)
self.buttonMapper.setMapping(self.contactsToolButton, 0)
self.buttonMapper.setMapping(self.groupsToolButton, 1)
self.buttonMapper.setMapping(self.chatsessionToolButton, 2)
self.buttonMapper.setMapping(self.messageToolButton, 3)
self.contactsToolButton.clicked.connect(self.buttonMapper.map)
self.groupsToolButton.clicked.connect(self.buttonMapper.map)
self.chatsessionToolButton.clicked.connect(self.buttonMapper.map)
self.messageToolButton.clicked.connect(self.buttonMapper.map)
self.buttonMapper.mapped.connect(self.dataStackedWidget.setCurrentIndex)
self.newQuery = Android_extractor.AndroidQuery(sys.argv[1])
self.contacts = self.newQuery.getContacts()
self.groups = self.newQuery.getGroups()
self.chats = self.newQuery.getChats()
self.contactsTableView.setModel(recordsTableModel(self.contacts))
self.contactsTableView.resizeColumnsToContents()
self.contactsTableView.resizeRowsToContents()
self.groupsTableView.setModel(recordsTableModel(self.groups))
self.groupsTableView.resizeColumnsToContents()
self.groupsTableView.resizeRowsToContents()
self.chatsessionTableView.setModel(recordsTableModel(self.chats))
self.chatsessionTableView.resizeColumnsToContents()
self.chatsessionTableView.resizeRowsToContents()
app = QApplication(sys.argv)
form = AndroidDialog()
form.show()
app.exec_()
You should have a look at QSortFilterProxyModel
Instead of setting your custom model directly on your tableview, set in as Source model on the proxy, then set the proxy model on your view.
self.proxyModelContact = QSortFilterProxyModel(self)
self.proxyModelContact.setSourceModel(recordsTableModel(self.contacts))
self.contactsTableView.setModel(self.proxyModelContact)
QSortFilterProxyModel provides two methods:
setFilterRegExp(pattern) allows you to set a regex filter on your view (i.e only the items that match the pattern will be displayed)
setFilterKeyColumn(index) allows you to define which column will be used to do the filtering (if index = -1, all columns will be looked at).
You just have to link the textChanged signal of your linedit to a slot which will update the filter eg:
def onTextChanged(self, text):
self.proxyModelContact.setFilterRegExp(str(text))

Categories