Related
I have been trying to update a QTableViewModel when inserting a new object that represents a row. I did follow the advise of several question in SO, but I cannot get an example to work.
After debugging, I found that the call to self.endInsertRows() produces the crash.
This is a minimal example:
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets
class Wire:
def __init__(self, name, x, y, gmr, r):
self.name = name
self.x = x
self.y = y
self.r = r
self.gmr = gmr
class WiresCollection(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.header = ['Name', 'R (Ohm/km)', 'GMR (m)']
self.index_prop = {0: 'name', 1: 'r', 2: 'gmr'}
self.wires = list()
def add(self, wire: Wire):
"""
Add wire
:param wire:
:return:
"""
row = len(self.wires)
self.beginInsertRows(QtCore.QModelIndex(), row, row)
self.wires.append(wire)
self.endInsertRows()
def delete(self, index):
"""
Delete wire
:param index:
:return:
"""
row = len(self.wires)
self.beginRemoveRows(QtCore.QModelIndex(), row, row)
self.wires.pop(index)
self.endRemoveRows()
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.wires)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.header)
def parent(self, index=None):
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole:
val = getattr(self.wires[index.row()], self.index_prop(index.column()))
return str(val)
return None
def headerData(self, p_int, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self.header[p_int]
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
"""
Set data by simple editor (whatever text)
:param index:
:param value:
:param role:
"""
wire = self.wires[index.row()]
attr = self.index_prop[index.column()]
setattr(wire, attr, value)
class TowerBuilderGUI(QtWidgets.QDialog):
def __init__(self, parent=None):
"""
Constructor
Args:
parent:
"""
QtWidgets.QDialog.__init__(self, parent)
self.setWindowTitle('Tower builder')
# GUI objects
self.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.layout = QVBoxLayout(self)
self.wires_tableView = QTableView()
self.add_wire_pushButton = QPushButton()
self.add_wire_pushButton.setText('Add')
self.delete_wire_pushButton = QPushButton()
self.delete_wire_pushButton.setText('Delete')
self.layout.addWidget(self.wires_tableView)
self.layout.addWidget(self.add_wire_pushButton)
self.layout.addWidget(self.delete_wire_pushButton)
self.setLayout(self.layout)
# Model
self.wire_collection = WiresCollection(self)
# set models
self.wires_tableView.setModel(self.wire_collection)
# button clicks
self.add_wire_pushButton.clicked.connect(self.add_wire_to_collection)
self.delete_wire_pushButton.clicked.connect(self.delete_wire_from_collection)
def msg(self, text, title="Warning"):
"""
Message box
:param text: Text to display
:param title: Name of the window
"""
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText(text)
# msg.setInformativeText("This is additional information")
msg.setWindowTitle(title)
# msg.setDetailedText("The details are as follows:")
msg.setStandardButtons(QMessageBox.Ok)
retval = msg.exec_()
def add_wire_to_collection(self):
"""
Add new wire to collection
:return:
"""
name = 'Wire_' + str(len(self.wire_collection.wires) + 1)
wire = Wire(name, x=0, y=0, gmr=0, r=0.01)
self.wire_collection.add(wire)
def delete_wire_from_collection(self):
"""
Delete wire from the collection
:return:
"""
idx = self.ui.wires_tableView.currentIndex()
sel_idx = idx.row()
if sel_idx > -1:
self.wire_collection.delete(sel_idx)
else:
self.msg('Select a wire in the wires collection')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = TowerBuilderGUI()
window.show()
sys.exit(app.exec_())
As indicated in the comments you have 2 errors:
The first is when you press add because when you add a new item you have to refresh the view and that's why it's called data() method and it's where the error is shown in self.index_prop(index.column()), index_pro is a dictionary so you should use [] instead of ().
val = getattr(self.wires[index.row()], self.index_prop[index.column()])
Another error is generated by the line idx = self.ui.wires_tableView.currentIndex(), the ui does not exist and it is not necessary, sure it is a remnant of a previous code, to access wires_tableView as this is a member of the class not it is necessary to use an intermediary, you must access directly with self: idx = self.wires_tableView.currentIndex()
The above are typos and probably mark it so that the question is closed there is another error that is not, and that is the reason for my answer.
In the line self.beginRemoveRows(...) you must pass the row that you are going to remove but you are passing the row that does not exist:
row = len(self.wires)
self.beginRemoveRows(QtCore.QModelIndex(), row, row) # <---- row does not exist in the table
The solution is simple, change it by index:
def delete(self, index):
"""
Delete wire
:param index:
:return:
"""
self.beginRemoveRows(QtCore.QModelIndex(), index, index)
self.wires.pop(index)
self.endRemoveRows()
So, Im currently taking just limited data from a ZeroMQ Messaging system appending it all to a Dataframe and then turning the Dataframe into a pandas model with code I acquired from someone on stack. Then running that model through a PyQt5 Tableview.
Every time I run the tableview code, after converting my Dataframe to a model for Tableview the kernal just dies. I tried to at least handle a exception but it wont even raise a runtimeerror that is super general. It just dies every time....
For the purpose of the test im using a CSV with data to try to get this into a workable model. You can use any csv or data you have on your end to test this as theres no hard coded formatting.
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas as pd
class PandasModel(QtCore.QAbstractTableModel):
def __init__(self, df = pd.DataFrame(), parent=None):
QtCore.QAbstractTableModel.__init__(self, parent=parent)
self._df = df
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
if orientation == QtCore.Qt.Horizontal:
try:
return self._df.columns.tolist()[section]
except (IndexError, ):
return QtCore.QVariant()
elif orientation == QtCore.Qt.Vertical:
try:
# return self.df.index.tolist()
return self._df.index.tolist()[section]
except (IndexError, ):
return QtCore.QVariant()
def data(self, index, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
if not index.isValid():
return QtCore.QVariant()
return QtCore.QVariant(str(self._df.ix[index.row(), index.column()]))
def setData(self, index, value, role):
row = self._df.index[index.row()]
col = self._df.columns[index.column()]
if hasattr(value, 'toPyObject'):
# PyQt4 gets a QVariant
value = value.toPyObject()
else:
# PySide gets an unicode
dtype = self._df[col].dtype
if dtype != object:
value = None if value == '' else dtype.type(value)
self._df.set_value(row, col, value)
return True
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._df.index)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self._df.columns)
def sort(self, column, order):
colname = self._df.columns.tolist()[column]
self.layoutAboutToBeChanged.emit()
self._df.sort_values(colname, ascending= order == QtCore.Qt.AscendingOrder, inplace=True)
self._df.reset_index(inplace=True, drop=True)
self.layoutChanged.emit()
def createview(title, model):
try:
view = QtWidgets.QTableView()
view.setWindowview
except:
raise RuntimeError("I know python!")
if __name__=="__main__":
df=pd.read_csv("C:\Excel Sheets\Test_CSV_6-18-18.csv")
model = PandasModel(df)
createview("Model", model)
I have trouble with trying create my own sorting function with QAbstractItemModel. It works but not case insensitive. I have tried to use QSortFilterProxyModel, but any success. My sort function:
def sort(self, col, order):
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self.tableData = sorted(self.tableData, key=operator.itemgetter(col))
if order == Qt.AscendingOrder:
self.tableData.reverse()
self.emit(SIGNAL("layoutChanged()"))
I am using QTableView. How I could make it case insensitive?
Full example:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import operator
import sys
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
header = ["one", "two"]
tableDict = [["abcdef", "tuvwx"], ["aBcde", "TUvWx"], ["acdef","tUvWX"], ["Acdef", "TUVwx"], ["ACdef", "TUVwx"]]
self.myTable = newTableModel(header, tableDict)
mainLayout = QHBoxLayout()
mainLayout.addWidget(self.myTable.tableView)
self.setLayout(mainLayout)
self.setWindowTitle("Test table")
class newTableModel(QAbstractTableModel):
def __init__(self, header, data, parent=None, *args):
super(newTableModel, self).__init__(parent)
self.tableView = QTableView()
self.tableData = data
self.header = header
self.tableView.setShowGrid(True)
self.tableView.setFrameStyle( QFrame.NoFrame )
self.tableView.setFocusPolicy( Qt.NoFocus )
self.tableView.setSelectionMode( QAbstractItemView.NoSelection )
vHeader = self.tableView.verticalHeader()
vHeader.setVisible(False)
vHeader.setStretchLastSection(False)
hHeader = self.tableView.horizontalHeader()
hHeader.setVisible(True)
hHeader.setStretchLastSection(False)
self.tableView.setSortingEnabled(True)
self.tableView.setModel(self)
self.tableView.resizeRowsToContents()
self.tableView.resizeColumnsToContents()
vHeader.setResizeMode(QHeaderView.ResizeToContents)
self.tableView.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.tableView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def rowCount(self, parent):
return len(self.tableData)
def columnCount(self, parent):
return len(self.tableData[0])
def data(self, index, role=Qt.DisplayRole):
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
return "{0}".format(self.tableData[row][col])
return None
def setData(self, index, value, role):
if index.isValid():
return True
return False
def flags(self, index):
fl = QAbstractTableModel.flags(self, index)
if index.column() == 0:
fl |= Qt.ItemIsUserCheckable
return fl
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.header[col]
def sort(self, col, order):
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self.tableData = sorted(self.tableData, key=operator.itemgetter(col))
if order == Qt.AscendingOrder:
self.tableData.reverse()
self.emit(SIGNAL("layoutChanged()"))
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
This table data in example shows sorting - non case sensitive.
You simply need to convert the values to lower (or upper) case in the sort-key that is passed to the sorted function. For improved efficiency, you can also use the reverse argument to avoid doing that in a separate step:
def sort(self, col, order):
self.layoutAboutToBeChanged.emit()
self.tableData = sorted(
self.tableData,
key=lambda row: row[col].lower(),
reverse=(order != Qt.AscendingOrder),
)
self.layoutChanged.emit()
Note that sorted does a stable sort, so equal values (after the key has been applied), will keep their original places. Thus, the second column in your example won't show any changes when it's sorted, since the values are all the "same" (if you ignore case).
UPDATE:
Here's a solution that will work for strings and numbers. It assumes the columns aren't a mixture of the two types:
def sort(self, col, order):
if self.tableData and self.tableData[0]:
self.layoutAboutToBeChanged.emit()
if isinstance(self.tableData[0][col], str):
sortkey = lambda row: row[col].lower()
else:
sortkey = operator.itemgetter(col)
self.tableData = sorted(
self.tableData, key=sortkey,
reverse=(order != Qt.AscendingOrder))
self.layoutChanged.emit()
I am trying to develop a GUI to managed scientific results. For this, I would like to present result from two datasets. The user would have a visual reprentation to help him to compare those results in two QTableView.
Image : comparer Result,
I want to link lines from the two table, so they always be present face to face.
When order change in one table, the other will follow and adapt its order to still have the linked lines face to face.
Eventually i would like to add empty row face to a line that haven't a relative line in the other table.
I was think to use a QSortFilterProxyModel but I am not sure how to use it.
Edit
My Question seem to be not clear. I Formulate here. I find myself a solution so here a example of what I was looking.
On this example line I link line according to the name (bla, bli blo, blu). We see on the same line, tables present face to face line of result "bla" and "bli", because there are in both left model and right.
There is no "blu" in the right table. so i add a empty line.
Idem in the left table with "blo"
In this example item are sorted with "configuration" of the right table. Left table have to follow the order choose by the right table.
Here my code without the solution
class Result(object):
def __init__(self, headerOrder, infoResult):
for n, headerName in enumerate(headerOrder):
self.__setattr__(headerName, infoResult[n])
self.diff = self.reference_redshift - self.estimate
ModelClass
class Result_model(QtCore.QAbstractTableModel):
def __init__(self, header, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.__datas = []
self.__headers = header
def rowCount(self, parent=None):
return len(self.__datas)
def columnCount(self, parent=None):
return len(self.__headers)
def data(self, index, role):
if role == QtCore.Qt.ToolTipRole:
row = index.row()
column = index.column()
return "{}: {}".format(self.__headers[column], getattr(self.__datas[row], self.__headers[column]))
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = getattr(self.__datas[row], self.__headers[column])
return value
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
if section < len(self.__headers):
return self.__headers[section]
else:
return "not implemented"
else:
return section
def supportedDragActions(self):
return QtCore.Qt.CopyAction
def supportedDropActions(self):
return Qt.CopyAction | Qt.MoveAction
def getResult(self, index):
row = index.row()
return self.__datas[row]
def sort(self, Ncol, order):
"""Sort table by given column number.
"""
self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()"))
attribut = self.__headers[Ncol]
self.__datas = sorted(
self.__datas, key=lambda x: getattr(x, attribut), reverse=(order == QtCore.Qt.DescendingOrder))
self.emit(QtCore.SIGNAL("layoutChanged()"))
def addResults(self, results):
self.beginInsertRows(QtCore.QModelIndex(), len(
self.__datas), len(self.__datas) + len(results))
for res in results:
self.__datas.append(res)
self.endInsertRows()
TableView only Drag
class TableResult(QtGui.QTableView):
def __init__(self, parent=None):
QtGui.QTableView.__init__(self, parent)
self.setDragEnabled(True)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.header, self.aid_index = [["aid", "estimate", "reference_redshift", "diff", "amazed_executable_id", "amazed_configuration_id",
"astronomical_object_name", "star_forming_rate", "magnitude", "log_f_halpha", "emission_velocity_dispersion", "res_dir"], 0]
self.tag_info_result = ["aid", "estimate", "reference_redshift", "amazed_executable_id", "amazed_configuration_id",
"astronomical_object_name", "star_forming_rate", "magnitude", "log_f_halpha", "emission_velocity_dispersion", "res_dir"]
self.setItemDelegateForColumn(self.aid_index, ButtonDelegate(self))
self.parent = parent
def startDrag(self, dropAction):
if(self.parent is not None and hasattr(self.parent, "selection")):
# create mime data object
mime = QtCore.QMimeData()
# start drag
drag = QtGui.QDrag(self)
drag.setMimeData(mime)
drag.start(QtCore.Qt.CopyAction | QtCore.Qt.CopyAction)
else:
print("Drag impossible")
def mouseMoveEvent(self, event):
self.startDrag(event)
TableView with drop
class Selection_receiver(TableResult):
"Add the drop possibility from TableResult"
def __init__(self, setResultFunction, parent=None):
TableResult.__init__(self, parent)
self.setAcceptDrops(True)
self.setResultFunction = setResultFunction
def dragEnterEvent(self, event):
if (isinstance(event.source(), TableResult)):
event.accept()
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
print("dropEvent")
if (isinstance(event.source(), TableResult)):
event.acceptProposedAction()
model_result = event.source().parent.resModel
self.setResultFunction(model_result)
else:
event.ignore()
The Widget presenting the two Tables
class Comparater_result_widget(QtGui.QWidget):
"""
Present two table for easy comparaison.
"""
def __init__(self,parent=None):
super(self.__class__, self).__init__(parent)
self.setWindowTitle("Result Comparer")
main_layout = QtGui.QVBoxLayout()
receiverSplitter = QtGui.QSplitter()
receiverSplitter.setOrientation(QtCore.Qt.Horizontal)
self.left_receiver = Selection_receiver(self.setLeftResult)
receiverSplitter.addWidget(self.left_receiver)
self.right_receiver = Selection_receiver( self.setRightResult)
receiverSplitter.addWidget(self.right_receiver)
main_layout.addWidget(receiverSplitter)
self.left_receiver.horizontalScrollBar().valueChanged.connect(
self.right_receiver.horizontalScrollBar().setValue)
self.right_receiver.horizontalScrollBar().valueChanged.connect(
self.left_receiver.horizontalScrollBar().setValue)
self.left_receiver.verticalScrollBar().valueChanged.connect(
self.right_receiver.verticalScrollBar().setValue)
self.right_receiver.verticalScrollBar().valueChanged.connect(
self.left_receiver.verticalScrollBar().setValue)
self.right_results = None
self.left_results = None
self.setLayout(main_layout)
def setLeftResult(self, model_result):
print("setLeftResult []".format(model_result))
self.left_results = model_result
self.add_model_result(self.left_receiver, model_result)
def setRightResult(self, model_result):
print("setRightResult {}".format(model_result))
self.right_results = model_result
self.add_model_result(self.right_receiver, model_result)
def add_model_result(self, receiver, model_result):
receiver.setModel(model_result)
if(self.right_results is not None and self.left_results is not None):
self.link_result()
def link_result(self):
# parse the two model and link results if the have equal on one
# particular attribut
pass
def OnLeftChangeOrder(self):
# somthing like right_proxy.reorder(left_order)
pass
def OnRightChangeOrder(self):
# something link left_proxy.reorder(right_order)
pass
Here is my solution i created.
I use Two Proxy:
my own mapping proxy model ("MixRowProxyModel")
a almost normal QSortFilterProxyModel ("MySortProxyModel")
Source model is the model of MixRowProxyModel.
MixRowProxyModel is the model of MySortProxyModel
Table can be in two state, master or slave. If I sort the right table at one column, the right become master and the left is slave.
When a table is master, its MySortProxyModel is active, MixRowProxyModel is inactive
When a table is slave, its MySortProxyModel is inactive, MixRowProxyModel is active
When the two source model are set. I create a map between rows of the sources models. This map will not change when table are sorted.
When a table become slave i construct the mapping for its MixRowProxyModel (see method change in Comparater_result_widget). For that i use the initial mapping i created between modelsource.
class MixRowProxyModel(QtGui.QAbstractProxyModel):
def __init__(self, controller, side, parent=None):
QtGui.QAbstractProxyModel.__init__(self, parent=parent)
self.controller = controller
self.nbEmpty = 0
self.mapProxyToSource = None
self.isMaster = True
self.side = side
def setMap(self, mapping):
self.isMaster = False
self.mapProxyToSource = mapping
self.mapSourceToProxy = {v: k for k,
v in self.mapProxyToSource.items()}
def mapFromSource(self, sourceIndex):
#print("MixRowProxyModel Proxy Index model {}".format(sourceIndex.model()))
if(not sourceIndex.isValid()):
return self.index(-1, -1)
if(self.isMaster):
return self.index(sourceIndex.row(), sourceIndex.column(), parent=QtCore.QModelIndex)
else:
row = sourceIndex.row()
if(row in self.mapSourceToProxy):
return self.index(self.mapSourceToProxy[row], sourceIndex.column())
else:
print("Invalid sourceIndex {}".format(row))
return self.index(-1, -1)
def mapToSource(self, proxyIndex):
if(not proxyIndex.isValid()):
return self.sourceModel().index(-1, -1)
if(self.isMaster):
return self.sourceModel().index(proxyIndex.row(), proxyIndex.column())
else:
row = proxyIndex.row()
if(row in self.mapProxyToSource):
return self.sourceModel().index(self.mapProxyToSource[row], proxyIndex.column())
else:
# print("Invalid proxyIndex {}".format(row))
return self.sourceModel().index(-1, -1)
def rowCount(self, parent=None):
return self.sourceModel().rowCount() + self.nbEmpty
def columnCount(self, parent=None):
return self.sourceModel().columnCount()
def addEmptyRow(self):
print("addEmptyRow {}".format(self.side))
self.beginInsertRows(QtCore.QModelIndex(),
self.rowCount(), self.rowCount())
self.nbEmpty += 1
self.endInsertRows()
return -1
def parent(self, index):
return QtCore.QModelIndex()
def index(self, row, column, parent=QtCore.QModelIndex):
if(row >= self.rowCount() or row < 0 or column < 0 or column >= self.columnCount()):
return QtCore.QModelIndex()
return self.createIndex(row, column, parent)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self.sourceModel().headerData(section, orientation, role)
else:
return section
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def data(self, index, role=QtCore.Qt.DisplayRole):
if(not index.isValid()):
return None
if(self.isMaster):
if(index.row() < self.sourceModel().rowCount()):
return self.sourceModel().data(index, role)
else:
return None
else:
if(not index.row() in self.mapProxyToSource):
return None
return self.sourceModel().data(self.sourceModel().index(self.mapProxyToSource[index.row()], index.column()), role)
the second ProxyModel
class MySortProxyModel(QtGui.QSortFilterProxyModel):
sortedSignal = QtCore.pyqtSignal(QtCore.Qt.SortOrder)
def sort(self, column, order):
self.sourceModel().isMaster = True
#print("Sort order : {} sort Role {}".format(order, self.sortRole()))
self.setSortRole(QtCore.Qt.DisplayRole)
super().sort(column, order)
self.sortedSignal.emit(order)
the Parent Widget controlling the two tables
class Comparater_result_widget(QtGui.QWidget):
def __init__(self, controller, parent=None):
super(self.__class__, self).__init__(parent)
self.controller = controller
self.setWindowTitle("Result Comparer")
main_layout = QtGui.QVBoxLayout()
receiverSplitter = QtGui.QSplitter()
receiverSplitter.setOrientation(QtCore.Qt.Horizontal)
self.left_receiver = Selection_receiver(
self.controller, self.setLeftResult)
receiverSplitter.addWidget(self.left_receiver)
self.right_receiver = Selection_receiver(
self.controller, self.setRightResult)
receiverSplitter.addWidget(self.right_receiver)
main_layout.addWidget(receiverSplitter)
self.left_receiver.horizontalScrollBar().valueChanged.connect(
self.right_receiver.horizontalScrollBar().setValue)
self.right_receiver.horizontalScrollBar().valueChanged.connect(
self.left_receiver.horizontalScrollBar().setValue)
self.left_receiver.verticalScrollBar().valueChanged.connect(
self.right_receiver.verticalScrollBar().setValue)
self.right_receiver.verticalScrollBar().valueChanged.connect(
self.left_receiver.verticalScrollBar().setValue)
self.right_source_model = None
self.right_proxy_model = None
self.right_proxy_model2 = None
self.left_source_model = None
self.left_proxy_model = None
self.left_proxy_model2 = None
self.setLayout(main_layout)
def setLeftResult(self, model_result):
self.left_source_model = model_result
self.left_proxy_model = MixRowProxyModel(
self.controller, "left", parent=self)
# self.left_proxy_model.sortedSignal.connect(self.onLeftChange)
self.left_proxy_model.setSourceModel(self.left_source_model)
self.left_proxy_model2 = MySortProxyModel(parent=self)
self.left_proxy_model2.sortedSignal.connect(self.onLeftChange)
self.left_proxy_model2.setSourceModel(self.left_proxy_model)
self.add_model_result(self.left_receiver, self.left_proxy_model2)
def setRightResult(self, model_result):
self.right_source_model = model_result
self.right_proxy_model = MixRowProxyModel(
self.controller, "right", parent=self)
self.right_proxy_model.setSourceModel(self.right_source_model)
# self.right_proxy_model.sortedSignal.connect(self.onRightChange)
self.right_proxy_model2 = MySortProxyModel(parent=self)
self.right_proxy_model2.sortedSignal.connect(self.onRightChange)
self.right_proxy_model2.setSourceModel(self.right_proxy_model)
self.add_model_result(self.right_receiver, self.right_proxy_model2)
def add_model_result(self, receiver, model_result):
receiver.setModel(model_result)
if(self.right_source_model is not None and self.left_source_model is not None):
self.link_result()
def link_result(self):
name_to_row = {}
for numSourceLeftRow in range(self.left_source_model.rowCount()):
res = self.left_source_model.getResultNumRow(numSourceLeftRow)
name = res.astronomical_object_name
if(name in name_to_row):
name_to_row[name][0].append(numSourceLeftRow)
else:
name_to_row[name] = ([numSourceLeftRow], [])
for numSourceRightRow in range(self.right_source_model.rowCount()):
res = self.right_source_model.getResultNumRow(numSourceRightRow)
name = res.astronomical_object_name
if(name in name_to_row):
name_to_row[name][1].append(numSourceRightRow)
else:
name_to_row[name] = ([], [numSourceRightRow])
self.mapLeftToRight = {} # key = leftRow; value = rightRow
self.mapRightToLeft = {} # key = rightRow; value = leftRow
for list_leftRow, list_rightRow in name_to_row.values():
if(len(list_rightRow) > 1):
print(
"Error more that index at right for same astronomical name {}".list_rightRow)
if(len(list_leftRow) > 1):
print(
"Error more that index at left for same astronomical name {}".list_leftRow)
if(len(list_leftRow) == 0):
leftRow = self.left_proxy_model.addEmptyRow()
else:
leftRow = list_leftRow[0]
if(len(list_rightRow) == 0):
rightRow = self.right_proxy_model.addEmptyRow()
else:
rightRow = list_rightRow[0]
self.mapLeftToRight[leftRow] = rightRow
self.mapRightToLeft[rightRow] = leftRow
self.left_receiver.rowCountChanged(
self.left_source_model.rowCount(), self.left_proxy_model.rowCount())
self.right_receiver.rowCountChanged(
self.right_source_model.rowCount(), self.right_proxy_model.rowCount())
print("Link Done : LtoR : {}; RtoL {}".format(
self.mapLeftToRight, self.mapRightToLeft))
def onRightChange(self, order):
print("RightChange")
self.change(self.left_source_model, self.left_proxy_model, self.left_proxy_model2, self.right_source_model, self.right_proxy_model,
self.right_proxy_model2, self.mapLeftToRight, self.left_receiver, order)
def onLeftChange(self, order):
print("LeftChange")
self.change(self.right_source_model, self.right_proxy_model, self.right_proxy_model2, self.left_source_model, self.left_proxy_model,
self.left_proxy_model2, self.mapRightToLeft, self.right_receiver, order)
def change(self, slave_source_model, slave_proxy_model,
slave_proxy_model2, master_source_model, master_proxy_model, master_proxy_model2,
map_slave_to_master, slave_receiver, order):
if(slave_source_model is not None):
slaveMapping = dict() # in slave table key = indexProxy , value = index Source
if(order == QtCore.Qt.AscendingOrder):
unlinkIndex = 0
else:
unlinkIndex = master_source_model.rowCount()
for slaveSourceRow in range(slave_source_model.rowCount()):
# this line is link to one in master, so we keep the same
# proxy number
master_source_row = map_slave_to_master[slaveSourceRow]
if(master_source_row != -1):
master_source_index = master_source_model.index(
master_source_row, 0)
master_proxy = master_proxy_model.mapFromSource(
master_source_index)
master_proxy2 = master_proxy_model2.mapFromSource(
master_proxy)
slaveProxyRow = master_proxy2.row() # same as master
else:
slaveProxyRow = unlinkIndex # we put it at the end or begining depending on order
unlinkIndex += 1
slaveMapping[slaveProxyRow] = slaveSourceRow
slave_proxy_model.layoutAboutToBeChanged.emit()
slave_proxy_model.setMap(slaveMapping)
slave_proxy_model2.setSortRole(
QtCore.Qt.InitialSortOrderRole) # proxy 2 is reinitialise
slave_proxy_model.layoutChanged.emit()
I am using PySide, with a tableview and a model as shown below:
# -*- coding: utf-8 -*-
from PySide import QtCore, QtGui
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, items, headers, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.items = items
self.headers = headers
def rowCount(self, parent):
return len(self.items)
def columnCount(self, parent):
return len(self.items[0])
def data(self, index, role):
if not index.isValid():
return None
elif role != QtCore.Qt.DisplayRole:
return None
return self.items[index.row()][index.column()]
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.headers[section]
return None
This is how I set the model, importing from models.py:
table_model = models.TableModel(data, headers)
self.tableView.setModel(table_model)
I get the tableview total rows:
table_model = tableview.model()
total_rows = table_model.rowCount(tableview)
If there is no data to display in the tableview, I set data = [[]].
If I try to get the rowCount, I get 1 instead of O.
I realize it probably counts the nested list set as data.
So, what should I do to get the right rou count?
Use data = [] when there's no data, and then implement columnCount like this:
def columnCount(self, parent):
return len(self.items[0]) if self.items else 0