Как принудительно обновить измененные ячейки с помощью Qt Custom TableModel (производной от QAbstractTableModel)? - PullRequest
0 голосов
/ 31 августа 2018

Я новичок в Qt. Я пытаюсь реализовать пользовательскую модель таблицы:

У меня есть класс DataSource, который содержит все мои данные и запускает события (традиционные, а не Qt signa / slots), когда строки изменяются или когда в одной строке изменяются только некоторые значения. Вот упрощенная версия:

class DataSource{
  public:
  int size();
  int nbColumns();
  const std::string& data(int row, int col);
  bool hasChanges();
};

Я реализую TableModel в качестве оболочки моего источника данных:

class MyTableModel : public QAbstractTableModel {
  public:

  int rowCount(const QModelIndex& parent) const {
      return datasource->size();
  }

  int columnCount(const QModelIndex& parent) const {
    return datasource->nbColumns();
  }

  QVariant data(const QModelIndex& index, int role) const {
    return QVariant(datasource.data(index.row(), index.column());
  }
};

Я обновляю свою таблицу по таймеру (слишком много обновлений в моем приложении):

void timerRefresh(){
  if(datasource->hasChanges()){
    ui->tableView->repaint();
    ui->tableView->update();
  }
}

Это не работает (я ожидал, что представление будет запрашивать модель при перерисовке, но, возможно, мне следует вызвать другой метод?)

Итак, я добавил это прямо перед вызовом перекрасить:

tableModel->update();

, который определяется следующим образом:

void MyTableModel::update()
{
  beginResetModel();
  endResetModel();
  //doesn't work: emit dataChanged(createIndex(0,0), createIndex(DataSource->size(), DataSource->nbColumns()));
}

Это работает, но если изменить только одну ячейку, я думаю, должно быть что-то более эффективное, чем сброс всей модели? А почему не сработал emit DataChanged? В документации неясно, какая функция вызывается фреймворком Qt и какую функцию я должен вызывать явно ...

РЕДАКТИРОВАТЬ: Благодаря комментарию ниже, я понял, как это работает. Мне не нужно ничего вызывать на самом QTableView, я просто обновляю свою модель и вызываю
beginResetModel (); ... endResetModel ();

или просто когда менялись только ячейки: emit dataChanged (...);

1 Ответ

0 голосов
/ 14 сентября 2018

Версии Qt все время меняются. Переход с одной версии на другую тривиален. Планирование вашего проекта очень важно. Qt Виджеты, которые вы выбираете для своего пользовательского интерфейса (ui), импортируются. Qt Widgets передает сигналы, вы можете подключиться к этим сигналам с помощью слотов. В зависимости от того, что вы хотите сделать с вашим проектом. При реализации некоторых функций важно вызывать соответствующие функции, чтобы все связанные представления были осведомлены о любых изменениях. QAbstractTableModel может быть полезен для разработчиков, которые хотят реализовать свои собственные модели таблиц или пользовательских делегатов. Существует теория, лежащая в основе MVC (модель, представление и контроллер). Внедрение MVC решит проблему. Есть много стратегий, которые вы можете использовать для его реализации. Отправной точкой является планирование. Вы можете использовать класс DataSource и класс MyTableModel в качестве моделей. Чего не хватает, так это определения видов и контроллеров. Ваши «#include» также важны в Qt. QAbstractTableModel предоставляет стандартный MV API для доступа к данным. Мы можем использовать QtableView для просмотра данных с использованием разных представлений. Основные функции, которые должны быть реализованы: rowCount (), columnCount (), data (), headerData (). Чтобы TableModel был редактируемым, он должен предоставлять реализации функций insertRows (), removeRows (), setData () и flags ().


  1. Demo Custom Table.

enter image description here


  1. main.cpp

//
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();

return a.exec();
}
//

  1. widget.h

//
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTableView>
#include <QListView>
#include <QLabel>
#include <QLineEdit>
#include <QTextBrowser>
#include <QDateEdit>
#include <QString>
//namespace Ui {
//class Widget;
//}
class MyTableModel;
class Widget : public QWidget
{
Q_OBJECT
public:
//explicit Widget(QObject t, QWidget *parent = 0);
Widget(QWidget *parent = 0);
~Widget();
public slots:
void updateView();
private:
//Ui::Widget *ui;
QTableView *tableView;
MyTableModel *tableModel;
QLineEdit *ledMessage;
QLabel *lblMessage;
QTextBrowser *logs;
QDateEdit *dateEdit;
};
#endif // WIDGET_H
//

  1. widget.cpp

//
#include <QtGui>
#include "mytablemodel.h"
#include "widget.h"
//#include "ui_widget.h"
#include <QGridLayout>

Widget::Widget(QWidget *parent) :
QWidget(parent)/*,
ui(new Ui::Widget*)*/
{
//ui->setupUi(this);
tableModel = new MyTableModel(this);
tableView = new QTableView;
tableView->setModel(tableModel);
lblMessage = new QLabel(tr("&Message:"));
ledMessage = new QLineEdit;
lblMessage->setBuddy(ledMessage);
dateEdit = new QDateEdit(QDate::currentDate());
logs = new QTextBrowser;
logs->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));

QGridLayout *layout = new QGridLayout;
layout->addWidget(lblMessage, 0, 0);
layout->addWidget(ledMessage, 0, 1);
layout->addWidget(dateEdit, 0, 2);
layout->addWidget(tableView, 1, 0, 1, 2);
layout->addWidget(logs, 2, 0, 1, 2);

setLayout(layout);
setWindowTitle(tr("Demo Custom Table."));
//connect(ledMessage, SIGNAL(textChanged(const QString &)),
//             tableModel, SLOT(newMessage(const QString &)));
//connect(tableModel, SIGNAL(updateMode()),
//             this, SLOT(updateView()));
connect(ledMessage, SIGNAL(editingFinished()), this, SLOT(updateView()));
}

Widget::~Widget()
{
//delete ui;
}

void Widget::updateView()
{
QModelIndex p = tableView->currentIndex();
tableModel->update(p,ledMessage->text(), dateEdit->text());
logs->append(tr("%1 : ").arg(p.row()));
logs->append(ledMessage->text());
}
//

  1. mytablemodel.h

//
#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H

#include <QAbstractTableModel>
#include <QLabel>
//#include <QDateEdit>
#include <QDateTime>
#include "datasource.h"

class MyTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
//explicit MyTableModel(QObject t, QObject t2,QObject *parent = 0);
MyTableModel(QObject *parent = 0);
MyTableModel(QList< QPair<QString,QString> > t, QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int part, Qt::Orientation orient, int role) const;
void update(const QModelIndex &parent,const QString &mes, const QString &date);
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex &index) const;
bool insertRows(int position, int rows, const QModelIndex &index=QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index=QModelIndex());

signals:
void updateMod();

public slots:
void newMessage(const QString &mes);

protected:
void update(const QModelIndex &parent);

private:
DataSource *datasource;
QPair<QString,QString> message;
QList< QPair<QString,QString> > messageList;
QList<QString> dList;
int listSize;
int col;
QLabel *lblNumber;
QDateTime *dTime;
//QDateEdit *dateEdit;
};

#endif // MYTABLEMODEL_H
//

  1. mytablemodel.cpp

//    
#include "mytablemodel.h"
#include <QApplication>

MyTableModel::MyTableModel(QObject *parent) :
QAbstractTableModel(parent)
{
datasource = new DataSource();
lblNumber = new QLabel;
dTime = new QDateTime();
//dateEdit = new QDateEdit(QDate::currentDate());
col = datasource->nbColumns();
int l = datasource->size();
for(int i = 0; i < l; i++)
{
    message.first = datasource->data(i,0);
    message.second = datasource->data(i,1);
    messageList.append(message);
    dList.append(dTime->currentDateTime().toString());
}
listSize = messageList.size();

}

MyTableModel::MyTableModel(QList< QPair<QString,QString> > t, QObject *parent) :
QAbstractTableModel(parent)
{
messageList = t;
datasource = new DataSource();
listSize = t.length();
col = datasource->nbColumns();
}

void MyTableModel::update(const QModelIndex &,const QString &mes, const QString &d)
{
int first = messageList.count();
int last = first + 1;
first = last;
lblNumber->setText(tr("%1").arg(first));
beginInsertRows(QModelIndex(),first,last);
message.first = lblNumber->text();
message.second = mes;
messageList.append(message);
dList.append(d);
listSize++;
endInsertRows();
datasource->hasChanges();
}

int MyTableModel::rowCount(const QModelIndex &p) const
{
Q_UNUSED(p);
return listSize;
}

int MyTableModel::columnCount(const QModelIndex &p) const
{
Q_UNUSED(p);
return col;
}

QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
int c = 0;
if (!index.isValid())
{
   return QVariant();
}
if(index.row() < 0)
{
   return QVariant();
}
if (role == Qt::DisplayRole)
{
    c = index.column();
    if(c == 0)
    {
       return messageList.at(index.row()).first;
    }
    else if (c == 1)
    {
       return messageList.at(index.row()).second;
    }

    return dList.at(index.row());
}

return QVariant();
}

void MyTableModel::update(const QModelIndex &p)
{
Q_UNUSED(p);
emit updateMod();
}

void MyTableModel::newMessage(const QString &mes)
{
listSize++;
message.first = lblNumber->text();
message.second = mes;
messageList.append(message);
dList.append(dTime->currentDateTime().toString());
}

bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
QString d = dTime->currentDateTime().toString();
if (index.isValid() && role == Qt::EditRole)
{
    int row = index.row();

    message = messageList.value(row);
    if (index.column() == 0)
    message.first = value.toString();
    else if (index.column() == 1)
    message.second = value.toString();
    else
    return false;

    messageList.replace(row, message);
    dList.replace(row,d);
    emit(dataChanged(index, index));

     return true;
 }

 return false;
}

bool MyTableModel::insertRows(int post, int rows, const QModelIndex &p)
{
 Q_UNUSED(p);
 beginInsertRows(QModelIndex(), post, post+rows-1);
 for (int row=0; row < rows; row++)
 {
     message.first = " ";
     message.second = " ";
     messageList.insert(post, message);
     dList.insert(post, " ");
 }
 endInsertRows();
 return true;
}

bool MyTableModel::removeRows(int post, int rows, const QModelIndex &p)
{
 Q_UNUSED(p);
 beginRemoveRows(QModelIndex(), post, post+rows-1);
 for (int row=0; row < rows; ++row)
 {
     messageList.removeAt(post);
     dList.removeAt(post);
 }
 endRemoveRows();
 return true;
}

Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
 if (!index.isValid())
     return Qt::ItemIsEnabled;

 return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}

QVariant MyTableModel::headerData(int part, Qt::Orientation orient, int role) const
{
 if (role != Qt::DisplayRole)
     return QVariant();

 if (orient == Qt::Horizontal)
 {
     switch (part)
     {
         case 0:
             return tr("Number");

         case 1:
             return tr("Message");

         case 2:
             return tr("Date");

         default:
             return QVariant();
     }
 }
 return QVariant();
}
//

  1. datasource.h

//
#include <QString>
#include <QVariant>
#include <QList>
#include <QPair>

class DataSource
{
public:
DataSource();
int size();
int nbColumns();
const QString& data(int row, int col);
bool hasChanges();

private:
int beginRow;
int columns;
QPair<QString,QString> message;
QList< QPair<QString,QString> > messages;
};

#endif // DATASOURCE_H
//

  1. datasource.cpp

//
#include "datasource.h"

DataSource::DataSource()
{
beginRow = 1;
columns = 3;
message.first = "One";
message.second = "Testing123";
messages.push_back(message);
message.first = "Two";
message.second = "QObject";
messages.push_back(message);
message.first = "Three";
message.second = "QWidget";
messages.push_back(message);
}

int DataSource::size()
{
return messages.length();
}

int DataSource::nbColumns()
{
return columns;
}

const QString& DataSource::data(int r, int c)
{
int l = messages.length();
if(r < l)
{
    message = messages.at(r);
    if (c == 0)
    {
        return message.first;
    }
    else if (c == 1)
    {
       return message.second;
    }
}
return "Testing123";
}


bool DataSource::hasChanges()
{
beginRow++;
return true;
}
//

  1. customtab.pro

//
#-------------------------------------------------
#
# Project created by QtCreator 2018-09-10T15:39:01
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = customtab
TEMPLATE = app


SOURCES += main.cpp\
    widget.cpp \
datasource.cpp \
mytablemodel.cpp

HEADERS  += widget.h \
datasource.h \
mytablemodel.h

#//FORMS    += widget.ui
//

  1. НАСЛАЖДАЙТЕСЬ.

Если вы новичок в Qt. Qt использует C ++. Он реализует графический интерфейс. Большинство приложений C ++ использует консольные приложения. Qt использует шаблоны проектирования и фреймворки. Наслаждайтесь!


...