Question

I have QTableView, filled by QSqlRelationalTableModel. Changes should be committed or reverted on button hit. When I edit some row, it changes state in the view when editing finishes, and succesfully commits changes to DB when submitAll() called. But when I trying to delete row, it stays in view. Here is slot, connected to Remove button:

def _removeSelectedStatuses(self):
    '''
    Удаляет выбранные строки из таблицы

    pre[self]: self._model is not None
    '''
    model = self.ConservationStatusesTableView.selectionModel()
    l = model.selectedRows()
    if not len(l): return

    rows = set([i.row() for i in l])
    rows = list(rows)
    rows.sort()
    first = rows[0]
    count = len(rows)
    self._model.removeRows(first, count)

What am I doing wrong?

Was it helpful?

Solution

I investigated, that this nasty behaviour is by design. Rows are deleted from model on commit, and no views know, which rows must be drawn and which aren't. Only thing done when rows removed from model is '!' marker in header.model().headerData(index, vert).text(). And it's disgusting.

I'm ashamed the way I fixed the problem, but here is my ugly hack:

from PyQt4 import QtGui
from PyQt4 import QtSql
from PyQt4 import QtCore

class SqlTableView(QtGui.QTableView):
    '''
    Представление, которое не показывает удалённые столбцы, 
    когда коммит ещё не прошёл
    '''


    def __init__(self, parent = None):
        '''
        Конструктор
        '''
        QtGui.QTableView.__init__(self, parent)

    def setModel(self, model):
        '''
        Мы не можем соединиться с моделями, не являющимися QSqlTableModel
        '''
        assert isinstance(model, QtSql.QSqlTableModel)
        QtGui.QTableView.setModel(self, model)

    def paintEvent(self, event):
        '''
        Тут всё и происходит. Осторожно, может стошнить.
        '''
        if self.model() is not None:
            header = self.verticalHeader()
            hm = header.model()
            for i in range(hm.rowCount()):
                if (hm.headerData(i, QtCore.Qt.Vertical).toPyObject() == '!' 
                    and not header.isSectionHidden(i)):
                    header.hideSection(i)
                elif (header.isSectionHidden(i) and 
                    hm.headerData(i, QtCore.Qt.Vertical).toPyObject() != '!'):
                    header.showSection(i)
        PyQt4.QtGui.QTableView.paintEvent(self, event)

I also added it to QtDesigner to simplify interface design.

Second solution, not so nasty:

class PSqlRelationalTableModel : public QSqlRelationalTableModel
{
    Q_OBJECT

public:
    explicit PSqlRelationalTableModel(QObject *parent = 0, 
        QSqlDatabase db = QSqlDatabase());
    virtual ~PSqlRelationalTableModel();

    bool removeRows(int row, int count, 
        const QModelIndex &parent = QModelIndex());

public slots:
    void revertRow(int row);

signals:
    void rowIsMarkedForDeletion(int index);
    void rowDeletionMarkRemoved(int index);

private:
    QSet<unsigned int> rowsToDelete;
};

//////////////////////////////////////////////////////////////////
void PTableView::setModel(PSqlRelationalTableModel *model)
{
    connect(model, SIGNAL(rowIsMarkedForDeletion(int)), 
        this, SLOT(onRowMarkedForDeletion(int)));
    connect(model, SIGNAL(rowDeletionMarkRemoved(int)), 
        this, SLOT(onRowDeletionMarkRemoved(int)));
    QTableView::setModel(model);
}

void PTableView::onRowMarkedForDeletion(int index)
{
    QHeaderView *hv = verticalHeader();
    hv->hideSection(index);
}

void PTableView::onRowDeletionMarkRemoved(int index)
{
    QHeaderView *hv = verticalHeader();
    hv->showSection(index);
}

OTHER TIPS

Did you implement the removeRows method ?

Have a look here :

pyqt: Trying to understand insertrows for QAbstractDataModel and QTreeView

I guess what is missing is simply a emitDataChanged that tells the view that something changed ! Without that, the view cannot know if it has to refresh itself !

Hope this helps !

If you want to remove the selected row from a model you just need to call:model->removeRow(row); Here row is the row number of which you want to delete. This works fine for me.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top