[SOLVED]which widget would be the best to display the following data?
-
It sounds like that you don't have to bother with users editing the data, as your data is for display purposes and is updated by your backend. That makes implementing the model simpler.
What is important to remember, is that you try to be as specific as possible in signalling what data has changed. Only then, the view can optimize properly what part of the view needs to be withdrawn. -
Hi phamtv,
sorry, no code snippets here. It's closed source. But It's not difficult. I suggest to first implement the model displaying the data without updates from a background thread. Think of the data structures to display and display them (in a tree view, table view, however). When this is working, implement the collector thread and create the data tree there.
What is then missing is just: compare old tree with new tree, build diff data and send it via signal/slot to the model.
-
My tip: try to stick to a simple model at first, a list or a table. Don't start your first model implementation with a tree model. They are just plain hard. For a table model, you can start with subclassing QAbstractTableModel, which simplifies the interface for QAbstractItemModel for the table case.
-
phamtv,
Qt 4.7.1 comes with a neat tutorial which shows also how to deal with changing models.
http://doc.qt.nokia.com/4.7/modelview.htmlinteresting for you: 2.3, a clock inside a table cell.
-
dialingo/Andre/Gerolf,
I have the following code implementation, however, when I run the application, the tableview controls is shown but does not populate with the rows and columns data... Any idea what is going on? NOTE: I simplified it into 3 files...
@
// File: cls_device.h
#ifndef CLS_DEVICE_H
#define CLS_DEVICE_H#include <QtGui>
#include <QObject>
#include <QThread>
#include <QMap>
#include <QtDebug>struct RegData
{
QString Desc;
int Addr;
double Value;
};class RegisterModel : public QAbstractTableModel
{
Q_OBJECT
public:
RegisterModel(){}void setCurrencyMap(const QMap<QString, double> &map) { registerMap = map; reset(); } int rowCount(const QModelIndex & /* parent */) const { qDebug() << registerMap.count(); return registerMap.count(); } int columnCount(const QModelIndex & /* parent */) const { return 2; //registerMap.count(); } Qt::ItemFlags flags(const QModelIndex & index) const { if (index.column() == 0) return Qt::ItemIsSelectable | Qt::ItemIsEnabled ; else return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ; } bool setData(const QModelIndex & index, const QVariant & value, int role) { if (index.isValid() && role == Qt::EditRole) { ValueAt(index.row()) = value.toString(); } return true; } QVariant data(const QModelIndex &index, int role) const { // NOTE: int role == Qt.ItemDataRole if (!index.isValid()) return QVariant(); switch(role) { case Qt::TextAlignmentRole: if (index.column() == 0) return int(Qt::AlignLeft | Qt::AlignVCenter); else if (index.column() == 1) return int(Qt::AlignHCenter | Qt::AlignVCenter); else return QVariant(); break; case Qt::DisplayRole: if (index.column() == 0) return DescAt(index.row()); else if (index.column() == 1) return ValueAt(index.row()); else return QVariant(); break; } return QVariant(); } QVariant headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) return (section == 0) ? "Register" : "Value"; else DescAt(section); }
private:
QString DescAt(int offset) const
{
return (registerMap.begin() + offset).key();
}QString ValueAt(int offset) const { return QString("%1").arg((registerMap.begin() + offset).value()); } QMap<QString, double> registerMap;
};
class cls_Device : public QThread
{
Q_OBJECTpublic:
RegData* data[10];cls_Device() { for (int i = 0; i < 10; i++) { // example if (i == 5) { data[i] = NULL; continue; } data[i] = new RegData(); data[i]->Desc = QString("Register %1").arg(i); qDebug() << data[i]->Desc; data[i]->Addr = i; data[i]->Value = 1; } } ~cls_Device(){} // destructor void run() { Read(); } void Read() { for (int i = 0; i < 10; i++) { } } void Write() { }
signals:
public slots:
};
#endif // CLS_DEVICE_H
// file: mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTableView>
#include <QLayout>
#include <cls_device.h>
#include <currencymodel.h>//namespace Ui {
// class MainWindow;
//}class MainWindow : public QMainWindow
{
Q_OBJECTpublic:
QMap<QString, double> registerMap;
cls_Device* device;explicit MainWindow(QWidget *parent = 0) { device = new cls_Device(); for (int i = 0; i < 10; i++) { if (device->data[i] != NULL) registerMap.insert(device->data[i]->Desc, device->data[i]->Value); } RegisterModel registerModel; registerModel.setCurrencyMap(registerMap); QTableView* tableView = new QTableView(); tableView->setModel(®isterModel); tableView->setAlternatingRowColors(true); tableView->verticalHeader()->hide(); tableView->horizontalHeader()->setStretchLastSection(true); tableView->resizeColumnsToContents(); tableView->setSelectionBehavior(QAbstractItemView::SelectRows); setCentralWidget(tableView); QTimer* timer = new QTimer(this); timer->setInterval(1000); connect(timer, SIGNAL(timeout()) , this, SLOT(timerHit())); timer->start(); } ~MainWindow() { disconnect(); if (device != NULL) delete device; }
public slots:
void timerHit()
{
}};
#endif // MAINWINDOW_H// File: main.cpp
#include <QObject>
#include <QtGui>
#include <mainwindow.h>int main(int argc, char *argv[])
{
QApplication app(argc, argv);MainWindow window; window.showMaximized(); return app.exec();
}
@ -
Hi,
the problem is pretty easy:
you create the register model on the stack
set it to the view and then, when the c'tor goes out of scope, the model is deleted.create the reister model on the heap as follows:
@
explicit MainWindow(QWidget *parent = 0) { device = new cls_Device(); for (int i = 0; i < 10; i++) { if (device->data[i] != NULL) registerMap.insert(device->data[i]->Desc, device->data[i]->Value); } RegisterModel* registerModel = new RegisterModel(this); registerModel->setCurrencyMap(registerMap); QTableView* tableView = new QTableView(); tableView->setModel(registerModel); tableView->setAlternatingRowColors(true); tableView->verticalHeader()->hide(); tableView->horizontalHeader()->setStretchLastSection(true); tableView->resizeColumnsToContents(); tableView->setSelectionBehavior(QAbstractItemView::SelectRows); setCentralWidget(tableView); QTimer* timer = new QTimer(this); timer->setInterval(1000); connect(timer, SIGNAL(timeout()) , this, SLOT(timerHit())); timer->start(); }
@
aditionally, the creator of RegisterModel should look like this:
@
RegisterModel(QObject* pParent) :
QAbstractTableModel(pParent)
{
}@
-
Thanks guys for your support. I have a follow up question to my model/view implementation. I added some code to spawn another thread to generate some random number, however, I don´t know how to update the table view when I receive the signal to update the table view. Can you guys give me some input? Thanks!
@
// file: cls_device.h
#ifndef CLS_DEVICE_H
#define CLS_DEVICE_H#include <QtGui>
#include <QObject>
#include <QThread>
#include <QMap>
#include <QtDebug>struct RegData
{
QString Desc;
int Addr;
double Value;
};class RegisterModel : public QAbstractTableModel
{
Q_OBJECT
public:
RegisterModel(QObject* pParent) :
QAbstractTableModel(pParent){}void setCurrencyMap(const QMap<QString, double> &map) { registerMap = map; reset(); } int rowCount(const QModelIndex & /* parent */) const { return registerMap.count(); } int columnCount(const QModelIndex & /* parent */) const { return 2; //registerMap.count(); } Qt::ItemFlags flags(const QModelIndex & index) const { if (index.column() == 0) return Qt::ItemIsSelectable | Qt::ItemIsEnabled ; else return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ; } bool setData(const QModelIndex & index, const QVariant & value, int role) { if (index.isValid() && role == Qt::EditRole) { ValueAt(index.row()) = value.toString(); } return true; } QVariant data(const QModelIndex &index, int role) const { // NOTE: int role == Qt.ItemDataRole if (!index.isValid()) return QVariant(); switch(role) { case Qt::TextAlignmentRole: if (index.column() == 0) return int(Qt::AlignLeft | Qt::AlignVCenter); else if (index.column() == 1) return int(Qt::AlignHCenter | Qt::AlignVCenter); else return QVariant(); break; case Qt::DisplayRole: if (index.column() == 0) return DescAt(index.row()); else if (index.column() == 1) return ValueAt(index.row()); else return QVariant(); break; } return QVariant(); } QVariant headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) return (section == 0) ? "Register" : "Value"; else DescAt(section); }
private:
QString DescAt(int offset) const
{
return (registerMap.begin() + offset).key();
}QString ValueAt(int offset) const { return QString("%1").arg((registerMap.begin() + offset).value()); } QMap<QString, double> registerMap;
};
class cls_Device : public QThread
{
Q_OBJECTpublic:
RegData* data[10];cls_Device() { for (int i = 0; i < 10; i++) { // example if (i == 5) { data[i] = NULL; continue; } data[i] = new RegData(); data[i]->Desc = QString("Register %1").arg(i); data[i]->Addr = i; data[i]->Value = 1; } } ~cls_Device(){} // destructor void run() { Read(); } void Read() { int min = 0; int max = 255; for (int i = 0; i < 10; i++) { double val = int( random() / (RAND_MAX + 1.0) * (max + 1 - min) + min ); if ((data[i] != NULL) && (data[i]->Value != val)) { data[i]->Value = val; emit RegisterValueChanged(this, data[i]); } } } void Write() { }
signals:
void RegisterValueChanged(QObject* obj, RegData* e);
public slots:};
#endif // CLS_DEVICE_H
// file: mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTableView>
#include <QLayout>
#include <cls_device.h>
#include <currencymodel.h>class MainWindow : public QMainWindow
{
Q_OBJECTpublic:
QMap<QString, double> registerMap;
cls_Device* device;explicit MainWindow(QWidget *parent = 0) { device = new cls_Device(); connect(device, SIGNAL(RegisterValueChanged(QObject*,RegData*)), this, SLOT(RegisterValueChanged(QObject*,RegData*)), Qt::QueuedConnection); for (int i = 0; i < 10; i++) { if (device->data[i] != NULL) registerMap.insert(device->data[i]->Desc, device->data[i]->Value); } RegisterModel* registerModel = new RegisterModel(this); registerModel->setCurrencyMap(registerMap); QTableView* tableView = new QTableView(this); tableView->setModel(registerModel); tableView->setAlternatingRowColors(true); tableView->verticalHeader()->hide(); tableView->horizontalHeader()->setStretchLastSection(true); tableView->resizeColumnsToContents(); tableView->setSelectionBehavior(QAbstractItemView::SelectRows); setCentralWidget(tableView); QTimer* timer = new QTimer(this); timer->setInterval(1000); connect(timer, SIGNAL(timeout()) , this, SLOT(timerHit())); timer->start(); } ~MainWindow() { disconnect(); if (device != NULL) delete device; }
public slots:
void timerHit()
{
if (!device->isRunning())
device->start();
}private slots:
void RegisterValueChanged(QObject* obj, RegData* e)
{
qDebug() << QString("How do I update the table view for Register address %1 with value %2").arg(e->Addr).arg(e->Value);
}
};#endif // MAINWINDOW_H
// file main.cpp
//use the previous code snippet@
-
You should start reading the "docs":http://doc.qt.nokia.com/4.7/modelview.html :-)
When you want to change a value, you have to emit a signal dataChange() and change the value before.
If you want to add/remnove rows/columns, there are also signals, which are emitted by special functions[begin|end][insert|remove][Columns|rows]
-
Hi Gerolf, Thanks!, :-) I have been reading the docs but just can´t seem to connect things together. As you can see in my implementation, I have an inherited QThread class that initializes my structure pointer array. If you have noticed, I intentionally place a null pointer to register 5 because I do not have a continguous set of register in my real world scenario. QMap basically is a subset of my structure pointer array without the null pointer. I can populate the table view with RegisterModel using QMap. However, as you can see, when my thread executes, it changes the structure array value. Do I have to loop through the QMap items, compare the description strings, and then update TableView by emitting a signal dataChange()? This seem time consuming if I have loop through the QMaps items for every registers that changes.... Please advise...