Row, deleted from model, stays in view, what am I doing wrong?
Asked Answered
I

4

6

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?

Illuminometer answered 10/11, 2010 at 16:27 Comment(0)
I
1

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);
}
Illuminometer answered 12/11, 2010 at 15:1 Comment(0)
M
2

I faced the same problem recently and found another solution for myself. You may use QTableView.setRowHidden() method after QSqlTableModel.deleteRow() if you have only one QTableView connected to this model. Works fine.

(I would prefere to strike out text in custom paint delegate... but I failed to find suitable flag to distinguish non-coommited rows.)

Molluscoid answered 25/1, 2020 at 8:9 Comment(0)
E
1

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 !

Earvin answered 11/11, 2010 at 12:29 Comment(3)
Or beginRemoveRows and endRemoveRows.Brundisium
QSqlRelationalTableModel already have remove Rows, properly implemented with beginRemoveRows and endRemoveRows.Illuminometer
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 шьздуьутеув the workaround and will post it here later.Illuminometer
I
1

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);
}
Illuminometer answered 12/11, 2010 at 15:1 Comment(0)
S
0

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.

Stateroom answered 12/12, 2013 at 9:31 Comment(1)
RemoveRow method for SqTableModel does not remove it immediately. It marks a row for deletion, and then delete it on commit(). I tried to change this behaviour.Illuminometer

© 2022 - 2024 — McMap. All rights reserved.